Compare commits
460 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 895d4d5cf1 | |||
| 801f0b95e7 | |||
| 11979240ab | |||
| bef6cbb212 | |||
| 032a89e0cd | |||
| c9afb6df02 | |||
| b8092447ff | |||
| bff20bea49 | |||
| 87f2d9c85f | |||
| 154dfc8538 | |||
| fddcdfb3aa | |||
| a305db7b13 | |||
| 58b1cd4b12 | |||
| 5e7559e43f | |||
| 2a74e35388 | |||
| 5b9bef79da | |||
| 9da6cbf097 | |||
| 4a1c81ffb4 | |||
| be26f5168b | |||
| 4e6c75995c | |||
| df693ce0c2 | |||
| f0236d7ad5 | |||
| 1d3e2b5c16 | |||
| 545d257135 | |||
| f5164d2102 | |||
| 9af83be9a7 | |||
| ad7543e7bc | |||
| 1bd0db013b | |||
| ef6a2c58ff | |||
| 2aa41661a2 | |||
| e326f072d2 | |||
| f53cc18ab6 | |||
| 723a504d7c | |||
| dab48607e8 | |||
| 3ee69146bb | |||
| ca86a66b14 | |||
| c92bdd3014 | |||
| 0be23f26f2 | |||
| 6ce4592e5e | |||
| 92caedb011 | |||
| f796411fa8 | |||
| d45f185f77 | |||
| 10028dfebc | |||
| fa06795cef | |||
| c1eb324d79 | |||
| cdf9528583 | |||
| 5f5a621bd1 | |||
| b8d54dfa59 | |||
| b4f39d09e4 | |||
| a3aea6259c | |||
| 73e4c90956 | |||
| c1432159f3 | |||
| 45f47d98ba | |||
| 2e9638b8c5 | |||
| 7ea30c1d0e | |||
| 034e04944a | |||
| 99326596d5 | |||
| 1cc2b85816 | |||
| f4cd9419a4 | |||
| a24b9d92c7 | |||
| 8e7e83ad72 | |||
| cd810f0048 | |||
| fa050039cd | |||
| 935fb1149f | |||
| 951817455a | |||
| e71e7f6163 | |||
| 09be5e157d | |||
| 1f5e59fc1d | |||
| e58861afa4 | |||
| 44e51ecb0a | |||
| 794c17a1bb | |||
| 1b287962f0 | |||
| 8bdf98b9d8 | |||
| e1aeb61c9b | |||
| a39a70ac2d | |||
| 4fdcaaf591 | |||
| b4502638ff | |||
| 05502d56b1 | |||
| 90e1e8a40f | |||
| b10293da50 | |||
| 83cf0687e6 | |||
| f56b03716a | |||
| 61a41e6039 | |||
| 20ae9fe0ec | |||
| 74fdec9d72 | |||
| 3437265cc1 | |||
| 271993a876 | |||
| 3a8b7bb920 | |||
| fd85f3889d | |||
| 556ecea749 | |||
| 8a97844676 | |||
| f152296e7a | |||
| 4ccc789c7c | |||
| 747b02eb7a | |||
| a73c033c03 | |||
| 939db8c820 | |||
| 8601440d97 | |||
| 7a1fa90175 | |||
| 46722ba69d | |||
| 70d9d461bf | |||
| 91944df6a4 | |||
| ef86c1158c | |||
| 0f3b6ed34b | |||
| 8b50620ddc | |||
| 8b2a9eb6ca | |||
| 7297f5480e | |||
| 3bee8cc034 | |||
| 584a16e111 | |||
| 6460c7f8d6 | |||
| feb99c9f78 | |||
| 68ac809bcb | |||
| de207f66d9 | |||
| 054fcd2049 | |||
| 497fc998fe | |||
| 03f76453ab | |||
| 0a87bd354a | |||
| 1baceaef15 | |||
| 9719a7fa28 | |||
| 1718a66126 | |||
| b317ef3a39 | |||
| 8b0cd69ae6 | |||
| c7126e9836 | |||
| f3d01335a4 | |||
| 88e029b129 | |||
| 2bc72328c1 | |||
| 81179b1f72 | |||
| 9a7d4997c2 | |||
| 3b6ac881c2 | |||
| 6b5fb7d8bc | |||
| 1551b0a358 | |||
| 6540af8386 | |||
| 9badcdc382 | |||
| 0ba94fa56f | |||
| 52c1343ade | |||
| 7a0e633b79 | |||
| 9b68b05d7d | |||
| 9e7f6b0854 | |||
| cf20ad6fc2 | |||
| cb7bdba338 | |||
| f1fc06ca84 | |||
| 18f9fe7fcf | |||
| 18816a8a4e | |||
| 8d379501cb | |||
| 8f4c6abfd3 | |||
| a24d0a9618 | |||
| 8cdd66cd89 | |||
| 3fd34576e8 | |||
| 78d5cbcc42 | |||
| 803f2bef75 | |||
| 40c7e8f9e6 | |||
| b3c5ca6112 | |||
| 31067ea66d | |||
| 4e7626ff41 | |||
| e62822505e | |||
| cbc705d1eb | |||
| aa2e147a51 | |||
| 6249726839 | |||
| 8f4bc5a164 | |||
| eee459d08a | |||
| 81b4e40dbf | |||
| 261068e286 | |||
| 84364a7c66 | |||
| 3beb47a8be | |||
| ca76af4474 | |||
| bc33673533 | |||
| c5d038a173 | |||
| b1e492df1b | |||
| 9d9c213af7 | |||
| d3f97ea527 | |||
| 0a5fb4cb1d | |||
| 6905f7191a | |||
| bad7fb4922 | |||
| 250fa7eb7b | |||
| f0cd8567cb | |||
| f6dd35e4b8 | |||
| 2dc299e7f4 | |||
| 366e8ded14 | |||
| 8175742143 | |||
| fc858f1272 | |||
| 84b668714b | |||
| 5ea5346ee8 | |||
| c2eb0b267c | |||
| 7fb502e87e | |||
| 98726ddc3a | |||
| 75ff76acf4 | |||
| ba552812a3 | |||
| 83bfeb0abc | |||
| 70f1b7a678 | |||
| 455d53fee0 | |||
| 39e4b5bf55 | |||
| 40a729b6f8 | |||
| 9643176e06 | |||
| 1a70c33bef | |||
| 888ebe5f54 | |||
| cb02dbae57 | |||
| 2683d02dcd | |||
| ceb924e8f1 | |||
| bd91609a80 | |||
| 2fd74a4698 | |||
| e9e0d3b43e | |||
| 17e09ddad3 | |||
| 65427c55d6 | |||
| e1fc23a1bb | |||
| 2d551a3f73 | |||
| 7f99f75c6f | |||
| 548aea8d13 | |||
| a177137744 | |||
| 3c6443d78f | |||
| ba81ed9cb0 | |||
| 6a1cbd10c6 | |||
| 9b205366f7 | |||
| 7e9ac0c4f1 | |||
| fe889639b3 | |||
| 848e43af28 | |||
| c7a3893fae | |||
| f7d633188c | |||
| e1e7d2d3d6 | |||
| 2b8a280768 | |||
| 6efe96eb0d | |||
| 3562fe9273 | |||
| 739ef44a8b | |||
| 879b42dbf2 | |||
| fae626bb98 | |||
| cebd639d78 | |||
| 17a99a1cda | |||
| 2a03683e1e | |||
| a579b3fe10 | |||
| 9c75dd18df | |||
| cdc9c86852 | |||
| fab1851436 | |||
| a6704e46a9 | |||
| e188f70eb6 | |||
| 02dd115886 | |||
| 5cbfc7b461 | |||
| b4742b5645 | |||
| 5ec25475ea | |||
| c58040ef83 | |||
| 75701b6875 | |||
| 2155a33689 | |||
| 68a9d5d771 | |||
| ebf6107faf | |||
| e4bc36a743 | |||
| 7a64251811 | |||
| 65409d75a7 | |||
| d40081d58b | |||
| f276e981ed | |||
| e50db66b47 | |||
| fc84022852 | |||
| b593f2f3ea | |||
| e782d0542c | |||
| 1a085cad98 | |||
| 1c33a0c4c5 | |||
| 3c4a7961c2 | |||
| 712f9b84cf | |||
| 2efb7b76cc | |||
| 5c4d93ce15 | |||
| 7addd92058 | |||
| 82c5898b9a | |||
| cced6b7035 | |||
| ae0b5b3738 | |||
| 6fdf9cbe5d | |||
| 009244c65d | |||
| 73a720bb9c | |||
| fd9df9904f | |||
| e75fb3a40d | |||
| 0bef1a2aa8 | |||
| 395eb641e5 | |||
| 9019242ffb | |||
| 9433bb72ca | |||
| 500f751152 | |||
| 9ebe3f4a0e | |||
| 4727f22b0f | |||
| 8c92fc9a42 | |||
| 2a7cb34218 | |||
| e1a42b49c1 | |||
| 5a825debf5 | |||
| e46b0a42b0 | |||
| eb0c442a5e | |||
| eb6460236b | |||
| ef051daffd | |||
| b6acb302d2 | |||
| 5cfc5a3971 | |||
| 04de97af16 | |||
| 6367f90589 | |||
| bc8f9d07bb | |||
| a3d693ddc1 | |||
| e205abd120 | |||
| 5da8fccef7 | |||
| c7e78142ee | |||
| 79cbb44d51 | |||
| dcde3db33a | |||
| 44fbc7a182 | |||
| 354c7d1f85 | |||
| 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 | |||
| 72cfcd66b1 | |||
| 0cb65ab216 | |||
| d18731ccfe | |||
| 91f79a5ff1 | |||
| d37929548e | |||
| 663891dd54 | |||
| 79d1c4fed4 | |||
| ecc8196701 | |||
| 83e44808cb | |||
| bfbc9900ed | |||
| 1735ba3312 |
@ -42,7 +42,7 @@ android {
|
||||
}
|
||||
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
abiFilters "armeabi-v7a"
|
||||
}
|
||||
|
||||
// 由于app只针对中文用户,所以仅保留zh资源,其他删掉
|
||||
@ -65,6 +65,15 @@ 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}\""
|
||||
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${MEIZUPUSH_APPKEY}\""
|
||||
|
||||
resValue "string", "huawei_push_appid", "appid=${HUAWEI_PUSH_APPID}"
|
||||
|
||||
/**
|
||||
* Build Time 供区分 jenkins 打包时间用
|
||||
@ -93,7 +102,7 @@ android {
|
||||
signingConfig signingConfigs.debug
|
||||
|
||||
buildConfigField "String", "EXPOSURE_REPO", "\"test\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E3\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E4\""
|
||||
|
||||
multiDexKeepProguard file("tinker_multidexkeep.pro")
|
||||
}
|
||||
@ -105,7 +114,7 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
|
||||
buildConfigField "String", "EXPOSURE_REPO", "\"exposure\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E3\""
|
||||
buildConfigField "String", "EXPOSURE_VERSION", "\"E4\""
|
||||
|
||||
multiDexKeepProguard file("tinker_multidexkeep.pro")
|
||||
}
|
||||
@ -121,15 +130,11 @@ android {
|
||||
publish {
|
||||
dimension "nonsense"
|
||||
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${COMMENT_HOST}\""
|
||||
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${SENSITIVE_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${MEIZUPUSH_APPKEY}\""
|
||||
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
}
|
||||
// internal test dev host
|
||||
@ -138,16 +143,12 @@ android {
|
||||
versionNameSuffix "-debug"
|
||||
|
||||
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
|
||||
buildConfigField "String", "COMMENT_HOST", "\"${DEV_COMMENT_HOST}\""
|
||||
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "MIPUSH_APPID", "\"${DEBUG_MIPUSH_APPID}\""
|
||||
buildConfigField "String", "MIPUSH_APPKEY", "\"${DEBUG_MIPUSH_APPKEY}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPID", "\"${DEBUG_MEIZUPUSH_APPID}\""
|
||||
buildConfigField "String", "MEIZUPUSH_APPKEY", "\"${DEBUG_MEIZUPUSH_APPKEY}\""
|
||||
buildConfigField "String", "SENSITIVE_API_HOST", "\"${DEV_API_HOST}\""
|
||||
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${DEBUG_BUGLY_APPID}\""
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${DEV_UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEV_UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${DEV_BUGLY_APPID}\""
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,7 +172,7 @@ rebuildChannel {
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs/aars'
|
||||
dirs 'libs', 'libs/aars'
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,9 +196,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"
|
||||
@ -206,7 +204,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}"
|
||||
|
||||
@ -267,13 +264,11 @@ dependencies {
|
||||
implementation "com.squareup.picasso:picasso:${picasso}"
|
||||
|
||||
// for video streaming
|
||||
implementation ("com.shuyu:gsyVideoPlayer-java:$gsyVideo",{
|
||||
implementation("com.shuyu:gsyVideoPlayer-java:$gsyVideo", {
|
||||
exclude module: "gsyvideoplayer-androidvideocache"
|
||||
})
|
||||
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
|
||||
|
||||
implementation "com.github.wendux:DSBridge-Android:$dsBridge"
|
||||
|
||||
implementation "android.arch.work:work-runtime:${workManager}"
|
||||
|
||||
implementation "com.llew.huawei:verifier:${verifier}"
|
||||
@ -297,9 +292,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')
|
||||
|
||||
BIN
app/libs/AVMPSDK-external-release-5.4.1002.aar
Normal file
BIN
app/libs/AVMPSDK-external-release-5.4.1002.aar
Normal file
Binary file not shown.
BIN
app/libs/SecurityBodySDK-external-release-5.4.112-preInstall.aar
Normal file
BIN
app/libs/SecurityBodySDK-external-release-5.4.112-preInstall.aar
Normal file
Binary file not shown.
Binary file not shown.
@ -240,4 +240,18 @@
|
||||
-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.**
|
||||
|
||||
-keep class com.taobao.securityjni.**{*;}
|
||||
-keep class com.taobao.wireless.security.**{*;}
|
||||
-keep class com.ut.secbody.**{*;}
|
||||
-keep class com.taobao.dp.**{*;}
|
||||
-keep class com.alibaba.wireless.security.**{*;}
|
||||
@ -4,7 +4,6 @@ import android.app.Application;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
@ -18,15 +17,6 @@ import okhttp3.logging.HttpLoggingInterceptor;
|
||||
public class Injection {
|
||||
|
||||
public static boolean appInit(Application application) {
|
||||
|
||||
// init leakcanary
|
||||
if (LeakCanary.isInAnalyzerProcess(application)) {
|
||||
// This process is dedicated to LeakCanary for heap analysis.
|
||||
// You should not init your app in this process.
|
||||
return false;
|
||||
}
|
||||
LeakCanary.install(application);
|
||||
|
||||
// init stetho
|
||||
Stetho.initializeWithDefaults(application);
|
||||
|
||||
|
||||
@ -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
|
||||
@ -67,6 +82,7 @@
|
||||
android:largeHeap="true"
|
||||
android:resizeableActivity="true"
|
||||
android:theme="@style/AppCompatTheme.APP"
|
||||
tools:replace="android:allowBackup"
|
||||
tools:targetApi="n">
|
||||
|
||||
<!--android:launchMode = "singleTask"-->
|
||||
@ -161,6 +177,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:name="com.gh.gamecenter.MessageDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -191,6 +208,7 @@
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.CommentDetailActivity"
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
@ -401,6 +419,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/TransparentStatusBarAndNavigationBar"
|
||||
android:name="com.gh.gamecenter.gamedetail.rating.RatingReplyActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -492,10 +511,15 @@
|
||||
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"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@android:style/Theme.Translucent" />
|
||||
|
||||
<activity
|
||||
@ -558,7 +582,7 @@
|
||||
</receiver>
|
||||
|
||||
<!--魅族push应用定义消息receiver声明 -->
|
||||
<receiver android:name="com.gh.gamecenter.receiver.MeizuPushReceiver">
|
||||
<receiver android:name="com.gh.gamecenter.receiver.UmengMeizuPushReceiver">
|
||||
<intent-filter>
|
||||
<!-- 接收push消息 -->
|
||||
<action android:name="com.meizu.flyme.push.intent.MESSAGE" />
|
||||
@ -583,10 +607,47 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name="com.gh.base.GHUmengNotificationService" />
|
||||
<meta-data android:name="com.huawei.hms.client.appid" android:value="@string/huawei_push_appid"/>
|
||||
|
||||
<service
|
||||
android:name="com.gh.base.GHUmengNotificationService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<!--<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
@ -3,16 +3,25 @@ package com.gh.base;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.fragment.BaseFragment;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.RunningUtils;
|
||||
@ -36,6 +45,8 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import butterknife.ButterKnife;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
@ -107,6 +118,15 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0) {
|
||||
view = getRootViewWithEnvIndicator(view);
|
||||
}
|
||||
super.setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (useEventBus()) EventBus.getDefault().unregister(this);
|
||||
@ -126,21 +146,55 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
String icon,
|
||||
String shareTitle,
|
||||
String shareSummary,
|
||||
ShareUtils.ShareType shareType) {
|
||||
ShareUtils.ShareEntrance shareEntrance, String id) {
|
||||
ShareUtils.getInstance(this).showShareWindows(this,
|
||||
getWindow().getDecorView(),
|
||||
url,
|
||||
icon,
|
||||
shareTitle,
|
||||
shareSummary,
|
||||
shareType);
|
||||
if (shareType == ShareUtils.ShareType.game || shareType == ShareUtils.ShareType.plugin) {
|
||||
shareEntrance, id);
|
||||
if (shareEntrance == ShareUtils.ShareEntrance.game || shareEntrance == ShareUtils.ShareEntrance.plugin) {
|
||||
MtaHelper.onEvent("内容分享", "内容分享", shareTitle + shareSummary);
|
||||
} else {
|
||||
MtaHelper.onEvent("内容分享", "内容分享", shareTitle);
|
||||
}
|
||||
}
|
||||
|
||||
private View getRootViewWithEnvIndicator(View view) {
|
||||
RelativeLayout screenRootView = new RelativeLayout(this);
|
||||
screenRootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
|
||||
LinearLayout ll = new LinearLayout(this);
|
||||
TextView tv = new TextView(this);
|
||||
String envText = "正式环境";
|
||||
if (BuildConfig.FLAVOR.equals("internal")) {
|
||||
envText = "测试环境";
|
||||
}
|
||||
tv.setText(envText);
|
||||
tv.setGravity(Gravity.CENTER);
|
||||
tv.setTextColor(Color.WHITE);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
tv.setBackground(ContextCompat.getDrawable(this, R.color.red));
|
||||
tv.measure(0, 0);
|
||||
tv.setAlpha(0.15F);
|
||||
int height = tv.getMeasuredHeight();
|
||||
int width = tv.getMeasuredWidth();
|
||||
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
|
||||
ll.setTranslationX(DisplayUtils.dip2px(20));
|
||||
ll.setRotation(45);
|
||||
ll.addView(tv);
|
||||
ll.setPadding(0, (width - height) / 2, 0, (width - height) / 2);
|
||||
|
||||
screenRootView.addView(view);
|
||||
screenRootView.addView(ll);
|
||||
|
||||
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) ll.getLayoutParams();
|
||||
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
return screenRootView;
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEventMainThread(final EBShowDialog showDialog) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
|
||||
@ -182,6 +236,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,7 +33,9 @@ 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 mEditorTextNumTv by bindView<TextView>(R.id.editorTextNumTv)
|
||||
private val mEditorFont by bindView<CheckableImageView>(R.id.editor_font)
|
||||
private val mEditorLink by bindView<CheckableImageView>(R.id.editor_link)
|
||||
private val mEditorParagraph by bindView<CheckableImageView>(R.id.editor_paragraph)
|
||||
@ -86,7 +90,12 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
// 防止个别手机在Js里无法获取粘贴内容
|
||||
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
|
||||
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
|
||||
mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener")
|
||||
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,
|
||||
@ -251,6 +260,14 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private inner class OnEditorTextChangeListener {
|
||||
@JavascriptInterface
|
||||
fun onTextChange(count: Int) {
|
||||
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
|
||||
mEditorTextNumTv.text = num.toString()
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun mtaEventName(): String
|
||||
|
||||
companion object {
|
||||
@ -266,5 +283,6 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
const val INSERT_ANSWER_CODE = 411
|
||||
const val INSERT_ARTICLE_CODE = 412
|
||||
const val INSERT_GAME_CODE = 413
|
||||
const val MAX_INPUT_TEXT_NUM = 10000
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -97,7 +95,7 @@ class GHUmengNotificationService : UmengMessageService() {
|
||||
|
||||
// 用户未登录的情况下不生成消息中心通知,避免用户掉登录了还收到跳转至消息中心的通知
|
||||
if (data != null
|
||||
&& data.link?.target == "system"
|
||||
&& data.link?.link == "system"
|
||||
&& !UserManager.getInstance().isLoggedIn) {
|
||||
return
|
||||
}
|
||||
@ -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,12 +11,10 @@ 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;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
@ -52,6 +49,8 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Nullable
|
||||
private PackageViewModel mPackageViewModel;
|
||||
|
||||
protected RelativeLayout mToolbarContainer;
|
||||
|
||||
protected Toolbar mToolbar;
|
||||
|
||||
protected TextView mTitleTv;
|
||||
@ -62,7 +61,6 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setStatusBarDarkMode(true, this);
|
||||
initToolbar();
|
||||
|
||||
@ -88,6 +86,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) {
|
||||
@ -239,7 +238,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_download) {
|
||||
MtaHelper.onEvent("下载管理", "下载管理入口", getActivityNameInChinese());
|
||||
// MtaHelper.onEvent("下载管理", "下载管理入口", getActivityNameInChinese());
|
||||
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(this, mEntrance);
|
||||
startActivity(intent);
|
||||
}
|
||||
@ -253,4 +252,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +211,13 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return mCachedView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
mCachedView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@ -304,6 +311,10 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
|
||||
return this;
|
||||
}
|
||||
|
||||
public void onParentActivityFinish() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RecyclerView.Adapter provideSyncAdapter() {
|
||||
return null;
|
||||
|
||||
@ -3,11 +3,10 @@ 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.PagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import butterknife.BindView
|
||||
import com.gh.base.adapter.FragmentAdapter
|
||||
@ -15,7 +14,6 @@ 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
|
||||
|
||||
@ -23,8 +21,10 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
|
||||
@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
|
||||
|
||||
@ -66,6 +66,10 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
if (arguments != null) mCheckedIndex = requireArguments().getInt(PAGE_INDEX, 0)
|
||||
}
|
||||
|
||||
open fun providePagerAdapter(): PagerAdapter? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
@ -74,7 +78,8 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
|
||||
mViewPager.offscreenPageLimit = mFragmentsList.size
|
||||
mViewPager.addOnPageChangeListener(this)
|
||||
mViewPager.adapter = FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
|
||||
mViewPager.adapter = providePagerAdapter()
|
||||
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
|
||||
mViewPager.currentItem = mCheckedIndex
|
||||
mTabLayout.setupWithViewPager(mViewPager)
|
||||
mTabIndicatorView.setupWithTabLayout(mTabLayout)
|
||||
@ -84,60 +89,10 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
|
||||
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)
|
||||
if (tabView == null) tabView = BaseFragment_TabLayout.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
|
||||
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex)
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||
|
||||
@ -23,7 +23,7 @@ object AppExecutor {
|
||||
@JvmStatic
|
||||
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
@JvmStatic
|
||||
val ioExecutor by lazy { Executors.newCachedThreadPool() }
|
||||
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
|
||||
|
||||
class MainThreadExecutor : Executor {
|
||||
private val mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
@ -6,6 +6,7 @@ import android.webkit.JavascriptInterface
|
||||
import androidx.annotation.Keep
|
||||
import com.gh.base.CurrentActivityHolder
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.dsbridge.CompletionHandler
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.LoginActivity
|
||||
import com.gh.gamecenter.ViewImageActivity
|
||||
@ -23,7 +24,6 @@ import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
import wendu.dsbridge.CompletionHandler
|
||||
|
||||
class DefaultJsApi(var context: Context) {
|
||||
|
||||
@ -142,6 +142,20 @@ class DefaultJsApi(var context: Context) {
|
||||
context?.startActivity(ViewImageActivity.getViewImageIntent(context, imageEvent.imageList, imageEvent.position, "浏览器"))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun isInstalled(event: Any): String {
|
||||
val localInstalledPackageList = PackageUtils.getAllPackageName(HaloApp.getInstance().application)
|
||||
val packageNameList: ArrayList<String> = event.toString().toObject() ?: ArrayList()
|
||||
|
||||
for (packageName in packageNameList) {
|
||||
if (!localInstalledPackageList.contains(packageName)) {
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
return "true"
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun openBase64Image(event: Any) {
|
||||
val context = CurrentActivityHolder.getCurrentActivity()
|
||||
|
||||
@ -4,21 +4,21 @@ 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.*
|
||||
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
|
||||
import com.gh.common.util.DirectUtils.directToGameVideo
|
||||
import com.gh.common.util.DirectUtils.directToVideoDetail
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.LibaoDetailActivity
|
||||
import com.gh.gamecenter.NewsDetailActivity
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.entity.CommunityEntity
|
||||
import com.gh.gamecenter.entity.SimpleGameEntity
|
||||
import com.gh.gamecenter.entity.SubjectRecommendEntity
|
||||
import com.gh.gamecenter.entity.VideoLinkEntity
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
object DefaultWebViewUrlHandler {
|
||||
object DefaultUrlHandler {
|
||||
|
||||
@JvmStatic
|
||||
fun interceptUrl(context: Context, url: String, entrance: String): Boolean {
|
||||
@ -124,30 +124,120 @@ object DefaultWebViewUrlHandler {
|
||||
val categoryId = uri.getQueryParameter("category_id") ?: ""
|
||||
val link = uri.getQueryParameter("link") ?: ""
|
||||
val linkEntity = VideoLinkEntity(title, categoryId, link)
|
||||
// if (!CheckLoginUtils.isLogin()) {
|
||||
// HaloApp.put(EntranceUtils.HOST_UPLOAD_VIDEO, linkEntity)
|
||||
// }
|
||||
val gameId = uri.getQueryParameter("gameId") ?: ""
|
||||
val gameName = uri.getQueryParameter("gameName") ?: ""
|
||||
val simpleGameEntity = SimpleGameEntity(gameId, gameName)
|
||||
CheckLoginUtils.checkLogin(context, null, true, EntranceUtils.ENTRANCE_BROWSER) {
|
||||
DirectUtils.directToVideoManager(context, linkEntity, EntranceUtils.ENTRANCE_BROWSER, "")
|
||||
DirectUtils.directToVideoManager(context, linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "")
|
||||
}
|
||||
}
|
||||
EntranceUtils.HOST_USERHOME -> {
|
||||
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, "")
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_BLOCK -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
val entity = SubjectRecommendEntity(link = id, name = name, text = name)
|
||||
DirectUtils.directToBlock(context, entity)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_SERVER_BLOCK -> {
|
||||
DirectUtils.directToGameServers(context, entrance = entrance, path = "")
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_AMWAY_BLOCK -> {
|
||||
DirectUtils.directToAmway(context, entrance = entrance, path = "")
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_HELP -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
DirectUtils.directToQa(context, name, id)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_HELP_COLLECTION -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
?: ""
|
||||
DirectUtils.directToQaCollection(context, name, id)
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_UPLOAD -> {
|
||||
DirectUtils.directGameUpload(context, entrance = entrance, path = "")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
68
app/src/main/java/com/gh/common/FixedRateJobHelper.kt
Normal file
68
app/src/main/java/com/gh/common/FixedRateJobHelper.kt
Normal file
@ -0,0 +1,68 @@
|
||||
package com.gh.common
|
||||
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.common.loghub.LoghubUtils
|
||||
import com.gh.common.util.doOnMainProcessOnly
|
||||
import com.gh.common.videolog.VideoRecordUtils
|
||||
import com.gh.gamecenter.entity.TimeEntity
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
object FixedRateJobHelper {
|
||||
private const val CHECKER_PERIOD: Long = 60 * 1000L
|
||||
private const val TIME_PERIOD: Long = 600 * 1000L
|
||||
private const val LOGHUB_PERIOD: Long = 120 * 1000L
|
||||
private const val EXPOSURE_PERIOD: Long = 300 * 1000L
|
||||
private const val REGION_SETTING_PERIOD: Long = 300 * 1000L
|
||||
private const val VIDEO_RECORD_PERIOD: Long = 60 * 1000L
|
||||
|
||||
private var mExecuteCount: Int = 0
|
||||
|
||||
var timeDeltaBetweenServerAndClient: Long = 0
|
||||
|
||||
@JvmStatic
|
||||
fun begin() {
|
||||
doOnMainProcessOnly {
|
||||
fixedRateTimer("Global-Fixed-Rate-Timer", initialDelay = 100, period = CHECKER_PERIOD) {
|
||||
// 时间校对,10分钟一次
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<TimeEntity>() {
|
||||
override fun onResponse(response: TimeEntity?) {
|
||||
val serverTime = response?.time
|
||||
serverTime?.let { timeDeltaBetweenServerAndClient = it * 1000 - System.currentTimeMillis() }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交曝光数据
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % EXPOSURE_PERIOD == 0L) {
|
||||
ExposureManager.commitSavedExposureEvents(true)
|
||||
}
|
||||
|
||||
// 提交普通 loghub 数据
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % LOGHUB_PERIOD == 0L) {
|
||||
LoghubUtils.commitSavedLoghubEvents()
|
||||
}
|
||||
|
||||
// 更新游戏屏蔽信息
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % REGION_SETTING_PERIOD == 0L) {
|
||||
RegionSettingHelper.getRegionSetting()
|
||||
}
|
||||
|
||||
// 提交视频浏览记录数据
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % VIDEO_RECORD_PERIOD == 0L) {
|
||||
VideoRecordUtils.commitVideoRecord()
|
||||
}
|
||||
|
||||
// ExposureUtils.logADownloadCompleteExposureEvent(GameEntity(id = mExecuteCount.toString(), name = "测试曝光上传"), platform = "", trace = null, downloadType = ExposureUtils.DownloadType.DOWNLOAD)
|
||||
mExecuteCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,22 +38,17 @@ object PushManager {
|
||||
fun init(channel: String) {
|
||||
tryWithDefaultCatch {
|
||||
//初始化友盟推送
|
||||
UMConfigure.init(mApplication,
|
||||
Config.UMENG_APPKEY, channel,
|
||||
UMConfigure.DEVICE_TYPE_PHONE,
|
||||
Config.UMENG_MESSAGE_SECRET)
|
||||
UMConfigure.init(mApplication, Config.UMENG_APPKEY, channel, UMConfigure.DEVICE_TYPE_PHONE, Config.UMENG_MESSAGE_SECRET)
|
||||
|
||||
val pushAgent = PushAgent.getInstance(mApplication)
|
||||
|
||||
runOnIoThread { registerDevice() }
|
||||
|
||||
// 注册小米、华为和魅族通道
|
||||
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)
|
||||
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
runOnIoThread { registerDevice() }
|
||||
|
||||
val aliasInSp = PreferenceManager.getDefaultSharedPreferences(mApplication).getString(SP_PUSH_ALIAS, "")
|
||||
mPreviousAlias = aliasInSp?.toObject()
|
||||
|
||||
|
||||
@ -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,13 @@ 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 SENSITIVE_API_HOST = BuildConfig.SENSITIVE_API_HOST;
|
||||
|
||||
/**
|
||||
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
|
||||
@ -49,6 +49,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";
|
||||
@ -235,7 +237,7 @@ public class Config {
|
||||
}
|
||||
|
||||
public static boolean isGameDomeSwitchOpen() {
|
||||
return getSettings().getGameDomeSwitch().equals("on");
|
||||
return getSettings() != null && getSettings().getGameDomeSwitch().equals("on");
|
||||
}
|
||||
|
||||
public static void fixHideFunction() {
|
||||
@ -252,7 +254,7 @@ public class Config {
|
||||
public static void getGhzsSettings() {
|
||||
String channel = HaloApp.getInstance().getChannel();
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().getApplication())
|
||||
.getApi().getSettings(PackageUtils.getVersionName(), channel)
|
||||
.getSensitiveApi().getSettings(PackageUtils.getVersionName(), channel)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<SettingsEntity>() {
|
||||
|
||||
@ -28,12 +28,23 @@ 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";
|
||||
|
||||
public static final String GAME_ID_DIVIDER = ":"; // 用于避免历史下载掺和到普通下载状态的 ID 修饰符
|
||||
|
||||
// 用于避免历史下载掺和到普通下载状态的 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";
|
||||
public static final String SP_LAST_OPENING_TIME = "last_opening_dialog_time";
|
||||
|
||||
// 新用户首次启动光环的时间
|
||||
public static final String SP_INITIAL_USAGE_TIME = "initial_usage_time";
|
||||
|
||||
//引导设置 “通知管理” 引导弹窗
|
||||
public static final String SP_SHOWED_NOTIFICATION_LOGIN = "show_notification_login_hint";
|
||||
@ -73,9 +84,13 @@ public class Constants {
|
||||
//游戏设备弹窗不再提示
|
||||
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_FILTER_TAGS = "filter_tags";
|
||||
//实名认证弹窗分类数据
|
||||
public static final String SP_AUTH_DIALOG= "auth_dialog";
|
||||
public static final String SP_AUTH_DIALOG = "auth_dialog";
|
||||
//顶部视频进度保存,重启恢复
|
||||
public static final String SP_TOP_VIDEO_SCHEDULE = "top_video_schedule";
|
||||
//我的光环小红点提示
|
||||
public static final String SP_GH_RED_POINT_REMIND = "gh_red_point_remind";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
@ -112,13 +127,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";
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.gh.common.databind;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
@ -25,7 +26,7 @@ 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;
|
||||
@ -59,6 +60,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;
|
||||
@ -69,6 +71,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;
|
||||
|
||||
@ -93,6 +96,26 @@ public class BindingAdapters {
|
||||
view.setTextSize(number);
|
||||
}
|
||||
|
||||
@BindingAdapter("setTypeface")
|
||||
public static void setTypeface(TextView view, String type) {
|
||||
if (type == null) return;
|
||||
|
||||
switch (type) {
|
||||
case "bold":
|
||||
view.setTypeface(null, Typeface.BOLD);
|
||||
break;
|
||||
case "italic":
|
||||
view.setTypeface(null, Typeface.ITALIC);
|
||||
break;
|
||||
case "bold_italic":
|
||||
view.setTypeface(null, Typeface.BOLD_ITALIC);
|
||||
break;
|
||||
default:
|
||||
view.setTypeface(null, Typeface.NORMAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"addDetailKaiFuView", "addDetailKaiFuViewListener", "isReadyPatch"})
|
||||
public static void addDetailKaiFuView(LinearLayout view, List<ServerCalendarEntity> list
|
||||
, OnViewClickListener listener, Boolean isReadyPatch) {
|
||||
@ -236,6 +259,14 @@ public class BindingAdapters {
|
||||
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) {
|
||||
@ -411,6 +442,7 @@ public class BindingAdapters {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
});
|
||||
@ -425,6 +457,7 @@ public class BindingAdapters {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
}
|
||||
@ -465,9 +498,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;
|
||||
}
|
||||
@ -613,9 +652,6 @@ public class BindingAdapters {
|
||||
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,
|
||||
@ -623,7 +659,7 @@ public class BindingAdapters {
|
||||
entrance,
|
||||
location + gameEntity.getName(),
|
||||
isSubscribe,
|
||||
downloadExposureEvent);
|
||||
traceEvent);
|
||||
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setDownloadType("插件化".equals(method) ?
|
||||
@ -638,6 +674,33 @@ public class BindingAdapters {
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
}
|
||||
|
||||
// 包含测试开服标签
|
||||
@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 {
|
||||
tagStyle = gameEntity.getTagStyle();
|
||||
}
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("isRefreshing")
|
||||
public static void isRefreshing(SwipeRefreshLayout layout, LoadStatus status) {
|
||||
if (status != LoadStatus.INIT_LOADING && status != LoadStatus.LIST_LOADING) {
|
||||
@ -645,14 +708,15 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter({"setGameName", "isShowPlatform"})
|
||||
public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform) {
|
||||
@BindingAdapter({"setGameName", "isShowPlatform", "isShowSuffix"})
|
||||
public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) {
|
||||
if (isShowSuffix == null) isShowSuffix = true; // 默认显示
|
||||
if (isShowPlatform && game.getApk().size() > 0) {
|
||||
view.setText(String.format("%s - %s", game.getName(),
|
||||
view.setText(String.format("%s - %s", !isShowSuffix ? game.getNameWithoutSuffix() : game.getName(),
|
||||
PlatformUtils.getInstance(view.getContext()).getPlatformName(
|
||||
game.getApk().get(0).getPlatform())));
|
||||
} else {
|
||||
view.setText(game.getName());
|
||||
view.setText(!isShowSuffix ? game.getNameWithoutSuffix() : game.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ import com.halo.assistant.fragment.SettingsFragment.AUTO_INSTALL_SP_KEY
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.dialog_device_remind.view.*
|
||||
import java.lang.ref.WeakReference
|
||||
@ -179,7 +178,7 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
|
||||
inner class BannerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return object : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_ad_banner, parent, false)) {}
|
||||
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
|
||||
|
||||
@ -10,6 +10,7 @@ import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
@ -18,7 +19,10 @@ import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.synthetic.main.dialog_game_off_service.*
|
||||
|
||||
// 游戏关闭下载弹窗
|
||||
class GameOffServiceDialogFragment : BaseTrackableDialogFragment() {
|
||||
class GameOffServiceDialogFragment
|
||||
// : BaseTrackableDialogFragment()
|
||||
:BaseDialogFragment() {
|
||||
|
||||
private var mDialog: GameEntity.Dialog? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
@ -42,7 +46,7 @@ class GameOffServiceDialogFragment : BaseTrackableDialogFragment() {
|
||||
siteTv.text = site.text
|
||||
siteTv.paintFlags = siteTv.paintFlags or Paint.UNDERLINE_TEXT_FLAG
|
||||
siteTv.setOnClickListener {
|
||||
MtaHelper.onEvent("游戏下载状态按钮", getKey(), site.text)
|
||||
// MtaHelper.onEvent("游戏下载状态按钮", getKey(), site.text)
|
||||
DirectUtils.directToWebView(requireContext(), site.url, "(关闭下载弹窗)")
|
||||
dismiss()
|
||||
}
|
||||
@ -52,13 +56,13 @@ class GameOffServiceDialogFragment : BaseTrackableDialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEvent(): String {
|
||||
return "游戏下载状态按钮"
|
||||
}
|
||||
|
||||
override fun getKey(): String {
|
||||
return "查看详情弹窗"
|
||||
}
|
||||
// override fun getEvent(): String {
|
||||
// return "游戏下载状态按钮"
|
||||
// }
|
||||
//
|
||||
// override fun getKey(): String {
|
||||
// return "查看详情弹窗"
|
||||
// }
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
|
||||
@ -14,7 +14,9 @@ import androidx.lifecycle.MutableLiveData
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.OnClick
|
||||
import com.gh.base.fragment.BaseDialogFragment
|
||||
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
|
||||
@ -28,7 +30,9 @@ import okhttp3.ResponseBody
|
||||
import org.json.JSONObject
|
||||
|
||||
// 预约弹窗
|
||||
class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
class ReserveDialogFragment
|
||||
: BaseDialogFragment() {
|
||||
// : BaseTrackableDialogFragment() {
|
||||
|
||||
@BindView(R.id.reserve_hint_tv)
|
||||
lateinit var reserveHintTv: TextView
|
||||
@ -49,6 +53,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
|
||||
private var mSuccessCallback: SuccessCallback? = null
|
||||
|
||||
private var mGame: GameEntity? = null
|
||||
private var mGameId: String = ""
|
||||
private var mGameName: String = ""
|
||||
|
||||
@ -62,13 +67,13 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
return inflater.inflate(R.layout.dialog_reserve_game, null)
|
||||
}
|
||||
|
||||
override fun getEvent(): String {
|
||||
return "预约游戏"
|
||||
}
|
||||
|
||||
override fun getKey(): String {
|
||||
return "预约功能操作"
|
||||
}
|
||||
// override fun getEvent(): String {
|
||||
// return "预约游戏"
|
||||
// }
|
||||
//
|
||||
// override fun getKey(): String {
|
||||
// return "预约功能操作"
|
||||
// }
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("SetTextI18n")
|
||||
@ -86,6 +91,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
if (it.success) {
|
||||
showSuccessDialog(it.withMobile, it.boundWechat)
|
||||
mSuccessCallback?.onSuccess()
|
||||
HistoryHelper.insertGameEntity(mGame!!)
|
||||
}
|
||||
}
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
@ -106,7 +112,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
} else {
|
||||
customizableBtn.text = dialogConfig?.text
|
||||
customizableBtn.setOnClickListener {
|
||||
MtaHelper.onEvent("预约游戏", "预约功能操作", "点击跳转按钮")
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击跳转按钮")
|
||||
DirectUtils.directToLinkPage(
|
||||
requireContext(),
|
||||
dialogConfig!!.toLinkEntity(),
|
||||
@ -124,7 +130,7 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.reserve_without_mobile_btn -> {
|
||||
MtaHelper.onEvent("预约游戏", "预约功能操作", "点击无手机号预约")
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击无手机号预约")
|
||||
mViewModel.reserve(gameId = mGameId, gameName = mGameName)
|
||||
}
|
||||
|
||||
@ -135,12 +141,12 @@ class ReserveDialogFragment : BaseTrackableDialogFragment() {
|
||||
return
|
||||
}
|
||||
|
||||
MtaHelper.onEvent("预约游戏", "预约功能操作", "点击立即预约")
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击立即预约")
|
||||
mViewModel.reserve(gameId = mGameId, gameName = mGameName, mobile = mobile)
|
||||
}
|
||||
|
||||
R.id.close_btn -> {
|
||||
MtaHelper.onEvent("预约游戏", "预约功能操作", "点击关闭")
|
||||
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击关闭")
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
@ -149,6 +155,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
|
||||
@ -185,7 +192,7 @@ class ReserveViewModel(application: Application) : AndroidViewModel(application)
|
||||
reservation.postValue(Reservation(success = true, withMobile = mobile.isNotEmpty(), boundWechat = boundWechat))
|
||||
ReservationRepository.addReservationToMemoryAndRefresh(gameId)
|
||||
|
||||
MtaHelper.onEvent("预约游戏", "预约", gameName)
|
||||
// MtaHelper.onEvent("预约游戏", "预约", gameName)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
|
||||
@ -11,6 +11,7 @@ data class ExposureEntity(
|
||||
@SerializedName("game_id")
|
||||
val gameId: String? = "",
|
||||
val gameName: String? = "",
|
||||
val gameVersion: String? = "",
|
||||
val sequence: Int? = 0,
|
||||
val platform: String? = "",
|
||||
val downloadType: String? = "",
|
||||
|
||||
@ -4,9 +4,11 @@ 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
|
||||
import com.gh.common.util.getFirstElementDividedByDivider
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.util.*
|
||||
@ -26,9 +28,14 @@ data class ExposureEvent(
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun createEvent(gameEntity: GameEntity?, source: List<ExposureSource>, eTrace: List<ExposureEvent>? = null, event: ExposureType = ExposureType.EXPOSURE): ExposureEvent {
|
||||
if (gameEntity?.getApk()?.size == 1) {
|
||||
gameEntity.gameVersion = gameEntity.getApk().elementAtOrNull(0)?.version ?: ""
|
||||
}
|
||||
return ExposureEvent(
|
||||
payload = ExposureEntity(gameId = gameEntity?.id,
|
||||
gameName = gameEntity?.name,
|
||||
payload = ExposureEntity(
|
||||
gameId = gameEntity?.id?.getFirstElementDividedByDivider(Constants.GAME_ID_DIVIDER),
|
||||
gameName = gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = gameEntity?.gameVersion,
|
||||
sequence = gameEntity?.sequence,
|
||||
platform = gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
|
||||
@ -26,6 +26,10 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
visibleState?.let { commitExposure(it) }
|
||||
throttleBus?.clear()
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ import com.gh.loghub.LoghubHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
/**
|
||||
* A handful tool for committing logs to aliyun loghub.
|
||||
@ -17,7 +16,7 @@ import kotlin.concurrent.fixedRateTimer
|
||||
* 如何简单地统计列表中每个 item 的曝光事件?
|
||||
*
|
||||
* 1. Adapter 实现 IExposable 接口,在 BindView 阶段更新 ExposureEvent,ExposureEvent 供 getEventByPosition(pos) 方法获取用
|
||||
* 2. 构建一个 ExposureListener 并作为入参添加至 recyclerview 的 Scroll 回调中
|
||||
* 2. 构建一个 ExposureListener 并作为入参添加至 recyclerview 的 onScroll 回调中
|
||||
* 3. 没了
|
||||
*/
|
||||
object ExposureManager {
|
||||
@ -25,7 +24,6 @@ object ExposureManager {
|
||||
private const val ENDPOINT = "cn-qingdao.log.aliyuncs.com"
|
||||
private const val PROJECT = "ghzs"
|
||||
private const val STORE_SIZE = 100
|
||||
private const val STORE_FORCE_UPLOAD_PERIOD = 300 * 1000L
|
||||
private const val LOG_STORE = BuildConfig.EXPOSURE_REPO
|
||||
|
||||
private val loghubHelper = LoghubHelper.getInstance()
|
||||
@ -38,18 +36,12 @@ object ExposureManager {
|
||||
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
TimeUtil.init()
|
||||
|
||||
loghubHelper.init(HaloApp.getInstance().application, ENDPOINT, PROJECT, LOG_STORE) { TimeUtil.currentTimeMillis() }
|
||||
|
||||
exposureExecutor.execute {
|
||||
val eventList = exposureDao.getAll()
|
||||
exposureSet.addAll(eventList)
|
||||
}
|
||||
|
||||
fixedRateTimer(name = "ExposureManager-Store-Checker", initialDelay = 500, period = STORE_FORCE_UPLOAD_PERIOD) {
|
||||
commitSavedExposureEvents(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package com.gh.common.exposure
|
||||
|
||||
import android.text.TextUtils
|
||||
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 +18,12 @@ 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.gameVersion = entity.getApk().elementAtOrNull(0)?.version ?: gameEntity.gameVersion
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadType = downloadType.toString()
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
@ -43,29 +52,30 @@ 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
|
||||
if (!TextUtils.isEmpty(apkEntity.ghVersion)) {
|
||||
DownloadType.PLUGIN_DOWNLOAD
|
||||
} else {
|
||||
DownloadType.UPDATE
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
package com.gh.common.exposure.time
|
||||
|
||||
import com.gh.gamecenter.entity.TimeEntity
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
class Corrector {
|
||||
|
||||
companion object {
|
||||
const val TIME_CORRECTOR_ADJUST_PERIOD: Long = 600000
|
||||
}
|
||||
|
||||
var delta: Long = 0
|
||||
|
||||
init {
|
||||
fixedRateTimer("TimeUtil-Corrector-Checker", initialDelay = 0, period = TIME_CORRECTOR_ADJUST_PERIOD) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<TimeEntity>() {
|
||||
override fun onResponse(response: TimeEntity?) {
|
||||
val serverTime = response?.time
|
||||
serverTime?.let { delta = it * 1000 - System.currentTimeMillis() }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,15 @@
|
||||
package com.gh.common.exposure.time
|
||||
|
||||
import com.gh.common.FixedRateJobHelper
|
||||
|
||||
object TimeUtil {
|
||||
|
||||
private lateinit var corrector: Corrector
|
||||
|
||||
fun init() {
|
||||
corrector = Corrector()
|
||||
}
|
||||
|
||||
fun currentTimeMillis(): Long {
|
||||
return corrector.delta + System.currentTimeMillis()
|
||||
return FixedRateJobHelper.timeDeltaBetweenServerAndClient + System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun currentTime(): Int {
|
||||
return if (::corrector.isInitialized) {
|
||||
((corrector.delta + System.currentTimeMillis()) / 1000).toInt()
|
||||
} else {
|
||||
(System.currentTimeMillis() / 1000).toInt()
|
||||
}
|
||||
return ((FixedRateJobHelper.timeDeltaBetweenServerAndClient + System.currentTimeMillis()) / 1000).toInt()
|
||||
}
|
||||
|
||||
}
|
||||
21
app/src/main/java/com/gh/common/filter/RegionSetting.kt
Normal file
21
app/src/main/java/com/gh/common/filter/RegionSetting.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package com.gh.common.filter
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class RegionSetting(
|
||||
@SerializedName("game_mirror")
|
||||
var mirrorGameIdSet: HashSet<String>,
|
||||
@SerializedName("game_block")
|
||||
var filterGameIdSet: HashSet<String>,
|
||||
@SerializedName("channel_control")
|
||||
var channelControl: ChannelControl) {
|
||||
|
||||
@Keep
|
||||
data class ChannelControl(
|
||||
@SerializedName("game_category")
|
||||
var gameCategory: String,
|
||||
@SerializedName("effect")
|
||||
var effect: Boolean)
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.gh.common.filter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.toJson
|
||||
import com.gh.common.util.toObject
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.functions.Function
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
object RegionSettingHelper {
|
||||
|
||||
private var mChannelControl: RegionSetting.ChannelControl? = null
|
||||
private var mFilterGameIdSet: HashSet<String>? = hashSetOf()
|
||||
private var mDisplayMirrorIfoGameIdSet: HashSet<String>? = hashSetOf()
|
||||
|
||||
private const val SP_SETTING = "region_setting"
|
||||
|
||||
fun shouldThisGameDisplayMirrorInfo(gameId: String): Boolean {
|
||||
return mDisplayMirrorIfoGameIdSet?.contains(gameId) ?: false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shouldThisGameBeFiltered(gameId: String?): Boolean {
|
||||
return mFilterGameIdSet?.contains(gameId) ?: false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun filterGame(list: List<GameEntity>?): ArrayList<GameEntity> {
|
||||
if (list == null) return arrayListOf()
|
||||
|
||||
if (mFilterGameIdSet?.isEmpty() == true) {
|
||||
if (list is ArrayList) return list
|
||||
}
|
||||
|
||||
val listCopy: ArrayList<GameEntity> = if (list is ArrayList) list else ArrayList(list)
|
||||
listCopy.removeAll { mFilterGameIdSet?.contains(it.id) ?: false }
|
||||
return listCopy
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var filterGame = Function { list: List<GameEntity> ->
|
||||
filterGame(list)
|
||||
list
|
||||
}
|
||||
|
||||
fun shouldGameOfThisCategoryUseMirrorInfo(category: String) : Boolean {
|
||||
return if (mChannelControl == null || mChannelControl?.effect == false || !isUserUsedLessThan24Hours()) {
|
||||
false
|
||||
} else {
|
||||
mChannelControl?.gameCategory == category
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun getRegionSetting() {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.sensitiveApi
|
||||
.getRegionSetting(HaloApp.getInstance().channel)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<RegionSetting>() {
|
||||
override fun onSuccess(data: RegionSetting) {
|
||||
updateSettingsInMemory(data)
|
||||
SPUtils.setString(SP_SETTING, data.toJson())
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
SPUtils.getString(SP_SETTING).toObject<RegionSetting>()?.let {
|
||||
updateSettingsInMemory(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateSettingsInMemory(data: RegionSetting) {
|
||||
mFilterGameIdSet = data.filterGameIdSet
|
||||
mDisplayMirrorIfoGameIdSet = data.mirrorGameIdSet
|
||||
mChannelControl = data.channelControl
|
||||
}
|
||||
|
||||
/**
|
||||
* 该用户是否是使用了不到 24 小时的新用户
|
||||
*/
|
||||
private fun isUserUsedLessThan24Hours(): Boolean {
|
||||
val initialUsageTime = SPUtils.getLong(Constants.SP_INITIAL_USAGE_TIME, 0)
|
||||
return !(initialUsageTime == 0L || System.currentTimeMillis() - initialUsageTime > 86400000)
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,7 +15,7 @@ import com.gh.gamecenter.room.converter.*
|
||||
import com.gh.gamecenter.room.dao.*
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 5, exportSchema = false)
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 6, exportSchema = false)
|
||||
@TypeConverters(CountConverter::class,
|
||||
CommunityConverter::class,
|
||||
TimeConverter::class,
|
||||
@ -23,7 +23,8 @@ import com.halo.assistant.HaloApp
|
||||
ThumbnailConverter::class,
|
||||
TagStyleListConverter::class,
|
||||
StringArrayListConverter::class,
|
||||
CommunityVideoConverter::class)
|
||||
CommunityVideoConverter::class,
|
||||
UserConverter::class)
|
||||
|
||||
abstract class HistoryDatabase : RoomDatabase() {
|
||||
|
||||
@ -54,11 +55,20 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_5_6: Migration = object : Migration(5, 6) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("Alter TABLE MyVideoEntity add title TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE MyVideoEntity add commentCount INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("Alter TABLE MyVideoEntity add user TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
||||
|
||||
val instance by lazy {
|
||||
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
.addMigrations(MIGRATION_3_4)
|
||||
.addMigrations(MIGRATION_4_5)
|
||||
.addMigrations(MIGRATION_5_6)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,13 +6,10 @@ import com.aliyun.sls.android.sdk.model.LogGroup
|
||||
import com.gh.loghub.LoghubHelper
|
||||
import org.json.JSONObject
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
|
||||
object LoghubUtils {
|
||||
|
||||
private const val STORE_SIZE = 100
|
||||
private const val STORE_FORCE_UPLOAD_INTERVAL = 120 * 1000L
|
||||
|
||||
private lateinit var mApplication: Application
|
||||
|
||||
private val loghubEventSet by lazy { hashSetOf<LoghubEvent>() }
|
||||
@ -27,10 +24,6 @@ object LoghubUtils {
|
||||
val eventList = loghubEventDao.getAll()
|
||||
loghubEventSet.addAll(eventList)
|
||||
}
|
||||
|
||||
fixedRateTimer(name = "Loghub-Event-Checker", initialDelay = 1000, period = STORE_FORCE_UPLOAD_INTERVAL) {
|
||||
commitSavedLoghubEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@ -54,7 +47,7 @@ object LoghubUtils {
|
||||
LoghubHelper.getInstance().uploadLogGroup(logGroup, logStore)
|
||||
}
|
||||
|
||||
private fun commitSavedLoghubEvents() {
|
||||
fun commitSavedLoghubEvents() {
|
||||
loghubEventExecutor.execute {
|
||||
if (loghubEventSet.isEmpty()) return@execute
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.repository
|
||||
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.common.util.ApkActiveUtils
|
||||
import com.gh.common.util.RandomUtils
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
@ -15,7 +16,8 @@ object RemenkapaiRepository {
|
||||
@JvmStatic
|
||||
fun getRemenkapai(size: Int): Observable<List<GameEntity>> {
|
||||
return if (remenkapaiList.isEmpty()) {
|
||||
RetrofitManager.getInstance(getApplication()).api.remenkapai
|
||||
RetrofitManager.getInstance(getApplication()).sensitiveApi.remenkapai
|
||||
.map(RegionSettingHelper.filterGame)
|
||||
.map { gameList -> filterEntityWithoutApk(gameList) }
|
||||
.map { pickRandomSizeEntity(size) }
|
||||
.map(ApkActiveUtils.filterMapperList)
|
||||
|
||||
14
app/src/main/java/com/gh/common/util/AntiBotHelper.kt
Normal file
14
app/src/main/java/com/gh/common/util/AntiBotHelper.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import com.alibaba.wireless.security.jaq.avmp.IJAQAVMPSignComponent
|
||||
import com.alibaba.wireless.security.open.SecurityGuardManager
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
object AntiBotHelper {
|
||||
@JvmStatic
|
||||
val manager by lazy {
|
||||
SecurityGuardManager.getInstance(HaloApp.getInstance().application).getInterface(IJAQAVMPSignComponent::class.java).apply {
|
||||
this.initialize()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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("复制成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ 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;
|
||||
@ -12,6 +13,8 @@ import com.lightgame.download.DownloadEntity;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import kotlin.text.StringsKt;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/12/9.
|
||||
* 数据收集 工具类(data.ghzs666.com)
|
||||
@ -31,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", "插件化");
|
||||
|
||||
@ -3,19 +3,16 @@ package com.gh.common.util;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gid.GidCallback;
|
||||
import com.gh.gid.GidHelper;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.config.CommonDebug;
|
||||
import com.lightgame.utils.Util_System_Phone_State;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.tencent.bugly.crashreport.CrashReport;
|
||||
import com.tencent.stat.MtaSDkException;
|
||||
@ -126,11 +123,18 @@ public class DataUtils {
|
||||
public static void getGid() {
|
||||
GidHelper.getInstance().registerDevice(new GidCallback() {
|
||||
@Override
|
||||
public void onSuccess(String s) {
|
||||
Utils.log("Gid", s);
|
||||
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, s).apply();
|
||||
public void onSuccess(String gid) {
|
||||
Utils.log("Gid", gid);
|
||||
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, gid).apply();
|
||||
|
||||
String originalGid = HaloApp.getInstance().getGid();
|
||||
HaloApp.getInstance().setGid(gid);
|
||||
|
||||
// 避免重复调用
|
||||
if (!TextUtils.isEmpty(gid) && !gid.equals(originalGid)) {
|
||||
GameSubstituteRepositoryHelper.updateSubstitutableGames();
|
||||
}
|
||||
|
||||
HaloApp.getInstance().setGid(s);
|
||||
// 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid
|
||||
MetaUtil.INSTANCE.refreshMeta();
|
||||
}
|
||||
@ -197,36 +201,36 @@ public class DataUtils {
|
||||
|
||||
// 游戏下载
|
||||
public static void onGameDownloadEvent(Context context, String gameName, String platform, String entrance, String status, String method) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
|
||||
platform = PlatformUtils.getInstance(HaloApp.getInstance().getApplication()).getPlatformName(platform);
|
||||
|
||||
kv.put("版本", platform);
|
||||
kv.put("用户机型", Build.MODEL);
|
||||
kv.put("设备IMEI", Util_System_Phone_State.getDeviceId(HaloApp.getInstance().getApplication()));
|
||||
kv.put("网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().getApplication()));
|
||||
kv.put("光环助手版本", BuildConfig.VERSION_NAME);
|
||||
kv.put("位置", entrance);
|
||||
kv.put("类型", method);
|
||||
kv.put("厂商", Build.MANUFACTURER);
|
||||
kv.put("Android版本", Build.VERSION.RELEASE);
|
||||
onEvent(context, "游戏下载", gameName, kv);
|
||||
|
||||
Map<String, Object> kv2 = new HashMap<>();
|
||||
kv2.put("状态", status);
|
||||
kv2.put("位置", entrance);
|
||||
|
||||
if (status.equals("开始")) {
|
||||
kv2.put("版本", entrance + "-开始");
|
||||
kv2.put("游戏分平台", gameName + "-" + platform + "-开始");
|
||||
kv2.put("光环助手版本", BuildConfig.VERSION_NAME + "-开始");
|
||||
} else {
|
||||
kv2.put("版本", platform);
|
||||
kv2.put("游戏分平台", gameName + "-" + platform);
|
||||
kv2.put("光环助手版本", BuildConfig.VERSION_NAME);
|
||||
}
|
||||
|
||||
onEvent(context, "游戏下载位置", gameName, kv2);
|
||||
// Map<String, Object> kv = new HashMap<>();
|
||||
//
|
||||
// platform = PlatformUtils.getInstance(HaloApp.getInstance().getApplication()).getPlatformName(platform);
|
||||
//
|
||||
// kv.put("版本", platform);
|
||||
// kv.put("用户机型", Build.MODEL);
|
||||
// kv.put("设备IMEI", Util_System_Phone_State.getDeviceId(HaloApp.getInstance().getApplication()));
|
||||
// kv.put("网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().getApplication()));
|
||||
// kv.put("光环助手版本", BuildConfig.VERSION_NAME);
|
||||
// kv.put("位置", entrance);
|
||||
// kv.put("类型", method);
|
||||
// kv.put("厂商", Build.MANUFACTURER);
|
||||
// kv.put("Android版本", Build.VERSION.RELEASE);
|
||||
// onEvent(context, "游戏下载", gameName, kv);
|
||||
//
|
||||
// Map<String, Object> kv2 = new HashMap<>();
|
||||
// kv2.put("状态", status);
|
||||
// kv2.put("位置", entrance);
|
||||
//
|
||||
// if (status.equals("开始")) {
|
||||
// kv2.put("版本", entrance + "-开始");
|
||||
// kv2.put("游戏分平台", gameName + "-" + platform + "-开始");
|
||||
// kv2.put("光环助手版本", BuildConfig.VERSION_NAME + "-开始");
|
||||
// } else {
|
||||
// kv2.put("版本", platform);
|
||||
// kv2.put("游戏分平台", gameName + "-" + platform);
|
||||
// kv2.put("光环助手版本", BuildConfig.VERSION_NAME);
|
||||
// }
|
||||
//
|
||||
// onEvent(context, "游戏下载位置", gameName, kv2);
|
||||
}
|
||||
|
||||
// 游戏更新
|
||||
|
||||
@ -269,18 +269,21 @@ public class DeviceUtils {
|
||||
|
||||
// 只能获取WiFi的IpAddress
|
||||
public static String getCurrentIpAddress() {
|
||||
String ipAddress;
|
||||
WifiManager wifiManager = (WifiManager) HaloApp.getInstance().
|
||||
getApplication().
|
||||
getApplicationContext().
|
||||
getSystemService(Context.WIFI_SERVICE);
|
||||
int address = wifiManager.getDhcpInfo().ipAddress;
|
||||
ipAddress = ((address & 0xFF)
|
||||
+ "." + ((address >> 8) & 0xFF)
|
||||
+ "." + ((address >> 16) & 0xFF)
|
||||
+ "." + ((address >> 24) & 0xFF));
|
||||
return ipAddress;
|
||||
String ipAddress = "0.0.0.0";
|
||||
try {
|
||||
WifiManager wifiManager = (WifiManager) HaloApp.getInstance().
|
||||
getApplication().
|
||||
getApplicationContext().
|
||||
getSystemService(Context.WIFI_SERVICE);
|
||||
int address = wifiManager.getDhcpInfo().ipAddress;
|
||||
ipAddress = ((address & 0xFF)
|
||||
+ "." + ((address >> 8) & 0xFF)
|
||||
+ "." + ((address >> 16) & 0xFF)
|
||||
+ "." + ((address >> 24) & 0xFF));
|
||||
return ipAddress;
|
||||
} catch (Exception e) {
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ 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;
|
||||
@ -60,7 +59,6 @@ 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;
|
||||
@ -196,7 +194,7 @@ public class DialogUtils {
|
||||
AppExecutor.getUiExecutor().executeWithDelay(() -> Utils.toast(context, "当前使用移动网络下载,请注意流量消耗"), 500);
|
||||
callBack.onResponse(false);
|
||||
} else {
|
||||
MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(context), "出现弹窗提示");
|
||||
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(context), "出现弹窗提示");
|
||||
showDownloadDialog(context,
|
||||
() -> callBack.onResponse(false),
|
||||
() -> callBack.onResponse(true));
|
||||
@ -256,12 +254,12 @@ public class DialogUtils {
|
||||
}, 500);
|
||||
listener.onConfirm();
|
||||
dialog.dismiss();
|
||||
MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "本次允许");
|
||||
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "本次允许");
|
||||
});
|
||||
wifiAuto.setOnClickListener(v -> {
|
||||
cancelListener.onCancel();
|
||||
dialog.dismiss();
|
||||
MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "连上WiFi后自动下载");
|
||||
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "连上WiFi后自动下载");
|
||||
});
|
||||
allowAlways.setOnClickListener(v -> {
|
||||
PreferenceManager
|
||||
@ -276,7 +274,7 @@ public class DialogUtils {
|
||||
}, 500);
|
||||
listener.onConfirm();
|
||||
dialog.dismiss();
|
||||
MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "总是允许");
|
||||
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "总是允许");
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
@ -1343,6 +1341,33 @@ public class DialogUtils {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showPluggableNeverRemindDialog(Context context, String nameAndPlatform, @NonNull ConfirmListener listener) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_pluggable_never_remind, null);
|
||||
|
||||
View cancelBtn = contentView.findViewById(R.id.cancel);
|
||||
View confirmBtn = contentView.findViewById(R.id.confirm);
|
||||
TextView contentTv = contentView.findViewById(R.id.content);
|
||||
|
||||
contentTv.setText(("助手首页将不再提示《" + nameAndPlatform + "》的所有插件化消息,确定吗?"));
|
||||
|
||||
cancelBtn.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
confirmBtn.setOnClickListener(v -> {
|
||||
listener.onConfirm();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context may be is application context
|
||||
* @return activity context
|
||||
|
||||
@ -6,11 +6,17 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.base.BaseActivity
|
||||
import com.gh.base.ToolBarActivity
|
||||
import com.gh.base.fragment.BaseFragment_TabLayout
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.exposure.ExposureEvent
|
||||
import com.gh.common.exposure.ExposureEvent.Companion.createEvent
|
||||
import com.gh.common.exposure.ExposureManager.log
|
||||
import com.gh.common.exposure.ExposureTraceUtils.appendTrace
|
||||
import com.gh.common.exposure.ExposureType
|
||||
import com.gh.common.util.EntranceUtils.*
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.amway.AmwayActivity
|
||||
@ -21,6 +27,7 @@ import com.gh.gamecenter.eventbus.EBReuse
|
||||
import com.gh.gamecenter.eventbus.EBSkip
|
||||
import com.gh.gamecenter.fragment.MainWrapperFragment
|
||||
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActivity
|
||||
import com.gh.gamecenter.game.upload.GameSubmissionActivity
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.mygame.PlayedGameActivity
|
||||
@ -124,30 +131,36 @@ object DirectUtils {
|
||||
when (linkEntity.type) {
|
||||
"article", "news", "文章" -> {
|
||||
NewsUtils.statNewsViews(context, linkEntity.link) // 统计阅读量
|
||||
context.startActivity(NewsDetailActivity.getIntentById(context, linkEntity.link, BaseActivity.mergeEntranceAndPath(entrance, path)))
|
||||
directToArticle(context, linkEntity.link
|
||||
?: "", BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
}
|
||||
|
||||
"game", "游戏" -> {
|
||||
if (exposureEvent != null) {
|
||||
GameDetailActivity.startGameDetailActivity(context, linkEntity.link, BaseActivity.mergeEntranceAndPath(entrance, path), exposureEvent)
|
||||
directToGameDetail(context, linkEntity.link
|
||||
?: "", BaseActivity.mergeEntranceAndPath(entrance, path), traceEvent = exposureEvent)
|
||||
} else {
|
||||
GameDetailActivity.startGameDetailActivity(context, linkEntity.link, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
directToGameDetail(context, linkEntity.link
|
||||
?: "", BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
}
|
||||
}
|
||||
|
||||
"column", "游戏专题" -> SubjectActivity.startSubjectActivity(context, linkEntity.link, linkEntity.text, false, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
"column", "游戏专题" -> directToSubject(context, linkEntity.link
|
||||
?: "", linkEntity.text, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
|
||||
"question", "社区问题" -> context.startActivity(QuestionsDetailActivity.getIntent(context, linkEntity.link, entrance, path))
|
||||
"question", "社区问题" -> directToQuestionDetail(context, linkEntity.link
|
||||
?: "", entrance, path)
|
||||
|
||||
"answer", "社区回答" -> context.startActivity(AnswerDetailActivity.getIntent(context, linkEntity.link, entrance, path))
|
||||
"answer", "社区回答" -> directToAnswerDetail(context, linkEntity.link ?: "", entrance, path)
|
||||
|
||||
"community", "问答社区" -> directToCommunity(context, CommunityEntity(linkEntity.link!!, linkEntity.text!!))
|
||||
|
||||
"community_article", "社区文章" -> context.startActivity(ArticleDetailActivity.getIntent(context, linkEntity.community!!, linkEntity.link!!, entrance, path))
|
||||
"community_article", "社区文章" -> directToCommunityArticle(context, linkEntity.community!!, linkEntity.link!!, entrance, path)
|
||||
|
||||
"community_column", "社区专题" -> directToCommunityColumn(context, linkEntity.community, linkEntity.link!!, entrance, path)
|
||||
|
||||
"community_special_column" -> context.startActivity(AskColumnDetailActivity.getIntentByColumnId(context, linkEntity.link, linkEntity.community!!, entrance, path))
|
||||
"community_special_column" -> directAskColumnDetail(context, linkEntity.link
|
||||
?: "", linkEntity.community!!, entrance, path)
|
||||
|
||||
"web", "inurl", "web链接" -> {
|
||||
when {
|
||||
@ -166,37 +179,28 @@ object DirectUtils {
|
||||
|
||||
"tag" -> context.startActivity(TagsActivity.getIntent(context, linkEntity.text!!, linkEntity.title, entrance, path))
|
||||
|
||||
"all_community_article" -> {
|
||||
context.startActivity(SimpleArticleListActivity.getIntent(
|
||||
context,
|
||||
linkEntity.link ?: "",
|
||||
entrance,
|
||||
path))
|
||||
}
|
||||
"all_community_article" -> directSimpleArticleList(context, linkEntity.link
|
||||
?: "", entrance, path)
|
||||
|
||||
"category", "分类" -> {
|
||||
context.startActivity(CategoryDirectoryActivity.getIntent(context, linkEntity.link!!, linkEntity.text!!))
|
||||
}
|
||||
"category", "分类" -> directCategoryDirectory(context, linkEntity.link!!, linkEntity.text!!)
|
||||
|
||||
"block", "版块" -> {
|
||||
context.startActivity(BlockActivity.getIntent(context, SubjectRecommendEntity(
|
||||
directToBlock(context, SubjectRecommendEntity(
|
||||
link = linkEntity.link,
|
||||
text = linkEntity.text,
|
||||
name = linkEntity.name,
|
||||
display = linkEntity.display ?: Display())))
|
||||
display = linkEntity.display ?: Display()))
|
||||
}
|
||||
|
||||
"column_collection", "专题合集" -> directToColumnCollection(context, linkEntity.link!!, -1, entrance)
|
||||
|
||||
"server", "game_server" -> {
|
||||
context.startActivity(GameServersActivity.getIntent(context, entrance, path))
|
||||
}
|
||||
"server", "game_server", "开服表" -> directToGameServers(context, entrance, path)
|
||||
|
||||
"top_game_comment" -> directToAmway(context, null, entrance, path)
|
||||
|
||||
"wechat_bind" -> context.startActivity(WebActivity.getBindWechatIntent(context))
|
||||
|
||||
"video", "video_stream" -> directToVideoDetail(context,
|
||||
"video", "video_stream", "视频" -> directToVideoDetail(context,
|
||||
videoId = linkEntity.link!!,
|
||||
fromLocation = VideoDetailContainerViewModel.Location.VIDEO_CHOICENESS.value,
|
||||
entrance = entrance,
|
||||
@ -204,16 +208,23 @@ object DirectUtils {
|
||||
|
||||
"game_video" -> directToGameVideo(context, linkEntity.link ?: "", entrance, path)
|
||||
|
||||
"libao" -> directToGiftDetail(context, linkEntity.link ?: "", entrance)
|
||||
"libao", "礼包" -> directToGiftDetail(context, linkEntity.link ?: "", entrance)
|
||||
|
||||
"feedback" -> directToFeedback(context, linkEntity.name, linkEntity.text, entrance)
|
||||
|
||||
"qa" -> directToQa(context, linkEntity.text ?: "", linkEntity.link ?: "")
|
||||
"qa", "Q&A" -> directToQa(context, linkEntity.text ?: "", linkEntity.link ?: "")
|
||||
|
||||
"qa_collection" -> directToQaCollection(context, linkEntity.text ?: "", linkEntity.link
|
||||
"qa_collection", "Q&A合集" -> directToQaCollection(context, linkEntity.text
|
||||
?: "", linkEntity.link
|
||||
?: "")
|
||||
|
||||
"anliwall" -> directToAmway(context, fixedTopAmwayCommentId = null, entrance = entrance, path = path)
|
||||
"anliwall", "安利墙" -> directToAmway(context, fixedTopAmwayCommentId = null, entrance = entrance, path = path)
|
||||
|
||||
"game_detail_comment" -> directToGameDetail(context, linkEntity.link ?: "", entrance)
|
||||
|
||||
"game_upload", "游戏投稿" -> directGameUpload(context, entrance, path)
|
||||
|
||||
//"h5_game_center" -> directLetoGameCenter(context)
|
||||
|
||||
"" -> {
|
||||
// do nothing
|
||||
@ -231,22 +242,42 @@ object DirectUtils {
|
||||
/**
|
||||
* 跳转至QA
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToQa(context: Context, text: String, id: String) {
|
||||
context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaId = id))
|
||||
// context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaId = id))
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, QaActivity::class.java.simpleName)
|
||||
bundle.putString(KEY_NAVIGATION_TITLE, text)
|
||||
bundle.putString(KEY_QA_ID, id)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至QA合集
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToQaCollection(context: Context, text: String, id: String) {
|
||||
context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaCollectionId = id))
|
||||
// context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaCollectionId = id))
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, QaActivity::class.java.simpleName)
|
||||
bundle.putString(KEY_NAVIGATION_TITLE, text)
|
||||
bundle.putString(KEY_QA_COLLECTION_ID, id)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至专题合集
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String, columnName: String = "") {
|
||||
context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance, columnName))
|
||||
// context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance, columnName))
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, ColumnCollectionDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, entrance)
|
||||
bundle.putString(KEY_COLLECTION_ID,id)
|
||||
bundle.putString(KEY_COLUMNNAME,columnName)
|
||||
bundle.putInt(KEY_POSITION,position)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,7 +327,7 @@ object DirectUtils {
|
||||
* 跳转到游戏详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToGameDetail(context: Context, id: String, entrance: String? = null, autoDownload: Boolean? = null, scrollToLibao: Boolean = false) {
|
||||
fun directToGameDetail(context: Context, id: String, entrance: String? = null, autoDownload: Boolean? = null, scrollToLibao: Boolean = false, traceEvent: ExposureEvent? = null) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_TO, GameDetailActivity::class.java.simpleName)
|
||||
@ -305,6 +336,11 @@ object DirectUtils {
|
||||
bundle.putInt(KEY_TARGET, GameDetailFragment.INDEX_TRENDES)
|
||||
bundle.putBoolean(KEY_SCROLL_TO_LIBAO, scrollToLibao)
|
||||
}
|
||||
if (traceEvent != null) {
|
||||
val clickEvent = createEvent(GameEntity(id), traceEvent.source, appendTrace(traceEvent), ExposureType.CLICK)
|
||||
log(clickEvent)
|
||||
bundle.putParcelable(KEY_TRACE_EVENT, clickEvent)
|
||||
}
|
||||
bundle.putBoolean(KEY_AUTO_DOWNLOAD, autoDownload ?: false)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
@ -447,6 +483,9 @@ object DirectUtils {
|
||||
@JvmStatic
|
||||
fun directToExternalBrowser(context: Context, url: String) {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
if (context !is AppCompatActivity){
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(browserIntent)
|
||||
}
|
||||
|
||||
@ -467,7 +506,11 @@ object DirectUtils {
|
||||
chatType = "wpa"
|
||||
}
|
||||
val str = "mqqwpa://im/chat?chat_type=$chatType&uin=$qq&version=1&src_type=web"
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(str)))
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(str))
|
||||
if (context !is AppCompatActivity){
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
// 没有安装QQ 复制账号
|
||||
Util_System_ClipboardManager.setText(context, qq)
|
||||
@ -478,17 +521,24 @@ object DirectUtils {
|
||||
// 跳转 QQ 群
|
||||
@JvmStatic
|
||||
fun directToQqGroup(context: Context, groupNumber: String? = null): Boolean {
|
||||
val intent = Intent()
|
||||
intent.data = Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$groupNumber")
|
||||
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
// 未安装手Q或安装的版本不支持
|
||||
if (ShareUtils.isQQClientAvailable(context)) {
|
||||
val intent = Intent()
|
||||
intent.data = Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$groupNumber")
|
||||
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
return try {
|
||||
if (context !is AppCompatActivity){
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}else{
|
||||
Utils.toast(context, "请安装QQ客户端")
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,6 +598,17 @@ object DirectUtils {
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToCommunityArticle(context: Context, community: CommunityEntity?, articleId: String?, entrance: String?, path: String?) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
bundle.putString(KEY_TO, ArticleDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_COMMUNITY_ARTICLE_ID, articleId)
|
||||
bundle.putParcelable(KEY_COMMUNITY_DATA, community)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到社区专题
|
||||
*/
|
||||
@ -616,10 +677,11 @@ object DirectUtils {
|
||||
* 跳转至上传视频
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToVideoManager(context: Context, linkEntity: VideoLinkEntity, entrance: String? = null, path: String? = "") {
|
||||
fun directToVideoManager(context: Context, linkEntity: VideoLinkEntity,simpleGameEntity: SimpleGameEntity, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_PATH, path)
|
||||
bundle.putParcelable(VideoLinkEntity::class.java.simpleName, linkEntity)
|
||||
bundle.putParcelable(SimpleGameEntity::class.java.simpleName, simpleGameEntity)
|
||||
bundle.putString(KEY_TO, VideoManagerActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
jumpActivity(context, bundle)
|
||||
@ -647,4 +709,117 @@ 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到板块
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToBlock(context: Context, blockData: SubjectRecommendEntity) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, BlockActivity::class.java.name)
|
||||
bundle.putParcelable(KEY_BLOCK_DATA, blockData)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到开服表
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToGameServers(context: Context, entrance: String, path: String) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, GameServersActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, ToolBarActivity.mergeEntranceAndPath(entrance, path))
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到游戏上传
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directGameUpload(context: Context, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, GameSubmissionActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 社区文章
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directSimpleArticleList(context: Context, sortType: String, entrance: String? = null, path: String? = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, SimpleArticleListActivity::class.java.name)
|
||||
bundle.putString(KEY_TYPE, sortType)
|
||||
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,30 @@ 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 int getToastOffset() {
|
||||
int i = Resources.getSystem().getIdentifier("toast_y_offset", "dimen", "android");
|
||||
return HaloApp.getInstance().getApplication().getResources().getDimensionPixelSize(i);
|
||||
}
|
||||
|
||||
public static int getToastDefaultGravity() {
|
||||
int i = Resources.getSystem().getIdentifier("config_toastDefaultGravity", "integer", "android");
|
||||
return HaloApp.getInstance().getApplication().getResources().getInteger(i);
|
||||
}
|
||||
|
||||
public static boolean hasSoftKeys(Context context) {
|
||||
if (!(context instanceof Activity)) return false;
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
@ -18,8 +19,12 @@ object DownloadHelper {
|
||||
* @param block 成功添加下载任务后执行的代码块
|
||||
*/
|
||||
fun createABrandNewDownloadTaskQuietly(gameId: String? = "", packageName: String? = "", block: () -> Unit) {
|
||||
if (RegionSettingHelper.shouldThisGameBeFiltered(gameId)) {
|
||||
return
|
||||
}
|
||||
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.api
|
||||
.sensitiveApi
|
||||
.getGameDigest(gameId)
|
||||
.map(ApkActiveUtils.filterMapper)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
@ -19,7 +19,7 @@ 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.download.DownloadManager;
|
||||
import com.gh.download.dialog.DownloadDialog;
|
||||
@ -134,16 +134,23 @@ public class DownloadItemUtils {
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform) {
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, false);
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, false, null);
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform, boolean hideDownloadBtnIfNoAvailableContent) {
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, hideDownloadBtnIfNoAvailableContent);
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, hideDownloadBtnIfNoAvailableContent, null);
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation, boolean hideDownloadBtnIfNoAvailableContent) {
|
||||
boolean isShowPlatform, String briefStyle) {
|
||||
updateItem(context, gameEntity, holder, isShowPlatform, PluginLocation.only_game, false, briefStyle);
|
||||
}
|
||||
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation,
|
||||
boolean hideDownloadBtnIfNoAvailableContent,
|
||||
@Nullable String briefStyle) {
|
||||
|
||||
// 控制是否显示下载按钮
|
||||
if (!Config.isShowDownload(gameEntity.getId()) || context.getString(R.string.app_name).equals(gameEntity.getName())) {
|
||||
@ -154,9 +161,7 @@ public class DownloadItemUtils {
|
||||
|
||||
// 显示预约
|
||||
if (gameEntity.isReservable()) {
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
updateItemViewStatus(holder, false, briefStyle);
|
||||
if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.getId())) {
|
||||
holder.gameDownloadBtn.setText("预约");
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
@ -174,9 +179,7 @@ public class DownloadItemUtils {
|
||||
LinkEntity h5LinkEntity = gameEntity.getH5Link();
|
||||
String offStatus = gameEntity.getDownloadOffStatus();
|
||||
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
updateItemViewStatus(holder, false, briefStyle);
|
||||
|
||||
if (h5LinkEntity != null) {
|
||||
if ("play".equals(h5LinkEntity.getType())) {
|
||||
@ -201,16 +204,16 @@ public class DownloadItemUtils {
|
||||
holder.gameDownloadBtn.setClickable(false);
|
||||
}
|
||||
} else if (gameEntity.getApk().size() == 1) {
|
||||
updateNormalItem(context, holder, gameEntity, isShowPlatform, pluginLocation);
|
||||
updateNormalItem(context, holder, gameEntity, isShowPlatform, pluginLocation, briefStyle);
|
||||
} else {
|
||||
updatePluginItem(context, holder, gameEntity, isShowPlatform, pluginLocation);
|
||||
updatePluginItem(context, holder, gameEntity, isShowPlatform, pluginLocation, briefStyle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 更新正常的条目,只有一个apk包
|
||||
static void updateNormalItem(Context context, GameViewHolder holder, GameEntity gameEntity,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation) {
|
||||
boolean isShowPlatform, PluginLocation pluginLocation, String briefStyle) {
|
||||
|
||||
final ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
|
||||
final ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
@ -226,14 +229,12 @@ public class DownloadItemUtils {
|
||||
|
||||
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn, pluginLocation);
|
||||
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
updateItemViewStatus(holder, false, briefStyle);
|
||||
}
|
||||
|
||||
// 更新插件的条目,有多个apk包
|
||||
private static void updatePluginItem(Context context, GameViewHolder holder, GameEntity gameEntity,
|
||||
boolean isShowPlatform, PluginLocation pluginLocation) {
|
||||
boolean isShowPlatform, PluginLocation pluginLocation, String briefStyle) {
|
||||
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn, pluginLocation);
|
||||
|
||||
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
|
||||
@ -252,18 +253,14 @@ public class DownloadItemUtils {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
updateItemViewStatus(holder, false, briefStyle);
|
||||
}
|
||||
|
||||
// 更改进度条和提示文本的状态
|
||||
public static void changeStatus(Context context, GameViewHolder holder, DownloadEntity downloadEntity,
|
||||
boolean isShowPlatform, boolean isNormal) {
|
||||
holder.gameDes.setVisibility(View.GONE);
|
||||
holder.gameProgressbar.setVisibility(View.VISIBLE);
|
||||
holder.gameInfo.setVisibility(View.VISIBLE);
|
||||
|
||||
updateItemViewStatus(holder, true, null);
|
||||
|
||||
String platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform());
|
||||
|
||||
@ -345,6 +342,29 @@ public class DownloadItemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateItemViewStatus(GameViewHolder holder, boolean hasDownload, @Nullable String briefStyle) {
|
||||
if (hasDownload) {
|
||||
if (holder.gameRating != null) holder.gameRating.setVisibility(View.GONE);
|
||||
holder.gameDes.setVisibility(View.GONE);
|
||||
holder.gameProgressbar.setVisibility(View.VISIBLE);
|
||||
holder.gameInfo.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
if (briefStyle != null && briefStyle.contains("star")) {
|
||||
if (holder.gameRating != null) holder.gameRating.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (holder.gameRating != null) holder.gameRating.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(briefStyle) || briefStyle.contains("brief")) {
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.gameDes.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setOnClickListener(final Context context,
|
||||
final TextView downloadBtn,
|
||||
final GameEntity gameEntity,
|
||||
@ -426,7 +446,15 @@ public class DownloadItemUtils {
|
||||
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) {
|
||||
@ -456,6 +484,7 @@ public class DownloadItemUtils {
|
||||
DownloadDialog.showDownloadDialog(
|
||||
v.getContext(),
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location);
|
||||
});
|
||||
@ -513,7 +542,7 @@ public class DownloadItemUtils {
|
||||
MtaHelper.onEvent("我的游戏_启动", "插件化", gameEntity.getName());
|
||||
}
|
||||
if (gameEntity.getPluggableCollection() != null) {
|
||||
DownloadDialog.showDownloadDialog(context, gameEntity, entrance, location);
|
||||
DownloadDialog.showDownloadDialog(context, gameEntity, traceEvent, entrance, location);
|
||||
} else {
|
||||
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
|
||||
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
|
||||
@ -557,9 +586,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.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);
|
||||
@ -578,9 +605,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);
|
||||
@ -617,12 +642,8 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -88,30 +88,33 @@ object DownloadNotificationHelper {
|
||||
else -> builder.setSortKey("C")
|
||||
}
|
||||
|
||||
val notification = builder.build()
|
||||
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR
|
||||
tryCatchInRelease {
|
||||
val notification = builder.build() // 可能会抛出异常
|
||||
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR
|
||||
|
||||
if (entity.status == DownloadStatus.delete
|
||||
|| entity.status == DownloadStatus.cancel
|
||||
|| entity.status == DownloadStatus.hijack
|
||||
|| entity.status == DownloadStatus.notfound
|
||||
|| entity.status == DownloadStatus.overflow
|
||||
|| (entity.status == DownloadStatus.done // 触发安装事件以后也 cancel 掉通知
|
||||
&& !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty())) {
|
||||
requireUpdateNotificationGroupDelay = true
|
||||
notificationManager.cancel(entity.path, DOWNLOAD_NOTIFICATION_ID)
|
||||
} else {
|
||||
if (entity.status != DownloadStatus.downloading) {
|
||||
notificationManager.notify(entity.path, DOWNLOAD_NOTIFICATION_ID, notification)
|
||||
if (entity.status == DownloadStatus.delete
|
||||
|| entity.status == DownloadStatus.cancel
|
||||
|| entity.status == DownloadStatus.hijack
|
||||
|| entity.status == DownloadStatus.notfound
|
||||
|| entity.status == DownloadStatus.overflow
|
||||
|| (entity.status == DownloadStatus.done // 触发安装事件以后也 cancel 掉通知
|
||||
&& !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty())) {
|
||||
requireUpdateNotificationGroupDelay = true
|
||||
notificationManager.cancel(entity.path, DOWNLOAD_NOTIFICATION_ID)
|
||||
} else {
|
||||
val time = mNotifyMap[entity.path]
|
||||
val curTime = System.currentTimeMillis()
|
||||
if (time == null || curTime - time > 2000) {
|
||||
mNotifyMap[entity.path] = curTime
|
||||
if (entity.status != DownloadStatus.downloading) {
|
||||
notificationManager.notify(entity.path, DOWNLOAD_NOTIFICATION_ID, notification)
|
||||
} else {
|
||||
val time = mNotifyMap[entity.path]
|
||||
val curTime = System.currentTimeMillis()
|
||||
if (time == null || curTime - time > 2000) {
|
||||
mNotifyMap[entity.path] = curTime
|
||||
notificationManager.notify(entity.path, DOWNLOAD_NOTIFICATION_ID, notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requireUpdateNotificationGroupDelay) {
|
||||
// 虽然运行到这里时 notification 已经被 cancel 了,但在下面的 notificationManager.getActiveNotifications 里它有可能还是 active 状态,
|
||||
// 这里延时 100 ms 避免出现所有的任务都取消了以后依旧有一条 notification group 常驻
|
||||
|
||||
@ -38,11 +38,13 @@ 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)
|
||||
@ -53,20 +55,20 @@ object DownloadObserver {
|
||||
processHijack(downloadEntity)
|
||||
val nameAndPlatform = (downloadEntity.name + ":"
|
||||
+ PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
MtaHelper.onEvent("下载劫持",
|
||||
"游戏名字", nameAndPlatform,
|
||||
"网络状态", DeviceUtils.getNetwork(mApplication))
|
||||
// MtaHelper.onEvent("下载劫持",
|
||||
// "游戏名字", nameAndPlatform,
|
||||
// "网络状态", DeviceUtils.getNetwork(mApplication))
|
||||
return
|
||||
} else if (DownloadStatus.notfound == downloadEntity.status) {
|
||||
// 404 Not Found
|
||||
// 删除任务
|
||||
downloadEntity.status = DownloadStatus.cancel
|
||||
DownloadManager.getInstance(mApplication).cancel(downloadEntity.url)
|
||||
downloadManager.cancel(downloadEntity.url)
|
||||
Utils.toast(mApplication, "该链接已失效!请联系管理员。")
|
||||
|
||||
MtaHelper.onEventWithBasicDeviceInfo("下载失败弹窗",
|
||||
"游戏", downloadEntity.name,
|
||||
"平台", downloadEntity.platform)
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("下载失败弹窗",
|
||||
// "游戏", downloadEntity.name,
|
||||
// "平台", downloadEntity.platform)
|
||||
|
||||
DialogUtils.showAlertDialog(AppManager.getInstance().currentActivity(), "下载失败", "下载链接已失效,建议提交反馈", "立即反馈", "取消", {
|
||||
SuggestionActivity.startSuggestionActivity(AppManager.getInstance().currentActivity(),
|
||||
@ -76,13 +78,25 @@ object DownloadObserver {
|
||||
}, 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))) {
|
||||
@ -112,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"
|
||||
@ -135,11 +149,11 @@ object DownloadObserver {
|
||||
val pm = mApplication.packageManager
|
||||
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, PackageManager.GET_ACTIVITIES)
|
||||
if (packageInfo == null) {
|
||||
MtaHelper.onEventWithBasicDeviceInfo("解析包错误分析",
|
||||
"游戏名字", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
|
||||
MtaHelper.onEventWithBasicDeviceInfo("解析包错误_新",
|
||||
"游戏", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误分析",
|
||||
// "游戏名字", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
//
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误_新",
|
||||
// "游戏", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +162,13 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,13 +199,13 @@ object DownloadObserver {
|
||||
type = ExposureUtils.DownloadType.DOWNLOAD
|
||||
}
|
||||
|
||||
val kv2 = HashMap<String, Any>()
|
||||
kv2["版本"] = downloadEntity.platform
|
||||
kv2["状态"] = "下载完成"
|
||||
kv2["位置"] = downloadEntity.entrance ?: "null"
|
||||
kv2["游戏分平台"] = downloadEntity.name + "-" + platform
|
||||
kv2["光环助手版本"] = BuildConfig.VERSION_NAME
|
||||
DataUtils.onEvent(mApplication, "游戏下载位置", downloadEntity.name, kv2)
|
||||
// val kv2 = HashMap<String, Any>()
|
||||
// kv2["版本"] = downloadEntity.platform
|
||||
// kv2["状态"] = "下载完成"
|
||||
// kv2["位置"] = downloadEntity.entrance ?: "null"
|
||||
// kv2["游戏分平台"] = downloadEntity.name + "-" + platform
|
||||
// kv2["光环助手版本"] = BuildConfig.VERSION_NAME
|
||||
// DataUtils.onEvent(mApplication, "游戏下载位置", downloadEntity.name, kv2)
|
||||
|
||||
if (downloadEntity.isPluggable) {
|
||||
val kv3 = HashMap<String, Any>()
|
||||
@ -194,16 +215,18 @@ object DownloadObserver {
|
||||
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
|
||||
DataUtils.onEvent(mApplication, "插件化", downloadEntity.name, kv3)
|
||||
|
||||
MtaHelper.onEvent(
|
||||
"插件化_新",
|
||||
"位置", downloadEntity.entrance,
|
||||
"游戏", downloadEntity.name + "-" + downloadEntity.platform,
|
||||
"操作", "下载完成",
|
||||
"网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().application))
|
||||
// MtaHelper.onEvent(
|
||||
// "插件化_新",
|
||||
// "位置", downloadEntity.entrance,
|
||||
// "游戏", downloadEntity.name + "-" + downloadEntity.platform,
|
||||
// "操作", "下载完成",
|
||||
// "网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().application))
|
||||
}
|
||||
|
||||
ExposureUtils.logADownloadCompleteExposureEvent(
|
||||
GameEntity(downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER), downloadEntity.name),
|
||||
GameEntity(id = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER),
|
||||
mName = downloadEntity.name.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = downloadEntity.versionName ?: ""),
|
||||
downloadEntity.platform,
|
||||
downloadEntity.exposureTrace,
|
||||
type)
|
||||
|
||||
@ -28,6 +28,7 @@ public class EntranceUtils {
|
||||
public static final String KEY_ID = "id";
|
||||
public static final String KEY_URL = "url";
|
||||
public static final String KEY_GAMENAME = "gameName";
|
||||
public static final String KEY_PACKAGE_MD5 = "package_md5";
|
||||
public static final String HOST_ARTICLE = "article";
|
||||
public static final String HOST_UPLOAD_VIDEO = "upload_video";//上传视频
|
||||
public static final String HOST_VIDEO_SINGLE = "video_single";//指定视频-不能划动
|
||||
@ -37,6 +38,16 @@ 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_BLOCK = "block";//板块
|
||||
public static final String HOST_SERVER_BLOCK = "server";//开服表板块
|
||||
public static final String HOST_AMWAY_BLOCK = "amway";//安利墙板块
|
||||
public static final String HOST_HELP = "help";//Q&A
|
||||
public static final String HOST_HELP_COLLECTION = "help_collection";//Q&A合集
|
||||
public static final String HOST_GAME_UPLOAD = "game_upload";//游戏上传
|
||||
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";
|
||||
@ -154,6 +165,10 @@ public class EntranceUtils {
|
||||
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 final String KEY_KAIFU_SELECT_TIME = "kaifuSelectTime";
|
||||
|
||||
public static void jumpActivity(Context context, Bundle bundle) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
@ -3,10 +3,11 @@ package com.gh.common.util
|
||||
import android.animation.Animator
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.text.Html
|
||||
import android.text.Spanned
|
||||
import android.text.TextWatcher
|
||||
import android.os.Build
|
||||
import android.text.*
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.text.style.URLSpan
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
@ -22,10 +23,15 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.common.DefaultUrlHandler
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.view.CenterImageSpan
|
||||
import com.gh.common.view.CustomLinkMovementMethod
|
||||
import com.gh.common.view.ExpandTextView
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
@ -36,6 +42,7 @@ import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import java.net.URI
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
@ -191,6 +198,28 @@ inline fun <reified T : Any> T.toJson(): String {
|
||||
return GsonUtils.toJson(this)
|
||||
}
|
||||
|
||||
fun String.insert(index: Int, string: String): String {
|
||||
return this.substring(0, index) + string + this.substring(index, this.length)
|
||||
}
|
||||
|
||||
/**
|
||||
* TextView 内部处理 ul li ol 得跟 Android 版本走,这里换成专属的标签手动处理
|
||||
*/
|
||||
fun String.replaceUnsupportedHtmlTag(): String {
|
||||
return this.replace("<ul", "<hul")
|
||||
.replace("</ul>", "</hul>")
|
||||
.replace("<li", "<hli")
|
||||
.replace("</li>", "</hli>")
|
||||
.replace("<ol", "<hol")
|
||||
.replace("</ol>", "</hol>")
|
||||
}
|
||||
|
||||
fun String.containHtmlTag(): Boolean {
|
||||
val pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>")
|
||||
val matcher = pattern.matcher(this)
|
||||
return matcher.find()
|
||||
}
|
||||
|
||||
/**
|
||||
* 在限定 interval 里只触发一次 action
|
||||
*/
|
||||
@ -285,11 +314,18 @@ fun String.subStringIfPossible(length: Int): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getFirstElementDividedByDivider(divider: String): String {
|
||||
if (this.contains(divider)) {
|
||||
return this.split(divider.toRegex()).toTypedArray()[0]
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -442,6 +478,75 @@ fun TextView.setTextChangedListener(action: (s: CharSequence, start: Int, before
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 TextView 中的 Url Span,用应用内页面的形式打开链接
|
||||
* @param shrankText 未展开时的文字
|
||||
* @param expandedText 展开后的文字
|
||||
*/
|
||||
fun ExpandTextView.setTextWithInterceptingInternalUrl(shrankText: CharSequence, expandedText: CharSequence) {
|
||||
var shrankSsb = shrankText.interceptUrlSpanAndRoundImageSpan()
|
||||
var expandedSsb = expandedText.interceptUrlSpanAndRoundImageSpan()
|
||||
|
||||
// 去掉旧版本 Android 系统 [Html.FROM_HTML_MODE_LEGACY] 产生的两个换行符 (丑陋的代码)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
while (shrankSsb.contains("\n\n")) {
|
||||
val index = shrankSsb.indexOf("\n\n", 0, true)
|
||||
shrankSsb = SpannableStringBuilder(shrankSsb.subSequence(0, index)).append(shrankSsb.subSequence(index + "\n".length, shrankSsb.length))
|
||||
}
|
||||
while (expandedSsb.contains("\n\n")) {
|
||||
val index = expandedSsb.indexOf("\n\n", 0, true)
|
||||
expandedSsb = SpannableStringBuilder(expandedSsb.subSequence(0, index)).append(expandedSsb.subSequence(index + "\n".length, expandedSsb.length))
|
||||
}
|
||||
}
|
||||
|
||||
// 去掉多余的 P 标签换行
|
||||
if (expandedSsb.endsWith("\n", true)) {
|
||||
expandedSsb = SpannableStringBuilder((expandedSsb.subSequence(0, expandedSsb.length - "\n".length)))
|
||||
}
|
||||
|
||||
movementMethod = CustomLinkMovementMethod.getInstance()
|
||||
|
||||
shrankSsb = TextHelper.updateSpannableStringWithHighlightedSpan(context, shrankSsb, highlightedTextClickListener = null)
|
||||
expandedSsb = TextHelper.updateSpannableStringWithHighlightedSpan(context, expandedSsb, highlightedTextClickListener = null)
|
||||
setShrankTextAndExpandedText(shrankSsb, expandedSsb)
|
||||
}
|
||||
|
||||
fun CharSequence.interceptUrlSpanAndRoundImageSpan(): SpannableStringBuilder {
|
||||
return SpannableStringBuilder.valueOf(this).apply {
|
||||
getSpans(0, length, URLSpan::class.java).forEach {
|
||||
setSpan(
|
||||
object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(HaloApp.getInstance().application, R.color.theme_font)
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
if (!DefaultUrlHandler.interceptUrl(widget.context, it.url, "")) {
|
||||
widget.context.startActivity(WebActivity.getIntent(widget.context, it.url, true))
|
||||
}
|
||||
}
|
||||
},
|
||||
getSpanStart(it),
|
||||
getSpanEnd(it),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
removeSpan(it)
|
||||
}
|
||||
|
||||
getSpans(0, length, ImageSpan::class.java).forEach {
|
||||
setSpan(
|
||||
CenterImageSpan(it.drawable),
|
||||
getSpanStart(it),
|
||||
getSpanEnd(it),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
removeSpan(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.toColor(): Int {
|
||||
return ContextCompat.getColor(HaloApp.getInstance().application, this)
|
||||
}
|
||||
@ -461,6 +566,35 @@ 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 {
|
||||
tryWithDefaultCatch {
|
||||
Utils.log("Block one useless sub process method call from ${Thread.currentThread().stackTrace[3].methodName} -> ${Thread.currentThread().stackTrace[2].methodName}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun doOnMainProcessOnly(f: () -> Unit) {
|
||||
val processName = PackageUtils.obtainProcessName(HaloApp.getInstance().application)
|
||||
if (processName == null || BuildConfig.APPLICATION_ID == processName) {
|
||||
f.invoke()
|
||||
} else {
|
||||
tryWithDefaultCatch {
|
||||
Utils.log("Block one useless sub process method call from ${Thread.currentThread().stackTrace[3].methodName} -> ${Thread.currentThread().stackTrace[2].methodName}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用包裹
|
||||
*/
|
||||
@ -480,15 +614,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) {
|
||||
@ -546,4 +680,30 @@ fun LottieAnimationView.doOnAnimationEnd(action: () -> Unit) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查内容是否一致
|
||||
* @return true:相同 false:不同
|
||||
*/
|
||||
fun List<String>?.checkSameFromStringArray(check2: List<String>?): Boolean {
|
||||
if (this == check2) {
|
||||
return true
|
||||
}
|
||||
if (this == null && check2 == null) {
|
||||
return true
|
||||
}
|
||||
if (this == null || check2 == null) {
|
||||
return false
|
||||
}
|
||||
if (this.size != check2.size) {
|
||||
return false
|
||||
}
|
||||
for (tag in this) {
|
||||
if (!check2.contains(tag)) return false
|
||||
}
|
||||
for (tag in check2) {
|
||||
if (!this.contains(tag)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
183
app/src/main/java/com/gh/common/util/ExtraTagHandler.kt
Normal file
183
app/src/main/java/com/gh/common/util/ExtraTagHandler.kt
Normal file
@ -0,0 +1,183 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.Html.TagHandler
|
||||
import android.text.Spanned
|
||||
import android.text.style.BulletSpan
|
||||
import android.text.style.LeadingMarginSpan
|
||||
import android.util.Log
|
||||
import org.xml.sax.XMLReader
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Implements support for ordered (`<ol>`) and unordered (`<ul>`) lists in to Android TextView.
|
||||
*
|
||||
*
|
||||
* This can be used as follows:<br></br>
|
||||
* `textView.setText(Html.fromHtml("<ul><li>item 1</li><li>item 2</li></ul>", null, new HtmlListTagHandler()));`
|
||||
*
|
||||
*
|
||||
* Implementation based on code by Juha Kuitunen (https://bitbucket.org/Kuitsi/android-textview-html-list),
|
||||
* released under Apache License v2.0. Refactored & improved by Matthias Stevens (InThePocket.mobi).
|
||||
*
|
||||
*
|
||||
* **Known issues:**
|
||||
* * The indentation on nested `<ul>`s isn't quite right (TODO fix this)
|
||||
* * the `start` attribute of `<ol>` is not supported. Doing so is tricky because
|
||||
* [Html.TagHandler.handleTag] does not expose tag attributes.
|
||||
* The only way to do it would be to use reflection to access the attribute information kept by the XMLReader
|
||||
* (see: http://stackoverflow.com/a/24534689/1084488).
|
||||
*
|
||||
* https://bitbucket.org/Kuitsi/android-textview-html-list/src/master/app/src/main/java/fi/iki/kuitsi/listtest/MyTagHandler.java
|
||||
*
|
||||
*/
|
||||
class ExtraTagHandler : TagHandler {
|
||||
/**
|
||||
* Keeps track of lists (ol, ul). On bottom of Stack is the outermost list
|
||||
* and on top of Stack is the most nested list
|
||||
*/
|
||||
private val lists = Stack<ListTag>()
|
||||
|
||||
/**
|
||||
* @see android.text.Html.TagHandler.handleTag
|
||||
*/
|
||||
override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader) {
|
||||
if (UL_TAG.equals(tag, ignoreCase = true)) {
|
||||
if (opening) { // handle <ul>
|
||||
lists.push(Ul())
|
||||
} else { // handle </ul>
|
||||
lists.pop()
|
||||
}
|
||||
} else if (OL_TAG.equals(tag, ignoreCase = true)) {
|
||||
if (opening) { // handle <ol>
|
||||
lists.push(Ol()) // use default start index of 1
|
||||
} else { // handle </ol>
|
||||
lists.pop()
|
||||
}
|
||||
} else if (LI_TAG.equals(tag, ignoreCase = true)) {
|
||||
if (opening) { // handle <li>
|
||||
lists.peek().openItem(output)
|
||||
} else { // handle </li>
|
||||
lists.peek().closeItem(output, lists.size)
|
||||
}
|
||||
} else {
|
||||
Log.d("TagHandler", "Found an unsupported tag $tag")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract super class for [Ul] and [Ol].
|
||||
*/
|
||||
private abstract class ListTag {
|
||||
/**
|
||||
* Opens a new list item.
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
open fun openItem(text: Editable) {
|
||||
if (text.length > 0 && text[text.length - 1] != '\n') {
|
||||
text.append("\n")
|
||||
}
|
||||
val len = text.length
|
||||
text.setSpan(this, len, len, Spanned.SPAN_MARK_MARK)
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a list item.
|
||||
*
|
||||
* @param text
|
||||
* @param indentation
|
||||
*/
|
||||
fun closeItem(text: Editable, indentation: Int) {
|
||||
if (text.length > 0 && text[text.length - 1] != '\n') {
|
||||
text.append("\n")
|
||||
}
|
||||
val replaces = getReplaces(text, indentation)
|
||||
val len = text.length
|
||||
val listTag = getLast(text)
|
||||
val where = text.getSpanStart(listTag)
|
||||
text.removeSpan(listTag)
|
||||
if (where != len) {
|
||||
for (replace in replaces) {
|
||||
text.setSpan(replace, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getReplaces(text: Editable?, indentation: Int): Array<Any>
|
||||
|
||||
/**
|
||||
* Note: This knows that the last returned object from getSpans() will be the most recently added.
|
||||
*
|
||||
* @see Html
|
||||
*/
|
||||
private fun getLast(text: Spanned): ListTag? {
|
||||
val listTags = text.getSpans(0, text.length, ListTag::class.java)
|
||||
return if (listTags.size == 0) {
|
||||
null
|
||||
} else listTags[listTags.size - 1]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing the unordered list (`<ul>`) HTML tag.
|
||||
*/
|
||||
private class Ul : ListTag() {
|
||||
override fun getReplaces(text: Editable?, indentation: Int): Array<Any> {
|
||||
// Nested BulletSpans increases distance between BULLET_SPAN and text, so we must prevent it.
|
||||
var bulletMargin = INDENT_PX
|
||||
if (indentation > 1) {
|
||||
bulletMargin = INDENT_PX - BULLET_SPAN.getLeadingMargin(true)
|
||||
if (indentation > 2) {
|
||||
// This get's more complicated when we add a LeadingMarginSpan into the same line:
|
||||
// we have also counter it's effect to BulletSpan
|
||||
bulletMargin -= (indentation - 2) * LIST_ITEM_INDENT_PX
|
||||
}
|
||||
}
|
||||
return arrayOf(
|
||||
LeadingMarginSpan.Standard(LIST_ITEM_INDENT_PX * (indentation - 1)),
|
||||
BulletSpan(bulletMargin)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing the ordered list (`<ol>`) HTML tag.
|
||||
*/
|
||||
private class Ol
|
||||
/**
|
||||
* Creates a new `<ul>` with start index of 1.
|
||||
*/ @JvmOverloads constructor(private var nextIdx: Int = 1) : ListTag() {
|
||||
override fun openItem(text: Editable) {
|
||||
super.openItem(text)
|
||||
text.append(Integer.toString(nextIdx++)).append(". ")
|
||||
}
|
||||
|
||||
override fun getReplaces(text: Editable?, indentation: Int): Array<Any> {
|
||||
var numberMargin = LIST_ITEM_INDENT_PX * (indentation - 1)
|
||||
if (indentation > 2) {
|
||||
// Same as in ordered lists: counter the effect of nested Spans
|
||||
numberMargin -= (indentation - 2) * LIST_ITEM_INDENT_PX
|
||||
}
|
||||
return arrayOf(LeadingMarginSpan.Standard(numberMargin))
|
||||
}
|
||||
/**
|
||||
* Creates a new `<ul>` with given start index.
|
||||
*
|
||||
* @param nextIdx
|
||||
*/
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OL_TAG = "hol"
|
||||
private const val UL_TAG = "hul"
|
||||
private const val LI_TAG = "hli"
|
||||
|
||||
/**
|
||||
* List indentation in pixels. Nested lists use multiple of this.
|
||||
*/
|
||||
private const val INDENT_PX = 10
|
||||
private const val LIST_ITEM_INDENT_PX = INDENT_PX * 2
|
||||
private val BULLET_SPAN = BulletSpan(INDENT_PX)
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,28 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.TextUtils
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectEntity
|
||||
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.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
/**
|
||||
* 首页补充游戏库辅助类
|
||||
*/
|
||||
object GameRepositoryHelper {
|
||||
object GameSubstituteRepositoryHelper {
|
||||
|
||||
private const val KEY_GAME_REPOSITORY = "game_repository"
|
||||
private const val KEY_GAME_REPOSITORY = "game_substitute_repository"
|
||||
|
||||
private var mSubstitutableGameIdSet = hashSetOf<String>()
|
||||
private var mApi = RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
private val mSensitiveApi = RetrofitManager.getInstance(HaloApp.getInstance().application).sensitiveApi
|
||||
private var mApplicationContext = HaloApp.getInstance().application
|
||||
|
||||
var gameCollectionList: List<SubjectEntity> = arrayListOf()
|
||||
|
||||
@ -25,11 +34,8 @@ object GameRepositoryHelper {
|
||||
* 获取游戏补充库
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getGameRepository(context: Context) {
|
||||
|
||||
RetrofitManager.getInstance(context)
|
||||
.api
|
||||
.reserveColumns
|
||||
fun updateGameSubstituteRepository() {
|
||||
mSensitiveApi.reserveColumns
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<List<SubjectEntity>>() {
|
||||
override fun onResponse(response: List<SubjectEntity>?) {
|
||||
@ -38,27 +44,41 @@ object GameRepositoryHelper {
|
||||
updateGameRepository(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun updateSubstitutableGames() {
|
||||
mApplicationContext.doOnMainProcessOnly {
|
||||
val single = if (UserManager.getInstance().isLoggedIn) {
|
||||
mApi.getIdListOfPlayedGames(UserManager.getInstance().userId, Utils.getTime(mApplicationContext))
|
||||
} else {
|
||||
mApi.getIdListOfDownloadedGames(HaloApp.getInstance().gid, Utils.getTime(mApplicationContext))
|
||||
}
|
||||
single.subscribeOn(Schedulers.io()).subscribe(object : BiResponse<List<String>>() {
|
||||
override fun onSuccess(data: List<String>) {
|
||||
mSubstitutableGameIdSet = data.toHashSet()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新内存中的游戏库(即从 SP 中再读一次)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun refreshGameRepository() = loadSavedRepository()
|
||||
fun refreshRepositoryFromLocal() = loadSavedRepository()
|
||||
|
||||
private fun loadSavedRepository() {
|
||||
gameCollectionList = SPUtils.getString(KEY_GAME_REPOSITORY).toObject() ?: arrayListOf()
|
||||
}
|
||||
|
||||
fun updateGameRepository(subjects: List<SubjectEntity>?) {
|
||||
|
||||
private fun updateGameRepository(subjects: List<SubjectEntity>?) {
|
||||
if (subjects == null) return
|
||||
|
||||
SPUtils.setString(KEY_GAME_REPOSITORY, subjects.toJson())
|
||||
|
||||
gameCollectionList = subjects
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +86,7 @@ object GameRepositoryHelper {
|
||||
* @param collectionId 补充游戏库相应专题 ID
|
||||
* @param gameIdList 该专题里已经包含的游戏 ID 列表
|
||||
*/
|
||||
fun getOneUniqueGame(collectionId: String?, gameIdList: HashSet<String>): GameEntity? {
|
||||
private fun getOneUniqueGame(collectionId: String?, gameIdList: HashSet<String>): GameEntity? {
|
||||
collectionId?.let {
|
||||
val collection = gameCollectionList.find { it.id == collectionId }
|
||||
collection?.let {
|
||||
@ -82,10 +102,13 @@ object GameRepositoryHelper {
|
||||
return null
|
||||
}
|
||||
|
||||
fun replaceInstalledApp(gameList: MutableList<GameEntity>,
|
||||
alreadyDisplayedGameIdSet: HashSet<String>,
|
||||
relatedCollectionId: String,
|
||||
shouldLogReplaceEvent: Boolean) {
|
||||
/**
|
||||
* 替换游戏,包括 已安装,历史下载,历史已安装等类型
|
||||
*/
|
||||
fun replaceGames(gameList: MutableList<GameEntity>,
|
||||
alreadyDisplayedGameIdSet: HashSet<String>,
|
||||
relatedCollectionId: String,
|
||||
shouldLogReplaceEvent: Boolean) {
|
||||
val positionOfTheGameToReplaceList = arrayListOf<Int>()
|
||||
|
||||
// 标记需要替换的已安装游戏
|
||||
@ -96,23 +119,32 @@ object GameRepositoryHelper {
|
||||
continue
|
||||
}
|
||||
|
||||
var isThisPositionAdded = false
|
||||
var isThisPositionLabeled = false
|
||||
|
||||
// 从 游戏ID 判断当前游戏是否需要被替换
|
||||
if (mSubstitutableGameIdSet.contains(game.id)) {
|
||||
positionOfTheGameToReplaceList.add(index)
|
||||
isThisPositionLabeled = true
|
||||
}
|
||||
|
||||
// 检查是否已安装该游戏里同包名的 APK
|
||||
for (apk in game.getApk()) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfTheGameToReplaceList.add(index)
|
||||
isThisPositionAdded = true
|
||||
break
|
||||
if (!isThisPositionLabeled) {
|
||||
for (apk in game.getApk()) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfTheGameToReplaceList.add(index)
|
||||
isThisPositionLabeled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
|
||||
if (!isThisPositionAdded) {
|
||||
if (!isThisPositionLabeled) {
|
||||
var relatedPackageList = arrayListOf<String>()
|
||||
for (entity in PackageHelper.relatedPackageList) {
|
||||
if (entity.gameId == game.id) {
|
||||
relatedPackageList = ArrayList(entity.packages)
|
||||
relatedPackageList = ArrayList(entity.packages!!)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -139,14 +171,17 @@ object GameRepositoryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun isThisGameUnique(game: GameEntity, gameIdList: HashSet<String>): Boolean {
|
||||
// 若该补充游戏已经存在关联关系,判定为非唯一
|
||||
// 判断该游戏是否出现在已安装列表
|
||||
if (mSubstitutableGameIdSet.contains(game.id)) return false
|
||||
|
||||
// 该补充游戏是否已经存在关联关系
|
||||
for (relatedId in game.relatedGameIds!!) {
|
||||
if (gameIdList.contains(relatedId)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for (apk in game.getApk()) {
|
||||
// 检查本地是否已安装该游戏,已过滤那部分框架服务的包名
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
@ -110,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())) {
|
||||
|
||||
@ -36,9 +36,10 @@ public class GameViewUtils {
|
||||
public static void setLabelList(Context context, LinearLayout labelLayout, List<TagStyleEntity> tagStyle) {
|
||||
labelLayout.removeAllViews();
|
||||
if (tagStyle == null || tagStyle.isEmpty()) {
|
||||
TagStyleEntity tagEntity = new TagStyleEntity();
|
||||
tagEntity.setName("官方版");
|
||||
labelLayout.addView(getNewGameTagView(context, tagEntity, 0));
|
||||
// 没有数据的话默认不显示
|
||||
// TagStyleEntity tagEntity = new TagStyleEntity();
|
||||
// tagEntity.setName("官方版");
|
||||
// labelLayout.addView(getNewGameTagView(context, tagEntity, 0));
|
||||
} else {
|
||||
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));
|
||||
|
||||
50
app/src/main/java/com/gh/common/util/HomePluggableHelper.kt
Normal file
50
app/src/main/java/com/gh/common/util/HomePluggableHelper.kt
Normal file
@ -0,0 +1,50 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.HomePluggableFilterEntity
|
||||
import com.gh.gamecenter.room.AppDatabase
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
object HomePluggableHelper {
|
||||
|
||||
private val mHomePluggableFilterDao = AppDatabase.getInstance(HaloApp.getInstance().application).homePluggableFilterDao()
|
||||
|
||||
@JvmStatic
|
||||
fun setHomePluggableFilterData(gameEntity: GameEntity, isNever: Boolean) {
|
||||
val apkList = gameEntity.getApk()
|
||||
if (apkList.isNotEmpty()) {
|
||||
val apk = apkList.first()
|
||||
val tag = if (isNever) "never" else apk.version ?: ""
|
||||
mHomePluggableFilterDao.addData(HomePluggableFilterEntity(pkgName = apk.packageName, tag = tag, active = isNever))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showHomePluggable(gameEntity: GameEntity): Boolean {
|
||||
val apkList = gameEntity.getApk()
|
||||
if (apkList.isNotEmpty()) {
|
||||
val apk = apkList.first()
|
||||
val filterData = mHomePluggableFilterDao.getDataByPkgName(apk.packageName)
|
||||
if (filterData?.active == true) {
|
||||
val filterTag = filterData.tag
|
||||
return filterTag != "never" && apk.version != filterTag
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getPermanentInactivePluggablePackage() = mHomePluggableFilterDao.getDataByTag("never")
|
||||
|
||||
@JvmStatic
|
||||
fun activationFilterData() {
|
||||
val filterList = mHomePluggableFilterDao.getDataByActive(false)
|
||||
|
||||
if (filterList != null) {
|
||||
for (entity in filterList) {
|
||||
entity.active = true
|
||||
}
|
||||
mHomePluggableFilterDao.addData(filterList)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,7 @@ 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)) {
|
||||
if (!downloadEntity.isPluggable() || PackageUtils.isSignedByGh(context, packageName)) {
|
||||
EventBus.getDefault().post(new EBPackage("安装", packageName, installVersion));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,82 +150,6 @@ public class LibaoUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void setLiBaoBtnStatus(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);
|
||||
break;
|
||||
case "tao":
|
||||
libaoBtn.setText(R.string.libao_tao);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
break;
|
||||
case "coming":
|
||||
libaoBtn.setText(R.string.libao_coming);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_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.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.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.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.setTextColor(context.getResources().getColor(R.color.theme_font));
|
||||
break;
|
||||
case "copy":
|
||||
libaoBtn.setText(R.string.libao_copy);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_style);
|
||||
break;
|
||||
case "repeatLing":
|
||||
libaoBtn.setText(R.string.libao_repeat_ling);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_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);
|
||||
break;
|
||||
case "repeatTao":
|
||||
libaoBtn.setText(R.string.libao_repeat_tao);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_normal_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);
|
||||
break;
|
||||
case "unshelve":
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_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);
|
||||
break;
|
||||
default:
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_gray);
|
||||
libaoBtn.setText("异常");
|
||||
libaoBtn.setTextColor(context.getResources().getColor(R.color.button_gray));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setLiBaoBtnStatusRound(final TextView libaoBtn, String status, Context context) {
|
||||
libaoBtn.setTextColor(Color.WHITE);
|
||||
if (TextUtils.isEmpty(status)) return;
|
||||
@ -306,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();
|
||||
|
||||
@ -283,7 +283,7 @@ public class LogUtils {
|
||||
|
||||
uploadToReservation(object);
|
||||
}
|
||||
|
||||
|
||||
private static void uploadToCommunity(JSONObject object) {
|
||||
uploadToCommunity(object, false);
|
||||
}
|
||||
@ -419,4 +419,89 @@ public class LogUtils {
|
||||
}
|
||||
uploadVideoStreaming(object);
|
||||
}
|
||||
|
||||
private static void uploadShare(JSONObject object) {
|
||||
Meta meta = MetaUtil.INSTANCE.getMeta();
|
||||
JSONObject metaObject = new JSONObject();
|
||||
try {
|
||||
metaObject.put("android_id", meta.getAndroid_id());
|
||||
metaObject.put("android_sdk", meta.getAndroid_sdk());
|
||||
metaObject.put("android_version", meta.getAndroid_version());
|
||||
metaObject.put("appVersion", meta.getAppVersion());
|
||||
metaObject.put("channel", meta.getChannel());
|
||||
metaObject.put("gid", meta.getGid());
|
||||
metaObject.put("imei", meta.getImei());
|
||||
metaObject.put("mac", meta.getMac());
|
||||
metaObject.put("manufacturer", meta.getManufacturer());
|
||||
metaObject.put("model", meta.getModel());
|
||||
metaObject.put("network", meta.getNetwork());
|
||||
metaObject.put("os", meta.getOs());
|
||||
metaObject.put("userId", meta.getUserId());
|
||||
|
||||
object.put("event", "SHARE");
|
||||
object.put("meta", metaObject);
|
||||
object.put("timestamp", System.currentTimeMillis() / 1000);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
|
||||
LoghubUtils.log(object, "event", false);
|
||||
}
|
||||
|
||||
public static void uploadShareEnter(String entrance, String url, String title, String summary, String resourceId) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payloadObject = new JSONObject();
|
||||
try {
|
||||
object.put("action", "entrance_source");
|
||||
payloadObject.put("entrance", entrance);
|
||||
payloadObject.put("url", url);
|
||||
payloadObject.put("title", title);
|
||||
payloadObject.put("summary", summary);
|
||||
payloadObject.put("resource_id", resourceId);
|
||||
object.put("payload", payloadObject);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
uploadShare(object);
|
||||
}
|
||||
|
||||
public static void uploadShareType(String shareType, String entrance, String url, String title, String summary, String resourceId) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payloadObject = new JSONObject();
|
||||
try {
|
||||
object.put("action", "share_type");
|
||||
payloadObject.put("share_type", shareType);
|
||||
payloadObject.put("entrance", entrance);
|
||||
payloadObject.put("url", url);
|
||||
payloadObject.put("title", title);
|
||||
payloadObject.put("summary", summary);
|
||||
payloadObject.put("resource_id", resourceId);
|
||||
object.put("payload", payloadObject);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
uploadShare(object);
|
||||
}
|
||||
|
||||
public static void uploadShareResult(String shareType, String entrance, String shareResult, String url, String title, String summary, String resourceId) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payloadObject = new JSONObject();
|
||||
try {
|
||||
object.put("action", "share_result");
|
||||
payloadObject.put("share_type", shareType);
|
||||
payloadObject.put("entrance", entrance);
|
||||
payloadObject.put("share_result", shareResult);
|
||||
payloadObject.put("url", url);
|
||||
payloadObject.put("title", title);
|
||||
payloadObject.put("summary", summary);
|
||||
payloadObject.put("resource_id", resourceId);
|
||||
object.put("payload", payloadObject);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
uploadShare(object);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import com.tencent.tauth.Tencent
|
||||
import com.tencent.tauth.UiError
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* 第三方登录辅助类
|
||||
@ -37,12 +37,12 @@ object LoginHelper {
|
||||
|
||||
private var mTencent: Tencent // QQ
|
||||
private var mIWXAPI: IWXAPI // 微信
|
||||
private var mSsoHandler: SsoHandler? = null // 微博 // TODO 完成回调时清掉这个 handler ?
|
||||
private var mSsoHandler: WeakReference<SsoHandler>? = null // 微博
|
||||
|
||||
private var mQqLoginListener: IUiListener
|
||||
|
||||
private var mAccessToken: Oauth2AccessToken? = null // weibo
|
||||
private var mLoginCallback: LoginCallback? = null
|
||||
private var mLoginCallback: WeakReference<LoginCallback>? = null
|
||||
|
||||
init {
|
||||
val context = HaloApp.getInstance().application.applicationContext
|
||||
@ -64,11 +64,11 @@ object LoginHelper {
|
||||
content.put("access_token_expire", Utils.getTime(context) + o.getLong("expires_in"))
|
||||
content.put("access_token", o.getString("access_token"))
|
||||
|
||||
mLoginCallback?.onLoginSuccess(LoginTag.qq, content) // 回调QQ登录成功
|
||||
mLoginCallback?.get()?.onLoginSuccess(LoginTag.qq, content) // 回调QQ登录成功
|
||||
} catch (e: JSONException) {
|
||||
val errorString = "QQ登录数据回调异常::$e"
|
||||
|
||||
mLoginCallback?.onLoginFailure(LoginTag.qq, errorString) // 回调QQ登录失败
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.qq, errorString) // 回调QQ登录失败
|
||||
|
||||
Utils.log(errorString)
|
||||
e.printStackTrace()
|
||||
@ -77,12 +77,12 @@ object LoginHelper {
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
mLoginCallback?.onLoginFailure(LoginTag.qq,"登录取消")
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.qq, "登录取消")
|
||||
Utils.log("QQ 登录取消")
|
||||
}
|
||||
|
||||
override fun onError(p0: UiError?) {
|
||||
mLoginCallback?.onLoginFailure(LoginTag.qq,"登录失败")
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.qq, "登录失败")
|
||||
Utils.log("QQ 登录失败")
|
||||
}
|
||||
}
|
||||
@ -99,23 +99,23 @@ object LoginHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun onWechatLoginSuccess(content: JSONObject) {
|
||||
mLoginCallback?.onLoginSuccess(LoginTag.wechat, content)
|
||||
mLoginCallback?.get()?.onLoginSuccess(LoginTag.wechat, content)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onWechatLoginFailure(error: String) {
|
||||
mLoginCallback?.onLoginFailure(LoginTag.wechat, error)
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.wechat, error)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onWeiboLoginCallback(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
mSsoHandler?.authorizeCallBack(requestCode, resultCode, data)
|
||||
mSsoHandler?.get()?.authorizeCallBack(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
// QQ登录
|
||||
@JvmStatic
|
||||
fun loginWithQQ(loginCallback: LoginCallback, activity: Activity) {
|
||||
mLoginCallback = loginCallback
|
||||
mLoginCallback = WeakReference(loginCallback)
|
||||
if (!mTencent.isSessionValid) {
|
||||
Utils.log("QQLogin")
|
||||
mTencent.login(activity, "all", mQqLoginListener)
|
||||
@ -132,7 +132,7 @@ object LoginHelper {
|
||||
// 微信登录
|
||||
@JvmStatic
|
||||
fun loginWithWechat(loginCallback: LoginCallback) {
|
||||
mLoginCallback = loginCallback
|
||||
mLoginCallback = WeakReference(loginCallback)
|
||||
val register = mIWXAPI.registerApp(Config.WECHAT_APPID)
|
||||
|
||||
val req = SendAuth.Req()
|
||||
@ -149,9 +149,9 @@ object LoginHelper {
|
||||
// 微博登录
|
||||
@JvmStatic
|
||||
fun loginWithWeibo(loginCallback: LoginCallback, context: Activity) {
|
||||
mLoginCallback = loginCallback
|
||||
mSsoHandler = SsoHandler(context)
|
||||
mSsoHandler?.authorizeClientSso(object : WbAuthListener {
|
||||
mLoginCallback = WeakReference(loginCallback)
|
||||
mSsoHandler = WeakReference(SsoHandler(context))
|
||||
mSsoHandler?.get()?.authorizeClientSso(object : WbAuthListener {
|
||||
override fun onSuccess(token: Oauth2AccessToken?) {
|
||||
token?.let {
|
||||
RuntimeUtils.getInstance().runOnUiThread {
|
||||
@ -159,7 +159,7 @@ object LoginHelper {
|
||||
if (mAccessToken?.isSessionValid == true) {
|
||||
// 保存 Token 到 SharedPreferences
|
||||
AccessTokenKeeper.writeAccessToken(context, mAccessToken)
|
||||
Toast.makeText(context, "授权成功", Toast.LENGTH_SHORT).show()
|
||||
Utils.toast(context, "授权成功")
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,17 +170,17 @@ object LoginHelper {
|
||||
content.put("access_token_expire", Utils.getTime(context) + token.expiresTime)
|
||||
content.put("refresh_token", token.refreshToken)
|
||||
// content.put("refresh_token_expire", Utils.getTime(mContext) + 86400 * 30); // refresh_token 有效期30天
|
||||
mLoginCallback?.onLoginSuccess(LoginTag.weibo, content)// 微博 登录回调
|
||||
mLoginCallback?.get()?.onLoginSuccess(LoginTag.weibo, content)// 微博 登录回调
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(p0: WbConnectErrorMessage?) {
|
||||
mLoginCallback?.onLoginFailure(LoginTag.weibo, "微博登录需要客户端支持,请先安装微博")
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.weibo, "微博登录需要客户端支持,请先安装微博")
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
mLoginCallback?.onLoginFailure(LoginTag.weibo, "取消授权")
|
||||
mLoginCallback?.get()?.onLoginFailure(LoginTag.weibo, "取消授权")
|
||||
}
|
||||
})
|
||||
// 第一次启动本应用,AccessToken 不可用
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.NewsEntity;
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
@ -11,6 +13,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;
|
||||
@ -77,7 +80,7 @@ public class NewsUtils {
|
||||
/**
|
||||
* 设置新闻类型
|
||||
*/
|
||||
public static void setNewsType(TextView textView, String type, int priority, int position) {
|
||||
public static void setNewsType(TextView textView, @Nullable String type, int priority, int position) {
|
||||
if (priority != 0) {
|
||||
if (position == 0) {
|
||||
textView.setText(R.string.article_top);
|
||||
@ -91,7 +94,7 @@ public class NewsUtils {
|
||||
}
|
||||
|
||||
textView.setTextColor(Color.WHITE);
|
||||
switch (type) {
|
||||
switch (type != null ? type : "") {
|
||||
case "活动":
|
||||
textView.setBackgroundResource(R.drawable.textview_orange_style);
|
||||
break;
|
||||
@ -117,64 +120,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 +152,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));
|
||||
|
||||
88
app/src/main/java/com/gh/common/util/OssUploadUtils.kt
Normal file
88
app/src/main/java/com/gh/common/util/OssUploadUtils.kt
Normal file
@ -0,0 +1,88 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import com.alibaba.sdk.android.oss.ClientConfiguration
|
||||
import com.alibaba.sdk.android.oss.ClientException
|
||||
import com.alibaba.sdk.android.oss.OSSClient
|
||||
import com.alibaba.sdk.android.oss.ServiceException
|
||||
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
|
||||
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
|
||||
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectRequest
|
||||
import com.alibaba.sdk.android.oss.model.PutObjectResult
|
||||
import com.gh.gamecenter.entity.OssEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
|
||||
object OssUploadUtils {
|
||||
//上传文件类型
|
||||
enum class UploadType(val value: String) {
|
||||
GAME("game")//后缀apk
|
||||
}
|
||||
|
||||
//获取Oss配置
|
||||
private fun getOssUpdateConfig(type: UploadType): Single<OssEntity> {
|
||||
return RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.api
|
||||
.getOssUpdateConfig(type.value)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
}
|
||||
|
||||
//异步上传文件
|
||||
@JvmStatic
|
||||
fun uploadFile(path: String, uploadType: UploadType, listener: OnUploadFileListener?): Disposable {
|
||||
return getOssUpdateConfig(uploadType)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap { mOssEntity ->
|
||||
Single.create<String> {
|
||||
val conf = ClientConfiguration()
|
||||
conf.connectionTimeout = 15 * 1000
|
||||
conf.socketTimeout = 15 * 1000
|
||||
conf.maxConcurrentRequest = 5
|
||||
conf.maxErrorRetry = 2
|
||||
|
||||
val credentialProvider = OSSStsTokenCredentialProvider(mOssEntity.accessKeyId, mOssEntity.accessKeySecret, mOssEntity.securityToken)
|
||||
val oss = OSSClient(HaloApp.getInstance().application, mOssEntity.endPoint, credentialProvider, conf)
|
||||
|
||||
// 构造上传请求
|
||||
val put = PutObjectRequest(mOssEntity.bucket, mOssEntity.key, path)
|
||||
|
||||
val task: OSSAsyncTask<*> = oss.asyncPutObject(put, object : OSSCompletedCallback<PutObjectRequest?, PutObjectResult> {
|
||||
override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult) {
|
||||
it.onSuccess(mOssEntity.domain + mOssEntity.key)
|
||||
}
|
||||
|
||||
override fun onFailure(request: PutObjectRequest?, clientExcepion: ClientException?, serviceException: ServiceException?) {
|
||||
clientExcepion?.printStackTrace()
|
||||
serviceException?.printStackTrace()
|
||||
if (serviceException != null) {
|
||||
it.onError(Throwable(message = clientExcepion?.message + "/" + serviceException.message))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { url, throwable ->
|
||||
if (url.isNotEmpty()) {
|
||||
listener?.onSuccess(url)
|
||||
return@subscribe
|
||||
}
|
||||
if (throwable != null) {
|
||||
listener?.onError(throwable)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface OnUploadFileListener {
|
||||
fun onSuccess(url: String)
|
||||
fun onError(e: Throwable?)
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -51,7 +52,7 @@ public class PackageUtils {
|
||||
/*
|
||||
* 判断是否可以更新,只判断gh_version的大小
|
||||
*/
|
||||
public static List<GameUpdateEntity> getUpdateData(Context context, GameEntity gameEntity) {
|
||||
public static List<GameUpdateEntity> getUpdateData(GameEntity gameEntity) {
|
||||
|
||||
List<GameUpdateEntity> updateList = new ArrayList<>();
|
||||
|
||||
@ -88,14 +89,14 @@ public class PackageUtils {
|
||||
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());
|
||||
@ -153,7 +154,7 @@ public class PackageUtils {
|
||||
/*
|
||||
* 判断是否是插件包
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@ -349,6 +350,8 @@ public class PackageUtils {
|
||||
|
||||
/*
|
||||
* 根据包名,判断是否已安装该游戏
|
||||
*
|
||||
* 注意:目测只对能启动的app有效(有桌面图标),对一些没有桌面图标的应用无效(参考应用:魅族游戏框架)
|
||||
*/
|
||||
public static boolean isInstalled(Context context, String packageName) {
|
||||
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
@ -502,7 +505,7 @@ public class PackageUtils {
|
||||
/**
|
||||
* todo 统一判断
|
||||
* <p>
|
||||
* 判断游戏包是否可以更新
|
||||
* 判断游戏包(插件包) 是否可以更新
|
||||
*
|
||||
* @param apkEntity apkEntity 必须是已安装的游戏
|
||||
* @param gameId 游戏id
|
||||
@ -547,6 +550,26 @@ public class PackageUtils {
|
||||
return PackageUtils.isInstalled(HaloApp.getInstance().getApplication(), apkEntity.getPackageName())
|
||||
&& gh_id == null
|
||||
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
|
||||
&& !PackageUtils.isSignature(HaloApp.getInstance().getApplication(), apkEntity.getPackageName());
|
||||
&& !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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
135
app/src/main/java/com/gh/common/util/PicassoImageGetter.java
Normal file
135
app/src/main/java/com/gh/common/util/PicassoImageGetter.java
Normal file
@ -0,0 +1,135 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Html;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
import com.squareup.picasso.Transformation;
|
||||
|
||||
public class PicassoImageGetter implements Html.ImageGetter {
|
||||
private TextView textView = null;
|
||||
|
||||
public PicassoImageGetter() {
|
||||
}
|
||||
|
||||
public PicassoImageGetter(TextView target) {
|
||||
textView = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(String source) {
|
||||
BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
|
||||
|
||||
Context context = HaloApp.getInstance().getApplication();
|
||||
Picasso.with(context)
|
||||
.load(source)
|
||||
.transform(new CircleTransformation())
|
||||
.placeholder(ResourcesCompat.getDrawable(context.getResources(), R.drawable.personal_user_default_icon, context
|
||||
.getTheme()))
|
||||
.into(drawable);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private class BitmapDrawablePlaceHolder extends BitmapDrawable implements Target {
|
||||
protected Drawable drawable;
|
||||
|
||||
@Override
|
||||
public void draw(final Canvas canvas) {
|
||||
if (drawable != null) {
|
||||
Paint.FontMetricsInt fmPaint = textView.getPaint().getFontMetricsInt();
|
||||
float fontHeight = fmPaint.descent - fmPaint.ascent;
|
||||
float textSize = textView.getTextSize();
|
||||
|
||||
if (fontHeight - textSize > 0) {
|
||||
canvas.translate((fontHeight - textSize) / 2, 0);
|
||||
}
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
public void setDrawable(Drawable drawable) {
|
||||
try {
|
||||
this.drawable = drawable;
|
||||
|
||||
Paint.FontMetricsInt fmPaint = textView.getPaint().getFontMetricsInt();
|
||||
int fontHeight = fmPaint.descent - fmPaint.ascent;
|
||||
int textSize = (int) textView.getTextSize();
|
||||
|
||||
drawable.setBounds(0, 0, textSize, textSize);
|
||||
setBounds(0, 0, fontHeight, fontHeight);
|
||||
if (textView != null) {
|
||||
textView.setText(textView.getText());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||
setDrawable(new BitmapDrawable(HaloApp.getInstance()
|
||||
.getApplication()
|
||||
.getResources(), bitmap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapFailed(Drawable errorDrawable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareLoad(Drawable placeHolderDrawable) {
|
||||
setDrawable(placeHolderDrawable);
|
||||
}
|
||||
}
|
||||
|
||||
private class CircleTransformation implements Transformation {
|
||||
@Override
|
||||
public Bitmap transform(Bitmap source) {
|
||||
int size = Math.min(source.getWidth(), source.getHeight());
|
||||
|
||||
int x = (source.getWidth() - size) / 2;
|
||||
int y = (source.getHeight() - size) / 2;
|
||||
|
||||
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
|
||||
if (squaredBitmap != source) {
|
||||
source.recycle();
|
||||
}
|
||||
|
||||
Bitmap.Config config = source.getConfig() != null ? source.getConfig() : Bitmap.Config.ARGB_8888;
|
||||
Bitmap bitmap = Bitmap.createBitmap(size, size, config);
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
BitmapShader shader = new BitmapShader(squaredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
paint.setShader(shader);
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
float r = size / 2f;
|
||||
canvas.drawCircle(r, r, r, paint);
|
||||
|
||||
squaredBitmap.recycle();
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String key() {
|
||||
return "circle";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,43 +36,23 @@ import io.reactivex.schedulers.Schedulers;
|
||||
import retrofit2.HttpException;
|
||||
|
||||
public class PlatformUtils {
|
||||
|
||||
|
||||
private static PlatformUtils mInstance;
|
||||
|
||||
|
||||
private Context context;
|
||||
|
||||
|
||||
private ArrayMap<String, String> platformMap;
|
||||
private ArrayMap<String, Integer> platformPicMap;
|
||||
private ArrayMap<String, String> platformPicUrlMap;
|
||||
private ArrayMap<String, String> platformColorMap;
|
||||
private boolean isCheck = false;
|
||||
private boolean isUpdate = false;
|
||||
|
||||
|
||||
private PlatformUtils(Context con) {
|
||||
this.context = con.getApplicationContext();
|
||||
initMap();
|
||||
}
|
||||
|
||||
|
||||
private void initMap() {
|
||||
ArrayMap<String, Integer> platformPicMap = new ArrayMap<>();
|
||||
platformPicMap.put("360", R.drawable.platform_360);
|
||||
platformPicMap.put("37wan", R.drawable.platform_37);
|
||||
platformPicMap.put("91", R.drawable.platform_91);
|
||||
platformPicMap.put("9u", R.drawable.platform_9u);
|
||||
platformPicMap.put("anzhi", R.drawable.platform_anzhi);
|
||||
platformPicMap.put("baidu", R.drawable.platform_baidu);
|
||||
platformPicMap.put("dangle", R.drawable.platform_dl);
|
||||
platformPicMap.put("ewan", R.drawable.platform_ewan);
|
||||
platformPicMap.put("gf", R.drawable.platform_gf);
|
||||
platformPicMap.put("gf-w", R.drawable.platform_gfw);
|
||||
platformPicMap.put("huawei", R.drawable.platform_hw);
|
||||
platformPicMap.put("mi", R.drawable.platform_mi);
|
||||
platformPicMap.put("oppo", R.drawable.platform_oppo);
|
||||
platformPicMap.put("ouwan", R.drawable.platform_ouwan);
|
||||
platformPicMap.put("pps", R.drawable.platform_pps);
|
||||
platformPicMap.put("vivo", R.drawable.platform_vivo);
|
||||
platformPicMap.put("wdj", R.drawable.platform_wdj);
|
||||
|
||||
ArrayMap<String, String> platformColorMap = new ArrayMap<>();
|
||||
platformColorMap.put("360", "#218FA4");
|
||||
platformColorMap.put("37wan", "#F5BD20");
|
||||
@ -91,24 +71,20 @@ public class PlatformUtils {
|
||||
platformColorMap.put("pps", "#FF8C27");
|
||||
platformColorMap.put("vivo", "#3FA5E3");
|
||||
platformColorMap.put("wdj", "#5ABA3F");
|
||||
|
||||
|
||||
ArrayMap<String, String> platformMap = new ArrayMap<>();
|
||||
ArrayMap<String, String> platformPicUrlMap = new ArrayMap<>();
|
||||
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
"gh_platform", Context.MODE_PRIVATE);
|
||||
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences("gh_platform", Context.MODE_PRIVATE);
|
||||
Set<String> set = sharedPreferences.getStringSet("platform", null);
|
||||
if (set == null) {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties
|
||||
.load(context.getAssets().open("platform.properties"));
|
||||
properties.load(context.getAssets().open("platform.properties"));
|
||||
Set<String> pset = new HashSet<>();
|
||||
for (Object object : properties.keySet()) {
|
||||
platformMap.put(object.toString(),
|
||||
(String) properties.get(object));
|
||||
pset.add(object.toString() + "="
|
||||
+ (String) properties.get(object) + "=" + "=");
|
||||
platformMap.put(object.toString(), (String) properties.get(object));
|
||||
pset.add(object.toString() + "=" + (String) properties.get(object) + "=" + "=");
|
||||
}
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putStringSet("platform", pset);
|
||||
@ -133,11 +109,10 @@ public class PlatformUtils {
|
||||
// checkPlatformPic(urls);
|
||||
}
|
||||
}
|
||||
|
||||
updatePlatform(platformMap, platformPicMap, platformPicUrlMap,
|
||||
platformColorMap);
|
||||
|
||||
updatePlatform(platformMap, platformPicUrlMap, platformColorMap);
|
||||
}
|
||||
|
||||
|
||||
private void checkPlatformPic(final ArrayList<String> urls) {
|
||||
isCheck = true;
|
||||
File file = new File(FileUtils.getPlatformPicDir(context));
|
||||
@ -154,53 +129,41 @@ public class PlatformUtils {
|
||||
}
|
||||
}
|
||||
if (urls.size() != 0) {
|
||||
AppExecutor.getIoExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int success = 0;
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
String url = urls.get(i);
|
||||
String savePath = FileUtils.getPlatformPicDir(context)
|
||||
+ File.separator
|
||||
+ url.substring(url.lastIndexOf("/") + 1);
|
||||
int code = FileUtils.downloadFile(url, savePath);
|
||||
if (code == HttpURLConnection.HTTP_OK) {
|
||||
success++;
|
||||
}
|
||||
AppExecutor.getIoExecutor().execute(() -> {
|
||||
int success = 0;
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
String url = urls.get(i);
|
||||
String savePath = FileUtils.getPlatformPicDir(context) + File.separator + url
|
||||
.substring(url.lastIndexOf("/") + 1);
|
||||
int code = FileUtils.downloadFile(url, savePath);
|
||||
if (code == HttpURLConnection.HTTP_OK) {
|
||||
success++;
|
||||
}
|
||||
if (success == urls.size()) {
|
||||
Handler handler = new Handler(context.getMainLooper());
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
EventBus.getDefault().post(new EBReuse("PlatformChanged"));
|
||||
}
|
||||
});
|
||||
}
|
||||
isCheck = false;
|
||||
}
|
||||
if (success == urls.size()) {
|
||||
Handler handler = new Handler(context.getMainLooper());
|
||||
handler.post(() -> EventBus.getDefault().post(new EBReuse("PlatformChanged")));
|
||||
}
|
||||
isCheck = false;
|
||||
});
|
||||
} else {
|
||||
isCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlatform(ArrayMap<String, String> pMap,
|
||||
ArrayMap<String, Integer> pPMap, ArrayMap<String, String> pUMap,
|
||||
ArrayMap<String, String> pCMap) {
|
||||
|
||||
private void updatePlatform(ArrayMap<String, String> pMap, ArrayMap<String, String> pUMap, ArrayMap<String, String> pCMap) {
|
||||
platformMap = pMap;
|
||||
platformPicMap = pPMap;
|
||||
platformPicUrlMap = pUMap;
|
||||
platformColorMap = pCMap;
|
||||
}
|
||||
|
||||
|
||||
public static PlatformUtils getInstance(Context context) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new PlatformUtils(context);
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
|
||||
public String getPlatformColor(String platform) {
|
||||
if ("".equals(platform) || "官方版".equals(platform)) {
|
||||
return "#BB3D42";
|
||||
@ -209,26 +172,16 @@ public class PlatformUtils {
|
||||
if (color != null) {
|
||||
return color;
|
||||
}
|
||||
|
||||
int themeColor = ContextCompat.getColor(HaloApp.getInstance().getApplication(), R.color.theme);
|
||||
|
||||
int themeColor = ContextCompat.getColor(HaloApp.getInstance()
|
||||
.getApplication(), R.color.theme);
|
||||
return String.format("#%06X", 0xFFFFFF & themeColor);
|
||||
}
|
||||
|
||||
public int getPlatformPic(String platform) {
|
||||
if ("".equals(platform) || "官方版".equals(platform)) {
|
||||
return R.drawable.platform_gf;
|
||||
}
|
||||
Integer id = platformPicMap.get(platform);
|
||||
if (id != null) {
|
||||
return id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public String getPlatformPicUrl(String platform) {
|
||||
return platformPicUrlMap.get(platform);
|
||||
}
|
||||
|
||||
|
||||
public String getPlatformName(String platform) {
|
||||
if ("".equals(platform) || "官方版".equals(platform)) {
|
||||
return "官方版";
|
||||
@ -242,38 +195,40 @@ public class PlatformUtils {
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
|
||||
public void getPlatform() {
|
||||
if (isUpdate) {
|
||||
return;
|
||||
}
|
||||
isUpdate = true;
|
||||
RetrofitManager.getInstance(context).getApi().getGamePlatform()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<List<PlatformEntity>>() {
|
||||
@Override
|
||||
public void onResponse(List<PlatformEntity> response) {
|
||||
Set<String> platformSet = new HashSet<>();
|
||||
for (PlatformEntity platformEntity : response) {
|
||||
platformSet.add(platformEntity.toString());
|
||||
}
|
||||
SharedPreferences sp = context.getSharedPreferences("gh_platform", Context.MODE_PRIVATE);
|
||||
sp.edit().putStringSet("platform", platformSet).apply();
|
||||
initMap();
|
||||
EventBus.getDefault().post(new EBReuse("PlatformChanged"));
|
||||
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
String today = format.format(new Date());
|
||||
sp.edit().putString("refresh_time", today).apply();
|
||||
isUpdate = false;
|
||||
RetrofitManager.getInstance(context)
|
||||
.getApi()
|
||||
.getGamePlatform()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<List<PlatformEntity>>() {
|
||||
@Override
|
||||
public void onResponse(List<PlatformEntity> response) {
|
||||
Set<String> platformSet = new HashSet<>();
|
||||
for (PlatformEntity platformEntity : response) {
|
||||
platformSet.add(platformEntity.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
isUpdate = false;
|
||||
}
|
||||
});
|
||||
SharedPreferences sp = context.getSharedPreferences("gh_platform", Context.MODE_PRIVATE);
|
||||
sp.edit().putStringSet("platform", platformSet).apply();
|
||||
initMap();
|
||||
EventBus.getDefault().post(new EBReuse("PlatformChanged"));
|
||||
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
String today = format.format(new Date());
|
||||
sp.edit().putString("refresh_time", today).apply();
|
||||
isUpdate = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
isUpdate = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -48,9 +48,9 @@ object ReservationHelper {
|
||||
ReservationRepository.removeReservationFromMemoryAndRefresh(game.id)
|
||||
|
||||
if (deleteReservation) {
|
||||
MtaHelper.onEvent("预约游戏", "取消预约", game.name)
|
||||
// MtaHelper.onEvent("预约游戏", "取消预约", game.name)
|
||||
} else {
|
||||
MtaHelper.onEvent("预约游戏", "删除预约", game.name)
|
||||
// MtaHelper.onEvent("预约游戏", "删除预约", game.name)
|
||||
}
|
||||
|
||||
refreshCallback.onCallback()
|
||||
@ -73,9 +73,10 @@ object ReservationHelper {
|
||||
"暂不删除",
|
||||
{ emptyCallback.onCallback() },
|
||||
null,
|
||||
trackMtaEvent = true,
|
||||
mtaEvent = "预约游戏",
|
||||
mtaKey = "删除预约弹窗")
|
||||
trackMtaEvent = true
|
||||
// , mtaEvent = "预约游戏",
|
||||
// mtaKey = "删除预约弹窗"
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@ -88,9 +89,10 @@ object ReservationHelper {
|
||||
"暂不取消",
|
||||
{ emptyCallback.onCallback() },
|
||||
null,
|
||||
trackMtaEvent = true,
|
||||
mtaEvent = "预约游戏",
|
||||
mtaKey = "取消预约弹窗")
|
||||
trackMtaEvent = true
|
||||
// , mtaEvent = "预约游戏",
|
||||
// mtaKey = "取消预约弹窗"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -24,6 +24,9 @@ import android.widget.PopupWindow;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
@ -31,6 +34,7 @@ import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WeiBoShareActivity;
|
||||
import com.gh.gamecenter.entity.ShareEntity;
|
||||
import com.gh.gamecenter.eventbus.EBShare;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.sina.weibo.sdk.WbSdk;
|
||||
@ -53,9 +57,6 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import static com.gh.common.util.LoginHelper.WEIBO_SCOPE;
|
||||
|
||||
/**
|
||||
@ -83,24 +84,39 @@ public class ShareUtils {
|
||||
R.drawable.share_cancel_logo
|
||||
};
|
||||
|
||||
public enum ShareType {
|
||||
news,
|
||||
game, // 普通游戏
|
||||
plugin, // 插件游戏
|
||||
tools,
|
||||
askInvite,
|
||||
askNormal, // 问答问题/答案
|
||||
shareGh,
|
||||
communityArticle,
|
||||
video
|
||||
public enum ShareEntrance {
|
||||
news("资讯文章"),
|
||||
game("游戏详情"), // 普通游戏
|
||||
plugin("游戏详情"), // 插件游戏
|
||||
tools("工具箱"),
|
||||
askInvite("邀请回答"),
|
||||
askNormal("问题详情"), //问答问题
|
||||
answerNormal("回答详情"), //问答答案
|
||||
shareGh("APP分享"),
|
||||
communityArticle("文章详情"),
|
||||
video("视频"),
|
||||
web("web链接");
|
||||
|
||||
private String name;
|
||||
|
||||
ShareEntrance(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] arrLabel = {"微信好友", "朋友圈", "QQ好友", "QQ空间", "新浪微博", "短信", "复制链接", "取消"};
|
||||
|
||||
private PopupWindow popupWindow;
|
||||
private WeakReference<PopupWindow> popupWindow;
|
||||
|
||||
private ShareType mShareType;
|
||||
public static ShareType shareType;//全局保存shareType,分享成功后判断分享的类型
|
||||
private ShareEntrance mShareEntrance;
|
||||
public static String shareType = "";//分享类型(事件上报用)
|
||||
public static ShareEntrance shareEntrance;//分享入口(事件上报和视频分享统计用)
|
||||
public static String resourceId = "";//分享内容的id(事件上报用)
|
||||
public static ShareEntity shareEntity;//分享信息(事件上报用)
|
||||
|
||||
private WeakReference<Activity> mActivity;
|
||||
|
||||
@ -110,25 +126,31 @@ public class ShareUtils {
|
||||
@Override
|
||||
public void onComplete(Object o) {
|
||||
Utils.toast(mContext, R.string.share_success_hint);
|
||||
EventBus.getDefault().post(new EBShare(ShareUtils.shareType));
|
||||
EventBus.getDefault().post(new EBShare(ShareUtils.shareEntrance));
|
||||
LogUtils.uploadShareResult(shareType, ShareUtils.shareEntrance.getName(), "success",
|
||||
ShareUtils.shareEntity.getShareUrl(), ShareUtils.shareEntity.getShareTitle(), ShareUtils.shareEntity.getSummary(), ShareUtils.resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(UiError uiError) {
|
||||
Utils.toast(mContext, R.string.share_fail_hint);
|
||||
LogUtils.uploadShareResult(shareType, ShareUtils.shareEntrance.getName(), "fail",
|
||||
ShareUtils.shareEntity.getShareUrl(), ShareUtils.shareEntity.getShareTitle(), ShareUtils.shareEntity.getSummary(), ShareUtils.resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
Utils.toast(mContext, R.string.share_cancel_hint);
|
||||
LogUtils.uploadShareResult(shareType, ShareUtils.shareEntrance.getName(), "cancel",
|
||||
ShareUtils.shareEntity.getShareUrl(), ShareUtils.shareEntity.getShareTitle(), ShareUtils.shareEntity.getSummary(), ShareUtils.resourceId);
|
||||
}
|
||||
};
|
||||
|
||||
private ShareUtils(Context context) {
|
||||
mTencent = Tencent.createInstance(Config.TENCENT_APPID, context); //初始化QQ分享
|
||||
mIWXAPI = WXAPIFactory.createWXAPI(context, Config.WECHAT_APPID); //初始化微信分享
|
||||
WbSdk.install(context, new AuthInfo(context, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE));
|
||||
mContext = context.getApplicationContext();
|
||||
mTencent = Tencent.createInstance(Config.TENCENT_APPID, mContext); //初始化QQ分享
|
||||
mIWXAPI = WXAPIFactory.createWXAPI(mContext, Config.WECHAT_APPID); //初始化微信分享
|
||||
WbSdk.install(mContext, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE));
|
||||
}
|
||||
|
||||
public static ShareUtils getInstance(Context context) {
|
||||
@ -153,17 +175,20 @@ public class ShareUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showShareWindowsCallback(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareType shareType, ShareCallBack callBack) {
|
||||
public void showShareWindowsCallback(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareEntrance shareEntrance, String id, ShareCallBack callBack) {
|
||||
if (activity.isFinishing()) return;
|
||||
this.mActivity = new WeakReference<>(activity);
|
||||
this.shareIcon = icon;
|
||||
this.shareUrl = url;
|
||||
this.mSummary = shareSummary;
|
||||
this.mTitle = shareTitle;
|
||||
this.mShareType = shareType;
|
||||
ShareUtils.shareType = mShareType;
|
||||
this.mShareEntrance = shareEntrance;
|
||||
ShareUtils.shareEntrance = mShareEntrance;
|
||||
ShareUtils.resourceId = id;
|
||||
ShareUtils.shareEntity = new ShareEntity(shareUrl, mTitle, mSummary);
|
||||
LogUtils.uploadShareEnter(mShareEntrance.getName(), shareUrl, mTitle, mSummary, id);
|
||||
|
||||
View contentView = View.inflate(activity, R.layout.share_popup_layout, null);
|
||||
View contentView = View.inflate(mActivity.get(), R.layout.share_popup_layout, null);
|
||||
contentView.setFocusable(true);
|
||||
contentView.setFocusableInTouchMode(true);
|
||||
RecyclerView shareRecyclerView = contentView.findViewById(R.id.share_rv);
|
||||
@ -191,7 +216,7 @@ public class ShareUtils {
|
||||
}
|
||||
});
|
||||
|
||||
if (mShareType == ShareType.shareGh) {
|
||||
if (mShareEntrance == ShareEntrance.shareGh) {
|
||||
RelativeLayout layout = (RelativeLayout) view;
|
||||
layout.addView(contentView);
|
||||
arrLabel[6] = "邮件";
|
||||
@ -206,41 +231,41 @@ public class ShareUtils {
|
||||
arrLogo[7] = R.drawable.share_cancel_logo;
|
||||
}
|
||||
|
||||
popupWindow = new SharePopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT
|
||||
, LinearLayout.LayoutParams.MATCH_PARENT, true);
|
||||
popupWindow.setAnimationStyle(R.style.popwindow_exit_only_anim_style);
|
||||
popupWindow = new WeakReference<>(new SharePopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT
|
||||
, LinearLayout.LayoutParams.MATCH_PARENT, true));
|
||||
popupWindow.get().setAnimationStyle(R.style.popwindow_exit_only_anim_style);
|
||||
//解决PopupWindow无法覆盖状态栏
|
||||
popupWindow.setClippingEnabled(false);
|
||||
popupWindow.get().setClippingEnabled(false);
|
||||
|
||||
int bottomLocation = -DisplayUtils.retrieveNavigationHeight(activity);
|
||||
if (!DisplayUtils.isNavigationBarShow(activity)) {
|
||||
bottomLocation = 0;
|
||||
}
|
||||
try {
|
||||
popupWindow.showAtLocation(view, Gravity.NO_GRAVITY, 0, bottomLocation);
|
||||
popupWindow.get().showAtLocation(view, Gravity.NO_GRAVITY, 0, bottomLocation);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
contentView.setOnClickListener(v -> popupWindow.dismiss());
|
||||
contentView.setOnClickListener(v -> safelyDismiss());
|
||||
|
||||
contentView.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0
|
||||
&& popupWindow != null
|
||||
&& popupWindow.isShowing()) {
|
||||
&& popupWindow.get() != null
|
||||
&& popupWindow.get().isShowing()) {
|
||||
if (callBack != null) {
|
||||
callBack.onCancel();
|
||||
}
|
||||
popupWindow.dismiss();
|
||||
safelyDismiss();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void showShareWindows(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareType shareType) {
|
||||
showShareWindowsCallback(activity, view, url, icon, shareTitle, shareSummary, shareType, null);
|
||||
public void showShareWindows(Activity activity, View view, String url, String icon, String shareTitle, String shareSummary, ShareEntrance shareEntrance, String id) {
|
||||
showShareWindowsCallback(activity, view, url, icon, shareTitle, shareSummary, shareEntrance, id, null);
|
||||
}
|
||||
|
||||
//QQ分享
|
||||
@ -248,11 +273,12 @@ public class ShareUtils {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
Bundle params = new Bundle();
|
||||
|
||||
switch (mShareType) {
|
||||
switch (mShareEntrance) {
|
||||
case plugin:
|
||||
mSummary += "(光环加速版)";
|
||||
break;
|
||||
case askNormal:
|
||||
case answerNormal:
|
||||
case video:
|
||||
case communityArticle:
|
||||
mTitle += " - 光环助手";
|
||||
@ -271,8 +297,8 @@ public class ShareUtils {
|
||||
mTencent.shareToQQ(activity, params, QqShareListener);
|
||||
}
|
||||
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,11 +315,12 @@ public class ShareUtils {
|
||||
WXMediaMessage msg = new WXMediaMessage(webpage);
|
||||
webpage.webpageUrl = shareUrl;
|
||||
|
||||
switch (mShareType) {
|
||||
switch (mShareEntrance) {
|
||||
case plugin:
|
||||
mSummary += "(光环加速版)";
|
||||
break;
|
||||
case askNormal:
|
||||
case answerNormal:
|
||||
case video:
|
||||
case communityArticle:
|
||||
mTitle += " - 光环助手";
|
||||
@ -310,8 +337,8 @@ public class ShareUtils {
|
||||
req.scene = SendMessageToWX.Req.WXSceneSession;
|
||||
|
||||
loadBitMap(shareIcon, msg, req);
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,14 +350,14 @@ public class ShareUtils {
|
||||
ImageUtils.display(mContext, iconUrl, new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
protected void onNewResultImpl(Bitmap bitmap) {
|
||||
if (mShareType == ShareType.video) {
|
||||
if (mShareEntrance == ShareEntrance.video) {
|
||||
// 分享类型为视频时裁为正方形
|
||||
int dimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
|
||||
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
|
||||
}
|
||||
|
||||
Bitmap compressBp = compressBitmap(bitmap);
|
||||
if (mShareType == ShareType.askNormal || mShareType == ShareType.askInvite) {
|
||||
if (mShareEntrance == ShareEntrance.askNormal || mShareEntrance == ShareEntrance.askInvite) {
|
||||
msg.thumbData = ImageUtils.bmpToByteArray(compressBp, true);
|
||||
} else {
|
||||
Bitmap resultBp = addBackGround(compressBp);
|
||||
@ -397,11 +424,12 @@ public class ShareUtils {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
Bundle params = new Bundle();
|
||||
|
||||
switch (mShareType) {
|
||||
switch (mShareEntrance) {
|
||||
case plugin:
|
||||
mSummary += "(光环加速版)";
|
||||
break;
|
||||
case askNormal:
|
||||
case answerNormal:
|
||||
case video:
|
||||
case communityArticle:
|
||||
mTitle += " - 光环助手";
|
||||
@ -425,8 +453,8 @@ public class ShareUtils {
|
||||
mTencent.shareToQzone(activity, params, QqShareListener);
|
||||
}
|
||||
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,7 +472,7 @@ public class ShareUtils {
|
||||
|
||||
webpage.webpageUrl = shareUrl;
|
||||
|
||||
switch (mShareType) {
|
||||
switch (mShareEntrance) {
|
||||
case plugin:
|
||||
msg.title = mSummary + "(光环加速版)";
|
||||
break;
|
||||
@ -452,6 +480,7 @@ public class ShareUtils {
|
||||
msg.title = mSummary;
|
||||
break;
|
||||
case askNormal:
|
||||
case answerNormal:
|
||||
case video:
|
||||
case communityArticle:
|
||||
msg.title = mTitle + " - 光环助手";
|
||||
@ -469,8 +498,8 @@ public class ShareUtils {
|
||||
req.scene = SendMessageToWX.Req.WXSceneTimeline;
|
||||
|
||||
loadBitMap(shareIcon, msg, req);
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,22 +514,23 @@ public class ShareUtils {
|
||||
shareIcon,
|
||||
mTitle,
|
||||
mSummary,
|
||||
mShareType.toString());
|
||||
mShareEntrance.toString());
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
//短信分享
|
||||
private void shortMessageShare() {
|
||||
String smsBody;
|
||||
switch (mShareType) {
|
||||
switch (mShareEntrance) {
|
||||
case news:
|
||||
case tools:
|
||||
case web:
|
||||
smsBody = mTitle + shareUrl;
|
||||
break;
|
||||
case plugin:
|
||||
@ -514,6 +544,7 @@ public class ShareUtils {
|
||||
break;
|
||||
case askInvite:
|
||||
case askNormal:
|
||||
case answerNormal:
|
||||
case video:
|
||||
case communityArticle:
|
||||
smsBody = mTitle + " - 光环助手" + shareUrl;
|
||||
@ -531,18 +562,20 @@ public class ShareUtils {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh ) {
|
||||
safelyDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
//复制文字链接
|
||||
private void copyLink(String copyContent) {
|
||||
shareType = "copy_link";
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
ClipboardManager cmb = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setText(copyContent);
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
Utils.toast(mContext, "复制成功");
|
||||
popupWindow.dismiss();
|
||||
safelyDismiss();
|
||||
} else {
|
||||
Utils.toast(mContext, "复制成功,请到微信/QQ粘贴分享");
|
||||
}
|
||||
@ -567,7 +600,7 @@ public class ShareUtils {
|
||||
holder.shareLogo.setImageResource(arrLogo[position]);
|
||||
holder.shareLabel.setText(arrLabel[position]);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (mShareType == ShareUtils.ShareType.shareGh) {
|
||||
if (mShareEntrance == ShareEntrance.shareGh) {
|
||||
MtaHelper.onEvent("我的光环_新", "分享光环", arrLabel[position]);
|
||||
}
|
||||
if (listener != null) {
|
||||
@ -575,23 +608,33 @@ public class ShareUtils {
|
||||
}
|
||||
switch (holder.getPosition()) {
|
||||
case 0:
|
||||
shareType = "wechat_friend";
|
||||
MtaHelper.onEvent("内容分享", "微信好友", mTitle);
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
wechatShare();
|
||||
break;
|
||||
case 1:
|
||||
shareType = "wechat_moment";
|
||||
MtaHelper.onEvent("内容分享", "微信朋友圈", mTitle);
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
wechatMomentsShare();
|
||||
break;
|
||||
case 2:
|
||||
shareType = "qq_friend";
|
||||
MtaHelper.onEvent("内容分享", "QQ好友", mTitle);
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
qqShare();
|
||||
break;
|
||||
case 3:
|
||||
shareType = "qq_zone";
|
||||
MtaHelper.onEvent("内容分享", "QQ空间", mTitle);
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
qZoneShare();
|
||||
break;
|
||||
case 4:
|
||||
shareType = "sina_weibo";
|
||||
MtaHelper.onEvent("内容分享", "新浪微博", mTitle);
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
sinaWeiboShare();
|
||||
break;
|
||||
case 5:
|
||||
@ -600,11 +643,11 @@ public class ShareUtils {
|
||||
break;
|
||||
case 6:
|
||||
MtaHelper.onEvent("内容分享", "复制链接", mTitle);
|
||||
if (mShareType == ShareType.askInvite) {
|
||||
if (mShareEntrance == ShareEntrance.askInvite) {
|
||||
copyLink(mTitle + " - 光环助手" + shareUrl);
|
||||
} else if (mShareType == ShareType.askNormal) {
|
||||
} else if (mShareEntrance == ShareEntrance.askNormal || mShareEntrance == ShareEntrance.answerNormal) {
|
||||
copyLink(shareUrl);
|
||||
} else if (mShareType != ShareType.shareGh) {
|
||||
} else if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
copyLink(shareUrl);
|
||||
} else {
|
||||
try {
|
||||
@ -617,10 +660,11 @@ public class ShareUtils {
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (mShareType != ShareType.shareGh) {
|
||||
popupWindow.dismiss();
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
safelyDismiss();
|
||||
} else {
|
||||
copyLink("推荐光环助手,绿色安全的手游加速助手:" + shareUrl);
|
||||
shareType = "copy_link";
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -645,6 +689,12 @@ public class ShareUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void safelyDismiss() {
|
||||
if (popupWindow.get() != null) {
|
||||
popupWindow.get().dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
void onItemClick(int position);
|
||||
@ -655,12 +705,12 @@ 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);
|
||||
@ -670,5 +720,5 @@ public class ShareUtils {
|
||||
getContentView().postDelayed(super::dismiss, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.gh.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
object SPUtils {
|
||||
@ -90,7 +91,20 @@ object SPUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun getStringSet(key: String): Set<String> {
|
||||
return sp.getStringSet(key, HashSet())?: HashSet()
|
||||
return sp.getStringSet(key, HashSet()) ?: HashSet()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setMap(key: String, map: Map<String, String>) {
|
||||
val mapJson = GsonUtils.toJson(map)
|
||||
setString(key, mapJson)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getMap(key: String): MutableMap<String, String> {
|
||||
val mapJson = getString(key)
|
||||
val type = object : TypeToken<MutableMap<String, String>>() {}.type
|
||||
return GsonUtils.gson.fromJson(mapJson, type) ?: mutableMapOf()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@ -12,6 +12,7 @@ import android.text.style.StyleSpan
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.view.CenterImageSpan
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class SpanBuilder(content: String) {
|
||||
private var spannableString: SpannableStringBuilder = SpannableStringBuilder(content)
|
||||
@ -58,8 +59,8 @@ class SpanBuilder(content: String) {
|
||||
}
|
||||
|
||||
//添加图标
|
||||
fun image(context: Context, start: Int, end: Int, @DrawableRes res: Int): SpanBuilder {
|
||||
val imageSpan = CenterImageSpan(context, res)
|
||||
fun image(start: Int, end: Int, @DrawableRes res: Int): SpanBuilder {
|
||||
val imageSpan = CenterImageSpan(HaloApp.getInstance().application, res)
|
||||
spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return this
|
||||
}
|
||||
|
||||
@ -35,4 +35,19 @@ public class SpeedUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRemainSecondTime(long totalSize, long currentSize, long speed) {
|
||||
long remainSize = totalSize - currentSize;
|
||||
long remainTime;
|
||||
if (speed != 0) {
|
||||
remainTime = remainSize / speed;
|
||||
} else {
|
||||
return "-s";
|
||||
}
|
||||
int hour = (int) (remainTime / 3600);
|
||||
remainTime = (remainTime - hour * 3660);
|
||||
int minute = (int) (remainTime / 60);
|
||||
int second = (int) (remainTime - minute * 60);
|
||||
return second + "s";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ public class TagUtils {
|
||||
return;
|
||||
}
|
||||
isUpdate = true;
|
||||
RetrofitManager.getInstance(context).getApi().getTags()
|
||||
RetrofitManager.getInstance(context).getSensitiveApi().getTags()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<List<TagEntity>>() {
|
||||
|
||||
@ -133,23 +133,38 @@ object TextHelper {
|
||||
Utils.toast(application, "已复制:$arg")
|
||||
}
|
||||
}): SpannableStringBuilder {
|
||||
val builder = SpannableStringBuilder(text)
|
||||
return updateSpannableStringWithHighlightedSpan(context, builder, wrapper, highlightColorId, highlightedTextClickListener)
|
||||
}
|
||||
|
||||
var modifiedText = text
|
||||
if (modifiedText.endsWith(wrapper)) {
|
||||
fun updateSpannableStringWithHighlightedSpan(
|
||||
context: Context,
|
||||
ssb: SpannableStringBuilder,
|
||||
wrapper: String = "###",
|
||||
@ColorRes
|
||||
highlightColorId: Int = R.color.theme_font,
|
||||
highlightedTextClickListener: SimpleCallback<String>? = object : SimpleCallback<String> {
|
||||
override fun onCallback(arg: String) {
|
||||
val application = HaloApp.getInstance().application
|
||||
val cmb = application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cmb.text = arg
|
||||
Utils.toast(application, "已复制:$arg")
|
||||
}
|
||||
}): SpannableStringBuilder {
|
||||
|
||||
if (ssb.endsWith(wrapper)) {
|
||||
// 若高亮符在最后一位就在后面加一个空格,避免整行都能点击
|
||||
modifiedText = "$modifiedText "
|
||||
ssb.append(" ")
|
||||
}
|
||||
|
||||
val sBuilder = SpannableStringBuilder(modifiedText)
|
||||
val wrapperTextLength = wrapper.length
|
||||
|
||||
val matcher = Pattern.compile("$wrapper(.+?)$wrapper", Pattern.DOTALL).matcher(modifiedText)
|
||||
val matcher = Pattern.compile("$wrapper(.+?)$wrapper", Pattern.DOTALL).matcher(ssb)
|
||||
|
||||
val pair = TreeMap<Int, Int>()
|
||||
while (matcher.find()) {
|
||||
// 保存起始位置和结束位置
|
||||
pair[matcher.start(1)] = matcher.end(1)
|
||||
sBuilder.setSpan(object : ClickableSpan() {
|
||||
ssb.setSpan(object : ClickableSpan() {
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = ContextCompat.getColor(context, highlightColorId)
|
||||
@ -174,12 +189,12 @@ object TextHelper {
|
||||
val end = reversePair[key]
|
||||
|
||||
end?.let {
|
||||
sBuilder.replace(end, end + wrapperTextLength, "")
|
||||
sBuilder.replace(key - wrapperTextLength, key, "")
|
||||
ssb.replace(end, end + wrapperTextLength, "")
|
||||
ssb.replace(key - wrapperTextLength, key, "")
|
||||
}
|
||||
}
|
||||
|
||||
return sBuilder
|
||||
return ssb
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import java.sql.Timestamp
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@ -57,4 +56,32 @@ object TimeUtils {
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化视频时长
|
||||
*/
|
||||
@JvmStatic
|
||||
fun formatVideoDuration(length: Long): String {
|
||||
val minute = length / 60
|
||||
val second = length % 60
|
||||
return String.format(Locale.CHINA, "%02d:%02d", minute, second)
|
||||
}
|
||||
}
|
||||
@ -3,14 +3,18 @@ package com.gh.common.util
|
||||
import android.widget.Toast
|
||||
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.toast.ToastHandler
|
||||
|
||||
object ToastUtils {
|
||||
/** 之前显示的内容 */
|
||||
private var mOldMsg: String? = null
|
||||
|
||||
/** Toast对象 */
|
||||
private var mToast: Toast? = null
|
||||
|
||||
/** 第一次时间 */
|
||||
private var mOneTime: Long = 0
|
||||
|
||||
/** 第二次时间 */
|
||||
private var mTwoTime: Long = 0
|
||||
|
||||
@ -19,19 +23,34 @@ object ToastUtils {
|
||||
* @param message
|
||||
*/
|
||||
fun showToast(message: String) {
|
||||
showToast(message, -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示Toast
|
||||
* @param message
|
||||
* @param gravity
|
||||
*/
|
||||
fun showToast(message: String, gravity: Int = -1, yOffset: Int = 0) {
|
||||
if (mToast == null) {
|
||||
mToast = Toast.makeText(HaloApp.getInstance().application, message, Toast.LENGTH_SHORT)
|
||||
mToast = ToastHandler.INSTANCE.getToastInstance(HaloApp.getInstance().application, message, Toast.LENGTH_SHORT)
|
||||
if (gravity != -1) mToast!!.setGravity(gravity, 0, yOffset) else
|
||||
mToast!!.setGravity(DisplayUtils.getToastDefaultGravity(), 0, DisplayUtils.getToastOffset())
|
||||
mToast!!.show()
|
||||
mOneTime = System.currentTimeMillis()
|
||||
} else {
|
||||
mTwoTime = System.currentTimeMillis()
|
||||
if (message == mOldMsg) {
|
||||
if (mTwoTime - mOneTime > Toast.LENGTH_SHORT) {
|
||||
if (gravity != -1) mToast!!.setGravity(gravity, 0, yOffset) else
|
||||
mToast!!.setGravity(DisplayUtils.getToastDefaultGravity(), 0, DisplayUtils.getToastOffset())
|
||||
mToast!!.show()
|
||||
}
|
||||
} else {
|
||||
mOldMsg = message
|
||||
mToast!!.setText(message)
|
||||
if (gravity != -1) mToast!!.setGravity(gravity, 0, yOffset) else
|
||||
mToast!!.setGravity(DisplayUtils.getToastDefaultGravity(), 0, DisplayUtils.getToastOffset())
|
||||
mToast!!.show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
127
app/src/main/java/com/gh/common/util/UriUtils.java
Normal file
127
app/src/main/java/com/gh/common/util/UriUtils.java
Normal file
@ -0,0 +1,127 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
public class UriUtils {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static String getPath(final Context context, final Uri uri) {
|
||||
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[]{split[1]};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
private static String getDataColumn(Context context, Uri uri, String selection,
|
||||
String[] selectionArgs) {
|
||||
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {column};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
private static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
private static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
private static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
}
|
||||
18
app/src/main/java/com/gh/common/videolog/VideoRecordDao.kt
Normal file
18
app/src/main/java/com/gh/common/videolog/VideoRecordDao.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package com.gh.common.videolog
|
||||
|
||||
import androidx.room.*
|
||||
|
||||
@Dao
|
||||
interface VideoRecordDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertMany(eventList: List<VideoRecordEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(event: VideoRecordEntity)
|
||||
|
||||
@Query("SELECT * FROM VideoRecord")
|
||||
fun getAll(): List<VideoRecordEntity>
|
||||
|
||||
@Delete
|
||||
fun deleteMany(eventList: List<VideoRecordEntity>)
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.gh.common.videolog
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
@Entity(tableName = "VideoRecord")
|
||||
data class VideoRecordEntity(
|
||||
@PrimaryKey
|
||||
val id: String = UUID.randomUUID().toString(),
|
||||
var videoId: String = "",
|
||||
var time: Long = 0
|
||||
) : Parcelable
|
||||
76
app/src/main/java/com/gh/common/videolog/VideoRecordUtils.kt
Normal file
76
app/src/main/java/com/gh/common/videolog/VideoRecordUtils.kt
Normal file
@ -0,0 +1,76 @@
|
||||
package com.gh.common.videolog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import com.gh.common.util.toRequestBody
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.room.AppDatabase
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
object VideoRecordUtils {
|
||||
private const val STORE_SIZE = 20
|
||||
private lateinit var mApplication: Application
|
||||
|
||||
private val videoRecordSet by lazy { hashSetOf<VideoRecordEntity>() }
|
||||
private val videoRecordExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private val videoRecordDao by lazy { AppDatabase.getInstance(mApplication).videoRecordDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application) {
|
||||
mApplication = application
|
||||
|
||||
videoRecordExecutor.execute {
|
||||
val recordList = videoRecordDao.getAll()
|
||||
videoRecordSet.addAll(recordList)
|
||||
}
|
||||
}
|
||||
|
||||
fun log(videoId: String) {
|
||||
videoRecordExecutor.execute {
|
||||
try {
|
||||
val entity = VideoRecordEntity(videoId = videoId, time = System.currentTimeMillis() / 1000L)
|
||||
videoRecordSet.add(entity)
|
||||
videoRecordDao.insert(entity)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (videoRecordSet.size >= STORE_SIZE) {
|
||||
commitVideoRecord()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun commitVideoRecord() {
|
||||
if (videoRecordSet.isEmpty()) return
|
||||
videoRecordExecutor.execute {
|
||||
uploadVideoRecord()
|
||||
val exposureList = videoRecordSet.toList()
|
||||
videoRecordSet.removeAll(exposureList)
|
||||
videoRecordDao.deleteMany(exposureList)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun uploadVideoRecord(time: Long = 0) {
|
||||
if (!UserManager.getInstance().isLoggedIn) return
|
||||
val requestMap = HashMap<String, Any>()
|
||||
val videoIds = videoRecordSet.toList().map { it.videoId }
|
||||
requestMap["g_id"] = HaloApp.getInstance().gid ?: ""
|
||||
requestMap["time"] = time
|
||||
requestMap["video_id"] = videoIds
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api
|
||||
.uploadVideoLog(requestMap.toRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -190,7 +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.display(view, ad.image)
|
||||
ImageUtils.display(view,ad.image)
|
||||
holder.itemView.setOnClickListener {
|
||||
onItemClick?.invoke(position % mDatas.size)
|
||||
}
|
||||
|
||||
@ -21,8 +21,10 @@ class ConfigFilterView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
: ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var mSizeTv: TextView
|
||||
private var mNewestTv: TextView
|
||||
private var mRecommendedTv: TextView
|
||||
|
||||
var newestTv: TextView
|
||||
var ratingTv: TextView //目前只在专题页面显示
|
||||
var recommendedTv: TextView
|
||||
|
||||
private var mOnConfigFilterSetupListener: OnConfigFilterSetupListener? = null
|
||||
|
||||
@ -30,23 +32,33 @@ class ConfigFilterView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
View.inflate(context, R.layout.layout_config_filter, this)
|
||||
|
||||
mSizeTv = findViewById(R.id.size_tv)
|
||||
mNewestTv = findViewById(R.id.newest_tv)
|
||||
mRecommendedTv = findViewById(R.id.recommended_tv)
|
||||
newestTv = findViewById(R.id.newest_tv)
|
||||
ratingTv = findViewById(R.id.rating_tv)
|
||||
recommendedTv = findViewById(R.id.recommended_tv)
|
||||
|
||||
mSizeTv.setOnClickListener {
|
||||
showSelectionPopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
}
|
||||
|
||||
mNewestTv.setOnClickListener {
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.NEWEST)
|
||||
toggleHighlightedTextView(mNewestTv, true)
|
||||
toggleHighlightedTextView(mRecommendedTv, false)
|
||||
ratingTv.setOnClickListener {
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RATING)
|
||||
toggleHighlightedTextView(ratingTv, true)
|
||||
toggleHighlightedTextView(newestTv, false)
|
||||
toggleHighlightedTextView(recommendedTv, false)
|
||||
}
|
||||
|
||||
mRecommendedTv.setOnClickListener {
|
||||
newestTv.setOnClickListener {
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.NEWEST)
|
||||
toggleHighlightedTextView(ratingTv, false)
|
||||
toggleHighlightedTextView(newestTv, true)
|
||||
toggleHighlightedTextView(recommendedTv, false)
|
||||
}
|
||||
|
||||
recommendedTv.setOnClickListener {
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RECOMMENDED)
|
||||
toggleHighlightedTextView(mNewestTv, false)
|
||||
toggleHighlightedTextView(mRecommendedTv, true)
|
||||
toggleHighlightedTextView(ratingTv, false)
|
||||
toggleHighlightedTextView(newestTv, false)
|
||||
toggleHighlightedTextView(recommendedTv, true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +66,7 @@ class ConfigFilterView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
mOnConfigFilterSetupListener = onConfigFilterSetupListener
|
||||
}
|
||||
|
||||
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
if (highlightIt) {
|
||||
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.text_blue_background)
|
||||
targetTextView.setTextColor(Color.WHITE)
|
||||
@ -140,7 +152,8 @@ class ConfigFilterView @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
|
||||
enum class SortType {
|
||||
RECOMMENDED,
|
||||
NEWEST
|
||||
NEWEST,
|
||||
RATING
|
||||
}
|
||||
|
||||
enum class SortSize(val value: String) {
|
||||
|
||||
@ -31,7 +31,7 @@ class EllipsizeTextView : AppCompatTextView {
|
||||
val width = paint.measureText(text.subSequence(secondLastLineEnd, lastLineEnd).toString() + "...")
|
||||
if (width > layout.width) {
|
||||
val lastLineText = text.subSequence(secondLastLineEnd, lastLineEnd)
|
||||
for (i in 0 until lastLineText.length) {
|
||||
for (i in lastLineText.indices) {
|
||||
val cutWidth = paint.measureText(text.subSequence(secondLastLineEnd, lastLineEnd - i).toString() + "...")
|
||||
if (cutWidth <= layout.width) {
|
||||
charSequence = text.subSequence(0, lastLineEnd - i)
|
||||
@ -41,7 +41,7 @@ class EllipsizeTextView : AppCompatTextView {
|
||||
}
|
||||
} else {
|
||||
charSequence = text.subSequence(0, lastLineEnd)
|
||||
text = charSequence
|
||||
text = SpannableStringBuilder().append(charSequence).append("...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,37 +2,47 @@ package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
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.TextUtils;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.gh.common.util.DisplayUtils;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
private CharSequence mSnapshotText;
|
||||
|
||||
private String mEndText = "...";
|
||||
private CharSequence mShrankText = "";
|
||||
private String mExpandText = mEndText + "全文";
|
||||
private CharSequence mExpandedText = "";
|
||||
private boolean mUseGradientAlphaEndText = false;
|
||||
private boolean mShowExpandTextRegardlessOfMaxLines = false; // 不论文字超过 maxLines 都显示"...展开"文字
|
||||
|
||||
private int mMaxLines = 3; // 由于sdk版本限制(getMaxLines) 这里设置默认值
|
||||
|
||||
private static int DEFAULT_ADDITIONAL_END_TEXT_COUNT = 2;
|
||||
|
||||
private boolean mInitLayout = false;
|
||||
private boolean mIsExpanded = false; // 位于 recyclerView 时需要自行在外层管理是否已展开
|
||||
|
||||
private ExpandCallback mExpandCallback;
|
||||
private SelfCalculateMaxLinesCallback mMaxLinesCalculatedCallback;
|
||||
|
||||
private Rect mLastVisibleLineRect;
|
||||
private Rect mLastActualLineRect;
|
||||
|
||||
private static int DEFAULT_ADDITIONAL_END_TEXT_COUNT = 2;
|
||||
|
||||
public ExpandTextView(Context context) {
|
||||
super(context);
|
||||
@ -43,6 +53,10 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
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);
|
||||
@ -50,17 +64,33 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (mShowExpandTextRegardlessOfMaxLines && !mIsExpanded) {
|
||||
updateMaxLines();
|
||||
}
|
||||
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - getExtraBottomPadding());
|
||||
}
|
||||
|
||||
private void updateMaxLines() {
|
||||
mMaxLines = getLineCount() - 1;
|
||||
setMaxLines(mMaxLines);
|
||||
mMaxLinesCalculatedCallback.onMaxLinesCalculated(mMaxLines);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if (mInitLayout && !mIsExpanded && getLineCount() > mMaxLines) {
|
||||
if (mMaxLines > 0
|
||||
&& ((mShowExpandTextRegardlessOfMaxLines && !mIsExpanded) || (mInitLayout && !mIsExpanded && getLineCount() > mMaxLines))) {
|
||||
mSnapshotText = getText();
|
||||
mInitLayout = false;
|
||||
showExpandButton();
|
||||
}
|
||||
}
|
||||
|
||||
public void setExpendText(String text) {
|
||||
public void setExpandText(String text) {
|
||||
this.mExpandText = text;
|
||||
}
|
||||
|
||||
@ -68,6 +98,23 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
this.mExpandCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于不使用 maxLines 而是整段收起时的文字来确定“...更多”的位置的样式
|
||||
* @param shrankText 收起时的文字 (“...更多”跟在 shrankText 后)
|
||||
* @param expandedText 展开时的文字
|
||||
*/
|
||||
public void setShrankTextAndExpandedText(CharSequence shrankText, CharSequence expandedText) {
|
||||
mShrankText = shrankText;
|
||||
mExpandedText = expandedText;
|
||||
mShowExpandTextRegardlessOfMaxLines = !TextUtils.isEmpty(shrankText);
|
||||
|
||||
if (!mIsExpanded && mShowExpandTextRegardlessOfMaxLines) {
|
||||
setText(mShrankText);
|
||||
} else {
|
||||
setText(mExpandedText);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
mInitLayout = true;
|
||||
@ -76,6 +123,7 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
private void showExpandButton() {
|
||||
String finalEndText = "";
|
||||
TextPaint paint = getPaint();
|
||||
|
||||
Layout layout = getLayout();
|
||||
int start = layout.getLineStart(0);
|
||||
@ -86,12 +134,10 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
int viewWidth = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||
int additionalEndTextCount = 0;
|
||||
|
||||
TextPaint paint = getPaint();
|
||||
float expandTextWidth;
|
||||
if (mUseGradientAlphaEndText) {
|
||||
additionalEndTextCount = DEFAULT_ADDITIONAL_END_TEXT_COUNT;
|
||||
// 如果不加多个括号的话有可能算不对,惊了,明明是同样的 paint 同样的文字,长度却会略有不同
|
||||
expandTextWidth = paint.measureText(mEndText + mExpandText + " ");
|
||||
expandTextWidth = paint.measureText(mEndText + mExpandText);
|
||||
} else {
|
||||
expandTextWidth = paint.measureText(mExpandText);
|
||||
}
|
||||
@ -100,7 +146,8 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
if (viewWidth - lastLineRight > expandTextWidth) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
finalEndText = content.toString().substring(content.length() - additionalEndTextCount, content.length()) + mEndText;
|
||||
finalEndText = content.toString()
|
||||
.substring(content.length() - additionalEndTextCount, content.length()) + mEndText;
|
||||
finalEndText = finalEndText.replace("\n", "");
|
||||
|
||||
content = content.subSequence(0, content.length() - additionalEndTextCount) + finalEndText + mExpandText;
|
||||
@ -108,60 +155,126 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
content = content.toString().trim() + mExpandText;
|
||||
}
|
||||
} else {
|
||||
CharSequence lastText = mSnapshotText.subSequence(lastLineStart, lastLineEnd);
|
||||
for (int i = lastText.length() - 1; i > 0; i--) {
|
||||
CharSequence sequence = lastText.subSequence(0, i);
|
||||
float w = paint.measureText(sequence.toString());
|
||||
if (viewWidth - w - DisplayUtils.dip2px(5) > expandTextWidth) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
finalEndText = lastText.subSequence(i - additionalEndTextCount, i) + mEndText;
|
||||
finalEndText = finalEndText.replace("\n", "");
|
||||
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;
|
||||
} else {
|
||||
content = mSnapshotText.subSequence(start, lastLineStart + i) + 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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpannableStringBuilder msp = new SpannableStringBuilder(mSnapshotText);
|
||||
int length = msp.length();
|
||||
int startPosition = 0;
|
||||
startPosition = content.length() - finalEndText.length() - mExpandText.length();
|
||||
startPosition = Math.max(startPosition, 0);
|
||||
int expandTextStartPosition;
|
||||
expandTextStartPosition = content.length() - finalEndText.length() - mExpandText.length();
|
||||
expandTextStartPosition = Math.max(expandTextStartPosition, 0);
|
||||
|
||||
// 避免越界
|
||||
if (startPosition >= length) return;
|
||||
if (expandTextStartPosition >= length) return;
|
||||
|
||||
msp.replace(startPosition, length, finalEndText + mExpandText);
|
||||
msp.replace(expandTextStartPosition, length, finalEndText + mExpandText);
|
||||
|
||||
if (expandTextStartPosition + mEndText.length() >= msp.length()) return;
|
||||
|
||||
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) {
|
||||
public void onClick(@NonNull View widget) {
|
||||
mIsExpanded = true;
|
||||
setMaxLines(Integer.MAX_VALUE);
|
||||
setText(mSnapshotText);
|
||||
if (mShowExpandTextRegardlessOfMaxLines) {
|
||||
setText(mExpandedText);
|
||||
} else {
|
||||
setText(mSnapshotText);
|
||||
}
|
||||
|
||||
if (mExpandCallback != null) {
|
||||
mExpandCallback.onExpand();
|
||||
}
|
||||
}
|
||||
}, startPosition + mEndText.length(), msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
msp.setSpan(new GradientAlphaTextSpan(), startPosition, startPosition + finalEndText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}, expandTextStartPosition + mEndText.length(), msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
int paintColor = 0;
|
||||
|
||||
// 找到第一个位置与 endTextStartPosition 贴合的 ForegroundSpan / ClickableSpan ,
|
||||
// 获取颜色赋值给 GradientAlphaTextSpan
|
||||
Object[] objects = msp.getSpans(0, expandTextStartPosition, Object.class);
|
||||
if (objects.length != 0) {
|
||||
for (Object span : objects) {
|
||||
int startPosition = msp.getSpanStart(span);
|
||||
int endPosition = msp.getSpanEnd(span);
|
||||
if (expandTextStartPosition >= startPosition && expandTextStartPosition <= endPosition) {
|
||||
if (span instanceof ForegroundColorSpan) {
|
||||
paintColor = ((ForegroundColorSpan) span).getForegroundColor();
|
||||
break;
|
||||
} else if (span instanceof ClickableSpan) {
|
||||
paintColor = getResources().getColor(R.color.theme_font);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
msp.setSpan(new GradientAlphaTextSpan(paintColor), expandTextStartPosition, expandTextStartPosition + finalEndText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
setText(msp);
|
||||
setMovementMethod(CustomLinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法仅更改标记,不做实际展开的操作
|
||||
*/
|
||||
@ -174,8 +287,16 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
setMaxLines(maxLines);
|
||||
}
|
||||
|
||||
public void setSelfCalculateMaxLinesCallback(SelfCalculateMaxLinesCallback callback) {
|
||||
mMaxLinesCalculatedCallback = callback;
|
||||
}
|
||||
|
||||
public interface ExpandCallback {
|
||||
void onExpand();
|
||||
}
|
||||
|
||||
public interface SelfCalculateMaxLinesCallback {
|
||||
void onMaxLinesCalculated(int maxLines);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
32
app/src/main/java/com/gh/common/view/GameTagContainerView.kt
Normal file
32
app/src/main/java/com/gh/common/view/GameTagContainerView.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import com.gh.common.util.tryCatchInRelease
|
||||
|
||||
/**
|
||||
* 游戏列表标签的容器(特定功能的View)
|
||||
*
|
||||
* 一行内,防止标签显示越界
|
||||
*/
|
||||
class GameTagContainerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
|
||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||
super.onLayout(changed, l, t, r, b)
|
||||
tryCatchInRelease {
|
||||
var childContentWidth = 0
|
||||
for (i in 0 until childCount) {
|
||||
val tag = getChildAt(i)
|
||||
val tagLp = tag.layoutParams
|
||||
if (tagLp is LayoutParams) {
|
||||
tag.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
|
||||
val tagWidth = tagLp.leftMargin + tagLp.rightMargin + tag.measuredWidth
|
||||
childContentWidth += tagWidth
|
||||
}
|
||||
}
|
||||
if (childContentWidth > width && childCount > 1) {
|
||||
removeViewAt(childCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,17 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Shader
|
||||
import android.graphics.*
|
||||
import android.text.style.ReplacementSpan
|
||||
import androidx.core.graphics.ColorUtils
|
||||
|
||||
class GradientAlphaTextSpan() : ReplacementSpan() {
|
||||
class GradientAlphaTextSpan(var textColor: Int) : 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 originalColor = if (textColor == 0) paint.color else textColor
|
||||
val originalColorWithAlphaChanged = ColorUtils.setAlphaComponent(paint.color, 1)
|
||||
|
||||
val textWidth = paint.measureText(text, start, end).toInt()
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
@ -7,7 +8,8 @@ import androidx.core.view.MotionEventCompat;
|
||||
import androidx.core.view.NestedScrollingChild;
|
||||
import androidx.core.view.NestedScrollingChildHelper;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import wendu.dsbridge.DWebView;
|
||||
|
||||
import com.gh.common.view.dsbridge.DWebView;
|
||||
|
||||
public class NestedScrollWebView extends DWebView implements NestedScrollingChild {
|
||||
|
||||
|
||||
792
app/src/main/java/com/gh/common/view/NestedScrollWebView2.java
Normal file
792
app/src/main/java/com/gh/common/view/NestedScrollWebView2.java
Normal file
@ -0,0 +1,792 @@
|
||||
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 com.gh.common.view.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);
|
||||
}
|
||||
initVelocityTrackerIfNotExists();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,9 @@ class WelcomeDialog : BaseDialogFragment() {
|
||||
EntranceUtils.HOST_COMMUNITY -> {
|
||||
DirectUtils.directToCommunity(requireContext(), CommunityEntity(mWelcomeEntity?.link!!, mWelcomeEntity?.text!!))
|
||||
}
|
||||
else -> DialogUtils.showLowVersionDialog(context)
|
||||
// else -> DialogUtils.showLowVersionDialog(context)
|
||||
else -> DirectUtils.directToLinkPage(requireContext(), mWelcomeEntity
|
||||
?: WelcomeDialogEntity(), EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
|
||||
}
|
||||
|
||||
mDismissByClickImage = true
|
||||
|
||||
@ -7,11 +7,11 @@ import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Auto Scroll View Pager
|
||||
* <ul>
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.gh.common.view.dsbridge;
|
||||
|
||||
/**
|
||||
* Created by du on 16/12/31.
|
||||
*/
|
||||
|
||||
public interface CompletionHandler<T> {
|
||||
void complete(T retValue);
|
||||
void complete();
|
||||
void setProgressData(T value);
|
||||
}
|
||||
1016
app/src/main/java/com/gh/common/view/dsbridge/DWebView.java
Normal file
1016
app/src/main/java/com/gh/common/view/dsbridge/DWebView.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
package com.gh.common.view.dsbridge;
|
||||
|
||||
/**
|
||||
* Created by du on 16/12/31.
|
||||
*/
|
||||
|
||||
public interface OnReturnValue<T> {
|
||||
void onValue(T retValue);
|
||||
}
|
||||
@ -206,7 +206,8 @@ public class ScrollPageHelper extends PagerSnapHelper {
|
||||
} else if (snapLastItem && endOfList) {
|
||||
return child;
|
||||
} else if (endOfList) {
|
||||
return null;
|
||||
// return null;
|
||||
return child;
|
||||
} else {
|
||||
return reverseLayout ? layoutManager.findViewByPosition(firstChild - offset)
|
||||
: layoutManager.findViewByPosition(firstChild + offset);
|
||||
@ -262,7 +263,8 @@ public class ScrollPageHelper extends PagerSnapHelper {
|
||||
} else if (snapLastItem && startOfList) {
|
||||
return child;
|
||||
} else if (startOfList) {
|
||||
return null;
|
||||
// return null;
|
||||
return child;
|
||||
} else {
|
||||
return reverseLayout ? layoutManager.findViewByPosition(lastChild + offset)
|
||||
: layoutManager.findViewByPosition(lastChild - offset);
|
||||
|
||||
@ -8,30 +8,35 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArrayMap;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
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;
|
||||
import com.gh.common.util.DialogUtils;
|
||||
import com.gh.common.util.GdtHelper;
|
||||
import com.gh.common.util.HomePluggableHelper;
|
||||
import com.gh.common.util.MD5Utils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity;
|
||||
import com.gh.gamecenter.entity.HomePluggableFilterEntity;
|
||||
import com.gh.gamecenter.entity.PluginLocation;
|
||||
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;
|
||||
@ -60,7 +65,9 @@ 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;
|
||||
@ -102,7 +109,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
public void onTaskError(DownloadEntity entity) {
|
||||
// 下载进度超出是任务出错,但不需要去掉状态栏通知 https://gitlab.ghzs.com/pm/halo-app-issues/issues/496
|
||||
if (entity.getStatus() == DownloadStatus.overflow) {
|
||||
MtaHelper.onEventWithBasicDeviceInfo("下载无法完成", "游戏", entity.getName());
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("下载无法完成", "游戏", entity.getName());
|
||||
} else {
|
||||
downloadingMap.remove(entity.getUrl());
|
||||
}
|
||||
@ -131,9 +138,8 @@ 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);
|
||||
|
||||
// 只有下载模块需要这坨东西,因此移动到这里初始化
|
||||
@ -187,7 +193,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
downloadingMap.put(downloadEntity.getUrl(), downloadEntity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ArrayMap<String, DownloadEntity> getDownloadingMap() {
|
||||
@ -224,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) {
|
||||
@ -261,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;
|
||||
@ -281,9 +270,16 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
|
||||
downloadEntity.setPlugin(!TextUtils.isEmpty(apkEntity.getGhVersion()));
|
||||
|
||||
ExposureUtils.DownloadType downloadType = ExposureUtils.getDownloadType(apkEntity, gameEntity.getId());
|
||||
ExposureEvent downloadExposureEvent = ExposureUtils.logADownloadExposureEvent(gameEntity, apkEntity.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);
|
||||
}
|
||||
|
||||
@ -294,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,
|
||||
@ -435,6 +432,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
* @param url 下载链接
|
||||
* @return null表示下载列表中不存在该任务,否则返回下载任务
|
||||
*/
|
||||
@Nullable
|
||||
public DownloadEntity getDownloadEntityByUrl(String url) {
|
||||
if (TextUtils.isEmpty(url)) return null;
|
||||
return mDownloadDao.get(url);
|
||||
@ -497,11 +495,13 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
*
|
||||
* @return null表示没有下载任务
|
||||
*/
|
||||
@NonNull
|
||||
public List<DownloadEntity> getAll() {
|
||||
if (CommonDebug.IS_DEBUG) {
|
||||
CommonDebug.logMethodName(this);
|
||||
}
|
||||
return mDownloadDao.getAll();
|
||||
List<DownloadEntity> all = mDownloadDao.getAll();
|
||||
return all != null ? all : new ArrayList<>();
|
||||
}
|
||||
|
||||
public ArrayMap<String, DownloadEntity> getEntryMap(String name) {
|
||||
@ -631,7 +631,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
}
|
||||
}
|
||||
|
||||
// 开启下载服务, Fuck me, 即便是在启动页调用的方法,依然有可能触发 `unable is in background`
|
||||
startDownloadService();
|
||||
checkRetryDownload();
|
||||
}
|
||||
@ -648,10 +647,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) {
|
||||
// 当满足系统版本大于 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) {
|
||||
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
|
||||
mContext.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
@ -674,7 +674,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) {
|
||||
&& ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
|
||||
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
|
||||
mContext.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
@ -682,14 +682,6 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void disableDownloadSpeedLimit() {
|
||||
// DownloadSpeedController.disableSpeedLimit();
|
||||
}
|
||||
|
||||
public void updateSpeedLimitationReleaseDelay(int delay) {
|
||||
// DownloadSpeedController.updateLimitationReleaseDelay(delay);
|
||||
}
|
||||
|
||||
public void checkRetryDownload() {
|
||||
if (!NetworkUtils.isWifiConnected(mContext)) return;
|
||||
|
||||
@ -713,33 +705,79 @@ 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 "";
|
||||
|
||||
if (updateList != null) {
|
||||
// 首页永久忽略的插件化列表
|
||||
List<HomePluggableFilterEntity> permanentInactiveUpdateList = HomePluggableHelper.getPermanentInactivePluggablePackage();
|
||||
for (GameUpdateEntity updateEntity : updateList) {
|
||||
if (updateEntity.isShowPlugin(PluginLocation.only_index)
|
||||
&& !mUpdateMarks.contains(updateEntity.getId() + updateEntity.getPackageName())) {
|
||||
return "";
|
||||
if (updateEntity.isShowPlugin(PluginLocation.only_index) && !mUpdateMarks.contains(updateEntity.getId() + updateEntity.getPackageName())) {
|
||||
// 判断该更新的的包名是否被永久忽略
|
||||
if (permanentInactiveUpdateList != null) {
|
||||
boolean isPluggablePermanentInactive = false;
|
||||
for (HomePluggableFilterEntity filterEntity : permanentInactiveUpdateList) {
|
||||
if (filterEntity.getPkgName().equals(updateEntity.getPackageName())) {
|
||||
isPluggablePermanentInactive = true;
|
||||
}
|
||||
}
|
||||
if (!isPluggablePermanentInactive) {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 +789,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 +878,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 +887,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ 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
|
||||
@ -25,41 +25,58 @@ object ExoCacheManager {
|
||||
|
||||
private val threads = ConcurrentHashMap<String, AtomicBoolean>()
|
||||
|
||||
private val preLength by lazy {
|
||||
private fun getPreLength(): Long {
|
||||
val totalRamSizeOfDevice = DeviceUtils.getTotalRamSizeOfDevice(HaloApp.getInstance().application)
|
||||
if (totalRamSizeOfDevice <= 2 * 1024) {
|
||||
10 * 1024 * 1024L
|
||||
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 {
|
||||
50 * 1024 * 1024L
|
||||
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: Throwable) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ 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
|
||||
@ -45,6 +46,7 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
private lateinit var mGestureDetector: GestureDetector
|
||||
|
||||
private var mAdapter: DownloadDialogAdapter? = null
|
||||
private var mTraceEvent: ExposureEvent? = null
|
||||
|
||||
// 合集页面保持和后台一样的顺序
|
||||
private var mCollectionAdapter: DownloadDialogAdapter? = null
|
||||
@ -63,7 +65,7 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
|
||||
private val mDataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
if (downloadEntity.gameId == mGameEntity.id &&
|
||||
if (downloadEntity.gameId == mGameEntity?.id &&
|
||||
DownloadStatus.delete != DownloadManager.getInstance(requireContext()).getStatus(downloadEntity.url)) {
|
||||
mAdapter?.listData?.forEachIndexed { index, entity ->
|
||||
if (entity.normal?.packageName == downloadEntity.packageName
|
||||
@ -95,13 +97,18 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
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, mEntrance, mLocation)
|
||||
mAdapter = DownloadDialogAdapter(requireContext(), mViewModel, itemList, false, mTraceEvent, mEntrance, mLocation)
|
||||
mBinding.contentList.layoutManager = createLayoutManager(itemList)
|
||||
mBinding.contentList.adapter = mAdapter
|
||||
|
||||
})
|
||||
|
||||
mViewModel.collectionLiveData.observe(this, Observer { collection ->
|
||||
@ -120,13 +127,13 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
itemList.add(DownloadDialogItemData(instruction = collection.downloadInstruction))
|
||||
}
|
||||
|
||||
mCollectionAdapter = DownloadDialogAdapter(requireContext(), mViewModel, itemList, true, mEntrance, mLocation)
|
||||
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 + "版本")
|
||||
mBinding.title.text = ("选择下载" + mGameEntity?.pluginDesc + "版本")
|
||||
|
||||
mCollectionAdapter = null
|
||||
collectionExitAnimation()
|
||||
@ -157,7 +164,7 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
downloadNotice,
|
||||
mEntrance, "下载多平台弹窗")
|
||||
}
|
||||
MtaHelper.onEvent(MTA_KEY, "点击", mViewModel.gameEntity.name + "_" + downloadNotice.title)
|
||||
// MtaHelper.onEvent(MTA_KEY, "点击", mViewModel.gameEntity.name + "_" + downloadNotice.title)
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +263,6 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
return layoutManager
|
||||
}
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
mCollectionAdapter?.notifyDataSetChanged()
|
||||
@ -266,7 +272,6 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
mElapsedHelper.resumeCounting()
|
||||
}
|
||||
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance(requireContext()).removeObserver(mDataWatcher)
|
||||
@ -275,13 +280,13 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
// 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() {
|
||||
@ -381,7 +386,7 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun showDownloadDialog(context: Context?, gameEntity: GameEntity, entrance: String?, location: String?) {
|
||||
fun showDownloadDialog(context: Context?, gameEntity: GameEntity, traceEvent: ExposureEvent?, entrance: String?, location: String?) {
|
||||
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
|
||||
context
|
||||
} else if (BuildConfig.DEBUG) {
|
||||
@ -399,9 +404,12 @@ class DownloadDialog : BaseDialogFragment(), View.OnTouchListener {
|
||||
if (hasDownloadDialogInCurrentActivity(fragmentActivity)) return
|
||||
|
||||
val downloadDialog = DownloadDialog().apply {
|
||||
mGameEntity = gameEntity
|
||||
mEntrance = entrance ?: ""
|
||||
mLocation = location ?: ""
|
||||
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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user