Compare commits
517 Commits
v3.7.6
...
v4.0.1-bug
| Author | SHA1 | Date | |
|---|---|---|---|
| e1fc23a1bb | |||
| 2d551a3f73 | |||
| 9b205366f7 | |||
| 4727f22b0f | |||
| a3d693ddc1 | |||
| 79cbb44d51 | |||
| dcde3db33a | |||
| 44fbc7a182 | |||
| d7a85edb76 | |||
| ceb9dc6707 | |||
| b0f20ee017 | |||
| cdf78f2bc5 | |||
| d3f0a8fe4a | |||
| 17acf1bd86 | |||
| 31a0bb24c7 | |||
| 13ff1f4343 | |||
| ec76be9d5e | |||
| 84d53616a0 | |||
| 2758216ae2 | |||
| c492f066ca | |||
| 897478e30e | |||
| a94d825ead | |||
| ba05e6137e | |||
| dc55342343 | |||
| 3ea96d27db | |||
| 48842c099b | |||
| 4f87cac46a | |||
| 52680b63b9 | |||
| f89defc78f | |||
| 46e9a161a4 | |||
| 8e602e8169 | |||
| c60df98577 | |||
| ad7db50336 | |||
| e40ef5292e | |||
| 94b844788c | |||
| 9c222baf91 | |||
| 0794606e57 | |||
| be34c486bf | |||
| fa7a5fef9b | |||
| 9e518d5414 | |||
| 2d17ecd438 | |||
| 56d6c28811 | |||
| 86b77e29e2 | |||
| 23b8b09834 | |||
| 30eb397c66 | |||
| 05bcc0f818 | |||
| 3f9434239f | |||
| 4caf7eabc0 | |||
| 0aee08bbb0 | |||
| 602dbff3c8 | |||
| d52cfd475f | |||
| 4512accb37 | |||
| 2d57c00149 | |||
| cae908da6a | |||
| 38f97673b8 | |||
| ed2bf89413 | |||
| 259d343e31 | |||
| 8e9dbafb4d | |||
| 7e737d7a46 | |||
| 50fd30d173 | |||
| 4c7310f71e | |||
| b3e7168922 | |||
| 261cc5b0eb | |||
| 60218eea97 | |||
| bc1945326a | |||
| 2fd12d56ed | |||
| cb28838b40 | |||
| 4cfd6952c0 | |||
| f5761e378e | |||
| 314144c384 | |||
| 81debc1cd8 | |||
| 9a2baf1d8c | |||
| 55ec26bd3d | |||
| 4c372eeb2b | |||
| c2f9e28edd | |||
| 65a8297fe0 | |||
| 1a48cca197 | |||
| 24984b5d67 | |||
| 619660e5e5 | |||
| c9c4a996dc | |||
| b8ae8b68dc | |||
| a9f4620d8b | |||
| 66abdb1eed | |||
| 1dc4be2d54 | |||
| a286f51801 | |||
| 27708da1bf | |||
| 3aa0e19b0f | |||
| 4d5627348a | |||
| 2aefeb5e53 | |||
| 728b663c7a | |||
| 36156cddc4 | |||
| 2b98702ceb | |||
| 2c2ab02398 | |||
| 8ae8c6d6fb | |||
| 1171424f19 | |||
| 3444922861 | |||
| 5821a519ee | |||
| 439a2353b0 | |||
| 8f8f3193dd | |||
| 5907b8b1a5 | |||
| ba5783417e | |||
| 56573deea1 | |||
| 892933888c | |||
| b3725baad0 | |||
| cf79735780 | |||
| ad059fe18d | |||
| 03fa2052da | |||
| d7d55b7341 | |||
| 6ea9a7de90 | |||
| 3bc65f42c7 | |||
| 854227304f | |||
| 62d34c6c06 | |||
| 69f0beff9c | |||
| 2a32859c48 | |||
| c6bdb4ee8d | |||
| cf5e981758 | |||
| 2bf246562b | |||
| c1cd25b89b | |||
| 2eb259055a | |||
| fe743590c9 | |||
| d3de1c238c | |||
| 931593d726 | |||
| 8f002fc804 | |||
| 10e0d0123e | |||
| b93cb06662 | |||
| 5471099e7b | |||
| 5a2f86be87 | |||
| 3d7ce17b07 | |||
| cf0e237529 | |||
| 60c84153e5 | |||
| f9a8efe084 | |||
| 6b48c546c4 | |||
| b1893718f6 | |||
| 7ad95d5f26 | |||
| 778b0e8f16 | |||
| ad847f6113 | |||
| 27778c2b31 | |||
| 484694cbdb | |||
| 969680a7ad | |||
| 6357e008dc | |||
| a570cfe32b | |||
| ccba561d47 | |||
| 0dcf86ce1c | |||
| 961d4ebb5c | |||
| 54b947504c | |||
| f5abd7e075 | |||
| 35cd97c751 | |||
| 147bc150f5 | |||
| ee7f102d34 | |||
| 7f0e59aba5 | |||
| 135b55f6f9 | |||
| 6986bd9a57 | |||
| 17782a500f | |||
| 64a64960ac | |||
| d4be850e68 | |||
| a6feb57f85 | |||
| 50fee01249 | |||
| 4ff12e91aa | |||
| 24b196f216 | |||
| 414064699a | |||
| d36b2f99ee | |||
| 704b73aef7 | |||
| 99878606d5 | |||
| e1cfccfcc0 | |||
| 4c2a46875d | |||
| 6ce64892e4 | |||
| 92e1c55913 | |||
| 72cfcd66b1 | |||
| 0cb65ab216 | |||
| d18731ccfe | |||
| b0a4aa66f6 | |||
| aad92e529e | |||
| 6e03e75110 | |||
| 91f79a5ff1 | |||
| cc53a1f3d6 | |||
| 25cd086298 | |||
| b27ecab969 | |||
| db1c0d954f | |||
| 03d20aed61 | |||
| d37929548e | |||
| 663891dd54 | |||
| 79d1c4fed4 | |||
| ecc8196701 | |||
| c2fb1a58db | |||
| 55998c3c4d | |||
| 83e44808cb | |||
| bfbc9900ed | |||
| 1735ba3312 | |||
| 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 | |||
| 0612582a46 | |||
| a3d0d91dd7 | |||
| 982e8f67bf | |||
| 0bb871bff2 | |||
| 0eb43d6552 | |||
| b4f29f4856 | |||
| 4fd68565d2 | |||
| 8241551438 | |||
| d7bb45f287 | |||
| dbb3078300 | |||
| a7d141efcc | |||
| 31c404021c | |||
| 631cec2fc6 | |||
| be6eab0cae | |||
| 71ee8bfd29 | |||
| 66c9c02dc6 | |||
| 7880ee4aef | |||
| 6274d1f778 | |||
| 5e98813f92 | |||
| 1a30800fb4 | |||
| 454e6933f4 | |||
| e3072071b7 | |||
| 83a88326f0 | |||
| 27109a810f | |||
| 3d5bd424c5 | |||
| c7cd56e7be | |||
| 860769c44a | |||
| a83761b88d | |||
| a79fc9c0b1 | |||
| 877bd63d9d | |||
| bb9d0582c4 | |||
| df7e89e7e4 | |||
| a12593ea51 | |||
| 5ca0af4285 | |||
| 89e72a0fea | |||
| fc25ad7ddb | |||
| 6b656cf446 |
@ -65,6 +65,8 @@ android {
|
||||
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
|
||||
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
|
||||
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
|
||||
buildConfigField "String", "LETO_APPID", "\"${LETO_APPID}\""
|
||||
buildConfigField "String", "TTAD_APPID", "\"${TTAD_APPID}\""
|
||||
|
||||
/**
|
||||
* Build Time 供区分 jenkins 打包时间用
|
||||
@ -121,8 +123,6 @@ android {
|
||||
publish {
|
||||
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}\""
|
||||
@ -139,8 +139,6 @@ android {
|
||||
versionNameSuffix "-debug"
|
||||
|
||||
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}\""
|
||||
@ -173,7 +171,7 @@ rebuildChannel {
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs/aars'
|
||||
dirs 'libs','libs/aars'
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,9 +195,6 @@ 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-viewmodel-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifeCycle"
|
||||
@ -208,7 +203,6 @@ dependencies {
|
||||
implementation "androidx.core:core-ktx:${ktx}"
|
||||
implementation "androidx.viewpager2:viewpager2:${viewpager2}"
|
||||
kapt "androidx.room:room-compiler:${room}"
|
||||
// kapt "androidx.databinding:databinding-compiler:${databinding}"
|
||||
|
||||
implementation "com.google.android.material:material:${material}"
|
||||
|
||||
@ -273,8 +267,6 @@ 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"
|
||||
|
||||
@ -301,9 +293,6 @@ dependencies {
|
||||
exclude group: 'com.squareup.okhttp3'
|
||||
})
|
||||
|
||||
debugImplementation "com.github.markzhai:blockcanary-android:$blockcanary"
|
||||
releaseImplementation "com.github.markzhai:blockcanary-no-op:$blockcanary"
|
||||
|
||||
implementation project(':libraries:LGLibrary')
|
||||
implementation project(':libraries:MTA')
|
||||
implementation project(':libraries:QQShare')
|
||||
|
||||
@ -240,4 +240,12 @@
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.utils.**
|
||||
-keep class tv.danmaku.ijk.** { *; }
|
||||
-dontwarn tv.danmaku.ijk.**
|
||||
-dontwarn tv.danmaku.ijk.**
|
||||
|
||||
#穿山甲
|
||||
-keep class com.bytedance.sdk.openadsdk.** { *; }
|
||||
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
|
||||
-keep class com.pgl.sys.ces.* {*;}
|
||||
|
||||
-keep class com.gyf.immersionbar.* {*;}
|
||||
-dontwarn com.gyf.immersionbar.**
|
||||
@ -4,7 +4,6 @@ import android.app.Application;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
||||
import com.gu.toolargetool.TooLargeTool;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
@ -30,7 +29,9 @@ public class Injection {
|
||||
|
||||
// init stetho
|
||||
Stetho.initializeWithDefaults(application);
|
||||
TooLargeTool.startLogging(application);
|
||||
|
||||
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
|
||||
// TooLargeTool.startLogging(application);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,15 @@
|
||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<!--可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告-->
|
||||
<!--请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息-->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<!-- 如果有视频相关的广告且使用textureView播放,请务必添加,否则黑屏 -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.shuyu.gsyvideoplayer,
|
||||
com.shuyu.gsyvideoplayer.lib,
|
||||
com.shuyu.gsyvideoplayer.armv7a,
|
||||
@ -44,7 +53,13 @@
|
||||
com.google.android.exoplayer2,
|
||||
tv.danmaku.ijk.media.exo2,
|
||||
shuyu.com.androidvideocache,
|
||||
pl.droidsonroids.gif" />
|
||||
pl.droidsonroids.gif,
|
||||
com.ledong.lib.minigame,
|
||||
com.ledong.lib.leto,
|
||||
com.leto.game.base.glide4,
|
||||
com.leto.game.ad.gdt,
|
||||
com.leto.game.fcm,
|
||||
com.leto.game.ad.toutiao" />
|
||||
|
||||
<!-- 去掉 SDK 一些流氓权限 -->
|
||||
<uses-permission
|
||||
@ -115,6 +130,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" />
|
||||
@ -157,6 +176,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:name="com.gh.gamecenter.MessageDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -187,6 +207,7 @@
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.CommentDetailActivity"
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
@ -397,6 +418,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:name="com.gh.gamecenter.gamedetail.rating.RatingReplyActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -458,8 +480,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 +502,18 @@
|
||||
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=".qa.answer.draft.AnswerDraftActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- 使用小米/华为推送弹窗功能提高推送成功率-->
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.PushProxyActivity"
|
||||
@ -575,6 +609,39 @@
|
||||
|
||||
<!--<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />-->
|
||||
|
||||
<!-- 梦工厂配置 开始 -->
|
||||
<!--<meta-data
|
||||
android:name="MGC_APPID"
|
||||
android:value="1001276" />
|
||||
|
||||
<provider
|
||||
android:name="com.leto.game.base.provider.LetoFileProvider"
|
||||
android:authorities="${applicationId}.leto.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/leto_file_path"
|
||||
tools:replace="android:resource" />
|
||||
</provider>-->
|
||||
<!-- 梦工厂配置 结束 -->
|
||||
|
||||
<!-- 穿山甲配置 开始 -->
|
||||
<!--<provider
|
||||
android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
|
||||
android:authorities="${applicationId}.TTFileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"
|
||||
android:authorities="${applicationId}.TTMultiProvider"
|
||||
android:exported="false" />-->
|
||||
<!-- 穿山甲配置 结束 -->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
File diff suppressed because one or more lines are too long
@ -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;
|
||||
@ -9,6 +11,7 @@ import android.text.TextUtils;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.gh.base.fragment.BaseFragment;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
@ -34,6 +37,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import butterknife.ButterKnife;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
@ -93,8 +97,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 +111,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();
|
||||
}
|
||||
@ -180,6 +184,11 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
super.onPause();
|
||||
if (isFinishing()) {
|
||||
onFinish();
|
||||
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
|
||||
if (fragment.isAdded() && fragment instanceof BaseFragment) {
|
||||
((BaseFragment) fragment).onParentActivityFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,13 +196,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 +254,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
|
||||
|
||||
@ -9,6 +9,7 @@ import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.widget.TextView
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
@ -16,6 +17,7 @@ import com.gh.common.view.RichEditor
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.qa.answer.edit.AnswerEditActivity
|
||||
import com.gh.gamecenter.qa.editor.GameActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
|
||||
@ -31,6 +33,7 @@ import kotterknife.bindView
|
||||
abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
|
||||
val mRichEditor by bindView<RichEditor>(R.id.rich_editor)
|
||||
val mDraftBtn by bindView<TextView>(R.id.draft_btn)
|
||||
|
||||
private val mEditorFont by bindView<CheckableImageView>(R.id.editor_font)
|
||||
private val mEditorLink by bindView<CheckableImageView>(R.id.editor_link)
|
||||
@ -87,6 +90,10 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
|
||||
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
|
||||
mRichEditor.setInputEnabled(true)
|
||||
|
||||
mDraftBtn.text = if (this is AnswerEditActivity) {
|
||||
"回答草稿"
|
||||
} else "文章草稿"
|
||||
}
|
||||
|
||||
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_paragraph,
|
||||
|
||||
@ -12,11 +12,9 @@ import android.preference.PreferenceManager
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.text.htmlEncode
|
||||
import com.gh.common.notifier.Notifier
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.common.util.StringUtils
|
||||
import com.gh.common.util.toObject
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.PushEntity
|
||||
import com.gh.gamecenter.entity.PushMessageEntity
|
||||
@ -132,7 +130,7 @@ class GHUmengNotificationService : UmengMessageService() {
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setTicker(pushData.body?.ticker)
|
||||
.setContentTitle(pushData.body?.title)
|
||||
.setContentText(pushData.body?.text)
|
||||
.setContentText(pushData.body?.text?.fromHtml())
|
||||
.setContentIntent(clickPendingIntent)
|
||||
.setDeleteIntent(deletePendingIntent)
|
||||
.build()
|
||||
|
||||
@ -3,7 +3,6 @@ package com.gh.base;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
@ -12,7 +11,6 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -52,6 +50,8 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Nullable
|
||||
private PackageViewModel mPackageViewModel;
|
||||
|
||||
protected RelativeLayout mToolbarContainer;
|
||||
|
||||
protected Toolbar mToolbar;
|
||||
|
||||
protected TextView mTitleTv;
|
||||
@ -62,7 +62,6 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setStatusBarDarkMode(true, this);
|
||||
initToolbar();
|
||||
|
||||
@ -88,6 +87,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
}
|
||||
|
||||
private void initToolbar() {
|
||||
mToolbarContainer = findViewById(R.id.normal_toolbar_container);
|
||||
mToolbar = findViewById(R.id.normal_toolbar);
|
||||
mTitleTv = findViewById(R.id.normal_title);
|
||||
if (mToolbar != null) {
|
||||
@ -253,4 +253,11 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
protected boolean showDownloadMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideToolbar(boolean isHide) {
|
||||
if (mToolbarContainer != null) {
|
||||
mToolbarContainer.setVisibility(isHide ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,14 +9,6 @@ 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;
|
||||
@ -37,6 +29,13 @@ 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 androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
@ -145,7 +144,10 @@ 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);
|
||||
|
||||
@ -301,6 +303,10 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return this;
|
||||
}
|
||||
|
||||
public void onParentActivityFinish() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RecyclerView.Adapter provideSyncAdapter() {
|
||||
return null;
|
||||
@ -309,4 +315,8 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
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
|
||||
|
||||
/**
|
||||
* 懒加载(支持多层嵌套)
|
||||
@ -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())
|
||||
|
||||
@ -4,21 +4,17 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.EntranceUtils.ENTRANCE_BROWSER
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.LibaoDetailActivity
|
||||
import com.gh.gamecenter.NewsDetailActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
|
||||
import com.gh.common.util.DirectUtils.directToGameVideo
|
||||
import com.gh.common.util.DirectUtils.directToVideoDetail
|
||||
import com.gh.gamecenter.*
|
||||
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
|
||||
import com.moor.imkf.requesturl.RequestUrl.type
|
||||
|
||||
|
||||
object DefaultWebViewUrlHandler {
|
||||
|
||||
@ -46,7 +42,7 @@ object DefaultWebViewUrlHandler {
|
||||
|
||||
"qq" -> try {
|
||||
DirectUtils.directToQqConversation(context, id)
|
||||
} catch (e: Exception) {
|
||||
} catch (e: Throwable) {
|
||||
Utils.toast(context, "请检查是否已经安装手机QQ")
|
||||
e.printStackTrace()
|
||||
}
|
||||
@ -81,10 +77,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" -> {
|
||||
@ -129,19 +133,78 @@ object DefaultWebViewUrlHandler {
|
||||
val position = uri.getQueryParameter("position")
|
||||
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)
|
||||
directToVideoDetail(context, id, loaction, false, "", entrance, "", referer, type, act)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_SINGLE -> {
|
||||
val referer = uri.getQueryParameter("referer") ?: ""
|
||||
directToVideoDetail(context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
|
||||
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_STREAMING_HOME -> {
|
||||
intent = Intent(context, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
intent.putExtra(MainActivity.SWITCH_TO_VIDEO, true)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_STREAMING_DESC -> {
|
||||
directToGameDetailVideoStreaming(context, id, entrance)
|
||||
}
|
||||
EntranceUtils.HOST_VIDEO_COLLECTION -> {
|
||||
directToGameVideo(context, id, entrance, "")
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_CATEGORY -> {
|
||||
val title = uri.getQueryParameter("title")
|
||||
DirectUtils.directCategoryDirectory(context, id, title ?: "", entrance, "")
|
||||
}
|
||||
EntranceUtils.HOST_COLUMN_COLLECTION -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
DirectUtils.directToColumnCollection(context, id, -1, entrance, name ?: "")
|
||||
}
|
||||
EntranceUtils.HOST_COLUMN -> {
|
||||
DirectUtils.directToSubject(context, id, uri.getQueryParameter(EntranceUtils.KEY_NAME), entrance)
|
||||
}
|
||||
EntranceUtils.HOST_COMMUNITY_QUESTION_LABEL_DETAIL -> {
|
||||
val community = CommunityEntity()
|
||||
community.id = uri.getQueryParameter("community_id") ?: ""
|
||||
community.name = uri.getQueryParameter("community_name") ?: ""
|
||||
val tag = uri.getQueryParameter("tag") ?: ""
|
||||
DirectUtils.directAskColumnLabelDetail(context, tag, community, entrance, "")
|
||||
}
|
||||
EntranceUtils.HOST_COMMUNITY_COLUMN_DETAIL -> {
|
||||
val community = CommunityEntity()
|
||||
community.id = uri.getQueryParameter("community_id") ?: ""
|
||||
community.name = uri.getQueryParameter("community_name") ?: ""
|
||||
val columnId = uri.getQueryParameter("column_id") ?: ""
|
||||
DirectUtils.directAskColumnDetail(context, columnId, community, entrance, "")
|
||||
}
|
||||
|
||||
else -> DialogUtils.showLowVersionDialog(context)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (url.startsWith("alipays:") || url.startsWith("alipay")) {
|
||||
try {
|
||||
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
|
||||
} catch (e: java.lang.Exception) {
|
||||
ToastUtils.showToast("请安装支付宝客户端")
|
||||
}
|
||||
return true
|
||||
} else if (url.startsWith("weixin")) {
|
||||
try {
|
||||
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
|
||||
} catch (e: java.lang.Exception) {
|
||||
ToastUtils.showToast("请安装微信客户端")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if ("http" != uri.scheme && "https" != uri.scheme) return true
|
||||
return false
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -5,6 +5,8 @@ import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.gh.common.util.GsonUtils;
|
||||
import com.gh.common.util.PackageHelper;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
@ -23,15 +25,12 @@ import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
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)}
|
||||
@ -49,6 +48,8 @@ public class Config {
|
||||
public static final String UMENG_APPKEY = BuildConfig.UMENG_APPKEY;
|
||||
public static final String UMENG_MESSAGE_SECRET = BuildConfig.UMENG_MESSAGE_SECRET;
|
||||
public static final String BUGLY_APPID = BuildConfig.BUGLY_APPID;
|
||||
public static final String LETO_APPID = BuildConfig.LETO_APPID;
|
||||
public static final String TTAD_APPID = BuildConfig.TTAD_APPID;
|
||||
// http://www.ghzs666.com/article/${articleId}.html
|
||||
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // ghzs/ghzs666 统一
|
||||
public static final String PATCHES = "patches";
|
||||
@ -234,6 +235,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());
|
||||
|
||||
@ -28,6 +28,16 @@ public class Constants {
|
||||
public static final String XPOSED_INSTALLER_PACKAGE_NAME = "de.robv.android.xposed.installer";
|
||||
|
||||
public static final String EB_QUIT_LOGIN = "quit_login";
|
||||
|
||||
// 用于避免历史下载掺和到普通下载状态的 ID 修饰符
|
||||
public static final String GAME_ID_DIVIDER = ":";
|
||||
// 用于避免历史下载影响到部分依赖名字作为数据更新条件的修饰符
|
||||
public static final String GAME_NAME_DECORATOR = " ";
|
||||
|
||||
// 游戏详情进入时的自定义栏目标签是否已经默认展开过一次的标记
|
||||
public static final String SP_HAS_EXPANDED_GAME_DETAIL_TAGS = "has_expanded_game_detail_tags";
|
||||
// 游戏详情进入时的自定义栏目标签是否已经显示过一次展开更多的浮窗提示
|
||||
public static final String SP_HAS_SHOWN_EXPANDED_GAME_DETAIL_TAGS_HINT = "has_shown_expanded_game_detail_tags_hint";
|
||||
|
||||
// 最近显示的弹窗信息
|
||||
public static final String SP_LAST_OPENING_ID = "last_opening_dialog_id";
|
||||
@ -64,6 +74,18 @@ public class Constants {
|
||||
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 SP_TOP_VIDEO_SCHEDULE = "top_video_schedule";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
@ -100,13 +122,16 @@ public class Constants {
|
||||
public static final int ADDONS_CD = 10 * 60 * 1000;
|
||||
//已收录包名更新 cd间隔
|
||||
public static final int PACKAGES_CD = 60 * 1000;
|
||||
|
||||
|
||||
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其它"};
|
||||
|
||||
public static final String ENTRANCE_UNKNOWN = "(unknown)";
|
||||
|
||||
public static final String DEFAULT_TEXT_WRAPPER = "###";
|
||||
|
||||
|
||||
// 触发了安装事件的标记
|
||||
public static final String MARK_ALREADY_TRIGGERED_INSTALLATION = "triggered_installation";
|
||||
|
||||
// 标记下载重试标记(值为任务已下载大小,为空表示需要重试)
|
||||
public static final String MARK_RETRY_DOWNLOAD = "retry_download";
|
||||
}
|
||||
|
||||
@ -16,9 +16,10 @@ import android.widget.TextView;
|
||||
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;
|
||||
import com.gh.common.history.HistoryHelper;
|
||||
import com.gh.common.repository.ReservationRepository;
|
||||
import com.gh.common.util.CheckLoginUtils;
|
||||
import com.gh.common.util.DataUtils;
|
||||
@ -36,10 +37,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;
|
||||
@ -52,6 +53,7 @@ import com.gh.gamecenter.entity.LinkEntity;
|
||||
import com.gh.gamecenter.entity.PluginLocation;
|
||||
import com.gh.gamecenter.entity.ServerCalendarEntity;
|
||||
import com.gh.gamecenter.entity.TagStyleEntity;
|
||||
import com.gh.gamecenter.entity.TestEntity;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.gh.gamecenter.qa.entity.CommunityVideoEntity;
|
||||
@ -62,6 +64,7 @@ import com.lightgame.utils.Utils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -198,6 +201,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 +221,40 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy 的 paddingBottom
|
||||
*/
|
||||
@BindingAdapter("lazyPaddingBottom")
|
||||
public static void lazyPaddingBottom(View view, int paddingBottomInDp) {
|
||||
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), DisplayUtils.dip2px(paddingBottomInDp));
|
||||
}
|
||||
|
||||
@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 +406,26 @@ 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,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
});
|
||||
});
|
||||
}
|
||||
break;
|
||||
case LAUNCH_OR_OPEN:
|
||||
@ -376,8 +433,12 @@ 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,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
}
|
||||
break;
|
||||
case INSTALL_PLUGIN:
|
||||
@ -416,9 +477,15 @@ public class BindingAdapters {
|
||||
}
|
||||
break;
|
||||
case H5_GAME:
|
||||
MtaHelper.onEvent("H5页面", "入口", "列表页_" + gameEntity.getName());
|
||||
LinkEntity linkEntity = gameEntity.getH5Link();
|
||||
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), "play".equals(linkEntity.getType())));
|
||||
boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩
|
||||
MtaHelper.onEvent("H5页面", "入口", "列表页_" + gameEntity.getName());
|
||||
|
||||
if (isPlay) {
|
||||
HistoryHelper.insertGameEntity(gameEntity);
|
||||
}
|
||||
|
||||
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay));
|
||||
progressBar.getContext().startActivity(i);
|
||||
break;
|
||||
}
|
||||
@ -455,7 +522,7 @@ public class BindingAdapters {
|
||||
}
|
||||
progressBar.setDownloadType(DownloadProgressBar.DownloadType.NONE);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
String status = GameUtils.getDownloadBtnText(progressBar.getContext(), gameEntity, PluginLocation.only_game);
|
||||
switch (status) {
|
||||
@ -511,6 +578,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 +630,7 @@ 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,
|
||||
@ -557,7 +638,7 @@ public class BindingAdapters {
|
||||
entrance,
|
||||
location + gameEntity.getName(),
|
||||
isSubscribe,
|
||||
downloadExposureEvent);
|
||||
traceEvent);
|
||||
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setDownloadType("插件化".equals(method) ?
|
||||
@ -567,27 +648,36 @@ 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));
|
||||
@BindingAdapter("gameLabelList")
|
||||
public static void setGameLabelList(LinearLayout layout, List<TagStyleEntity> tagStyle) {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
}
|
||||
|
||||
if (gameEntity.getTest().getStart() == 0) {
|
||||
testTime.setVisibility(View.GONE);
|
||||
// 包含测试开服标签
|
||||
@BindingAdapter("setGameTags")
|
||||
public static void setGameTags(LinearLayout layout, GameEntity gameEntity) {
|
||||
try {
|
||||
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
|
||||
TestEntity test = gameEntity.getTest();
|
||||
if (test != null) {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
typeTag.setBackground("FFF3E0");
|
||||
typeTag.setColor("FA8500");
|
||||
tagStyle.add(typeTag);
|
||||
|
||||
TagStyleEntity timeTag = new TagStyleEntity();
|
||||
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
|
||||
timeTag.setBackground("E0FFF9");
|
||||
timeTag.setColor("00A887");
|
||||
tagStyle.add(timeTag);
|
||||
} else {
|
||||
testTime.setText(GameViewUtils.getGameTestDate(gameEntity.getTest().getStart()));
|
||||
tagStyle = gameEntity.getTagStyle();
|
||||
}
|
||||
layout.addView(testView);
|
||||
} else {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, gameEntity.getTag(), subjectTag, gameEntity.getTagStyle());
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
233
app/src/main/java/com/gh/common/dialog/DeviceRemindDialog.kt
Normal file
233
app/src/main/java/com/gh/common/dialog/DeviceRemindDialog.kt
Normal file
@ -0,0 +1,233 @@
|
||||
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 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_device_remind_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)
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.common.repository.ReservationRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
@ -49,6 +50,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
|
||||
private var mSuccessCallback: SuccessCallback? = null
|
||||
|
||||
private var mGame: GameEntity? = null
|
||||
private var mGameId: String = ""
|
||||
private var mGameName: String = ""
|
||||
|
||||
@ -86,6 +88,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
if (it.success) {
|
||||
showSuccessDialog(it.withMobile, it.boundWechat)
|
||||
mSuccessCallback?.onSuccess()
|
||||
HistoryHelper.insertGameEntity(mGame!!)
|
||||
}
|
||||
}
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
@ -149,6 +152,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(gameEntity: GameEntity, successCallback: SuccessCallback) = ReserveDialogFragment().apply {
|
||||
this.mGame = gameEntity
|
||||
this.mGameId = gameEntity.id
|
||||
this.mGameName = gameEntity.name ?: ""
|
||||
this.mSuccessCallback = successCallback
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.exposure.meta.Meta
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.time.TimeUtil
|
||||
@ -28,7 +29,7 @@ data class ExposureEvent(
|
||||
fun createEvent(gameEntity: GameEntity?, source: List<ExposureSource>, eTrace: List<ExposureEvent>? = null, event: ExposureType = ExposureType.EXPOSURE): ExposureEvent {
|
||||
return ExposureEvent(
|
||||
payload = ExposureEntity(gameId = gameEntity?.id,
|
||||
gameName = gameEntity?.name,
|
||||
gameName = gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
sequence = gameEntity?.sequence,
|
||||
platform = gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import com.g00fy2.versioncompare.Version
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.util.toObject
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
@ -15,6 +17,11 @@ object ExposureUtils {
|
||||
traceEvent: ExposureEvent?,
|
||||
downloadType: DownloadType): ExposureEvent {
|
||||
val gameEntity = entity.clone()
|
||||
gameEntity.id = if (entity.id.contains(Constants.GAME_ID_DIVIDER)) {
|
||||
entity.id.split(Constants.GAME_ID_DIVIDER).toTypedArray()[0]
|
||||
} else {
|
||||
entity.id
|
||||
}
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadType = downloadType.toString()
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
@ -43,29 +50,26 @@ object ExposureUtils {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDownloadType(apkEntity: ApkEntity, method: String) : DownloadType {
|
||||
return if ("更新" == method) {
|
||||
if (PackageUtils.isSignature(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
DownloadType.PLUGIN_UPDATE
|
||||
fun getDownloadType(apkEntity: ApkEntity, gameId: String): DownloadType {
|
||||
return if (PackageUtils.isInstalled(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
if (PackageUtils.isSignedByGh(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
if (PackageUtils.isCanUpdate(apkEntity, gameId)) {
|
||||
DownloadType.PLUGIN_UPDATE
|
||||
} else {
|
||||
if (Version(apkEntity.version).isHigherThan(PackageUtils.getVersionByPackage(apkEntity.packageName))) {
|
||||
DownloadType.UPDATE
|
||||
} else {
|
||||
DownloadType.DOWNLOAD
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DownloadType.UPDATE
|
||||
DownloadType.PLUGIN_DOWNLOAD
|
||||
}
|
||||
} else if ("插件化" == method) {
|
||||
DownloadType.PLUGIN_DOWNLOAD
|
||||
} else {
|
||||
DownloadType.DOWNLOAD
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getUpdateType(apkEntity: ApkEntity) : DownloadType {
|
||||
return if (PackageUtils.isSignature(HaloApp.getInstance().application, apkEntity.packageName)) {
|
||||
DownloadType.PLUGIN_UPDATE
|
||||
} else {
|
||||
DownloadType.UPDATE
|
||||
}
|
||||
}
|
||||
|
||||
enum class DownloadType {
|
||||
DOWNLOAD,
|
||||
|
||||
|
||||
@ -4,9 +4,7 @@ import com.gh.common.runOnIoThread
|
||||
import com.gh.common.util.clearHtmlFormatCompletely
|
||||
import com.gh.common.util.removeInsertedContent
|
||||
import com.gh.common.util.removeVideoContent
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.entity.NewsEntity
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.qa.entity.AnswerDetailEntity
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
|
||||
@ -24,11 +22,32 @@ object HistoryHelper {
|
||||
runOnIoThread { HistoryDatabase.instance.articleDao().addArticle(articleEntity) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun insertGameEntity(gameEntity: GameEntity) {
|
||||
val historyGameEntity = convertGameEntityToHistoryGameEntity(gameEntity)
|
||||
runOnIoThread { HistoryDatabase.instance.gameDao().addGame(historyGameEntity) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun insertGameEntity(updateEntity: GameUpdateEntity) {
|
||||
val historyGameEntity = convertGameUpdateEntityToHistoryGameEntity(updateEntity)
|
||||
runOnIoThread { HistoryDatabase.instance.gameDao().addGame(historyGameEntity) }
|
||||
}
|
||||
|
||||
private fun convertGameUpdateEntityToHistoryGameEntity(updateEntity: GameUpdateEntity): HistoryGameEntity{
|
||||
val historyGame = HistoryGameEntity()
|
||||
|
||||
historyGame.orderTag = System.currentTimeMillis()
|
||||
historyGame.id = updateEntity.id
|
||||
historyGame.brief = updateEntity.brief
|
||||
historyGame.des = ""
|
||||
historyGame.icon = updateEntity.icon
|
||||
historyGame.name = updateEntity.name
|
||||
historyGame.tagStyle = updateEntity.tagStyle
|
||||
historyGame.tag = updateEntity.tag
|
||||
return historyGame
|
||||
}
|
||||
|
||||
private fun convertGameEntityToHistoryGameEntity(gameEntity: GameEntity): HistoryGameEntity {
|
||||
val historyGame = HistoryGameEntity()
|
||||
|
||||
@ -70,6 +89,11 @@ object HistoryHelper {
|
||||
runOnIoThread { HistoryDatabase.instance.answerDao().deleteAnswer(AnswerEntity().apply { primaryKey = answerId }) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteVideoEntity(videoId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.videoHistoryDao().deleteVideo(MyVideoEntity().apply { id = videoId }) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun emptyDatabase() {
|
||||
runOnIoThread { HistoryDatabase.instance.clearAllTables() }
|
||||
|
||||
@ -10,6 +10,7 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.util.doOnMainProcessOnly
|
||||
import com.gh.gamecenter.R
|
||||
import com.m7.imkfsdk.chat.ChatActivity
|
||||
import com.m7.imkfsdk.utils.Utils
|
||||
@ -25,51 +26,53 @@ class ImReceiver : BroadcastReceiver() {
|
||||
var notificationManager: NotificationManager? = null
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
intent?.let {
|
||||
if (intent.action == IMChatManager.NEW_MSG_ACTION) {
|
||||
notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
// 判断 ImActivity 是否在最顶端
|
||||
if (CurrentActivityHolder.getCurrentActivity() is ChatActivity) {
|
||||
ImManager.showFloatingWindow()
|
||||
ImManager.updateShouldShowFloatingWindowDot(false)
|
||||
} else {
|
||||
val contentIntent = Intent(Utils.getApp(), ChatActivity::class.java)
|
||||
|
||||
contentIntent.putExtra("PeerId", "")
|
||||
contentIntent.putExtra("type", "peedId")
|
||||
|
||||
contentIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val resultPendingIntent = PendingIntent.getActivity(
|
||||
Utils.getApp(),
|
||||
0,
|
||||
contentIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
// 新的通知
|
||||
val builder = NotificationCompat.Builder(Utils.getApp(), "Halo_IM")
|
||||
val notification = builder.setTicker("您有新的消息")
|
||||
.setDefaults(Notification.DEFAULT_ALL)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setContentTitle("光环助手客服回复")
|
||||
.setContentText("您有新的消息")
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel("Halo_IM", "Halo_IM", NotificationManager.IMPORTANCE_DEFAULT)
|
||||
notificationManager?.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
notificationManager?.notify(NOTIFICATION_ID, notification)
|
||||
context?.doOnMainProcessOnly {
|
||||
intent?.let {
|
||||
if (intent.action == IMChatManager.NEW_MSG_ACTION) {
|
||||
notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
// 判断 ImActivity 是否在最顶端
|
||||
if (CurrentActivityHolder.getCurrentActivity() is ChatActivity) {
|
||||
ImManager.showFloatingWindow()
|
||||
ImManager.updateShouldShowFloatingWindowDot(false)
|
||||
} else {
|
||||
val contentIntent = Intent(Utils.getApp(), ChatActivity::class.java)
|
||||
|
||||
contentIntent.putExtra("PeerId", "")
|
||||
contentIntent.putExtra("type", "peedId")
|
||||
|
||||
contentIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val resultPendingIntent = PendingIntent.getActivity(
|
||||
Utils.getApp(),
|
||||
0,
|
||||
contentIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
// 新的通知
|
||||
val builder = NotificationCompat.Builder(Utils.getApp(), "Halo_IM")
|
||||
val notification = builder.setTicker("您有新的消息")
|
||||
.setDefaults(Notification.DEFAULT_ALL)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(resultPendingIntent)
|
||||
.setContentTitle("光环助手客服回复")
|
||||
.setContentText("您有新的消息")
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel("Halo_IM", "Halo_IM", NotificationManager.IMPORTANCE_DEFAULT)
|
||||
notificationManager?.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
if (notification != null) {
|
||||
notificationManager?.notify(NOTIFICATION_ID, notification)
|
||||
ImManager.showFloatingWindow()
|
||||
}
|
||||
}
|
||||
} else if (intent.action == IMChatManager.FINISH_ACTION) {
|
||||
ImManager.dismissFloatingWindow()
|
||||
}
|
||||
} else if (intent.action == IMChatManager.FINISH_ACTION) {
|
||||
ImManager.dismissFloatingWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -14,4 +14,7 @@ object SyncFieldConstants {
|
||||
const val ANSWER_COMMENT_COUNT = "ANSWER_COMMENT_COUNT"
|
||||
const val ARTICLE_COMMENT_COUNT = "ARTICLE_COMMENT_COUNT"
|
||||
|
||||
// 回答数量
|
||||
const val ANSWER_COUNT = "ANSWER_COUNT"
|
||||
|
||||
}
|
||||
@ -33,6 +33,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -51,8 +52,12 @@ public class CommentUtils {
|
||||
public static void setCommentTime(TextView textView, long time) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
long day = time * 1000;
|
||||
String year = String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
|
||||
format.applyPattern("yyyy");
|
||||
String currentYear = format.format(day);
|
||||
format.applyPattern("yyyyMMdd");
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
if (day >= today && day < today + 86400 * 1000) {
|
||||
long min = new Date().getTime() / 1000 - day / 1000;
|
||||
int hour = (int) (min / (60 * 60));
|
||||
@ -68,6 +73,13 @@ public class CommentUtils {
|
||||
} else if (day >= today - 86400 * 1000 && day < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText("昨天 ");
|
||||
} else if (day >= today - 86400 * 1000 * 7 && day < today - 86400 * 1000) {
|
||||
format.applyPattern("HH:mm");
|
||||
long days = (today - day) / 86400000 + 1;
|
||||
textView.setText(String.format(Locale.getDefault(), "%d天前 ", days));
|
||||
} else if (day < today - 86400 * 1000 * 7 && year.equals(currentYear)) {
|
||||
format.applyPattern("MM-dd");
|
||||
textView.setText(format.format(day));
|
||||
} else {
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
textView.setText(format.format(day));
|
||||
@ -199,7 +211,7 @@ public class CommentUtils {
|
||||
final TextView commentLikeCountTv, final ImageView commentLikeIv,
|
||||
final OnVoteListener listener) {
|
||||
if (commentLikeCountTv.getCurrentTextColor() == ContextCompat.getColor(context, R.color.theme_font)) {
|
||||
Utils.toast(context, "已经点过赞啦!");
|
||||
ToastUtils.INSTANCE.showToast("已经点过赞啦!");
|
||||
return;
|
||||
}
|
||||
commentEntity.setVote(commentEntity.getVote() + 1);
|
||||
@ -236,7 +248,7 @@ public class CommentUtils {
|
||||
try {
|
||||
String detail = new JSONObject(exception.response().errorBody().string()).getString("detail");
|
||||
if ("voted".equals(detail)) {
|
||||
Utils.toast(context, "已经点过赞啦!");
|
||||
ToastUtils.INSTANCE.showToast("已经点过赞啦!");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
@ -265,7 +277,7 @@ public class CommentUtils {
|
||||
}
|
||||
CheckLoginUtils.checkLogin(context, entrance, () -> {
|
||||
if (commentLikeCountTv.getCurrentTextColor() == ContextCompat.getColor(context, R.color.theme_font)) {
|
||||
Utils.toast(context, "已经点过赞啦!");
|
||||
ToastUtils.INSTANCE.showToast("已经点过赞啦!");
|
||||
return;
|
||||
}
|
||||
commentEntity.setVote(commentEntity.getVote() + 1);
|
||||
@ -301,7 +313,7 @@ public class CommentUtils {
|
||||
try {
|
||||
String detail = new JSONObject(exception.response().errorBody().string()).getString("detail");
|
||||
if ("voted".equals(detail)) {
|
||||
Utils.toast(context, "已经点过赞啦!");
|
||||
ToastUtils.INSTANCE.showToast("已经点过赞啦!");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
@ -421,7 +433,7 @@ public class CommentUtils {
|
||||
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setText(copyContent);
|
||||
|
||||
Utils.toast(context, "复制成功");
|
||||
ToastUtils.INSTANCE.showToast("复制成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -3,17 +3,18 @@ package com.gh.common.util;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.NewsDetailEntity;
|
||||
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;
|
||||
|
||||
import kotlin.text.StringsKt;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/12/9.
|
||||
* 数据收集 工具类(data.ghzs666.com)
|
||||
@ -33,8 +34,8 @@ public class DataCollectionUtils {
|
||||
// 上传下载数据(开始、完成)
|
||||
public static void uploadDownload(Context context, DownloadEntity downloadEntity, String status) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", downloadEntity.getName());
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
map.put("game", StringsKt.removeSuffix(downloadEntity.getName(), Constants.GAME_NAME_DECORATOR));
|
||||
map.put("game_id", downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER));
|
||||
if (downloadEntity.isPluggable()) {
|
||||
map.put("method", "插件化");
|
||||
map.put("btn_status", "插件化");
|
||||
@ -169,24 +170,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
|
||||
|
||||
@ -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,22 @@ 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)
|
||||
|
||||
//"h5_game_center" -> directLetoGameCenter(context)
|
||||
|
||||
"" -> {
|
||||
// do nothing
|
||||
}
|
||||
else -> {
|
||||
if (unknownCallback != null) {
|
||||
unknownCallback.invoke()
|
||||
@ -214,11 +230,26 @@ 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))
|
||||
@JvmStatic
|
||||
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String, columnName: String = "") {
|
||||
context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance, columnName))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,12 +346,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 +489,7 @@ object DirectUtils {
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
// 未安装手Q或安装的版本不支持
|
||||
Utils.toast(context, "请安装QQ客户端")
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -609,4 +650,70 @@ object DirectUtils {
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转梦工厂小游戏
|
||||
*/
|
||||
/*@JvmStatic
|
||||
fun directLetoGameCenter(context: Context) {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
UserManager.getInstance().userInfoEntity?.run {
|
||||
MgcAccountManager.syncAccount(context, if (idCard != null) idCard!!.name else name, mobile, name, icon, true,
|
||||
object : SyncUserInfoListener {
|
||||
override fun onSuccess(data: LoginResultBean?) {}
|
||||
|
||||
override fun onFail(code: String?, message: String?) {}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
MgcAccountManager.exitAccount(context, object : SyncUserInfoListener {
|
||||
override fun onSuccess(data: LoginResultBean?) {}
|
||||
|
||||
override fun onFail(code: String?, message: String?) {}
|
||||
})
|
||||
}
|
||||
Leto.getInstance().startGameCenter(context)
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 跳转分类
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directCategoryDirectory(context: Context, categoryId: String, categoryTitle: String, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
|
||||
bundle.putString(KEY_CATEGORY_ID, categoryId)
|
||||
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到问题标签详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directAskColumnLabelDetail(context: Context, tag: String, community: CommunityEntity, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, AskColumnDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_ASK_TAG, tag)
|
||||
bundle.putParcelable(KEY_COMMUNITY_DATA, community)
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到专栏详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directAskColumnDetail(context: Context, columnId: String, community: CommunityEntity, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, AskColumnDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_COLUMN_ID, columnId)
|
||||
bundle.putParcelable(KEY_COMMUNITY_DATA, community)
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
}
|
||||
@ -206,6 +206,13 @@ public class DisplayUtils {
|
||||
public static void setStatusBarColor(Activity activity, int color, boolean lightStatusBar) {
|
||||
Window window = activity.getWindow();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (!isMiuiOs()) {
|
||||
//取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
}
|
||||
|
||||
window.setStatusBarColor(ContextCompat.getColor(activity, color));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
@ -250,6 +257,20 @@ public class DisplayUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int getScreenWidth() {
|
||||
WindowManager manager = (WindowManager) HaloApp.getInstance().getApplication().getSystemService(Context.WINDOW_SERVICE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
manager.getDefaultDisplay().getMetrics(metrics);
|
||||
return metrics.widthPixels;
|
||||
}
|
||||
|
||||
public static int getScreenHeight() {
|
||||
WindowManager manager = (WindowManager) HaloApp.getInstance().getApplication().getSystemService(Context.WINDOW_SERVICE);
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
manager.getDefaultDisplay().getMetrics(metrics);
|
||||
return metrics.heightPixels;
|
||||
}
|
||||
|
||||
public static boolean hasSoftKeys(Context context) {
|
||||
if (!(context instanceof Activity)) return false;
|
||||
|
||||
|
||||
@ -9,12 +9,14 @@ import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.history.HistoryHelper;
|
||||
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;
|
||||
@ -38,6 +40,9 @@ 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,11 +422,19 @@ public class DownloadItemUtils {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (gameEntity.getApk().size() == 0 && gameEntity.getH5Link() != null) {
|
||||
downloadBtn.setOnClickListener(v -> {
|
||||
MtaHelper.onEvent("H5页面", "入口", "列表页_" + gameEntity.getName());
|
||||
Intent i = WebActivity.getIntentForWebGame(context, gameEntity.getH5Link().getLink(), gameEntity.getName(), "play".equals(gameEntity.getH5Link().getType()));
|
||||
|
||||
LinkEntity linkEntity = gameEntity.getH5Link();
|
||||
|
||||
boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩
|
||||
if (isPlay) {
|
||||
HistoryHelper.insertGameEntity(gameEntity);
|
||||
}
|
||||
|
||||
Intent i = WebActivity.getIntentForWebGame(context, gameEntity.getH5Link().getLink(), gameEntity.getName(), isPlay);
|
||||
context.startActivity(i);
|
||||
});
|
||||
} else if (gameEntity.getApk().size() == 1) {
|
||||
@ -449,7 +459,16 @@ 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,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -480,19 +499,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, traceEvent, 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))) {
|
||||
@ -528,14 +566,13 @@ public class DownloadItemUtils {
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始", "下载");
|
||||
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, gameEntity.getApk().get(0).getPlatform(), traceEvent, ExposureUtils.DownloadType.DOWNLOAD);
|
||||
|
||||
DownloadManager.createDownload(context, gameEntity, context.getString(R.string.download), entrance, location, isSubscribe, downloadExposureEvent);
|
||||
DownloadManager.createDownload(context, gameEntity, context.getString(R.string.download), entrance, location, isSubscribe, traceEvent);
|
||||
Utils.toast(context, gameEntity.getName() + "已加入下载队列");
|
||||
|
||||
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);
|
||||
}
|
||||
@ -548,9 +585,7 @@ public class DownloadItemUtils {
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始", "插件化");
|
||||
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, gameEntity.getApk().get(0).getPlatform(), traceEvent, ExposureUtils.DownloadType.PLUGIN_DOWNLOAD);
|
||||
|
||||
DownloadManager.createDownload(context, gameEntity, "插件化", entrance, location, isSubscribe, downloadExposureEvent);
|
||||
DownloadManager.createDownload(context, gameEntity, "插件化", entrance, location, isSubscribe, traceEvent);
|
||||
Utils.toast(context, gameEntity.getName() + "已加入下载队列");
|
||||
|
||||
downloadBtn.setText(R.string.downloading);
|
||||
@ -575,6 +610,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,13 +621,9 @@ 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);
|
||||
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location, isSubscribe, downloadExposureEvent);
|
||||
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location, isSubscribe, traceEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,13 +35,17 @@ import org.json.JSONObject
|
||||
import java.util.*
|
||||
|
||||
object DownloadObserver {
|
||||
|
||||
|
||||
private val mApplication = HaloApp.getInstance().application
|
||||
|
||||
// 如果在WIFI状态下,下载自动暂停,则再重试一遍
|
||||
@JvmStatic
|
||||
fun initObserver() {
|
||||
val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
val gameId = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER)
|
||||
val downloadManager = DownloadManager.getInstance(HaloApp.getInstance().application)
|
||||
|
||||
if (downloadEntity.status != DownloadStatus.downloading) {
|
||||
LogUtils.uploadDownloadEvent(downloadEntity)
|
||||
}
|
||||
@ -51,7 +55,7 @@ object DownloadObserver {
|
||||
processHijack(downloadEntity)
|
||||
val nameAndPlatform = (downloadEntity.name + ":"
|
||||
+ PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
MtaHelper.onEvent( "下载劫持",
|
||||
MtaHelper.onEvent("下载劫持",
|
||||
"游戏名字", nameAndPlatform,
|
||||
"网络状态", DeviceUtils.getNetwork(mApplication))
|
||||
return
|
||||
@ -59,7 +63,7 @@ object DownloadObserver {
|
||||
// 404 Not Found
|
||||
// 删除任务
|
||||
downloadEntity.status = DownloadStatus.cancel
|
||||
DownloadManager.getInstance(mApplication).cancel(downloadEntity.url)
|
||||
downloadManager.cancel(downloadEntity.url)
|
||||
Utils.toast(mApplication, "该链接已失效!请联系管理员。")
|
||||
|
||||
MtaHelper.onEventWithBasicDeviceInfo("下载失败弹窗",
|
||||
@ -70,17 +74,29 @@ 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) {
|
||||
if (downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD].isNullOrEmpty()
|
||||
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) {
|
||||
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = downloadEntity.progress.toString()
|
||||
downloadManager.updateDownloadEntity(downloadEntity)
|
||||
downloadManager.startDownload(downloadEntity.url)
|
||||
debugOnly {
|
||||
Utils.log("DownloadObserver", "下载重试->" + downloadEntity.toJson())
|
||||
}
|
||||
} else {
|
||||
Utils.toast(mApplication, "网络不稳定,下载任务已暂停")
|
||||
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
|
||||
|
||||
Utils.toast(mApplication, "网络不稳定,下载任务已暂停")
|
||||
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
|
||||
|
||||
MtaHelper.onEventWithBasicDeviceInfo("下载自动暂停",
|
||||
"游戏", downloadEntity.name,
|
||||
"平台", downloadEntity.platform)
|
||||
MtaHelper.onEventWithBasicDeviceInfo("下载自动暂停",
|
||||
"游戏", downloadEntity.name,
|
||||
"平台", downloadEntity.platform)
|
||||
debugOnly {
|
||||
Utils.log("DownloadObserver", "下载自动暂停->" + downloadEntity.toJson())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DownloadStatus.done == downloadEntity.status) {
|
||||
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
|
||||
@ -110,7 +126,7 @@ object DownloadObserver {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(SettingsFragment.AUTO_INSTALL_SP_KEY, true)) {
|
||||
if (FileUtils.isEmptyFile(downloadEntity.path)) {
|
||||
Utils.toast(mApplication, R.string.install_failure_hint)
|
||||
DownloadManager.getInstance(mApplication).cancel(downloadEntity.url)
|
||||
downloadManager.cancel(downloadEntity.url)
|
||||
} else {
|
||||
if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) {
|
||||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||||
@ -126,7 +142,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
// 统计下载完成
|
||||
uploadData(downloadEntity.gameId, downloadEntity.platform)
|
||||
uploadData(gameId, downloadEntity.platform)
|
||||
}
|
||||
|
||||
// 下载过程分析统计
|
||||
@ -142,10 +158,17 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
if (downloadEntity.status == DownloadStatus.done) {
|
||||
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", "", ""))
|
||||
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", downloadEntity.packageName, ""))
|
||||
}
|
||||
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
|
||||
// 如果已下载大小发生变化,表示成功恢复下载,则重置重试标记
|
||||
if (downloadEntity.status == DownloadStatus.downloading &&
|
||||
downloadEntity.progress.toString() != downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD]) {
|
||||
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = ""
|
||||
downloadManager.updateDownloadEntity(downloadEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +211,7 @@ object DownloadObserver {
|
||||
val kv3 = HashMap<String, Any>()
|
||||
kv3["下载"] = "下载完成"
|
||||
kv3["版本"] = downloadEntity.platform
|
||||
kv3["位置"] = downloadEntity.entrance ?: "null"
|
||||
kv3["位置"] = downloadEntity.entrance ?: "null"
|
||||
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
|
||||
DataUtils.onEvent(mApplication, "插件化", downloadEntity.name, kv3)
|
||||
|
||||
@ -201,7 +224,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
ExposureUtils.logADownloadCompleteExposureEvent(
|
||||
GameEntity(downloadEntity.gameId, downloadEntity.name),
|
||||
GameEntity(id = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER), mName = downloadEntity.name.removeSuffix(Constants.GAME_NAME_DECORATOR)),
|
||||
downloadEntity.platform,
|
||||
downloadEntity.exposureTrace,
|
||||
type)
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.gh.common.avoidcallback.AvoidOnResultManager;
|
||||
import com.gh.common.avoidcallback.Callback;
|
||||
import com.gh.gamecenter.MainActivity;
|
||||
@ -11,8 +13,6 @@ import com.gh.gamecenter.NormalActivity;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.gh.gamecenter.normal.NormalFragment;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
/**
|
||||
* @author CsHeng
|
||||
* @Date 2017/4/25
|
||||
@ -37,6 +37,10 @@ public class EntranceUtils {
|
||||
public static final String HOST_VIDEO_COLLECTION = "video_collection";//视频合集
|
||||
public static final String HOST_USERHOME = "userhome";//个人主页
|
||||
public static final String HOST_VIDEO = "video";
|
||||
public static final String HOST_CATEGORY = "category";//分类
|
||||
public static final String HOST_COLUMN_COLLECTION = "column_collection";//专题合集
|
||||
public static final String HOST_COMMUNITY_QUESTION_LABEL_DETAIL = "community_question_label_detail";//问题标签详情
|
||||
public static final String HOST_COMMUNITY_COLUMN_DETAIL = "community_column_detail";//专栏详情
|
||||
public static final String HOST_COMMUNITY_ARTICLE = "community_article";
|
||||
public static final String HOST_COMMUNITY_COLUMN = "community_column";
|
||||
public static final String HOST_GAME = "game";
|
||||
@ -86,6 +90,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 +143,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";
|
||||
@ -149,6 +155,12 @@ public class EntranceUtils {
|
||||
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 final String KEY_SHOW_EDIT_DRAFT = "showEditDraft";
|
||||
public static final String KEY_ARTICLE_OPEN_IN_NEW_PAGE = "openArticleInNewPage";
|
||||
public static final String KEY_ONLY_CREATE_DRAFT = "onlyCreateDraft";
|
||||
|
||||
public static void jumpActivity(Context context, Bundle bundle) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
@ -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
|
||||
*/
|
||||
@ -280,7 +289,7 @@ fun String.copyTextAndToast(toastText: String = "复制成功") {
|
||||
val application = HaloApp.getInstance().application
|
||||
val cmb = application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cmb.text = this
|
||||
Utils.toast(application, toastText)
|
||||
ToastUtils.showToast(toastText)
|
||||
}
|
||||
|
||||
fun Map<String, String>.createRequestBody(): RequestBody {
|
||||
@ -452,6 +461,22 @@ fun SimpleDraweeView.display(url: String) {
|
||||
ImageUtils.display(this, url)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process related
|
||||
*/
|
||||
fun Context.doOnMainProcessOnly(callback: EmptyCallback) {
|
||||
doOnMainProcessOnly { callback.onCallback() }
|
||||
}
|
||||
|
||||
inline fun Context.doOnMainProcessOnly(f: () -> Unit) {
|
||||
val processName = PackageUtils.obtainProcessName(this)
|
||||
if (processName == null || BuildConfig.APPLICATION_ID == processName) {
|
||||
f.invoke()
|
||||
} else {
|
||||
Utils.log("Block one useless sub process method call.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用包裹
|
||||
*/
|
||||
@ -471,15 +496,15 @@ inline fun testChannelOnly(f: () -> Unit) {
|
||||
* 倒计时,单位s
|
||||
*/
|
||||
inline fun countDownTimer(
|
||||
millisUntilFinish: Long,
|
||||
crossinline block: (finish: Boolean, millisUntilFinished: Long) -> Unit
|
||||
timeInSeconds: Long,
|
||||
crossinline block: (finish: Boolean, remainingTime: Long) -> Unit
|
||||
): Disposable {
|
||||
var subscribe: Disposable? = null
|
||||
subscribe = Observable.interval(0, 1000, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it < millisUntilFinish) {
|
||||
block.invoke(false, millisUntilFinish - it)
|
||||
if (it < timeInSeconds) {
|
||||
block.invoke(false, timeInSeconds - it)
|
||||
} else {
|
||||
block.invoke(true, 0)
|
||||
if (subscribe != null && !subscribe!!.isDisposed) {
|
||||
|
||||
@ -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; // 可更新数量
|
||||
@ -103,7 +110,7 @@ public class GameUtils {
|
||||
gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
|
||||
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
|
||||
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
|
||||
&& !PackageUtils.isSignature(context, apkEntity.getPackageName())
|
||||
&& !PackageUtils.isSignedByGh(context, apkEntity.getPackageName())
|
||||
&& apkEntity.isShowPlugin(pluginLocation)) {
|
||||
pluginCount++;
|
||||
} else if (gh_id == null || gh_id.equals(gameEntity.getId())) {
|
||||
@ -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,17 @@ 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 +73,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?)
|
||||
}
|
||||
|
||||
@ -67,8 +67,8 @@ public class InstallUtils {
|
||||
String installVersion = PackageUtils.getVersionByPackage(packageName);
|
||||
if (!TextUtils.isEmpty(installVersion) && downloadEntity != null &&
|
||||
installVersion.equals(downloadEntity.getVersionName())) {
|
||||
if (!downloadEntity.isPluggable() || PackageUtils.isSignature(context, packageName)) {
|
||||
EventBus.getDefault().post(new EBPackage("安装", packageName));
|
||||
if (!downloadEntity.isPluggable() || PackageUtils.isSignedByGh(context, 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();
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
@ -75,4 +82,61 @@ public class MD5Utils {
|
||||
return bigInt.toString(16);
|
||||
}
|
||||
|
||||
|
||||
public static boolean checkMD5(String md5, File updateFile) {
|
||||
if (TextUtils.isEmpty(md5) || updateFile == null) {
|
||||
Utils.log("MD5 string empty or updateFile null");
|
||||
return false;
|
||||
}
|
||||
|
||||
String calculatedDigest = calculateMD5(updateFile);
|
||||
if (calculatedDigest == null) {
|
||||
Utils.log("calculatedDigest null");
|
||||
return false;
|
||||
}
|
||||
|
||||
Utils.log("Calculated digest: " + calculatedDigest);
|
||||
Utils.log("Provided digest: " + md5);
|
||||
return calculatedDigest.equalsIgnoreCase(md5);
|
||||
}
|
||||
|
||||
public static String calculateMD5(File updateFile) {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("MD5");
|
||||
} catch (Exception e) {
|
||||
Utils.log("Exception while getting digest", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream is;
|
||||
try {
|
||||
is = new FileInputStream(updateFile);
|
||||
} catch (Exception e) {
|
||||
Utils.log("Exception while getting FileInputStream", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
try {
|
||||
while ((read = is.read(buffer)) > 0) {
|
||||
digest.update(buffer, 0, read);
|
||||
}
|
||||
byte[] md5sum = digest.digest();
|
||||
BigInteger bigInt = new BigInteger(1, md5sum);
|
||||
String output = bigInt.toString(16);
|
||||
// Fill to 32 chars
|
||||
output = String.format("%32s", output).replace(' ', '0');
|
||||
return output;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to process file for MD5", e);
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
Utils.log("Exception on closing MD5 input stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,6 +146,8 @@ public class NetworkUtils {
|
||||
return "3G";
|
||||
case TelephonyManager.NETWORK_TYPE_LTE:
|
||||
return "4G";
|
||||
case 20://暂未适配AndroidQ,这里粗暴设置为20
|
||||
return "5G";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -46,7 +47,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>());
|
||||
@ -117,64 +118,23 @@ public class NewsUtils {
|
||||
* 设置新闻发布时间
|
||||
*/
|
||||
public static void setNewsPublishOn(TextView textView, long time) {
|
||||
time = time * 1000;
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
if (time >= today && time < today + 86400 * 1000) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(format.format(time));
|
||||
} else if (time >= today - 86400 * 1000 && time < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("昨天 %s", format.format(time)));
|
||||
} else {
|
||||
format.applyPattern("yyyy年MM月dd日 HH:mm");
|
||||
textView.setText(format.format(time));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
format.applyPattern("yyyy年MM月dd日 HH:mm");
|
||||
textView.setText(format.format(time));
|
||||
}
|
||||
CommentUtils.setCommentTime(textView, time);
|
||||
}
|
||||
|
||||
|
||||
public static void setNewsDetailTime(TextView textView, long time) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
long day = time * 1000;
|
||||
if (day >= today && day < today + 86400 * 1000) {
|
||||
long min = new Date().getTime() / 1000 - day / 1000;
|
||||
int hour = (int) (min / (60 * 60));
|
||||
if (hour == 0) {
|
||||
if (min < 60) {
|
||||
textView.setText("刚刚");
|
||||
} else {
|
||||
textView.setText(String.format(Locale.getDefault(), "%d分钟前", (int) (min / 60)));
|
||||
}
|
||||
} else {
|
||||
textView.setText(String.format(Locale.getDefault(), "%d小时前", hour));
|
||||
}
|
||||
} else if (day >= today - 86400 * 1000 * 100 && day < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText("昨天 ");
|
||||
} else {
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
textView.setText(format.format(day));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
textView.setText(format.format(time * 1000));
|
||||
}
|
||||
CommentUtils.setCommentTime(textView, time);
|
||||
}
|
||||
|
||||
public static String getFormattedTime(long time) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
long day = time * 1000;
|
||||
String year = String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
|
||||
format.applyPattern("yyyy");
|
||||
String currentYear = format.format(day);
|
||||
format.applyPattern("yyyyMMdd");
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
if (day >= today && day < today + 86400 * 1000) {
|
||||
long min = new Date().getTime() / 1000 - day / 1000;
|
||||
int hour = (int) (min / (60 * 60));
|
||||
@ -190,6 +150,13 @@ public class NewsUtils {
|
||||
} else if (day >= today - 86400 * 1000 && day < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
return ("昨天 ");
|
||||
} else if (day >= today - 86400 * 1000 * 7 && day < today - 86400 * 1000) {
|
||||
format.applyPattern("HH:mm");
|
||||
long days = (today - day) / 86400000 + 1;
|
||||
return String.format(Locale.getDefault(), "%d天前 ", days);
|
||||
} else if (day < today - 86400 * 1000 * 7 && year.equals(currentYear)) {
|
||||
format.applyPattern("MM-dd");
|
||||
return (format.format(day));
|
||||
} else {
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
return (format.format(day));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -8,7 +9,6 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SigningInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -21,6 +21,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;
|
||||
@ -31,12 +32,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 {
|
||||
@ -46,59 +52,51 @@ public class PackageUtils {
|
||||
/*
|
||||
* 判断是否可以更新,只判断gh_version的大小
|
||||
*/
|
||||
public static List<GameUpdateEntity> isCanUpdate(Context context, GameEntity gameEntity) {
|
||||
public static List<GameUpdateEntity> getUpdateData(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());
|
||||
|
||||
// 是否需要显示更新
|
||||
boolean shouldShouldUpdate = apkEntity.getForce();
|
||||
boolean shouldShowUpdate = apkEntity.getForce();
|
||||
|
||||
if (shouldShouldUpdate && !TextUtils.isEmpty(versionFromRequest) && !TextUtils.isEmpty(versionFromInstalledApp)) {
|
||||
if (shouldShowUpdate && !TextUtils.isEmpty(versionFromRequest) && !TextUtils.isEmpty(versionFromInstalledApp)) {
|
||||
|
||||
// 根据版本判断是否需要更新
|
||||
shouldShouldUpdate = new Version(versionFromRequest).isHigherThan(versionFromInstalledApp);
|
||||
shouldShowUpdate = new Version(versionFromRequest).isHigherThan(versionFromInstalledApp);
|
||||
|
||||
if (shouldShouldUpdate) {
|
||||
if (shouldShowUpdate) {
|
||||
GameUpdateEntity updateEntity = new GameUpdateEntity();
|
||||
updateEntity.setId(gameEntity.getId());
|
||||
updateEntity.setName(gameEntity.getName());
|
||||
@ -114,6 +112,7 @@ public class PackageUtils {
|
||||
updateEntity.setTag(gameEntity.getTag());
|
||||
updateEntity.setTagStyle(gameEntity.getTagStyle());
|
||||
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
|
||||
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
|
||||
updateList.add(updateEntity);
|
||||
}
|
||||
}
|
||||
@ -139,10 +138,23 @@ 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");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 判断是否是插件包
|
||||
*/
|
||||
public static boolean isSignature(Context context, String packageName) {
|
||||
public static boolean isSignedByGh(Context context, String packageName) {
|
||||
String signature = getApkSignatureByPackageName(context, packageName);
|
||||
return publicKey.equals(signature);
|
||||
}
|
||||
@ -182,6 +194,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 "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 解析签名
|
||||
*/
|
||||
@ -197,7 +231,7 @@ public class PackageUtils {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 启动安装应用程序
|
||||
*/
|
||||
@ -223,6 +257,7 @@ public class PackageUtils {
|
||||
|
||||
/**
|
||||
* 根据 path 获取 apk 信息确定处理方式
|
||||
*
|
||||
* @return true 为直接唤起系统 PackageInstaller, false 为需要插件化
|
||||
*/
|
||||
public static boolean isCanLaunchSetup(Context context, String path) {
|
||||
@ -231,7 +266,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;
|
||||
}
|
||||
@ -246,6 +281,12 @@ public class PackageUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 从 APK 文件里读取公钥,与已安装的公钥比较判断是否一样
|
||||
String signatureFromApk = getApkSignatureFromFile(path);
|
||||
if (!TextUtils.isEmpty(signatureFromApk)) {
|
||||
return signatureFromApk.equals(getApkSignatureByPackageName(context, packageName));
|
||||
}
|
||||
|
||||
// 若已安装的应用的签名与即将要安装的签名一致也返回 true
|
||||
return compareSignatureBetweenInstalledAppWithApk(context, packageName, path);
|
||||
}
|
||||
@ -309,6 +350,8 @@ public class PackageUtils {
|
||||
|
||||
/*
|
||||
* 根据包名,判断是否已安装该游戏
|
||||
*
|
||||
* 注意:目测只对能启动的app有效(有桌面图标),对一些没有桌面图标的应用无效(参考应用:魅族游戏框架)
|
||||
*/
|
||||
public static boolean isInstalled(Context context, String packageName) {
|
||||
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
@ -458,4 +501,75 @@ 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.isSignedByGh(HaloApp.getInstance().getApplication(), apkEntity.getPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调用者的进程名
|
||||
* @param context 调用者的上下文
|
||||
* @return 进程名
|
||||
*/
|
||||
public static String obtainProcessName(Context context) {
|
||||
final int pid = android.os.Process.myPid();
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> listTaskInfo = am.getRunningAppProcesses();
|
||||
if (listTaskInfo != null && !listTaskInfo.isEmpty()) {
|
||||
for (ActivityManager.RunningAppProcessInfo info : listTaskInfo) {
|
||||
if (info != null && info.pid == pid) {
|
||||
return info.processName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -206,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);
|
||||
|
||||
@ -655,5 +655,20 @@ public class ShareUtils {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,6 +20,8 @@ import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import java.lang.Exception
|
||||
|
||||
|
||||
object UploadImageUtils {
|
||||
@ -109,6 +111,7 @@ object UploadImageUtils {
|
||||
fun compressAndUploadImageList(type: UploadType, imgs: List<String>, compressGif: Boolean, listener: OnUploadImageListListener): Disposable? {
|
||||
var subscription: Disposable? = null
|
||||
val postImageList = LinkedHashMap<String, String>()
|
||||
val errorMap = HashMap<String, Exception>()
|
||||
|
||||
Observable.create(ObservableOnSubscribe<Map<String, String>> {
|
||||
val compressList = compressImageList(imgs, compressGif)
|
||||
@ -145,11 +148,13 @@ object UploadImageUtils {
|
||||
onFailure(IllegalAccessException("HeHe"))
|
||||
}
|
||||
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
// 若遇到错误且 subscription?.isDisposed 为 true 时会抛出 io.reactivex.exceptions.UndeliverableException 异常
|
||||
// if (subscription?.isDisposed == true) return
|
||||
// it.onError(exception) // fuck
|
||||
it.onNext(Collections.emptyMap())
|
||||
// it.onNext(Collections.emptyMap())
|
||||
errorMap[img.path] = exception
|
||||
}
|
||||
})
|
||||
listProgress += img.length()
|
||||
@ -165,18 +170,18 @@ object UploadImageUtils {
|
||||
|
||||
override fun onComplete() {
|
||||
if (postImageList.size == 0) {
|
||||
listener.onError()
|
||||
listener.onError(errorMap)
|
||||
} else {
|
||||
listener.onSuccess(postImageList)
|
||||
listener.onSuccess(postImageList, errorMap)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNext(t: Map<String, String>) {
|
||||
if (!t.isEmpty()) postImageList.putAll(t)
|
||||
if (t.isNotEmpty()) postImageList.putAll(t)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
e.printStackTrace()
|
||||
override fun onError(ignore: Throwable) {
|
||||
|
||||
}
|
||||
})
|
||||
return subscription
|
||||
@ -285,8 +290,8 @@ object UploadImageUtils {
|
||||
}
|
||||
|
||||
interface OnUploadImageListListener {
|
||||
fun onSuccess(imageUrl: LinkedHashMap<String, String>) // key:sourceImage value:compressImage
|
||||
fun onError() // 全部上传失败时回调
|
||||
fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) // key:sourceImage value:compressImage
|
||||
fun onError(errorMap: Map<String, Exception>) // 全部上传失败时回调
|
||||
fun onProgress(total: Long, progress: Long)
|
||||
}
|
||||
|
||||
|
||||
@ -3,17 +3,18 @@ package com.gh.common.view
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.*
|
||||
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.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 com.squareup.picasso.Picasso
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlin.math.abs
|
||||
|
||||
@ -68,7 +69,6 @@ class AdBannerView : LinearLayout {
|
||||
val params = LayoutParams(LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(5F))
|
||||
params.topMargin = DisplayUtils.dip2px(8f)
|
||||
layoutParams = params
|
||||
visibility = View.GONE
|
||||
}
|
||||
addView(mIndicatorLayout)
|
||||
}
|
||||
@ -191,13 +191,7 @@ class AdBannerView : LinearLayout {
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val ad = mDatas[position % mDatas.size]
|
||||
val view = holder.itemView as SimpleDraweeView
|
||||
ImageUtils.addLimitWidthAndLoad(view, ad.image, bannerWidth, object : ImageUtils.OnImageloadListener {
|
||||
override fun onLoadFinal(imageInfo: ImageInfo?) {
|
||||
if (mIndicatorLayout.visibility == View.GONE) {
|
||||
mIndicatorLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
})
|
||||
ImageUtils.display(view,ad.image)
|
||||
holder.itemView.setOnClickListener {
|
||||
onItemClick?.invoke(position % mDatas.size)
|
||||
}
|
||||
|
||||
@ -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,134 +1,235 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
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 com.gh.gamecenter.R;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
private Rect mLastVisibleLineRect;
|
||||
private Rect mLastActualLineRect;
|
||||
|
||||
public ExpandTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
||||
public ExpandTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
mMaxLines = getMaxLines();
|
||||
}
|
||||
|
||||
mLastVisibleLineRect = new Rect();
|
||||
mLastActualLineRect = new Rect();
|
||||
|
||||
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 onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - getExtraBottomPadding());
|
||||
}
|
||||
|
||||
@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) {
|
||||
this.mExpandCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
mInitLayout = true;
|
||||
super.setText(text, type);
|
||||
}
|
||||
|
||||
private void showExpendButton() {
|
||||
|
||||
private void showExpandButton() {
|
||||
String finalEndText = "";
|
||||
TextPaint paint = getPaint();
|
||||
|
||||
Layout layout = getLayout();
|
||||
int start = layout.getLineStart(0);
|
||||
int lastLineEnd = layout.getLineEnd(mMaxLines - 1);
|
||||
int lastLineStart = layout.getLineStart(mMaxLines - 1);
|
||||
float lastLineRight = layout.getLineRight(mMaxLines - 1);
|
||||
|
||||
|
||||
int viewWidth = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||
|
||||
TextPaint paint = getPaint();
|
||||
float expendTextWidth = paint.measureText(mExpendText);
|
||||
CharSequence content = mSnapshotText.subSequence(start, lastLineEnd);
|
||||
if (viewWidth - lastLineRight > expendTextWidth) {
|
||||
content = content.toString().trim() + mExpendText;
|
||||
int additionalEndTextCount = 0;
|
||||
|
||||
float expandTextWidth;
|
||||
if (mUseGradientAlphaEndText) {
|
||||
additionalEndTextCount = DEFAULT_ADDITIONAL_END_TEXT_COUNT;
|
||||
expandTextWidth = paint.measureText(mEndText + 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;
|
||||
break;
|
||||
expandTextWidth = paint.measureText(mExpandText);
|
||||
}
|
||||
|
||||
CharSequence content = mSnapshotText.subSequence(start, lastLineEnd);
|
||||
|
||||
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 lastLineText = mSnapshotText.subSequence(lastLineStart, lastLineEnd);
|
||||
CharSequence subSequence;
|
||||
float subSequenceWidth;
|
||||
for (int i = lastLineText.length() - 1; i > 0; i--) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
subSequence = lastLineText.subSequence(0, i - additionalEndTextCount);
|
||||
subSequenceWidth = paint.measureText(subSequence.toString());
|
||||
|
||||
finalEndText = lastLineText.subSequence(i - additionalEndTextCount, i) + mEndText;
|
||||
expandTextWidth = paint.measureText(finalEndText + mExpandText);
|
||||
|
||||
if (viewWidth - subSequenceWidth > expandTextWidth) {
|
||||
finalEndText = finalEndText.replace("\n", "");
|
||||
content = mSnapshotText.subSequence(start, lastLineStart + i - additionalEndTextCount) + finalEndText + mExpandText;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
subSequence = lastLineText.subSequence(0, i);
|
||||
subSequenceWidth = paint.measureText(subSequence.toString());
|
||||
|
||||
if (viewWidth - subSequenceWidth > expandTextWidth) {
|
||||
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;
|
||||
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) {
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setColor(ContextCompat.getColor(getContext(), R.color.theme_font));
|
||||
ds.setUnderlineText(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
mOpenLayout = true;
|
||||
public void onClick(@NonNull View widget) {
|
||||
mIsExpanded = true;
|
||||
setMaxLines(Integer.MAX_VALUE);
|
||||
setText(mSnapshotText);
|
||||
|
||||
|
||||
if (mExpandCallback != null) {
|
||||
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 setExpendMaxLines(int maxLines) {
|
||||
|
||||
/**
|
||||
* 获取 maxLines + lineSpacingExtra + movementMethod 一起使用时产生的大小与 lineSpacingExtra 一样的底部空间
|
||||
*/
|
||||
private int getExtraBottomPadding() {
|
||||
int result = 0;
|
||||
// 界面上显示的最后一行的 index
|
||||
int lastVisibleLineIndex = Math.min(getMaxLines(), getLineCount()) - 1;
|
||||
// 获取实际文字的最后一行的 index
|
||||
int lastActualLineIndex = getLineCount() - 1;
|
||||
|
||||
if (lastVisibleLineIndex >= 0) {
|
||||
Layout layout = getLayout();
|
||||
int lastVisibleLineBaseline = getLineBounds(lastVisibleLineIndex, mLastVisibleLineRect);
|
||||
getLineBounds(lastActualLineIndex, mLastActualLineRect);
|
||||
int heightBetweenLastVisibleLineRectAndLastActualLineRect = (mLastActualLineRect.bottom - mLastVisibleLineRect.bottom);
|
||||
|
||||
if (getMeasuredHeight() == getLayout().getHeight() - heightBetweenLastVisibleLineRectAndLastActualLineRect) {
|
||||
result = mLastVisibleLineRect.bottom - (lastVisibleLineBaseline + layout.getPaint()
|
||||
.getFontMetricsInt().descent + getPaddingBottom());
|
||||
if (getLineSpacingExtra() > result) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = (int) getLineSpacingExtra();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法仅更改标记,不做实际展开的操作
|
||||
*/
|
||||
public void setIsExpanded(boolean isExpanded) {
|
||||
mIsExpanded = isExpanded;
|
||||
}
|
||||
|
||||
public void setExpandMaxLines(int maxLines) {
|
||||
mMaxLines = maxLines;
|
||||
setMaxLines(maxLines);
|
||||
}
|
||||
|
||||
|
||||
public interface ExpandCallback {
|
||||
void onExpand();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
790
app/src/main/java/com/gh/common/view/NestedScrollWebView2.java
Normal file
790
app/src/main/java/com/gh/common/view/NestedScrollWebView2.java
Normal file
@ -0,0 +1,790 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewParent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.OverScroller;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import androidx.core.view.AccessibilityDelegateCompat;
|
||||
import androidx.core.view.InputDeviceCompat;
|
||||
import androidx.core.view.NestedScrollingChild2;
|
||||
import androidx.core.view.NestedScrollingChildHelper;
|
||||
import androidx.core.view.NestedScrollingParent;
|
||||
import androidx.core.view.NestedScrollingParentHelper;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
||||
import wendu.dsbridge.DWebView;
|
||||
|
||||
/**
|
||||
* Copyright (c) Tuenti Technologies. All rights reserved.
|
||||
* <p>
|
||||
* WebView compatible with CoordinatorLayout.
|
||||
* The implementation based on NestedScrollView of design library
|
||||
*/
|
||||
public class NestedScrollWebView2 extends DWebView implements NestedScrollingChild2, NestedScrollingParent {
|
||||
|
||||
private static final int INVALID_POINTER = -1;
|
||||
private static final String TAG = "NestedWebView";
|
||||
|
||||
private final int[] mScrollOffset = new int[2];
|
||||
private final int[] mScrollConsumed = new int[2];
|
||||
|
||||
private int mLastMotionY;
|
||||
private NestedScrollingParentHelper mParentHelper;
|
||||
private NestedScrollingChildHelper mChildHelper;
|
||||
private boolean mIsBeingDragged = false;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private int mTouchSlop;
|
||||
private int mActivePointerId = INVALID_POINTER;
|
||||
private int mNestedYOffset;
|
||||
private OverScroller mScroller;
|
||||
private int mMinimumVelocity;
|
||||
private int mMaximumVelocity;
|
||||
private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
|
||||
|
||||
public NestedScrollWebView2(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public NestedScrollWebView2(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
private void init(){
|
||||
setOverScrollMode(WebView.OVER_SCROLL_NEVER);
|
||||
initScrollView();
|
||||
mChildHelper = new NestedScrollingChildHelper(this);
|
||||
mParentHelper = new NestedScrollingParentHelper(this);
|
||||
setNestedScrollingEnabled(true);
|
||||
ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
|
||||
}
|
||||
|
||||
|
||||
private void initScrollView() {
|
||||
mScroller = new OverScroller(getContext());
|
||||
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
|
||||
mTouchSlop = configuration.getScaledTouchSlop();
|
||||
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
|
||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
if (disallowIntercept) {
|
||||
recycleVelocityTracker();
|
||||
}
|
||||
super.requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
|
||||
final int action = ev.getAction();
|
||||
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
final int activePointerId = mActivePointerId;
|
||||
if (activePointerId == INVALID_POINTER) {
|
||||
break;
|
||||
}
|
||||
|
||||
final int pointerIndex = ev.findPointerIndex(activePointerId);
|
||||
if (pointerIndex == -1) {
|
||||
Log.e(TAG, "Invalid pointerId=" + activePointerId
|
||||
+ " in onInterceptTouchEvent");
|
||||
break;
|
||||
}
|
||||
|
||||
final int y = (int) ev.getY(pointerIndex);
|
||||
final int yDiff = Math.abs(y - mLastMotionY);
|
||||
if (yDiff > mTouchSlop
|
||||
&& (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
|
||||
mIsBeingDragged = true;
|
||||
mLastMotionY = y;
|
||||
initVelocityTrackerIfNotExists();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mNestedYOffset = 0;
|
||||
final ViewParent parent = getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
final int y = (int) ev.getY();
|
||||
|
||||
mLastMotionY = y;
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
mScroller.computeScrollOffset();
|
||||
mIsBeingDragged = !mScroller.isFinished();
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
mIsBeingDragged = false;
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
recycleVelocityTracker();
|
||||
if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
stopNestedScroll();
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onSecondaryPointerUp(ev);
|
||||
break;
|
||||
}
|
||||
|
||||
return mIsBeingDragged;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
boolean returnValue = false;
|
||||
initVelocityTrackerIfNotExists();
|
||||
|
||||
MotionEvent vtev = MotionEvent.obtain(ev);
|
||||
|
||||
final int actionMasked = ev.getActionMasked();
|
||||
|
||||
if (actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
mNestedYOffset = 0;
|
||||
}
|
||||
vtev.offsetLocation(0, mNestedYOffset);
|
||||
|
||||
switch (actionMasked) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
returnValue = super.onTouchEvent(ev);
|
||||
if (mIsBeingDragged = !mScroller.isFinished()) {
|
||||
final ViewParent parent = getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mScroller.isFinished()) {
|
||||
mScroller.abortAnimation();
|
||||
}
|
||||
mLastMotionY = (int) ev.getY();
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (activePointerIndex == -1) {
|
||||
Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
|
||||
break;
|
||||
}
|
||||
|
||||
final int y = (int) ev.getY(activePointerIndex);
|
||||
int deltaY = mLastMotionY - y;
|
||||
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
|
||||
ViewCompat.TYPE_TOUCH)) {
|
||||
deltaY -= mScrollConsumed[1];
|
||||
ev.offsetLocation(0, -mScrollOffset[1]);
|
||||
vtev.offsetLocation(0, -mScrollOffset[1]);
|
||||
mNestedYOffset += mScrollOffset[1];
|
||||
}
|
||||
|
||||
boolean notMove = mScrollOffset[1] == 0;
|
||||
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
|
||||
final ViewParent parent = getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
mIsBeingDragged = true;
|
||||
if (deltaY > 0) {
|
||||
deltaY -= mTouchSlop;
|
||||
} else {
|
||||
deltaY += mTouchSlop;
|
||||
}
|
||||
}
|
||||
if (mIsBeingDragged) {
|
||||
mLastMotionY = y - mScrollOffset[1];
|
||||
final int oldY = getScrollY();
|
||||
final int range = getScrollRange();
|
||||
int unconsumedY = 0;
|
||||
int scrolledDeltaY = deltaY;
|
||||
int expectScroll = oldY + deltaY;
|
||||
if (expectScroll < 0) {
|
||||
unconsumedY = expectScroll;
|
||||
scrolledDeltaY = oldY;
|
||||
} else if (expectScroll > range) {
|
||||
unconsumedY = range - expectScroll;
|
||||
scrolledDeltaY = expectScroll - range;
|
||||
}
|
||||
if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset,
|
||||
ViewCompat.TYPE_TOUCH)) {
|
||||
vtev.offsetLocation(0, mScrollOffset[1]);
|
||||
mNestedYOffset += mScrollOffset[1];
|
||||
mLastMotionY -= mScrollOffset[1];
|
||||
}
|
||||
}
|
||||
notMove &= (mScrollOffset[1] == 0);
|
||||
if (notMove) {
|
||||
returnValue = super.onTouchEvent(ev);
|
||||
} else {
|
||||
final ViewParent parent = getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (Math.abs(mNestedYOffset) < mTouchSlop) {
|
||||
returnValue = super.onTouchEvent(ev);
|
||||
} else {
|
||||
ev.setAction(MotionEvent.ACTION_CANCEL);
|
||||
returnValue = super.onTouchEvent(ev);
|
||||
}
|
||||
final VelocityTracker velocityTracker = mVelocityTracker;
|
||||
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
|
||||
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
|
||||
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
|
||||
flingWithNestedDispatch(-initialVelocity);
|
||||
} else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
|
||||
getScrollRange())) {
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
endDrag();
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
returnValue = true;
|
||||
if (mIsBeingDragged && getChildCount() > 0) {
|
||||
if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
|
||||
getScrollRange())) {
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
}
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
endDrag();
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
final int index = ev.getActionIndex();
|
||||
mLastMotionY = (int) ev.getY(index);
|
||||
mActivePointerId = ev.getPointerId(index);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onSecondaryPointerUp(ev);
|
||||
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.addMovement(vtev);
|
||||
}
|
||||
vtev.recycle();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
int getScrollRange() {
|
||||
//Using scroll range of webview instead of childs as NestedScrollView does.
|
||||
return computeVerticalScrollRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOverScrolled(int scrollX, int scrollY,
|
||||
boolean clampedX, boolean clampedY) {
|
||||
super.scrollTo(scrollX, scrollY);
|
||||
}
|
||||
|
||||
boolean overScrollByCompat(int deltaX, int deltaY,
|
||||
int scrollX, int scrollY,
|
||||
int scrollRangeX, int scrollRangeY,
|
||||
int maxOverScrollX, int maxOverScrollY,
|
||||
boolean isTouchEvent) {
|
||||
final int overScrollMode = getOverScrollMode();
|
||||
final boolean canScrollHorizontal =
|
||||
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
|
||||
final boolean canScrollVertical =
|
||||
computeVerticalScrollRange() > computeVerticalScrollExtent();
|
||||
final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
|
||||
|| (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
|
||||
final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
|
||||
|| (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
|
||||
|
||||
int newScrollX = scrollX + deltaX;
|
||||
if (!overScrollHorizontal) {
|
||||
maxOverScrollX = 0;
|
||||
}
|
||||
|
||||
int newScrollY = scrollY + deltaY;
|
||||
if (!overScrollVertical) {
|
||||
maxOverScrollY = 0;
|
||||
}
|
||||
|
||||
// Clamp values if at the limits and record
|
||||
final int left = -maxOverScrollX;
|
||||
final int right = maxOverScrollX + scrollRangeX;
|
||||
final int top = -maxOverScrollY;
|
||||
final int bottom = maxOverScrollY + scrollRangeY;
|
||||
|
||||
boolean clampedX = false;
|
||||
if (newScrollX > right) {
|
||||
newScrollX = right;
|
||||
clampedX = true;
|
||||
} else if (newScrollX < left) {
|
||||
newScrollX = left;
|
||||
clampedX = true;
|
||||
}
|
||||
|
||||
boolean clampedY = false;
|
||||
if (newScrollY > bottom) {
|
||||
newScrollY = bottom;
|
||||
clampedY = true;
|
||||
} else if (newScrollY < top) {
|
||||
newScrollY = top;
|
||||
clampedY = true;
|
||||
}
|
||||
|
||||
if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
|
||||
mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
|
||||
}
|
||||
|
||||
onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
|
||||
|
||||
return clampedX || clampedY;
|
||||
}
|
||||
|
||||
private float mVerticalScrollFactor;
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_SCROLL: {
|
||||
if (!mIsBeingDragged) {
|
||||
final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
||||
if (vscroll != 0) {
|
||||
final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
|
||||
final int range = getScrollRange();
|
||||
int oldScrollY = getScrollY();
|
||||
int newScrollY = oldScrollY - delta;
|
||||
if (newScrollY < 0) {
|
||||
newScrollY = 0;
|
||||
} else if (newScrollY > range) {
|
||||
newScrollY = range;
|
||||
}
|
||||
if (newScrollY != oldScrollY) {
|
||||
super.scrollTo(getScrollX(), newScrollY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private float getVerticalScrollFactorCompat() {
|
||||
if (mVerticalScrollFactor == 0) {
|
||||
TypedValue outValue = new TypedValue();
|
||||
final Context context = getContext();
|
||||
if (!context.getTheme().resolveAttribute(
|
||||
android.R.attr.listPreferredItemHeight, outValue, true)) {
|
||||
throw new IllegalStateException(
|
||||
"Expected theme to define listPreferredItemHeight.");
|
||||
}
|
||||
mVerticalScrollFactor = outValue.getDimension(
|
||||
context.getResources().getDisplayMetrics());
|
||||
}
|
||||
return mVerticalScrollFactor;
|
||||
}
|
||||
|
||||
|
||||
private void endDrag() {
|
||||
mIsBeingDragged = false;
|
||||
recycleVelocityTracker();
|
||||
stopNestedScroll();
|
||||
}
|
||||
|
||||
private void onSecondaryPointerUp(MotionEvent ev) {
|
||||
final int pointerIndex = ev.getActionIndex();
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// This was our active pointer going up. Choose a new
|
||||
// active pointer and adjust accordingly.
|
||||
// TODO: Make this decision more intelligent.
|
||||
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
||||
mLastMotionY = (int) ev.getY(newPointerIndex);
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initOrResetVelocityTracker() {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
} else {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void initVelocityTrackerIfNotExists() {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
}
|
||||
|
||||
private void recycleVelocityTracker() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void flingWithNestedDispatch(int velocityY) {
|
||||
final int scrollY = getScrollY();
|
||||
final boolean canFling = (scrollY > 0 || velocityY > 0)
|
||||
&& (scrollY < getScrollRange() || velocityY < 0);
|
||||
if (!dispatchNestedPreFling(0, velocityY)) {
|
||||
dispatchNestedFling(0, velocityY, canFling);
|
||||
fling(velocityY);
|
||||
}
|
||||
}
|
||||
|
||||
public void fling(int velocityY) {
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
|
||||
mScroller.fling(getScrollX(), getScrollY(), // start
|
||||
0, velocityY, // velocities
|
||||
0, 0, // x
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE, // y
|
||||
0, 0); // overscroll
|
||||
mLastScrollerY = getScrollY();
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
}
|
||||
|
||||
private int mLastScrollerY;
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
final int x = mScroller.getCurrX();
|
||||
final int y = mScroller.getCurrY();
|
||||
|
||||
int dy = y - mLastScrollerY;
|
||||
|
||||
// Dispatch up to parent
|
||||
if (dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH)) {
|
||||
dy -= mScrollConsumed[1];
|
||||
}
|
||||
|
||||
if (dy != 0) {
|
||||
final int range = getScrollRange();
|
||||
final int oldScrollY = getScrollY();
|
||||
|
||||
overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false);
|
||||
|
||||
final int scrolledDeltaY = getScrollY() - oldScrollY;
|
||||
final int unconsumedY = dy - scrolledDeltaY;
|
||||
|
||||
if (!dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, null,
|
||||
ViewCompat.TYPE_NON_TOUCH)) {
|
||||
final int mode = getOverScrollMode();
|
||||
final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS
|
||||
|| (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
|
||||
if (canOverscroll) {
|
||||
// ensureGlows();
|
||||
// if (y <= 0 && oldScrollY > 0) {
|
||||
// mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
|
||||
// } else if (y >= range && oldScrollY < range) {
|
||||
// mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally update the scroll positions and post an invalidation
|
||||
mLastScrollerY = y;
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
} else {
|
||||
// We can't scroll any more, so stop any indirect scrolling
|
||||
if (hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
|
||||
stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
|
||||
}
|
||||
// and reset the scroller y
|
||||
mLastScrollerY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedScrollingEnabled() {
|
||||
return mChildHelper.isNestedScrollingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNestedScrollingEnabled(boolean enabled) {
|
||||
mChildHelper.setNestedScrollingEnabled(enabled);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNestedScrollAxes() {
|
||||
return mParentHelper.getNestedScrollAxes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startNestedScroll(int axes, int type) {
|
||||
return mChildHelper.startNestedScroll(axes, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopNestedScroll(int type) {
|
||||
mChildHelper.stopNestedScroll(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNestedScrollingParent(int type) {
|
||||
return mChildHelper.hasNestedScrollingParent(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
|
||||
int dyUnconsumed, int[] offsetInWindow, int type) {
|
||||
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
|
||||
offsetInWindow, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
|
||||
int type) {
|
||||
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
|
||||
int dyUnconsumed) {
|
||||
final int oldScrollY = getScrollY();
|
||||
scrollBy(0, dyUnconsumed);
|
||||
final int myConsumed = getScrollY() - oldScrollY;
|
||||
final int myUnconsumed = dyUnconsumed - myConsumed;
|
||||
dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
|
||||
dispatchNestedPreScroll(dx, dy, consumed, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
|
||||
if (!consumed) {
|
||||
flingWithNestedDispatch((int) velocityY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
|
||||
return dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
|
||||
// nested scroll parent
|
||||
@Override
|
||||
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
|
||||
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
|
||||
mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopNestedScroll(View target) {
|
||||
mParentHelper.onStopNestedScroll(target);
|
||||
stopNestedScroll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #scrollTo}, but scroll smoothly instead of immediately.
|
||||
*
|
||||
* @param x the position where to scroll on the X axis
|
||||
* @param y the position where to scroll on the Y axis
|
||||
*/
|
||||
public final void smoothScrollTo(int x, int y) {
|
||||
smoothScrollBy(x - getScrollX(), y - getScrollY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
|
||||
*
|
||||
* @param dx the number of pixels to scroll by on the X axis
|
||||
* @param dy the number of pixels to scroll by on the Y axis
|
||||
*/
|
||||
private long mLastScroll;
|
||||
static final int ANIMATED_SCROLL_GAP = 250;
|
||||
|
||||
public final void smoothScrollBy(int dx, int dy) {
|
||||
long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
|
||||
if (duration > ANIMATED_SCROLL_GAP) {
|
||||
final int height = getHeight() - getPaddingBottom() - getPaddingTop();
|
||||
final int bottom = getHeight();
|
||||
final int maxY = Math.max(0, bottom - height);
|
||||
final int scrollY = getScrollY();
|
||||
dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
|
||||
|
||||
mScroller.startScroll(getScrollX(), scrollY, 0, dy);
|
||||
ViewCompat.postInvalidateOnAnimation(this);
|
||||
} else {
|
||||
if (!mScroller.isFinished()) {
|
||||
mScroller.abortAnimation();
|
||||
}
|
||||
scrollBy(dx, dy);
|
||||
}
|
||||
mLastScroll = AnimationUtils.currentAnimationTimeMillis();
|
||||
}
|
||||
|
||||
static class AccessibilityDelegate extends AccessibilityDelegateCompat {
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
|
||||
if (super.performAccessibilityAction(host, action, arguments)) {
|
||||
return true;
|
||||
}
|
||||
final NestedScrollWebView2 nsvHost = (NestedScrollWebView2) host;
|
||||
if (!nsvHost.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
switch (action) {
|
||||
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
|
||||
final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
|
||||
- nsvHost.getPaddingTop();
|
||||
final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
|
||||
nsvHost.getScrollRange());
|
||||
if (targetScrollY != nsvHost.getScrollY()) {
|
||||
nsvHost.smoothScrollTo(0, targetScrollY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
|
||||
final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
|
||||
- nsvHost.getPaddingTop();
|
||||
final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
|
||||
if (targetScrollY != nsvHost.getScrollY()) {
|
||||
nsvHost.smoothScrollTo(0, targetScrollY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info);
|
||||
final NestedScrollWebView2 nsvHost = (NestedScrollWebView2) host;
|
||||
info.setClassName(ScrollView.class.getName());
|
||||
if (nsvHost.isEnabled()) {
|
||||
final int scrollRange = nsvHost.getScrollRange();
|
||||
if (scrollRange > 0) {
|
||||
info.setScrollable(true);
|
||||
if (nsvHost.getScrollY() > 0) {
|
||||
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
if (nsvHost.getScrollY() < scrollRange) {
|
||||
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(host, event);
|
||||
final NestedScrollWebView2 nsvHost = (NestedScrollWebView2) host;
|
||||
event.setClassName(ScrollView.class.getName());
|
||||
final boolean scrollable = nsvHost.getScrollRange() > 0;
|
||||
event.setScrollable(scrollable);
|
||||
event.setScrollX(nsvHost.getScrollX());
|
||||
event.setScrollY(nsvHost.getScrollY());
|
||||
AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX());
|
||||
AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any current scroll
|
||||
* #fling(int, int)} or a touch-initiated fling.
|
||||
*/
|
||||
public void stopScroll() {
|
||||
if (mScroller != null) {
|
||||
mScroller.forceFinished(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
});
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@ 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 com.gh.common.AppExecutor;
|
||||
import com.gh.common.exposure.ExposureEvent;
|
||||
import com.gh.common.exposure.ExposureUtils;
|
||||
import com.gh.common.history.HistoryHelper;
|
||||
import com.gh.common.util.AppDebugConfig;
|
||||
import com.gh.common.util.DataCollectionUtils;
|
||||
import com.gh.common.util.DeviceUtils;
|
||||
@ -30,7 +30,6 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository;
|
||||
import com.google.gson.Gson;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.config.CommonDebug;
|
||||
import com.lightgame.download.ConnectionUtils;
|
||||
import com.lightgame.download.DataChanger;
|
||||
@ -57,14 +56,16 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
import static android.os.Build.MANUFACTURER;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
private static DownloadManager mInstance;
|
||||
private static Gson gson = new Gson();
|
||||
private static final String HINT_MARK = "hint_mark";
|
||||
private static final String UPDATE_IS_READ_MARK = "update_is_read";
|
||||
private static final String DOWNLOADING_IS_READ_MARK = "downloading_is_read";
|
||||
private static final String DOWNLOADED_IS_READ_MARK = "downloaded_is_read";
|
||||
|
||||
private Context mContext;
|
||||
private Handler mHandler;
|
||||
@ -135,7 +136,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
mContext = context.getApplicationContext();
|
||||
mDownloadDao = DownloadDao.getInstance(mContext);
|
||||
|
||||
mUpdateMarks = SPUtils.getStringSet(HINT_MARK);
|
||||
mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK);
|
||||
|
||||
//TODO unregister this
|
||||
DownloadStatusManager.getInstance().registerTaskStatusListener(this);
|
||||
@ -151,6 +152,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
statusMap = new ArrayMap<>();
|
||||
downloadingMap = new ArrayMap<>();
|
||||
|
||||
// TODO 这里的 handler 类的 case 可能会被多次调用,原因未知
|
||||
mHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
@ -191,7 +193,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
downloadingMap.put(downloadEntity.getUrl(), downloadEntity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ArrayMap<String, DownloadEntity> getDownloadingMap() {
|
||||
@ -228,21 +229,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
boolean isSubscribe,
|
||||
@Nullable ExposureEvent traceEvent) {
|
||||
|
||||
// 安装指引
|
||||
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)) {
|
||||
try {
|
||||
DialogUtils.showInstallHintDialog(context,
|
||||
() -> edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply());
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
edit.putBoolean("InstallHint" + PackageUtils.getVersionName(), false).apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 插件版本下载互斥弹窗
|
||||
List<String> mutexPackage = gameEntity.getMutexPackage();
|
||||
if (mutexPackage != null && mutexPackage.size() > 0) {
|
||||
@ -265,7 +251,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
downloadEntity.setPackageName(apkEntity.getPackageName());
|
||||
downloadEntity.setGameId(gameEntity.getId());
|
||||
downloadEntity.setEntrance(entrance);
|
||||
downloadEntity.setExposureTrace(gson.toJson(traceEvent));
|
||||
downloadEntity.setLocation(location);
|
||||
downloadEntity.setVersionName(apkEntity.getVersion());
|
||||
int installed = 0;
|
||||
@ -275,16 +260,26 @@ 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()));
|
||||
|
||||
ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, gameEntity.getId());
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, gameEntity.getPlatform(), traceEvent, downloadType);
|
||||
|
||||
// 将下载事件放入 downloadEntity 中供下载完成时取出使用
|
||||
downloadEntity.setExposureTrace(gson.toJson(downloadExposureEvent));
|
||||
|
||||
if (isSubscribe) {
|
||||
DownloadManager.getInstance(context).subscribe(downloadEntity);
|
||||
} else {
|
||||
HistoryHelper.insertGameEntity(gameEntity);
|
||||
DownloadManager.getInstance(context).add(downloadEntity);
|
||||
}
|
||||
|
||||
@ -295,6 +290,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
//TODO remove
|
||||
DownloadManager.getInstance(context).putStatus(downloadEntity.getUrl(), DownloadStatus.downloading);
|
||||
|
||||
DownloadManager.getInstance(context).markDownloadingTaskAsUnread();
|
||||
// 收集下载数据
|
||||
DataCollectionUtils.uploadDownload(context, downloadEntity, "开始");
|
||||
GdtHelper.INSTANCE.logAction(ActionType.DOWNLOAD_APP,
|
||||
@ -632,8 +628,8 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
}
|
||||
}
|
||||
|
||||
// 开启下载服务, Fuck me, 即便是在启动页调用的方法,依然有可能触发 `unable is in background`
|
||||
startDownloadService();
|
||||
checkRetryDownload();
|
||||
}
|
||||
|
||||
public void addObserver(DataWatcher dataWatcher) {
|
||||
@ -648,11 +644,11 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
private void startDownloadService() {
|
||||
Intent serviceIntent = new Intent(mContext, DownloadService.class);
|
||||
// 当满足系统版本大于 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);
|
||||
// 当满足系统版本大于 8.0 并且进程处于 CREATED 状态 (ON_STOP后的状态) 的时候才以前台服务开启
|
||||
// "not allowed to start service, app is in background" 的错误概率比 "startForegroundService() did not then call Service.startForeground() " 低
|
||||
// 所以还是老老实实地以 startService 为主吧
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
|
||||
mContext.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
mContext.startService(serviceIntent);
|
||||
@ -674,8 +670,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
Intent serviceIntent = getIntent(downloadEntity, status);
|
||||
// 当满足系统版本大于 8.0 、应用在后台运行时以前台服务开启
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& !HaloApp.getInstance().isRunningForeground) {
|
||||
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
|
||||
&& ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
|
||||
mContext.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
mContext.startService(serviceIntent);
|
||||
@ -713,19 +708,26 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
*/
|
||||
@Nullable
|
||||
public String getDownloadOrUpdateCount(List<GameUpdateEntity> updateList) {
|
||||
boolean containsReadDownloadingTask = false; // 存在已读的下载中任务
|
||||
boolean showRedPoint = false;
|
||||
int downloadingSize = 0;
|
||||
|
||||
for (DownloadEntity downloadEntity : getAll()) {
|
||||
if (DownloadStatus.done.equals(downloadEntity.getStatus())) {
|
||||
String mark = downloadEntity.getMeta().get(HINT_MARK);
|
||||
String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK);
|
||||
if (TextUtils.isEmpty(mark)) showRedPoint = true;
|
||||
} else {
|
||||
// 存在已读的下载中任务就直接不返回下载中数量,因为在新建下载时就执行了将所有下载中任务的已读变为未读的操作
|
||||
if (!TextUtils.isEmpty(downloadEntity.getMeta().get(DOWNLOADING_IS_READ_MARK))) {
|
||||
containsReadDownloadingTask = true;
|
||||
}
|
||||
downloadingSize++;
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadingSize != 0) return String.valueOf(downloadingSize);
|
||||
if (downloadingSize != 0 && !containsReadDownloadingTask) {
|
||||
return String.valueOf(downloadingSize);
|
||||
}
|
||||
|
||||
if (showRedPoint) return "";
|
||||
|
||||
@ -740,6 +742,31 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getUnreadUpdateCount(List<GameUpdateEntity> updateList) {
|
||||
int unreadUpdateCount = 0;
|
||||
if (updateList != null) {
|
||||
for (GameUpdateEntity updateEntity : updateList) {
|
||||
if (updateEntity.isShowPlugin(PluginLocation.only_index)
|
||||
&& !mUpdateMarks.contains(updateEntity.getId() + updateEntity.getPackageName())) {
|
||||
unreadUpdateCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return unreadUpdateCount;
|
||||
}
|
||||
|
||||
public boolean isContainsUnreadDownloadedTask() {
|
||||
for (DownloadEntity downloadEntity : getAll()) {
|
||||
if (DownloadStatus.done.equals(downloadEntity.getStatus())) {
|
||||
String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK);
|
||||
if (TextUtils.isEmpty(mark)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记下载已完成的任务为已读 (用于下载管理页入口的 toolbar 红点显示)
|
||||
*/
|
||||
@ -751,9 +778,54 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
for (DownloadEntity downloadEntity : all) {
|
||||
DownloadStatus status = downloadEntity.getStatus();
|
||||
if (status == DownloadStatus.done) {
|
||||
String mark = downloadEntity.getMeta().get(HINT_MARK);
|
||||
String mark = downloadEntity.getMeta().get(DOWNLOADED_IS_READ_MARK);
|
||||
if (TextUtils.isEmpty(mark)) {
|
||||
downloadEntity.getMeta().put(HINT_MARK, HINT_MARK);
|
||||
downloadEntity.getMeta().put(DOWNLOADED_IS_READ_MARK, DOWNLOADED_IS_READ_MARK);
|
||||
mDownloadDao.newOrUpdate(downloadEntity);
|
||||
if (!markHasChanged) markHasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (markHasChanged) {
|
||||
EventBus.getDefault().post(new EBDownloadStatus("download", "", "", "", "", ""));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记下载中任务为已读状态(用于下载页及外部toolbar)
|
||||
*/
|
||||
public void markDownloadingTaskAsRead() {
|
||||
markDownloadingTaskAsReadOrUnread(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记下载中任务为未读状态(用于下载页及外部toolbar)
|
||||
*/
|
||||
public void markDownloadingTaskAsUnread() {
|
||||
markDownloadingTaskAsReadOrUnread(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改下载中任务的已读状态(用于下载页及外部toolbar)
|
||||
*/
|
||||
private void markDownloadingTaskAsReadOrUnread(boolean isRead) {
|
||||
AppExecutor.getIoExecutor().execute(() -> {
|
||||
boolean markHasChanged = false;
|
||||
|
||||
List<DownloadEntity> all = getAll();
|
||||
for (DownloadEntity downloadEntity : all) {
|
||||
if (downloadEntity.getStatus() != DownloadStatus.done) {
|
||||
if (isRead) {
|
||||
String mark = downloadEntity.getMeta().get(DOWNLOADING_IS_READ_MARK);
|
||||
if (TextUtils.isEmpty(mark)) {
|
||||
downloadEntity.getMeta().put(DOWNLOADING_IS_READ_MARK, DOWNLOADING_IS_READ_MARK);
|
||||
mDownloadDao.newOrUpdate(downloadEntity);
|
||||
if (!markHasChanged) markHasChanged = true;
|
||||
}
|
||||
} else {
|
||||
downloadEntity.getMeta().put(DOWNLOADING_IS_READ_MARK, "");
|
||||
mDownloadDao.newOrUpdate(downloadEntity);
|
||||
if (!markHasChanged) markHasChanged = true;
|
||||
}
|
||||
@ -795,7 +867,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
public void saveUpdateMarkToStorage() {
|
||||
ArrayList<GameUpdateEntity> updates = PackageRepository.INSTANCE.getGameUpdate();
|
||||
if (updates.size() == mUpdateMarks.size()) {
|
||||
SPUtils.setStringSet(HINT_MARK, mUpdateMarks);
|
||||
SPUtils.setStringSet(UPDATE_IS_READ_MARK, mUpdateMarks);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -804,7 +876,18 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
String mark = update.getId() + update.getPackageName();
|
||||
marks.add(mark);
|
||||
}
|
||||
SPUtils.setStringSet(HINT_MARK, marks);
|
||||
SPUtils.setStringSet(UPDATE_IS_READ_MARK, marks);
|
||||
}
|
||||
|
||||
public void updateDownloadEntity(DownloadEntity downloadEntity) {
|
||||
mDownloadDao.newOrUpdate(downloadEntity);
|
||||
}
|
||||
|
||||
public void startDownload(String url) {
|
||||
put(url, System.currentTimeMillis());
|
||||
Message msg = Message.obtain();
|
||||
msg.what = DownloadConfig.CONTINUE_DOWNLOAD_AUTO_TASK;
|
||||
msg.obj = url;
|
||||
sendMessageDelayed(msg, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -15,43 +16,67 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import tv.danmaku.ijk.media.exo2.ExoSourceManager
|
||||
import tv.danmaku.ijk.media.exo2.source.GSYExoHttpDataSource
|
||||
import tv.danmaku.ijk.media.exo2.source.GSYDefaultHttpDataSource
|
||||
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 fun getPreLength(): Long {
|
||||
val totalRamSizeOfDevice = DeviceUtils.getTotalRamSizeOfDevice(HaloApp.getInstance().application)
|
||||
return if (totalRamSizeOfDevice <= 2 * 1024) {
|
||||
if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) {
|
||||
10 * 1024 * 1024L
|
||||
} else {
|
||||
when (NetworkUtils.getMobileNetworkType(HaloApp.getInstance().application)) {
|
||||
"5G", "4G" -> 10 * 1024 * 1024L
|
||||
"3G" -> 5 * 1024 * 1024L
|
||||
else -> 0L
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) {
|
||||
50 * 1024 * 1024L
|
||||
} else {
|
||||
when (NetworkUtils.getMobileNetworkType(HaloApp.getInstance().application)) {
|
||||
"5G", "4G" -> 20 * 1024 * 1024L
|
||||
"3G" -> 5 * 1024 * 1024L
|
||||
else -> 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun preload(videoUri: String) {
|
||||
if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) {
|
||||
runOnIoThread {
|
||||
threads[videoUri] = AtomicBoolean(false)
|
||||
val contentLength = getContentLength(videoUri)
|
||||
val cacheLength = if (contentLength >= preLength) preLength else contentLength
|
||||
val dataSpec = DataSpec(Uri.parse(videoUri), 0, cacheLength, null)
|
||||
val simpleCache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
|
||||
val dataSourceFactory = GSYExoHttpDataSourceFactory(Util.getUserAgent(HaloApp.getInstance().application,
|
||||
"ExoCacheManager"), DefaultBandwidthMeter.Builder(HaloApp.getInstance().application).build(),
|
||||
GSYExoHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||
GSYExoHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false)
|
||||
val cacheDataSource = CacheDataSource(simpleCache, dataSourceFactory.createDataSource())
|
||||
try {
|
||||
CacheUtil.cache(dataSpec, simpleCache, CacheUtil.DEFAULT_CACHE_KEY_FACTORY, cacheDataSource, CacheUtil.ProgressListener { requestLength, bytesCached, newBytesCached ->
|
||||
if (requestLength == bytesCached) {
|
||||
threads.remove(videoUri)
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("$requestLength--$bytesCached--$newBytesCached")
|
||||
}
|
||||
}, threads[videoUri])
|
||||
} catch (e: Exception) {
|
||||
threads.remove(videoUri)
|
||||
e.printStackTrace()
|
||||
}
|
||||
val preLength = getPreLength()
|
||||
if (preLength == 0L) return
|
||||
runOnIoThread {
|
||||
Thread.sleep(100)
|
||||
threads[videoUri] = AtomicBoolean(false)
|
||||
val contentLength = getContentLength(videoUri)
|
||||
val cacheLength = if (contentLength >= preLength) preLength else contentLength
|
||||
val dataSpec = DataSpec(Uri.parse(videoUri), 0, cacheLength, null)
|
||||
val simpleCache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
|
||||
val dataSourceFactory = GSYExoHttpDataSourceFactory(Util.getUserAgent(HaloApp.getInstance().application,
|
||||
"ExoCacheManager"), DefaultBandwidthMeter.Builder(HaloApp.getInstance().application).build(),
|
||||
GSYDefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
|
||||
GSYDefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false)
|
||||
val cacheDataSource = CacheDataSource(simpleCache, dataSourceFactory.createDataSource())
|
||||
try {
|
||||
CacheUtil.cache(dataSpec, simpleCache, CacheUtil.DEFAULT_CACHE_KEY_FACTORY, cacheDataSource, CacheUtil.ProgressListener { requestLength, bytesCached, newBytesCached ->
|
||||
if (requestLength == bytesCached) {
|
||||
threads.remove(videoUri)
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("$requestLength--$bytesCached--$newBytesCached")
|
||||
}
|
||||
}, threads[videoUri])
|
||||
} catch (e: Throwable) {
|
||||
threads.remove(videoUri)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
427
app/src/main/java/com/gh/download/dialog/DownloadDialog.kt
Normal file
427
app/src/main/java/com/gh/download/dialog/DownloadDialog.kt
Normal file
@ -0,0 +1,427 @@
|
||||
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.exposure.ExposureEvent
|
||||
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 mTraceEvent: ExposureEvent? = 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) ?: ""
|
||||
mTraceEvent = requireArguments().getParcelable(EntranceUtils.KEY_TRACE_EVENT) ?: null
|
||||
|
||||
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, mTraceEvent, 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, mTraceEvent, 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, traceEvent: ExposureEvent?, 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)
|
||||
bundle.putParcelable(EntranceUtils.KEY_TRACE_EVENT, traceEvent)
|
||||
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,132 @@
|
||||
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.exposure.ExposureEvent
|
||||
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 mTraceEvent: ExposureEvent?,
|
||||
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, mTraceEvent, mEntrance, mPath, mLocation)
|
||||
throwExceptionInDebug("合集页面不应该存在该条数据", isCollectionPage)
|
||||
}
|
||||
is DownloadDialogItemViewHolder -> {
|
||||
holder.bindItem(listData[position].normal!!, viewModel, isCollectionPage, mTraceEvent, 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,129 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import android.view.View
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.exposure.ExposureEvent
|
||||
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, traceEvent: ExposureEvent?, 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, traceEvent, 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,300 @@
|
||||
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.exposure.ExposureEvent
|
||||
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.gh.gamecenter.manager.PackagesManager
|
||||
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,
|
||||
traceEvent: ExposureEvent?,
|
||||
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 ||
|
||||
PackagesManager.isInstalled(apkEntity.packageName) && Config.getSettings()?.gameDownloadBlackList?.contains(apkEntity.packageName) == true) {
|
||||
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, traceEvent, 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,
|
||||
traceEvent: ExposureEvent?,
|
||||
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, "下载", traceEvent, 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, "插件化", traceEvent, entrance, location)
|
||||
|
||||
mtaValue = gameEntity.name + "_" + apkEntity.getPlatformName() + "_插件化"
|
||||
}
|
||||
DownloadDialogItemStatus.UPDATE -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, "更新", traceEvent, 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,
|
||||
traceEvent: ExposureEvent?,
|
||||
entrance: String,
|
||||
location: String) {
|
||||
// todo 有时间存储判断统一处理
|
||||
val msg = FileUtils.isCanDownload(context, apkEntity.size)
|
||||
if (msg.isNullOrEmpty()) {
|
||||
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, traceEvent)
|
||||
|
||||
DeviceRemindDialog.showDeviceRemindDialog(context, gameEntity)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Utils.toast(context, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 + "_求版本")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.gh.download.dialog
|
||||
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.databinding.DownloadDialogLinkItemBinding
|
||||
import com.gh.gamecenter.databinding.DownloadDialogSectionItemBinding
|
||||
|
||||
class DownloadDialogSectionItemViewHolder(val binding: DownloadDialogSectionItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user