Compare commits
342 Commits
v4.5.2-252
...
v4.7.0-290
| Author | SHA1 | Date | |
|---|---|---|---|
| 278dcc33be | |||
| 87be3bed61 | |||
| 078b102152 | |||
| 85e97bc64d | |||
| 951ac06948 | |||
| 92cdb6fdbb | |||
| cb0dd1d082 | |||
| 276655a8ec | |||
| df8b612188 | |||
| 4d413eb1f2 | |||
| d496ab0283 | |||
| b519ff6aac | |||
| 8c05d2be8a | |||
| 23dd89198b | |||
| 6569ab2d33 | |||
| 1bead6a756 | |||
| c0df3af298 | |||
| 267363f46d | |||
| 44cf54aee6 | |||
| 322d7117f8 | |||
| c8684f837a | |||
| 9e5ecf1520 | |||
| 2df94dddc5 | |||
| 1731980e88 | |||
| f789f0cb73 | |||
| d043a89f3e | |||
| 4ab0f066df | |||
| 096679029b | |||
| 2634228d21 | |||
| a33784221d | |||
| 2ef7216bda | |||
| 2554fab389 | |||
| a4e3121489 | |||
| 8dfde8ddd0 | |||
| 63403c7c29 | |||
| aa9269aecc | |||
| 489e7105bb | |||
| 8dbc407503 | |||
| 3e032fa540 | |||
| 3df381b255 | |||
| 7c59181bc6 | |||
| 9244a65371 | |||
| a7b8b678f7 | |||
| 9877d4d51e | |||
| 32b4570738 | |||
| af3fdb3cfb | |||
| 5ce992ee63 | |||
| ba17019428 | |||
| f77087907f | |||
| 3080f59ef6 | |||
| 74b1f388e2 | |||
| dcbc2fe352 | |||
| b892c58c52 | |||
| 0a455c7b09 | |||
| 7a254c4609 | |||
| 4f08a4b9d2 | |||
| 75670b7801 | |||
| af386f6bdc | |||
| ff813d8784 | |||
| dba87fe09c | |||
| cf13003c2a | |||
| 7fe337c557 | |||
| 1902ed29c1 | |||
| a67927e9e7 | |||
| 33b6835ae6 | |||
| 1ca6a2c795 | |||
| 372230092a | |||
| 2ee1db1074 | |||
| caabf416bb | |||
| d33d0e98c9 | |||
| a4a029f31e | |||
| 3b8d34f8c3 | |||
| 2368e268bf | |||
| 6390198899 | |||
| 4a6aef4caa | |||
| 91529f51a3 | |||
| 57096b2b08 | |||
| 40f1f57844 | |||
| b9da7fbcb8 | |||
| 7138177df2 | |||
| aa4cf291dc | |||
| 8f4c1651a3 | |||
| 30a6cfd7d9 | |||
| c1abb977bf | |||
| fef633e9d8 | |||
| 5207b5675c | |||
| b7059abcee | |||
| 45a84fc45c | |||
| cf96b00036 | |||
| 49dacc472d | |||
| 72d069ad7e | |||
| 2f49e05891 | |||
| fd2d277470 | |||
| fc52694fdc | |||
| 7123b3d59a | |||
| 7684c247bf | |||
| f2b9d26e8a | |||
| 6d5d407aa8 | |||
| 338de605ec | |||
| 53930b0257 | |||
| 34d5e2cc29 | |||
| 591a694bdf | |||
| 9fd9a4ebcc | |||
| f2712c3db7 | |||
| 61c2c7b218 | |||
| 8a54c47d9e | |||
| 7f60a575a4 | |||
| eb2dd7c6b8 | |||
| ae8433d8bd | |||
| 7ec4ba6582 | |||
| 190434a855 | |||
| 4b82e40eb4 | |||
| d8e858ddb8 | |||
| 9b8c8f328d | |||
| bebdbe64cd | |||
| a0555d6ffc | |||
| d6674efe4a | |||
| 20235c6908 | |||
| bc155d580c | |||
| 9cf852b490 | |||
| f32bc837b4 | |||
| 36efcdb75a | |||
| a232ef9d03 | |||
| 05b90bd5b4 | |||
| bc382e6f31 | |||
| 116d71bb9c | |||
| 2aadb78301 | |||
| 22801846e4 | |||
| f9f6886250 | |||
| 30c8d4db02 | |||
| 08a5ea100e | |||
| db33ed0d0d | |||
| fd424b0204 | |||
| c1bf0a628f | |||
| 086bf75314 | |||
| af31fdc562 | |||
| 99e0a2e0a2 | |||
| 069d0db00e | |||
| 68f1343c93 | |||
| fbe25c9099 | |||
| 9b893bcb42 | |||
| 5182d4cd8d | |||
| cce077de96 | |||
| 03092a0ec1 | |||
| 4906ecc06b | |||
| 7a831ce646 | |||
| 290a26c4e2 | |||
| 571080b1e5 | |||
| d9567138ee | |||
| 4a31f74dcb | |||
| 79cf96b161 | |||
| 7fdd4dbf04 | |||
| 8eeeb5de60 | |||
| 150109cef4 | |||
| 7e7cc38d6d | |||
| b3df30680e | |||
| f73048d3a5 | |||
| c540c14e1c | |||
| f216bf4097 | |||
| a42bcbd160 | |||
| bc6786b21e | |||
| 47e357143c | |||
| 74eb8ad0e8 | |||
| d2fc927ffb | |||
| db4b049591 | |||
| 86db2b5688 | |||
| baa1c9f9e5 | |||
| be3a3e82af | |||
| 120ea26455 | |||
| fc476ca889 | |||
| fc36ce6a03 | |||
| 0a1d330c2f | |||
| e5ec42576c | |||
| b57f74a411 | |||
| cb3884ea2e | |||
| 0cb3184e35 | |||
| 76c5009fbc | |||
| 312ec153f1 | |||
| 1ef79df4c5 | |||
| d7a809f8a6 | |||
| ebadeeb873 | |||
| 55155ee8ae | |||
| be98a6b6d6 | |||
| e2f396f8c7 | |||
| 2a8024368a | |||
| 0cd025b42d | |||
| d6adda78c4 | |||
| 99708d7801 | |||
| 584986a9d5 | |||
| 507da05841 | |||
| 72a8fe4764 | |||
| f9f23f6324 | |||
| e2964f93c7 | |||
| cb2a9a3ca0 | |||
| ea68941bb9 | |||
| b598d331d4 | |||
| d7b876bfed | |||
| 6aca60080a | |||
| 59d76688b9 | |||
| 8e5e677228 | |||
| dd1bfcaddd | |||
| f4c66e1ab7 | |||
| 6ebdf6e42a | |||
| 4c01dd442b | |||
| ac226e3301 | |||
| 569f1b8cf1 | |||
| 46f5da88a6 | |||
| f5b876b018 | |||
| c5df856023 | |||
| 4f775847dc | |||
| f53710fdf5 | |||
| 80c56def08 | |||
| a1d88d999e | |||
| fbc6f6adaa | |||
| e7c55b2467 | |||
| 2c45bb1da9 | |||
| 173825dc74 | |||
| a63e28809b | |||
| 0eb8cb6e66 | |||
| e1514e2b25 | |||
| bef0da821b | |||
| 54741729f3 | |||
| e2640c22f8 | |||
| 2f16d5ba99 | |||
| 1d5301f887 | |||
| cc09c702f7 | |||
| 6b2a88766e | |||
| 0eb629fe67 | |||
| 44c398c9fb | |||
| 96122c5919 | |||
| bd4c438036 | |||
| adc8648f5a | |||
| 8eb3922a33 | |||
| 30743bb015 | |||
| 853f51ad0e | |||
| 763aad99ee | |||
| 2ed2317b02 | |||
| 0663f3eb2b | |||
| 11a2eeae7a | |||
| 8c2619bb22 | |||
| 46320fe07d | |||
| 1ee933b115 | |||
| 1d9152ca1a | |||
| 81ebf21bb9 | |||
| dd4bf98f28 | |||
| ba3a06da43 | |||
| 6fe096383c | |||
| d16f99958f | |||
| 5fa3aa42dc | |||
| f4f28a2daa | |||
| 3c1cd14bdc | |||
| 6dcc251312 | |||
| f692aa4bff | |||
| 7cde55ebe0 | |||
| 94eba08af4 | |||
| 2bf207661c | |||
| 2fff7f60c2 | |||
| 1b3c93ebb5 | |||
| 5b529b044e | |||
| 91f417f479 | |||
| fc0bf595bd | |||
| c0f6499577 | |||
| b9bef384f1 | |||
| 58bfae2fdb | |||
| d2c2fff884 | |||
| ab178c47b5 | |||
| 40be004376 | |||
| 0b501c9ce1 | |||
| cff82379f5 | |||
| 825503bd58 | |||
| 77a715a4bd | |||
| 78b233c506 | |||
| a6e799bb9a | |||
| b65992099a | |||
| 3d1a5f52bf | |||
| 82d0d5745c | |||
| fe7968cb32 | |||
| 94c503af74 | |||
| bdd9815ffc | |||
| 693acceca8 | |||
| 90971bc299 | |||
| e5b3613348 | |||
| 652ca8f69c | |||
| f15e351c1e | |||
| 47c81c3dac | |||
| 7453a61e4f | |||
| 911f6397e8 | |||
| 8d21a4f774 | |||
| 991c54b680 | |||
| 48dcb5089b | |||
| 461efe7101 | |||
| 0735161a20 | |||
| 093c1e2b15 | |||
| 7d0e02c899 | |||
| 8294913f04 | |||
| e811c4f90b | |||
| 69e248b389 | |||
| 43f6bfc4c2 | |||
| c8b81ab56a | |||
| aa41fd98e8 | |||
| ff6c4c2de9 | |||
| 136761d2f7 | |||
| 9374fc5264 | |||
| 324aaa5056 | |||
| f22afbd819 | |||
| e149231cb2 | |||
| 9c8155ddf8 | |||
| dd95419a36 | |||
| 870b10dd13 | |||
| 637b426649 | |||
| ee8ce87e28 | |||
| a944a7f730 | |||
| fdbb16b45f | |||
| 29bc098dcf | |||
| b2433cf13a | |||
| e908e23bb2 | |||
| 1a4dc827b5 | |||
| 9fd5e65fa2 | |||
| 5405dcd30e | |||
| 5c35f7fe5d | |||
| fc907a398f | |||
| 1b8dc6eba0 | |||
| c17a36c866 | |||
| a29cf832f1 | |||
| c9a628a5e9 | |||
| 165ba01afd | |||
| a39a8c2cce | |||
| 4002f138bb | |||
| 48fdb38902 | |||
| 38a3120ea1 | |||
| c0e370dfd2 | |||
| e4765089fa | |||
| 721e73ca1e | |||
| e406c90027 | |||
| e7c4886219 | |||
| 80a2e4f336 | |||
| 9f5940c6f6 | |||
| d01fda44b3 | |||
| 2020033bc0 | |||
| dc2c8e590c | |||
| b115db51e9 | |||
| 7d194c7078 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,5 +7,6 @@ local.properties
|
||||
captures/
|
||||
build/
|
||||
release-app/
|
||||
test-app/
|
||||
scripts/apk-channel/
|
||||
app/src/test/java/com/gh/gamecenter
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,4 +1,4 @@
|
||||
[submodule "libraries/LGLibrary"]
|
||||
path = libraries/LGLibrary
|
||||
url = git@gitlab.ghzs.com:android/common-library.git
|
||||
url = git@git.ghzs.com:android/common-library.git
|
||||
branch = master
|
||||
|
||||
@ -2,9 +2,7 @@ apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android' // kotlin
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
// apkChannelPackage
|
||||
apply plugin: 'channel'
|
||||
apply plugin: "com.gh.gamecenter.plugin"
|
||||
|
||||
import groovy.xml.XmlUtil
|
||||
|
||||
@ -38,6 +36,10 @@ android {
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
additionalParameters "--no-version-vectors"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
multiDexEnabled true
|
||||
@ -184,6 +186,8 @@ android {
|
||||
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
|
||||
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
|
||||
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
|
||||
|
||||
manifestPlaceholders.put("APPLOG_SCHEME","rangersapplog.byAx6uYt".toLowerCase())
|
||||
}
|
||||
|
||||
gdt {
|
||||
@ -200,23 +204,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// apkChannelPackage
|
||||
channel {
|
||||
//多渠道包的输出目录,默认为new File(project.buildDir,"channel")
|
||||
baseOutputDir = new File(project.buildDir, "channel")
|
||||
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
|
||||
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
|
||||
}
|
||||
|
||||
rebuildChannel {
|
||||
// baseDebugApk = 已有Debug APK
|
||||
// baseReleaseApk = 已有Release APK
|
||||
// //默认为new File(project.buildDir, "rebuildChannel/debug")
|
||||
// debugOutputDir = Debug渠道包输出目录
|
||||
// //默认为new File(project.buildDir, "rebuildChannel/release")
|
||||
// releaseOutputDir = Release渠道包输出目录
|
||||
}
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs', 'libs/aars'
|
||||
@ -237,8 +224,8 @@ dependencies {
|
||||
debugImplementation "com.gu.android:toolargetool:${toolargetool}"
|
||||
debugImplementation "com.github.nichbar:WhatTheStack:$whatTheStack"
|
||||
|
||||
implementation "androidx.core:core:${core}"
|
||||
implementation "androidx.fragment:fragment:${fragment}"
|
||||
implementation "androidx.core:core-ktx:${core}"
|
||||
implementation "androidx.fragment:fragment-ktx:${fragment}"
|
||||
implementation "androidx.multidex:multidex:${multiDex}"
|
||||
implementation "androidx.appcompat:appcompat:${appCompat}"
|
||||
implementation "androidx.cardview:cardview:${cardView}"
|
||||
@ -259,7 +246,7 @@ dependencies {
|
||||
implementation "com.kyleduo.switchbutton:library:${switchButton}"
|
||||
|
||||
implementation "com.facebook.fresco:fresco:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-gif:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-gif-lite:${fresco}"
|
||||
implementation "com.facebook.fresco:animated-drawable:${fresco}"
|
||||
|
||||
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
|
||||
@ -328,7 +315,7 @@ dependencies {
|
||||
implementation "com.ethanhua:skeleton:${skeleton}"
|
||||
implementation "io.supercharge:shimmerlayout:${shimmerlayout}"
|
||||
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
|
||||
implementation "com.walkud.rom.checker:RomChecker:${romChecker}"
|
||||
implementation "com.github.nichbar:AndroidRomChecker:${romChecker}"
|
||||
|
||||
debugImplementation "com.github.nichbar.chucker:library:$chucker"
|
||||
releaseImplementation "com.github.nichbar.chucker:library-no-op:$chucker"
|
||||
@ -342,15 +329,21 @@ dependencies {
|
||||
|
||||
implementation "net.lingala.zip4j:zip4j:${zip4j}"
|
||||
|
||||
implementation "io.sentry:sentry-android:$sentry"
|
||||
// plugin 需要字符串,故不能用值
|
||||
implementation "io.sentry:sentry-android:3.2.0"
|
||||
|
||||
implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
|
||||
// implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
|
||||
implementation("com.github.nichbar.BigImageViewer:BigImageViewer:$bigImageViewer", {
|
||||
exclude group: 'com.squareup.okhttp3'
|
||||
exclude group: 'androidx.swiperefreshlayout'
|
||||
exclude group: 'com.github.bumptech.glide'
|
||||
})
|
||||
implementation "com.github.nichbar.BigImageViewer:FrescoImageLoader:$bigImageViewer"
|
||||
implementation "com.github.nichbar.BigImageViewer:FrescoImageViewFactory:$bigImageViewer"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
|
||||
|
||||
implementation "com.github.hsiafan:apk-parser:$apkParser"
|
||||
|
||||
implementation project(':libraries:LGLibrary')
|
||||
// implementation project(':libraries:MTA')
|
||||
implementation project(':libraries:QQShare')
|
||||
|
||||
@ -1,20 +1,3 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
#--------- remove logs start ----------------
|
||||
-assumenosideeffects class com.lightgame.config.CommonDebug {
|
||||
@ -26,126 +9,62 @@
|
||||
public static void logMethodWithParams(...);
|
||||
}
|
||||
|
||||
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
|
||||
|
||||
#-dontoptimize
|
||||
-assumenosideeffects class com.lightgame.utils.Utils {
|
||||
public static void log(...);
|
||||
}
|
||||
#--------- remove logs end ----------------
|
||||
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
-dontwarn InnerClasses
|
||||
# TODO Dicard sourceFile in final release build but remain in internal build.
|
||||
-renamesourcefileattribute SourceFile
|
||||
# Keep Attribute
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
|
||||
|
||||
# OrmLite uses reflection
|
||||
-keep class com.j256.**
|
||||
-keepclassmembers class com.j256.** { *; }
|
||||
-keep enum com.j256.**
|
||||
-keepclassmembers enum com.j256.** { *; }
|
||||
-keep interface com.j256.**
|
||||
-keepclassmembers interface com.j256.** { *; }
|
||||
# OrmLite
|
||||
-keep class com.j256.*
|
||||
-keepclassmembers class com.j256.* { *; }
|
||||
-keep enum com.j256.*
|
||||
-keepclassmembers enum com.j256.* { *; }
|
||||
-keep interface com.j256.*
|
||||
-keepclassmembers interface com.j256.* { *; }
|
||||
-dontwarn com.j256.**
|
||||
|
||||
#okhttp3
|
||||
-dontwarn com.squareup.okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-keep class com.squareup.okhttp3.** { *;}
|
||||
|
||||
# stetho
|
||||
-keep class com.facebook.stetho.** { *; }
|
||||
-dontwarn com.facebook.stetho.**
|
||||
|
||||
# Retrofit 2.2
|
||||
# Platform calls Class.forName on types which do not exist on Android to determine platform.
|
||||
-dontnote retrofit2.Platform
|
||||
# Platform used when running on Java 8 VMs. Will not be used at runtime.
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
# Retain generic type information for use by reflection by converters and adapters.
|
||||
-keepattributes Signature
|
||||
# Retain declared checked exceptions for use by a Proxy instance.
|
||||
-keepattributes Exceptions
|
||||
|
||||
# Retrofit 2.X
|
||||
## https://square.github.io/retrofit/ ##
|
||||
|
||||
-dontwarn retrofit2.**
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
|
||||
# rxjava
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.ImmediateScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.TestScheduler {
|
||||
public <methods>;
|
||||
}
|
||||
-keep class rx.schedulers.Schedulers {
|
||||
public static ** test();
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
|
||||
long producerIndex;
|
||||
long consumerIndex;
|
||||
}
|
||||
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
|
||||
long producerNode;
|
||||
long consumerNode;
|
||||
}
|
||||
-dontwarn rx.internal.util.**
|
||||
|
||||
## AutoScrollViewPager
|
||||
-keep class cn.trinea.android.** { *; }
|
||||
-keepclassmembers class cn.trinea.android.** { *; }
|
||||
### AutoScrollViewPager
|
||||
-keep class cn.trinea.android.* { *; }
|
||||
-keepclassmembers class cn.trinea.android.* { *; }
|
||||
-dontwarn cn.trinea.android.**
|
||||
|
||||
## butterknife
|
||||
# Retain generated class which implement Unbinder.
|
||||
#-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
|
||||
#
|
||||
## Prevent obfuscation of types which use ButterKnife annotations since the simple name
|
||||
## is used to reflectively look up the generated ViewBinding.
|
||||
#-keep class butterknife.*
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <methods>; }
|
||||
#-keepclasseswithmembernames class * { @butterknife.* <fields>; }
|
||||
### Butterknife
|
||||
-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
|
||||
-keep class butterknife.*
|
||||
-keepclasseswithmembernames class * { @butterknife.* <methods>; }
|
||||
-keepclasseswithmembernames class * { @butterknife.* <fields>; }
|
||||
|
||||
-dontwarn butterknife.internal.**
|
||||
-keep class **$$ViewInjector { *; }
|
||||
-keepnames class * { @butterknife.InjectView *;}
|
||||
-dontwarn butterknife.Views$InjectViewProcessor
|
||||
-dontwarn com.gc.materialdesign.views.**
|
||||
|
||||
# eventbus
|
||||
-keepattributes *Annotation*
|
||||
-keepclassmembers class ** {
|
||||
### eventbus
|
||||
-keepclassmembers class * {
|
||||
@org.greenrobot.eventbus.Subscribe <methods>;
|
||||
}
|
||||
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
|
||||
|
||||
# Only required if you use AsyncExecutor
|
||||
### Only required if you use AsyncExecutor
|
||||
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
||||
<init>(java.lang.Throwable);
|
||||
}
|
||||
|
||||
# weiboSdk
|
||||
-keep class com.sina.weibo.sdk.** { *; }
|
||||
### weiboSdk
|
||||
-keep class com.sina.weibo.sdk.* { *; }
|
||||
-dontwarn android.webkit.WebView
|
||||
-dontwarn android.webkit.WebViewClient
|
||||
|
||||
# app models
|
||||
-keep class com.gh.common.view.** {*;}
|
||||
-keep class com.gh.gamecenter.db.info.** {*;}
|
||||
-keep class com.gh.gamecenter.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.qa.entity.** {*;}
|
||||
-keep class com.gh.gamecenter.retrofit.** {*;}
|
||||
-keep class com.gh.gamecenter.eventbus.** {*;}
|
||||
-keep class * extends rx.Subscriber
|
||||
### app models
|
||||
-keep class com.gh.common.view.* {*;}
|
||||
-keep class com.gh.gamecenter.db.info.* {*;}
|
||||
-keep class com.gh.gamecenter.entity.* {*;}
|
||||
-keep class com.gh.gamecenter.qa.entity.* {*;}
|
||||
-keep class com.gh.gamecenter.retrofit.* {*;}
|
||||
-keep class com.gh.gamecenter.eventbus.* {*;}
|
||||
-keep class com.gh.gamecenter.video.detail.* {*;}
|
||||
|
||||
#---------------------------------webview------------------------------------
|
||||
###
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
|
||||
public boolean *(android.webkit.WebView, java.lang.String);
|
||||
@ -153,93 +72,54 @@
|
||||
-keepclassmembers class * extends android.webkit.WebViewClient {
|
||||
public void *(android.webkit.WebView, java.lang.String);
|
||||
}
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
##---------------Begin: proguard configuration for Gson ----------
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
-keepattributes Signature
|
||||
|
||||
# For using GSON @Expose annotation
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Gson specific classes
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
#-keep class com.google.gson.stream.** { *; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
-keepclassmembers enum * { *; }
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
|
||||
# ------ bugly ---------
|
||||
-dontwarn com.tencent.bugly.**
|
||||
-keep public class com.tencent.bugly.**{*;}
|
||||
|
||||
# easypermission
|
||||
### easypermission
|
||||
-keepclassmembers class * {
|
||||
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
|
||||
}
|
||||
|
||||
# 重命名文件为SourceFile,再配合mapping符号表,可以拿到真实的类名
|
||||
-renamesourcefileattribute SourceFile
|
||||
# 保留源文件行号
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# TODO What's this ?
|
||||
-ignorewarnings
|
||||
|
||||
### Keep Annotation
|
||||
-keep @androidx.annotation.Keep class *
|
||||
-keepclassmembers class ** {
|
||||
-keepclassmembers class * {
|
||||
@androidx.annotation.Keep *;
|
||||
}
|
||||
|
||||
-keep class com.gh.loghub.** { *; }
|
||||
### LoghubHelper
|
||||
-keep class com.gh.loghub.* { *; }
|
||||
|
||||
### greenDAO 3
|
||||
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
|
||||
public static java.lang.String TABLENAME;
|
||||
}
|
||||
-keep class **$Properties
|
||||
-keep class org.greenrobot.greendao.** { *; }
|
||||
-keep class **$Properties { *; }
|
||||
-keep class org.greenrobot.greendao.* { *; }
|
||||
# If you do not use SQLCipher:
|
||||
-dontwarn org.greenrobot.greendao.database.**
|
||||
# If you do not use RxJava:
|
||||
-dontwarn rx.**
|
||||
-dontwarn org.greenrobot.greendao.rx.**
|
||||
-dontwarn org.greenrobot.greendao.**
|
||||
|
||||
### fastJson
|
||||
-dontwarn com.alibaba.fastjson.**
|
||||
-keep class com.alibaba.fastjson.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Annotation
|
||||
-keep class com.alibaba.fastjson.* { *; }
|
||||
|
||||
### 广点通
|
||||
-dontwarn com.qq.gdt.action.**
|
||||
-keep class com.qq.gdt.action.** {*;}
|
||||
-keep class com.qq.gdt.action.* {*;}
|
||||
|
||||
### AndroidX
|
||||
-keep class androidx.core.app.CoreComponentFactory { *; }
|
||||
|
||||
#阿里云上传
|
||||
-keep class com.alibaba.sdk.android.oss.** { *; }
|
||||
### 阿里云上传
|
||||
-keep class com.alibaba.sdk.android.oss.* { *; }
|
||||
-dontwarn okio.**
|
||||
-dontwarn org.apache.commons.codec.binary.**
|
||||
|
||||
#视频相关
|
||||
-keep class com.shuyu.gsyvideoplayer.video.** { *; }
|
||||
### 视频相关
|
||||
-keep class com.shuyu.gsyvideoplayer.video.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.**
|
||||
-keep class com.shuyu.gsyvideoplayer.video.base.** { *; }
|
||||
-keep class com.shuyu.gsyvideoplayer.video.base.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
|
||||
-keep class com.shuyu.gsyvideoplayer.utils.* { *; }
|
||||
-dontwarn com.shuyu.gsyvideoplayer.utils.**
|
||||
-keep class tv.danmaku.ijk.** { *; }
|
||||
-keep class tv.danmaku.ijk.* { *; }
|
||||
-dontwarn tv.danmaku.ijk.**
|
||||
-keep public class * extends android.view.View{
|
||||
*** get*();
|
||||
@ -249,23 +129,10 @@
|
||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||
}
|
||||
|
||||
#穿山甲
|
||||
-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.**{*;}
|
||||
|
||||
-keep class com.alibaba.sdk.android.**{*;}
|
||||
-keep class com.ut.**{*;}
|
||||
-keep class com.ta.**{*;}
|
||||
-keep class com.alibaba.sdk.android.*{*;}
|
||||
-keep class com.ut.*{*;}
|
||||
-keep class com.ta.*{*;}
|
||||
|
||||
### GDT & TEA
|
||||
-keep class com.gh.gamecenter.GdtHelper { *; }
|
||||
-keep class com.gh.gamecenter.TeaHelper { *; }
|
||||
@ -31,6 +31,8 @@ object GdtHelper {
|
||||
GDTAction.init(application, "1111012969", "9d3d9da5b0948a317c03d08f14d445dc")
|
||||
} else if (channel == "GH_729") {
|
||||
GDTAction.init(application, "1111013063", "f53dabf458a356b101d99fc4069eb7f1")
|
||||
} else if (channel == "GH_765") {
|
||||
GDTAction.init(application, "1111327925", "588d503f0990f98f9b2394fbb795c570")
|
||||
} else {
|
||||
GDTAction.init(application, "1110680399", "f5ddaafbf520d7d7385499232a408d0a")
|
||||
}
|
||||
|
||||
@ -23,8 +23,12 @@
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<!-- 允许应用程序打开系统窗口,显示其他应用程序 -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<!-- 创建快捷方式的权限 -->
|
||||
<!-- 允许应用程序快捷方式 -->
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<!-- 允许应用程序读取日历-->
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||
<!-- 允许应用程序写入日历-->
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.PACKAGE_USAGE_STATS"
|
||||
@ -603,6 +607,26 @@
|
||||
android:name=".forum.search.ForumOrUserSearchActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".energy.EnergyCenterActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".energy.EnergyHouseActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".personal.NewPersonalActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".qa.questions.draft.QuestionDraftActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".home.skip.PackageSkipActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- <!– 使用小米/华为推送弹窗功能提高推送成功率–>-->
|
||||
<!-- <activity-->
|
||||
<!-- android:name="com.gh.gamecenter.PushProxyActivity"-->
|
||||
|
||||
@ -25,8 +25,9 @@ import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import io.sentry.core.Sentry;
|
||||
import io.sentry.Sentry;
|
||||
|
||||
public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
|
||||
@ -38,19 +39,21 @@ public class AppUncaughtHandler implements UncaughtExceptionHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
if (("FinalizerWatchdogDaemon").equals(t.getName())
|
||||
&& e instanceof TimeoutException) {
|
||||
// ignore timeoutException
|
||||
// detail can be found in this didi tech blog post https://mp.weixin.qq.com/s?__biz=MzU1ODEzNjI2NA==&mid=2247487185&idx=2&sn=cf2d9e10053f625bde0f61d246f14870&source=41#wechat_redirect
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
Looper.prepare();
|
||||
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
|
||||
Looper.loop();
|
||||
}
|
||||
});
|
||||
|
||||
saveLocalLog(mContext, ex);
|
||||
Sentry.captureException(ex);
|
||||
restart(mContext);
|
||||
});
|
||||
saveLocalLog(mContext, e);
|
||||
restart(mContext);
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void restart(final Context context) {
|
||||
|
||||
@ -8,6 +8,8 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.TransactionTooLargeException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
@ -41,6 +43,7 @@ import com.gh.gamecenter.LoginActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.gh.gamecenter.eventbus.EBShowDialog;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.BaseAppCompatActivity;
|
||||
import com.lightgame.download.DownloadEntity;
|
||||
import com.lightgame.download.FileUtils;
|
||||
@ -72,6 +75,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
public final static String DOWNLOAD_HIJACK = "hijack";
|
||||
public final static String LOGIN_EXCEPTION = "loginException";
|
||||
public final static String PLUGGABLE = "plugin";
|
||||
public final static int ID_ROOT_INDICATOR = 999;
|
||||
public final int MAX_BUNDLE_SIZE = 300;
|
||||
|
||||
@NonNull
|
||||
protected String mEntrance;
|
||||
@ -116,6 +121,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (useEventBus()) EventBus.getDefault().register(this);
|
||||
if (useButterKnife()) ButterKnife.bind(this);
|
||||
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
|
||||
@ -206,6 +212,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
tv.measure(0, 0);
|
||||
tv.setAlpha(0.15F);
|
||||
tv.setId(ID_ROOT_INDICATOR);
|
||||
int height = tv.getMeasuredHeight();
|
||||
int width = tv.getMeasuredWidth();
|
||||
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
|
||||
@ -359,4 +366,30 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ActivityThread每次调用onSaveInstanceState时outState大小都会累加,最终会导致{@link TransactionTooLargeException}异常
|
||||
* 解决方案:判断每次获取到的outState大小,当达到300k时手动clear掉
|
||||
*/
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
long bundleSize = getBundleSize(outState);
|
||||
if (bundleSize > MAX_BUNDLE_SIZE * 1024) {
|
||||
outState.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private long getBundleSize(Bundle bundle) {
|
||||
long dataSize;
|
||||
Parcel obtain = Parcel.obtain();
|
||||
try {
|
||||
obtain.writeBundle(bundle);
|
||||
dataSize = obtain.dataSize();
|
||||
} finally {
|
||||
obtain.recycle();
|
||||
}
|
||||
return dataSize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,15 +9,18 @@ import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.util.*
|
||||
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.article.edit.ArticleEditActivity
|
||||
import com.gh.gamecenter.qa.editor.GameActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
|
||||
@ -26,6 +29,7 @@ import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.ArticleEntity
|
||||
import com.gh.gamecenter.qa.entity.EditorInsertEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Util_System_Keyboard
|
||||
import com.lightgame.utils.Utils
|
||||
import com.lightgame.view.CheckableImageView
|
||||
import kotterknife.bindView
|
||||
@ -33,15 +37,15 @@ 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)
|
||||
|
||||
private val mEditorFontBold by bindView<CheckableImageView>(R.id.editor_font_bold)
|
||||
private val mEditorFontItalic by bindView<CheckableImageView>(R.id.editor_font_italic)
|
||||
private val mEditorFontStrikeThrough by bindView<CheckableImageView>(R.id.editor_font_strikethrough)
|
||||
private val mEditorFontUnderline by bindView<CheckableImageView>(R.id.editor_font_underline)
|
||||
private val mEditorParagraphH1 by bindView<CheckableImageView>(R.id.editor_paragraph_h1)
|
||||
private val mEditorParagraphH2 by bindView<CheckableImageView>(R.id.editor_paragraph_h2)
|
||||
private val mEditorParagraphH3 by bindView<CheckableImageView>(R.id.editor_paragraph_h3)
|
||||
@ -50,9 +54,15 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
private val mEditorFontContainer by bindView<View>(R.id.editor_font_container)
|
||||
private val mEditorParagraphContainer by bindView<View>(R.id.editor_paragraph_container)
|
||||
private val mEditorLinkContainer by bindView<View>(R.id.editor_link_container)
|
||||
private val mEditorInsertDetail by bindView<View>(R.id.editor_insert_detail)
|
||||
private val mEditorInsertDetailContainer by bindView<View>(R.id.editor_insert_detail_container)
|
||||
val mAddLabelContainer by bindView<View>(R.id.add_label_container)
|
||||
val mAddLabelTv by bindView<TextView>(R.id.add_label_tv)
|
||||
val mLabelNumTv by bindView<TextView>(R.id.label_num_tv)
|
||||
val mLabelArrowIv by bindView<ImageView>(R.id.label_arrow)
|
||||
val mTagsContainer by bindView<FrameLayout>(R.id.tagsContainer)
|
||||
|
||||
private var mCurrentParagraphStyle = ""
|
||||
private var mIsExtendedKeyboardShow = false
|
||||
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
@ -63,29 +73,50 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
when (requestCode) {
|
||||
INSERT_ANSWER_CODE -> {
|
||||
val answer = data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
|
||||
if (answer != null) insertData = EditorInsertEntity.transform(answer)
|
||||
if (answer != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(answer)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
INSERT_ARTICLE_CODE -> {
|
||||
val article = data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
|
||||
if (article != null) insertData = EditorInsertEntity.transform(article)
|
||||
if (article != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(article)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
INSERT_GAME_CODE -> {
|
||||
val game = data?.getParcelableExtra<GameEntity>(GameEntity::class.java.simpleName)
|
||||
if (game != null) insertData = EditorInsertEntity.transform(game)
|
||||
if (game != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(game)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
VideoActivity.INSERT_VIDEO_CODE -> {
|
||||
val video = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
|
||||
if (video != null) mRichEditor.insertCustomVideo(video)
|
||||
return
|
||||
if (video != null) {
|
||||
mRichEditor.focusEditor()
|
||||
mRichEditor.insertCustomVideo(video)
|
||||
}
|
||||
}
|
||||
}
|
||||
closeExtendedKeyboard()
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
}, 100)
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
mRichEditor.scrollTo(0, 1000000)
|
||||
}, 500)
|
||||
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mAddLabelContainer.visibility = if (this is ArticleEditActivity) View.VISIBLE else View.GONE
|
||||
mRichEditor.setPadding(20, 15, 20, 15)
|
||||
// 防止个别手机在Js里无法获取粘贴内容
|
||||
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
|
||||
@ -93,12 +124,59 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener")
|
||||
mRichEditor.setInputEnabled(true)
|
||||
|
||||
mDraftBtn.text = if (this is AnswerEditActivity) {
|
||||
"回答草稿"
|
||||
} else "帖子草稿"
|
||||
mRichEditor.setOnTouchListener { _, _ ->
|
||||
if (mIsExtendedKeyboardShow) {
|
||||
closeExtendedKeyboard()
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
//是否消费事件根据mRichEditor是否含有焦点决定,mRichEditor没有焦点则不消费事件
|
||||
mRichEditor.hasFocus()
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_paragraph,
|
||||
|
||||
fun closeExtendedKeyboard() {
|
||||
mEditorInsertDetailContainer.visibility = View.GONE
|
||||
mEditorFont.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mAddLabelContainer.isSelected = false
|
||||
mIsExtendedKeyboardShow = false
|
||||
}
|
||||
|
||||
protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) {
|
||||
mEditorFont.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
fun changeAddLabel(isLabelContainerShow: Boolean) {
|
||||
if (isLabelContainerShow) {
|
||||
mLabelNumTv.visibility = View.GONE
|
||||
mLabelArrowIv.visibility = View.VISIBLE
|
||||
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font))
|
||||
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
|
||||
mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.bg_editor_insert_add_label)
|
||||
} else {
|
||||
val selectedLabel = getSelectedLabel()
|
||||
if (selectedLabel == 0) {
|
||||
mAddLabelTv.text = "添加标签"
|
||||
mLabelNumTv.visibility = View.GONE
|
||||
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.text_666666))
|
||||
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this, R.drawable.ic_add_label), null, null, null)
|
||||
} else {
|
||||
mAddLabelTv.text = "标签"
|
||||
mLabelNumTv.visibility = View.VISIBLE
|
||||
mLabelNumTv.text = selectedLabel.toString()
|
||||
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font))
|
||||
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
|
||||
}
|
||||
mLabelArrowIv.visibility = View.GONE
|
||||
mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.border_round_stroke_eee_999)
|
||||
}
|
||||
}
|
||||
|
||||
open fun getSelectedLabel(): Int = 0
|
||||
|
||||
|
||||
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.add_label_container, R.id.editor_font_underline,
|
||||
R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough,
|
||||
R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3,
|
||||
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
|
||||
@ -107,31 +185,13 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
fun onRichClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.editor_font -> {
|
||||
mEditorFont.isChecked = !mEditorFont.isChecked
|
||||
mEditorParagraph.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (!mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorFontContainer.visibility
|
||||
}
|
||||
R.id.editor_paragraph -> {
|
||||
mEditorParagraph.isChecked = !mEditorParagraph.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
mEditorLink.isChecked = false
|
||||
mEditorParagraphContainer.visibility = if (mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (!mEditorParagraph.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorParagraphContainer.visibility
|
||||
controlEditorFontContainer()
|
||||
}
|
||||
R.id.editor_link -> {
|
||||
mEditorLink.isChecked = !mEditorLink.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
mEditorParagraph.isChecked = false
|
||||
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (!mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorInsertDetail.visibility = mEditorLinkContainer.visibility
|
||||
controlEditorLinkContainer()
|
||||
}
|
||||
R.id.add_label_container -> {
|
||||
controlAddLabelContainer()
|
||||
}
|
||||
R.id.editor_font_bold -> {
|
||||
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
|
||||
@ -155,6 +215,14 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
|
||||
}
|
||||
}
|
||||
R.id.editor_font_underline -> {
|
||||
mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked
|
||||
mRichEditor.setUnderline()
|
||||
|
||||
if (mEditorFontUnderline.isChecked) {
|
||||
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线")
|
||||
}
|
||||
}
|
||||
R.id.editor_paragraph_h1 -> {
|
||||
if (mEditorParagraphH1.isChecked) {
|
||||
mRichEditor.formatBlock()
|
||||
@ -213,12 +281,93 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
startActivityForResult(GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE), INSERT_GAME_CODE)
|
||||
}
|
||||
R.id.editor_link_video -> {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
|
||||
startActivityForResult(VideoActivity.getIntent(this), VideoActivity.INSERT_VIDEO_CODE)
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(this,
|
||||
object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
|
||||
startActivityForResult(VideoActivity.getIntent(this@BaseRichEditorActivity), VideoActivity.INSERT_VIDEO_CODE)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun controlEditorFontContainer() {
|
||||
mEditorFont.isChecked = !mEditorFont.isChecked
|
||||
mEditorLink.isChecked = false
|
||||
mAddLabelContainer.isSelected = false
|
||||
val isShouldDelay = if (mEditorFont.isChecked) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this)
|
||||
true
|
||||
} else {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
false
|
||||
}
|
||||
mEditorInsertDetailContainer.postDelayed({
|
||||
mEditorInsertDetailContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorParagraphContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = View.GONE
|
||||
mTagsContainer.visibility = View.GONE
|
||||
mIsExtendedKeyboardShow = mEditorFont.isChecked
|
||||
changeAddLabel(false)
|
||||
}, if (isShouldDelay) 200 else 0L)
|
||||
}
|
||||
|
||||
private fun controlEditorLinkContainer() {
|
||||
mEditorLink.isChecked = !mEditorLink.isChecked
|
||||
mEditorFont.isChecked = false
|
||||
mAddLabelContainer.isSelected = false
|
||||
val isShouldDelay = if (mEditorLink.isChecked) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this)
|
||||
true
|
||||
} else {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
false
|
||||
}
|
||||
mEditorInsertDetailContainer.postDelayed({
|
||||
mEditorInsertDetailContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
|
||||
mEditorFontContainer.visibility = View.GONE
|
||||
mEditorParagraphContainer.visibility = View.GONE
|
||||
mTagsContainer.visibility = View.GONE
|
||||
mIsExtendedKeyboardShow = mEditorLink.isChecked
|
||||
changeAddLabel(false)
|
||||
}, if (isShouldDelay) 200 else 0L)
|
||||
}
|
||||
|
||||
fun controlAddLabelContainer() {
|
||||
mEditorLink.isChecked = false
|
||||
mEditorFont.isChecked = false
|
||||
mAddLabelContainer.isSelected = !mAddLabelContainer.isSelected
|
||||
val isShouldDelay = if (mAddLabelContainer.isSelected) {
|
||||
Util_System_Keyboard.hideSoftKeyboard(this)
|
||||
changeAddLabel(true)
|
||||
true
|
||||
} else {
|
||||
Util_System_Keyboard.showSoftKeyboard(this)
|
||||
changeAddLabel(false)
|
||||
false
|
||||
}
|
||||
mEditorInsertDetailContainer.postDelayed({
|
||||
mEditorInsertDetailContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE
|
||||
mTagsContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE
|
||||
mEditorLinkContainer.visibility = View.GONE
|
||||
mEditorFontContainer.visibility = View.GONE
|
||||
mEditorParagraphContainer.visibility = View.GONE
|
||||
mIsExtendedKeyboardShow = mAddLabelContainer.isSelected
|
||||
}, if (isShouldDelay) 200 else 0L)
|
||||
}
|
||||
|
||||
override fun handleBackPressed(): Boolean {
|
||||
if (mIsExtendedKeyboardShow) {
|
||||
closeExtendedKeyboard()
|
||||
return true
|
||||
}
|
||||
return super.handleBackPressed()
|
||||
}
|
||||
|
||||
|
||||
private inner class OnCursorChangeListener {
|
||||
@JavascriptInterface
|
||||
fun onElements(elements: String) {
|
||||
@ -237,6 +386,7 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
mEditorFontBold.isChecked = elements.contains(ELEMENT_NAME_BOLD)
|
||||
mEditorFontItalic.isChecked = elements.contains(ELEMENT_NAME_ITALIC)
|
||||
mEditorFontStrikeThrough.isChecked = elements.contains(ELEMENT_NAME_STRIKE)
|
||||
mEditorFontUnderline.isChecked = elements.contains(ELEMENT_NAME_UNDERLINE)
|
||||
mEditorParagraphH1.isChecked = elements.contains(ELEMENT_PARAGRAPH_H1)
|
||||
mEditorParagraphH2.isChecked = elements.contains(ELEMENT_PARAGRAPH_H2)
|
||||
mEditorParagraphH3.isChecked = elements.contains(ELEMENT_PARAGRAPH_H3)
|
||||
@ -264,7 +414,9 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
@JavascriptInterface
|
||||
fun onTextChange(count: Int) {
|
||||
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
|
||||
mEditorTextNumTv.text = num.toString()
|
||||
mEditorTextNumTv.post {
|
||||
mEditorTextNumTv.text = num.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,6 +426,7 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
|
||||
const val ELEMENT_NAME_BOLD = " b "
|
||||
const val ELEMENT_NAME_ITALIC = " i "
|
||||
const val ELEMENT_NAME_STRIKE = " strike "
|
||||
const val ELEMENT_NAME_UNDERLINE = " u "
|
||||
const val ELEMENT_PARAGRAPH_H1 = " h1 "
|
||||
const val ELEMENT_PARAGRAPH_H2 = " h2 "
|
||||
const val ELEMENT_PARAGRAPH_H3 = " h3 "
|
||||
|
||||
@ -7,6 +7,7 @@ import android.os.Bundle;
|
||||
import com.gh.common.notifier.Notifier;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.AppManager;
|
||||
|
||||
@ -41,7 +42,9 @@ public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallba
|
||||
//FIXME 这里应该只是部分Activity需要
|
||||
try {
|
||||
// 初始化gameMap
|
||||
DownloadManager.getInstance(activity).initGameMap();
|
||||
if (!(activity instanceof SplashScreenActivity)) {
|
||||
DownloadManager.getInstance(activity).initGameMap();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
17
app/src/main/java/com/gh/base/GHThreadFactory.kt
Normal file
17
app/src/main/java/com/gh/base/GHThreadFactory.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.gh.base
|
||||
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class GHThreadFactory(threadNamePrefix: String) : ThreadFactory {
|
||||
|
||||
private val THREAD_NAME_STEM = "${threadNamePrefix}_%d"
|
||||
private val mThreadId = AtomicInteger(0)
|
||||
|
||||
override fun newThread(r: Runnable?): Thread {
|
||||
val t = Thread(r)
|
||||
t.name = String.format(THREAD_NAME_STEM, mThreadId.getAndIncrement())
|
||||
return t
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,6 +9,8 @@ import com.gh.gamecenter.R;
|
||||
import com.lightgame.utils.RuntimeUtils;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
@ -29,7 +31,7 @@ public class BaseDialogFragment extends DialogFragment {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog dialog = new Dialog(getActivity(), R.style.DialogWindowTransparent);
|
||||
final Dialog dialog = new Dialog(getActivity(), getThemeRes());
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && !ClickUtils.isFastDoubleClick()) {
|
||||
@ -41,6 +43,10 @@ public class BaseDialogFragment extends DialogFragment {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public int getThemeRes() {
|
||||
return R.style.DialogWindowTransparent;
|
||||
}
|
||||
|
||||
public void toast(@StringRes int res) {
|
||||
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
|
||||
toast(getString(res));
|
||||
@ -71,7 +77,22 @@ public class BaseDialogFragment extends DialogFragment {
|
||||
transaction.show(fragment);
|
||||
transaction.commit();
|
||||
} else {
|
||||
super.show(manager, tag);
|
||||
try {
|
||||
Class<?> clazz = DialogFragment.class;
|
||||
Field dismissed = clazz.getDeclaredField("mDismissed");
|
||||
dismissed.setAccessible(true);
|
||||
dismissed.set(this, false);
|
||||
|
||||
Field shownByMe = clazz.getDeclaredField("mShownByMe");
|
||||
shownByMe.setAccessible(true);
|
||||
shownByMe.set(this, true);
|
||||
FragmentTransaction transaction = manager.beginTransaction();
|
||||
transaction.add(this, tag);
|
||||
transaction.commitAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
super.show(manager, tag);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.gh.common.util.ExtensionsKt;
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
/**
|
||||
@ -87,7 +88,11 @@ public class WaitingDialogFragment extends BaseDialogFragment {
|
||||
@Override
|
||||
public void dismissAllowingStateLoss() {
|
||||
mBackListener = null;
|
||||
super.dismissAllowingStateLoss();
|
||||
try {
|
||||
super.dismissAllowingStateLoss();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static class WaitingDialogData {
|
||||
|
||||
@ -2,12 +2,13 @@ package com.gh.common
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.gh.base.GHThreadFactory
|
||||
import com.gh.common.AppExecutor.ioExecutor
|
||||
import com.gh.common.AppExecutor.lightWeightIoExecutor
|
||||
import com.gh.common.AppExecutor.logExecutor
|
||||
import com.gh.common.AppExecutor.uiExecutor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* APP 线程池管理类
|
||||
@ -15,16 +16,30 @@ import java.util.concurrent.Executors
|
||||
* [ioExecutor] 是一个最大线程数固定的线程池,较为繁重的 IO 任务可以交给它
|
||||
* [uiExecutor] 是主线程的包裹,需要切换至主线程执行可以用它
|
||||
* [lightWeightIoExecutor] 是一个单线程的线程池,轻量级且需要保证同一线程的 IO 任务可以交给它
|
||||
*
|
||||
* [logExecutor] 只为上传 log 而使用的线程池
|
||||
*/
|
||||
object AppExecutor {
|
||||
|
||||
private val mCoreSize = Runtime.getRuntime().availableProcessors()
|
||||
private val mMinimumPoolSize = 6.coerceAtLeast(mCoreSize * 2)
|
||||
private val mMaximumPoolSize = 32.coerceAtLeast(mCoreSize * 8)
|
||||
|
||||
@JvmStatic
|
||||
val uiExecutor by lazy { MainThreadExecutor() }
|
||||
|
||||
@JvmStatic
|
||||
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
val lightWeightIoExecutor by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LIGHT_WEIGHT_IO_THREAD")) }
|
||||
|
||||
@JvmStatic
|
||||
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
|
||||
val logExecutor by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LOG_THREAD")) }
|
||||
|
||||
@JvmStatic
|
||||
val ioExecutor = ThreadPoolExecutor(
|
||||
mMinimumPoolSize,
|
||||
mMaximumPoolSize,
|
||||
20L, TimeUnit.SECONDS,
|
||||
LinkedBlockingQueue<Runnable>(1000),
|
||||
GHThreadFactory("GH_IO_THREAD"))
|
||||
|
||||
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@ import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.Badge
|
||||
import com.gh.gamecenter.entity.MtaEvent
|
||||
import com.gh.gamecenter.entity.NotificationUgc
|
||||
import com.gh.gamecenter.energy.EnergyCenterActivity
|
||||
import com.gh.gamecenter.energy.EnergyHouseActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -115,6 +117,7 @@ class DefaultJsApi(var context: Context) {
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
EnergyTaskHelper.postEnergyTask("bind_wechat")
|
||||
handler.complete(true)
|
||||
}
|
||||
|
||||
@ -198,6 +201,7 @@ class DefaultJsApi(var context: Context) {
|
||||
@JavascriptInterface
|
||||
fun updateRegulationTestStatus(msg: Any) {
|
||||
if (msg.toString().toLowerCase(Locale.getDefault()) == "pass") {
|
||||
EnergyTaskHelper.postEnergyTask("finish_etiquette_exam")
|
||||
SPUtils.setString(Constants.SP_REGULATION_TEST_PASS_STATUS, "pass")
|
||||
}
|
||||
}
|
||||
@ -228,6 +232,20 @@ class DefaultJsApi(var context: Context) {
|
||||
MessageShareUtils.getInstance(context).shareFromWeb(context, imageShareEvent.type)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun inviteFriends(event: Any) {
|
||||
val inviteEvent = event.toString().toObject() ?: InviteFriendsEvent()
|
||||
val context = CurrentActivityHolder.getCurrentActivity()
|
||||
if ("poster" == inviteEvent.type) {
|
||||
Base64ImageHolder.image = inviteEvent.poster.run {
|
||||
if (this.startsWith("data:image/png;base64")) this.split(",")[1] else this
|
||||
}
|
||||
MessageShareUtils.getInstance(context).shareFromWeb(context, inviteEvent.way)
|
||||
} else {
|
||||
ShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.url, inviteEvent.way)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun bindPhone(msg: Any) {
|
||||
val intent = BindPhoneActivity.getNormalIntent(context, false)
|
||||
@ -257,10 +275,31 @@ class DefaultJsApi(var context: Context) {
|
||||
runOnUiThread { DirectUtils.directToWebView(context, url.toString(), "内部网页") }
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun postWearBadgeTask(msg: Any) {
|
||||
EnergyTaskHelper.postEnergyTask("wear_badge")
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startEnergyCenter(msg: Any) {
|
||||
context.startActivity(EnergyCenterActivity.getIntent(context))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun startEnergyHouse(msg: Any) {
|
||||
context.startActivity(EnergyHouseActivity.getIntent(context))
|
||||
}
|
||||
|
||||
@Keep
|
||||
internal data class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)
|
||||
|
||||
@Keep
|
||||
internal data class ImageShareEvent(var image: String = "", var type: String = "")
|
||||
|
||||
@Keep
|
||||
internal data class InviteFriendsEvent(var type: String = "",
|
||||
var way: String = "",
|
||||
var url: String = "",
|
||||
var poster: String = "")
|
||||
|
||||
}
|
||||
|
||||
@ -344,6 +344,11 @@ object DefaultUrlHandler {
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理内部页面逻辑
|
||||
if (transformNormalScheme(context, url, entrance)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ("http" != uri.scheme && "https" != uri.scheme) return true
|
||||
return false
|
||||
}
|
||||
@ -360,7 +365,7 @@ object DefaultUrlHandler {
|
||||
uri.path?.apply {
|
||||
when {
|
||||
contains("game") -> {
|
||||
val gameId = uri.getQueryParameter("gameId") ?: ""
|
||||
val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last() ?: ""
|
||||
DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, traceEvent = null)
|
||||
}
|
||||
contains("question") -> {
|
||||
|
||||
@ -11,6 +11,7 @@ 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 {
|
||||
@ -35,7 +36,7 @@ object FixedRateJobHelper {
|
||||
// 时间校对,10分钟一次
|
||||
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
|
||||
.subscribeOn(AppExecutor.cachedScheduler)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<TimeEntity>() {
|
||||
override fun onResponse(response: TimeEntity?) {
|
||||
val serverTime = response?.time
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.gh.base.GHThreadFactory
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
@ -29,31 +30,31 @@ class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
|
||||
|
||||
init {
|
||||
activity?.application?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityStarted(a: Activity?) {
|
||||
override fun onActivityStarted(a: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(a: Activity?, outState: Bundle?) {
|
||||
override fun onActivitySaveInstanceState(a: Activity, outState: Bundle) {
|
||||
}
|
||||
|
||||
override fun onActivityStopped(a: Activity?) {
|
||||
override fun onActivityStopped(a: Activity) {
|
||||
}
|
||||
|
||||
override fun onActivityCreated(a: Activity?, savedInstanceState: Bundle?) {
|
||||
override fun onActivityCreated(a: Activity, savedInstanceState: Bundle?) {
|
||||
}
|
||||
|
||||
override fun onActivityPaused(a: Activity?) {
|
||||
override fun onActivityPaused(a: Activity) {
|
||||
if (activity == a) {
|
||||
pauseCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(a: Activity?) {
|
||||
override fun onActivityResumed(a: Activity) {
|
||||
if (activity == a) {
|
||||
resumeCounting()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(a: Activity?) {
|
||||
override fun onActivityDestroyed(a: Activity) {
|
||||
if (activity == a) {
|
||||
HaloApp.getInstance().application.unregisterActivityLifecycleCallbacks(this)
|
||||
}
|
||||
@ -114,7 +115,7 @@ class TimeElapsedHelper(val fragment: Fragment?, val activity: Activity?) {
|
||||
}
|
||||
|
||||
object TimeElapsedThreadHolder {
|
||||
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor() }
|
||||
val threadService: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("TIME_ELAPSED_THREAD")) }
|
||||
}
|
||||
|
||||
interface TimeoutCallback {
|
||||
|
||||
@ -56,7 +56,7 @@ public class Config {
|
||||
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // ghzs/ghzs666 统一
|
||||
public static final String PATCHES = "patches";
|
||||
|
||||
public static final String DEFAULT_CHANNEL = "GH_TEST";
|
||||
public static final String DEFAULT_CHANNEL = "TEST";
|
||||
|
||||
private static String SETTINGS_KEY = "settingsKey";
|
||||
|
||||
|
||||
@ -27,12 +27,14 @@ public class Constants {
|
||||
|
||||
public static final String GAME_DETAIL_COME_IN = "game_detail_come_in"; // 从游戏详情进入
|
||||
|
||||
public static final String SPLASH_AD = "splash_ad"; // 启动广告
|
||||
|
||||
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 EB_SHOW_AD = "show_ad";
|
||||
|
||||
// 用于避免历史下载掺和到普通下载状态的 ID 修饰符
|
||||
public static final String GAME_ID_DIVIDER = ":";
|
||||
// 用于避免历史下载影响到部分依赖名字作为数据更新条件的修饰符
|
||||
public static final String GAME_NAME_DECORATOR = " ";
|
||||
|
||||
@ -49,10 +51,17 @@ public class Constants {
|
||||
public static final String RAW_GAME_ICON = "raw_game_icon";
|
||||
public static final String GAME_ICON_SUBSCRIPT = "game_icon_subscript";
|
||||
|
||||
// 下载 id,一般来说跟下载文件名一样
|
||||
public static final String DOWNLOAD_ID = "download_id";
|
||||
|
||||
public static final String GHZS_GAME_ID = "5ae4462c2924bc7936438d07";
|
||||
|
||||
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
|
||||
public static final String SILENT_UPDATE = "静默更新";
|
||||
public static final String SIMULATOR_DOWNLOAD = "下载模拟器";
|
||||
public static final String SIMULATOR_GAME = "simulator_game";
|
||||
public static final String SIMULATOR = "simulator";
|
||||
public static final String GAME_NAME = "game_name";
|
||||
public static final String SIMULATOR_DOWNLOAD_START_TIME = "simulator_download_start_time";
|
||||
public static final String LAST_GHZS_UPDATE_FILE_SIZE = "last_ghzs_update_file_size";
|
||||
|
||||
@ -61,6 +70,8 @@ public class Constants {
|
||||
|
||||
public static final String SP_IMEI = "imei";
|
||||
public static final String SP_ANDROID_ID = "android_id";
|
||||
|
||||
public static final String LAST_INSTALL_GAME = "last_install_game";
|
||||
|
||||
//引导设置 “通知管理” 引导弹窗
|
||||
public static final String SP_SHOWED_NOTIFICATION_LOGIN = "show_notification_login_hint";
|
||||
@ -106,8 +117,6 @@ public class Constants {
|
||||
public static final String SP_FILTER_TAGS = "filter_tags";
|
||||
//实名认证弹窗分类数据
|
||||
public static final String SP_AUTH_DIALOG = "auth_dialog";
|
||||
//顶部视频进度保存,重启恢复
|
||||
public static final String SP_TOP_VIDEO_SCHEDULE = "top_video_schedule";
|
||||
//我的光环小红点提示
|
||||
public static final String SP_GH_RED_POINT_REMIND = "gh_red_point_remind";
|
||||
//论坛首页引导
|
||||
@ -131,10 +140,24 @@ public class Constants {
|
||||
public static final String SP_BRAND_NEW_USER = "brand_new_user";
|
||||
//包名检测是否点击不再提示
|
||||
public static final String SP_PACKAGE_CHECK = "package_check";
|
||||
//游戏详情预约引导提示
|
||||
public static final String SP_GAME_DETAIL_RESERVE_GUIDE = "game_detail_reserve_guide";
|
||||
|
||||
public static final String SP_XAPK_UNZIP_ACTIVITY = "xapk_unzip_activity";
|
||||
public static final String SP_XAPK_URL = "xapk_url";
|
||||
|
||||
//首页视频播放进度
|
||||
public static final String SP_HOME_VIDEO_PLAY_RECORD = "home_video_play_record";
|
||||
|
||||
// 用户是否曾经永久拒绝过存储权限
|
||||
public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission";
|
||||
|
||||
// 是否已经填写邀请码
|
||||
public static final String SP_HAS_COMPLETE_INVITE_CODE = "has_complete_invite_code";
|
||||
|
||||
// 签到提醒开关
|
||||
public static final String SP_SIGN_REMIND = "sign_remind";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
|
||||
@ -169,6 +192,9 @@ public class Constants {
|
||||
//版规声明
|
||||
public static final String FORUM_REGULATIONS_NEWS_ID = "5f4db9cc34d44d01b92fd670";
|
||||
|
||||
// 权限使用场景地址
|
||||
public static final String PERMISSION_SCENARIO_ADDRESS = "https://resource.ghzs.com/page/permissions/android.html";
|
||||
|
||||
//帮助内容详情
|
||||
public static final String HELP_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=";
|
||||
public static final String HELP_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=";
|
||||
@ -177,6 +203,30 @@ public class Constants {
|
||||
public static final String LOGOUT_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_help_dev/help.html?content=5f6b1f02786564003944a693";
|
||||
public static final String LOGOUT_ADDRESS = "https://static-web.ghzs.com/ghzs_help/help.html?content=5f534111b1f72909fc225672";
|
||||
|
||||
// 商品详情
|
||||
public static final String COMMODITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/product?from=ghzs";
|
||||
public static final String COMMODITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/product?from=ghzs";
|
||||
|
||||
// 光能记录
|
||||
public static final String ENERGY_RECORD_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/record?from=ghzs";
|
||||
public static final String ENERGY_RECORD_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/record?from=ghzs";
|
||||
|
||||
// 订单中心
|
||||
public static final String ORDER_CENTER_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/orders?from=ghzs";
|
||||
public static final String ORDER_CENTER_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/orders?from=ghzs";
|
||||
|
||||
// 订单详情
|
||||
public static final String ORDER_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/order-detail?from=ghzs";
|
||||
public static final String ORDER_DETAIL_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/order-detail?from=ghzs";
|
||||
|
||||
// 邀请好友
|
||||
public static final String INVITE_FRIENDS_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/inviteFriends.html#/invite";
|
||||
public static final String INVITE_FRIENDS_ADDRESS = "https://static-web.ghzs.com/ghzs_activity/inviteFriends.html#/invite";
|
||||
|
||||
// 等级页面
|
||||
public static final String LEVEL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs-userhome-dev/index.html#/level";
|
||||
public static final String LEVEL_ADDRESS = "https://static-web.ghzs.com/ghzs-userhome/index.html#/level";
|
||||
|
||||
//最少需要多少数据才能上传
|
||||
public static final int DATA_AMOUNT = 20;
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.lightgame.dialog.BaseDialogFragment
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@ -63,4 +65,29 @@ abstract class BaseTrackableDialogFragment : BaseDialogFragment() {
|
||||
|
||||
open fun trackWithBasicDeviceInfo() = false
|
||||
|
||||
|
||||
override fun show(manager: FragmentManager, tag: String?) {
|
||||
val fragment = manager.findFragmentByTag(tag)
|
||||
if (fragment != null) {
|
||||
val transaction = manager.beginTransaction()
|
||||
transaction.show(fragment)
|
||||
transaction.commit()
|
||||
} else {
|
||||
try {
|
||||
val clazz: Class<*> = DialogFragment::class.java
|
||||
val dismissed = clazz.getDeclaredField("mDismissed")
|
||||
dismissed.isAccessible = true
|
||||
dismissed[this] = false
|
||||
val shownByMe = clazz.getDeclaredField("mShownByMe")
|
||||
shownByMe.isAccessible = true
|
||||
shownByMe[this] = true
|
||||
val transaction = manager.beginTransaction()
|
||||
transaction.add(this, tag)
|
||||
transaction.commitAllowingStateLoss()
|
||||
} catch (e: Exception) {
|
||||
super.show(manager, tag)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,7 +215,7 @@ class DeviceRemindDialog(context: Context, val entity: DeviceDialogEntity, val g
|
||||
|
||||
class LooperHandle(val mAdapter: BannerAdapter) : Handler() {
|
||||
private val mWeakReference: WeakReference<BannerAdapter> = WeakReference(mAdapter)
|
||||
override fun handleMessage(msg: Message?) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
val adapter = mWeakReference.get()
|
||||
adapter?.scrollToNextPage()
|
||||
adapter?.startScroll()
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Paint
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
@ -13,7 +14,6 @@ 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
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import kotlinx.android.synthetic.main.dialog_game_off_service.*
|
||||
@ -21,7 +21,7 @@ import kotlinx.android.synthetic.main.dialog_game_off_service.*
|
||||
// 游戏关闭下载弹窗
|
||||
class GameOffServiceDialogFragment
|
||||
// : BaseTrackableDialogFragment()
|
||||
:BaseDialogFragment() {
|
||||
: BaseDialogFragment() {
|
||||
|
||||
private var mDialog: GameEntity.Dialog? = null
|
||||
|
||||
@ -56,6 +56,10 @@ class GameOffServiceDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply { setCanceledOnTouchOutside(true) }
|
||||
}
|
||||
|
||||
// override fun getEvent(): String {
|
||||
// return "游戏下载状态按钮"
|
||||
// }
|
||||
|
||||
@ -67,7 +67,7 @@ class NotificationHintDialogFragment : BaseTrackableDialogFragment() {
|
||||
activateTv.setOnClickListener {
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击立即开启")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击立即开启")
|
||||
dismiss()
|
||||
dismissAllowingStateLoss()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
|
||||
val intent = Intent()
|
||||
@ -80,7 +80,7 @@ class NotificationHintDialogFragment : BaseTrackableDialogFragment() {
|
||||
}
|
||||
|
||||
closeIv.setOnClickListener {
|
||||
dismiss()
|
||||
dismissAllowingStateLoss()
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "点击关闭")
|
||||
MtaHelper.onEventWithBasicDeviceInfo(getEvent(), getKey(), "${styleEntity.scenes}_${styleEntity.styleNo}_点击关闭")
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import android.widget.RelativeLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
@ -107,7 +108,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
private fun initListener(entity: PackageDialogEntity) {
|
||||
binding.downloadBtn.setOnClickListener {
|
||||
if (binding.noRemindAgainCb.isChecked) {
|
||||
SPUtils.setBoolean(Constants.SP_PACKAGE_CHECK, true)
|
||||
SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${packageDialogEntity?.id}", true)
|
||||
}
|
||||
val isAllPackageInstalled = isAllPackageInstalled(entity)
|
||||
if (isAllPackageInstalled) {
|
||||
@ -126,7 +127,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
callBack?.onConfirm()
|
||||
}
|
||||
if (binding.noRemindAgainCb.isChecked) {
|
||||
SPUtils.setBoolean(Constants.SP_PACKAGE_CHECK, true)
|
||||
SPUtils.setBoolean("${Constants.SP_PACKAGE_CHECK}:${packageDialogEntity?.id}", true)
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
@ -254,12 +255,14 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
val isChoose = SPUtils.getBoolean(Constants.SP_PACKAGE_CHECK, false)
|
||||
val isChoose = SPUtils.getBoolean("${Constants.SP_PACKAGE_CHECK}:${packageDialogEntity.id}", false)
|
||||
if (packageDialogEntity.level == "OPTIONAL_HINT" && isChoose) {
|
||||
callBack.onConfirm()
|
||||
return
|
||||
}
|
||||
|
||||
if (!activity.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return
|
||||
|
||||
var dialogFragment = activity.supportFragmentManager.findFragmentByTag(PackageCheckDialogFragment::class.java.simpleName) as? PackageCheckDialogFragment
|
||||
if (dialogFragment == null) {
|
||||
dialogFragment = PackageCheckDialogFragment()
|
||||
@ -280,11 +283,12 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
|
||||
|
||||
fun isAllPackageInstalled(packageDialogEntity: PackageDialogEntity): Boolean {
|
||||
var isAllInstalled = true
|
||||
val allInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
|
||||
|
||||
val isPackagesInstall: (ArrayList<String>) -> Boolean = { packages ->
|
||||
var isPackagesInstalled = false
|
||||
packages.forEach {packageName->
|
||||
val isInstalled = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0).find { it.packageName == packageName } != null
|
||||
packages.forEach { packageName ->
|
||||
val isInstalled = allInstalledPackages.find { it.packageName == packageName } != null
|
||||
if (isInstalled) {
|
||||
isPackagesInstalled = true
|
||||
return@forEach
|
||||
|
||||
@ -13,9 +13,13 @@ data class ExposureEntity(
|
||||
val gameName: String? = "",
|
||||
val gameVersion: String? = "",
|
||||
val sequence: Int? = 0,
|
||||
val outerSequence: Int? = 0,
|
||||
val platform: String? = "",
|
||||
var isMirrorData: Boolean = false,
|
||||
val downloadType: String? = "",
|
||||
val downloadCompleteType: String? = "",
|
||||
@SerializedName("display_type")
|
||||
val displayType: String? = "",
|
||||
|
||||
// 下载地址的 host 和 path
|
||||
var host: String? = "",
|
||||
|
||||
@ -10,6 +10,7 @@ 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 com.lightgame.download.DownloadEntity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@ -35,13 +36,16 @@ data class ExposureEvent(
|
||||
}
|
||||
return ExposureEvent(
|
||||
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,
|
||||
gameId = gameEntity?.id?.getFirstElementDividedByDivider(DownloadEntity.GAME_ID_DIVIDER),
|
||||
gameName = eTrace?.firstOrNull()?.payload?.gameName ?: gameEntity?.name?.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = eTrace?.firstOrNull()?.payload?.gameVersion ?:gameEntity?.gameVersion,
|
||||
isMirrorData = eTrace?.firstOrNull()?.payload?.isMirrorData ?: gameEntity?.shouldUseMirrorInfo() ?: false,
|
||||
sequence = eTrace?.firstOrNull()?.payload?.sequence ?: gameEntity?.sequence,
|
||||
outerSequence = eTrace?.firstOrNull()?.payload?.outerSequence ?: gameEntity?.outerSequence,
|
||||
platform = eTrace?.firstOrNull()?.payload?.platform ?:gameEntity?.platform,
|
||||
downloadType = gameEntity?.downloadType,
|
||||
downloadCompleteType = gameEntity?.downloadCompleteType,
|
||||
displayType = eTrace?.firstOrNull()?.payload?.displayType ?:gameEntity?.displayContent,
|
||||
// ugly
|
||||
welcomeDialogId = gameEntity?.welcomeDialogId ?: eTrace?.firstOrNull()?.payload?.welcomeDialogId,
|
||||
welcomeDialogLinkTitle = gameEntity?.welcomeDialogTitle ?: eTrace?.firstOrNull()?.payload?.welcomeDialogLinkTitle),
|
||||
|
||||
@ -11,20 +11,16 @@ import io.reactivex.functions.Consumer
|
||||
*/
|
||||
class ExposureListener(var fragment: Fragment, var exposable: IExposable) : RecyclerView.OnScrollListener() {
|
||||
|
||||
var throttleBus: ExposureThrottleBus? = null
|
||||
val throttleBus: ExposureThrottleBus by lazy { ExposureThrottleBus(Consumer { commitExposure(it) }, Consumer(Throwable::printStackTrace)) }
|
||||
var layoutManager: LinearLayoutManager? = null
|
||||
var visibleState: ExposureThrottleBus.VisibleState? = null
|
||||
|
||||
init {
|
||||
fragment.fragmentManager?.registerFragmentLifecycleCallbacks(
|
||||
object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||
throttleBus = ExposureThrottleBus(Consumer { commitExposure(it) }, Consumer(Throwable::printStackTrace))
|
||||
}
|
||||
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
visibleState?.let { commitExposure(it) }
|
||||
throttleBus?.clear()
|
||||
throttleBus.clear()
|
||||
}
|
||||
|
||||
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
|
||||
@ -44,7 +40,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
|
||||
layoutManager?.run {
|
||||
visibleState = ExposureThrottleBus.VisibleState(findFirstVisibleItemPosition(), findLastVisibleItemPosition())
|
||||
throttleBus?.postVisibleState(visibleState!!)
|
||||
throttleBus.postVisibleState(visibleState!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import com.gh.loghub.LgLOG
|
||||
import com.gh.loghub.LoghubHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
/**
|
||||
* A handful tool for committing logs to aliyun loghub.
|
||||
@ -31,17 +31,19 @@ object ExposureManager {
|
||||
|
||||
// exposureCache 用来过滤掉具有相同 id 的曝光事件,避免重复发送事件
|
||||
private val exposureSet by lazy { hashSetOf<ExposureEvent>() }
|
||||
private val exposureExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private var exposureExecutor: ExecutorService? = null
|
||||
private val exposureCache by lazy { FixedSizeLinkedHashSet<String>(300) }
|
||||
private val exposureDao by lazy { ExposureDatabase.buildDatabase(HaloApp.getInstance().application).logHubEventDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
fun init(excutor: ExecutorService) {
|
||||
loghubHelper.init(HaloApp.getInstance().application, ENDPOINT, PROJECT, LOG_STORE) { TimeUtil.currentTimeMillis() }
|
||||
|
||||
exposureExecutor.execute {
|
||||
val eventList = exposureDao.getAll()
|
||||
exposureSet.addAll(eventList)
|
||||
exposureExecutor = excutor
|
||||
exposureExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
val eventList = exposureDao.getAll()
|
||||
exposureSet.addAll(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +51,7 @@ object ExposureManager {
|
||||
* Log a single exposure event.
|
||||
*/
|
||||
fun log(event: ExposureEvent) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
try {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
// Catch `android.database.sqlite.SQLiteFullException: database or disk is full` exception.
|
||||
@ -71,7 +73,7 @@ object ExposureManager {
|
||||
* Log a collection of exposure event.
|
||||
*/
|
||||
fun log(eventList: List<ExposureEvent>) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
for (event in eventList) {
|
||||
try {
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
@ -94,7 +96,7 @@ object ExposureManager {
|
||||
* @param forced Ignore all restrictions.
|
||||
*/
|
||||
fun commitSavedExposureEvents(forced: Boolean = false) {
|
||||
exposureExecutor.execute {
|
||||
exposureExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
// TODO 初始化 loghubHelper 去掉这个 tryCatch 块
|
||||
if (exposureSet.size < STORE_SIZE && !forced || exposureSet.size == 0) return@execute
|
||||
@ -138,7 +140,7 @@ object ExposureManager {
|
||||
return log
|
||||
}
|
||||
|
||||
internal class FixedSizeLinkedHashSet<T>(var maxSize: Int) : LinkedHashSet<T>() {
|
||||
class FixedSizeLinkedHashSet<T>(var maxSize: Int) : LinkedHashSet<T>() {
|
||||
override fun add(element: T): Boolean {
|
||||
if (size == maxSize) {
|
||||
pop()
|
||||
|
||||
@ -2,35 +2,39 @@ 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
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import java.util.*
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
object ExposureUtils {
|
||||
|
||||
private val mDownloadCompleteTraceEventIdSet = ExposureManager.FixedSizeLinkedHashSet<String>(3)
|
||||
|
||||
@JvmStatic
|
||||
fun logADownloadExposureEvent(entity: GameEntity,
|
||||
platform: String?,
|
||||
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]
|
||||
gameEntity.id = if (entity.id.contains(DownloadEntity.GAME_ID_DIVIDER)) {
|
||||
entity.id.split(DownloadEntity.GAME_ID_DIVIDER).toTypedArray()[0]
|
||||
} else {
|
||||
entity.id
|
||||
}
|
||||
gameEntity.gameVersion = entity.getApk().elementAtOrNull(0)?.version ?: gameEntity.gameVersion
|
||||
gameEntity.gameVersion = entity.getApk().elementAtOrNull(0)?.version
|
||||
?: gameEntity.gameVersion
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadType = downloadType.toString()
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
event = ExposureType.DOWNLOAD)
|
||||
ExposureManager.log(exposureEvent)
|
||||
if (!TextUtils.isEmpty(entity.id)) {
|
||||
ExposureManager.log(exposureEvent)
|
||||
}
|
||||
return exposureEvent
|
||||
}
|
||||
|
||||
@ -45,6 +49,16 @@ object ExposureUtils {
|
||||
gameEntity.platform = platform
|
||||
gameEntity.downloadCompleteType = downloadType.toString()
|
||||
val traceEvent = trace?.toObject<ExposureEvent>()
|
||||
|
||||
if (TextUtils.isEmpty(entity.id)) return
|
||||
|
||||
// 避免生成 trace 相同的下载完成事件,根据日志看下载完成的同一秒有可能生成两条
|
||||
if (mDownloadCompleteTraceEventIdSet.contains(traceEvent?.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
traceEvent?.payload?.gameId?.let { mDownloadCompleteTraceEventIdSet.add(it) }
|
||||
|
||||
val exposureEvent = ExposureEvent.createEvent(gameEntity = gameEntity,
|
||||
source = traceEvent?.source ?: ArrayList(),
|
||||
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
|
||||
@ -80,6 +94,15 @@ object ExposureUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun updateExposureSequence(gameList: List<GameEntity>?) {
|
||||
gameList?.let {
|
||||
for ((index, game) in it.withIndex()) {
|
||||
game.sequence = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DownloadType {
|
||||
DOWNLOAD,
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@ import kotlinx.android.parcel.Parcelize
|
||||
@Parcelize
|
||||
data class Meta(
|
||||
val mac: String? = "",
|
||||
val imei: String? = "",
|
||||
val jnfj: String? = "",
|
||||
val model: String? = "",
|
||||
val manufacturer: String? = "",
|
||||
val android_id: String? = "",
|
||||
val dia: String? = "",
|
||||
val android_sdk: Int? = -1,
|
||||
val android_version: String? = "",
|
||||
val network: String? = "",
|
||||
|
||||
@ -11,6 +11,7 @@ import android.telephony.TelephonyManager
|
||||
import android.text.TextUtils
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.halo.assistant.HaloApp
|
||||
@ -26,13 +27,25 @@ object MetaUtil {
|
||||
private var imei: String? = null
|
||||
private var base64EncodedImei: String? = null
|
||||
private var androidId: String? = null
|
||||
private var base64EncodedAndroidId: String? = null
|
||||
|
||||
private var romName: String? = null
|
||||
private var romVersion: String? = null
|
||||
|
||||
fun refreshMeta() {
|
||||
|
||||
if (romName == null) {
|
||||
tryWithDefaultCatch {
|
||||
romName = RomIdentifier.getRom().name
|
||||
romVersion = RomIdentifier.getRom().versionName
|
||||
}
|
||||
}
|
||||
|
||||
m = Meta(mac = "",
|
||||
imei = getIMEI(),
|
||||
jnfj = getBase64EncodedIMEI(),
|
||||
model = getModel(),
|
||||
manufacturer = getManufacturer(),
|
||||
android_id = getAndroidId(),
|
||||
dia = getBase64EncodedAndroidId(),
|
||||
android_sdk = getAndroidSDK(),
|
||||
android_version = getAndroidVersion(),
|
||||
network = getNetwork(),
|
||||
@ -43,7 +56,7 @@ object MetaUtil {
|
||||
appVersion = BuildConfig.VERSION_NAME,
|
||||
userId = UserManager.getInstance().userId,
|
||||
exposureVersion = BuildConfig.EXPOSURE_VERSION,
|
||||
rom = RomIdentifier.getRom().name + "" + RomIdentifier.getRom().versionName)
|
||||
rom = romName + "" + romVersion)
|
||||
}
|
||||
|
||||
fun getMeta(): Meta {
|
||||
@ -107,6 +120,10 @@ object MetaUtil {
|
||||
|
||||
@JvmStatic
|
||||
fun getBase64EncodedIMEI(): String {
|
||||
if (imei == null) {
|
||||
getIMEI()
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(base64EncodedImei) && imei != null) {
|
||||
try {
|
||||
base64EncodedImei = android.util.Base64.encodeToString(getIMEI().trim().toByteArray(), android.util.Base64.NO_WRAP)
|
||||
@ -119,6 +136,24 @@ object MetaUtil {
|
||||
return base64EncodedImei ?: ""
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBase64EncodedAndroidId(): String {
|
||||
if (androidId == null) {
|
||||
getAndroidId()
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(base64EncodedAndroidId) && androidId != null) {
|
||||
try {
|
||||
base64EncodedAndroidId = android.util.Base64.encodeToString(getAndroidId().trim().toByteArray(), android.util.Base64.NO_WRAP)
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return base64EncodedAndroidId ?: ""
|
||||
}
|
||||
|
||||
fun getModel(): String? {
|
||||
return Build.MODEL
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.gh.common.filter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.SPUtils
|
||||
import com.gh.common.util.debounceActionWithInterval
|
||||
@ -14,6 +13,7 @@ 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 {
|
||||
|
||||
@ -67,7 +67,7 @@ object RegionSettingHelper {
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.sensitiveApi
|
||||
.getRegionSetting(HaloApp.getInstance().channel)
|
||||
.subscribeOn(AppExecutor.cachedScheduler)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<RegionSetting>() {
|
||||
override fun onSuccess(data: RegionSetting) {
|
||||
@ -76,7 +76,7 @@ object RegionSettingHelper {
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
SPUtils.getString(SP_SETTING).toObject<RegionSetting>()?.let {
|
||||
SPUtils.getString(SP_SETTING)?.toObject<RegionSetting>()?.let {
|
||||
updateSettingsInMemory(it)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 = 7, exportSchema = false)
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 8, exportSchema = false)
|
||||
@TypeConverters(CountConverter::class,
|
||||
CommunityConverter::class,
|
||||
TimeConverter::class,
|
||||
@ -23,8 +23,10 @@ import com.halo.assistant.HaloApp
|
||||
ThumbnailConverter::class,
|
||||
TagStyleListConverter::class,
|
||||
StringArrayListConverter::class,
|
||||
ListStringConverter::class,
|
||||
CommunityVideoConverter::class,
|
||||
UserConverter::class)
|
||||
UserConverter::class,
|
||||
ImageInfoConverter::class)
|
||||
|
||||
abstract class HistoryDatabase : RoomDatabase() {
|
||||
|
||||
@ -69,6 +71,14 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_7_8: Migration = object : Migration(7, 8) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("Alter TABLE ArticleEntity add images TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE ArticleEntity add imagesInfo TEXT NOT NULL DEFAULT ''")
|
||||
database.execSQL("Alter TABLE AnswerEntity add imagesInfo TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
||||
|
||||
val instance by lazy {
|
||||
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
@ -76,6 +86,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_4_5)
|
||||
.addMigrations(MIGRATION_5_6)
|
||||
.addMigrations(MIGRATION_6_7)
|
||||
.addMigrations(MIGRATION_7_8)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,48 +69,44 @@ object HistoryHelper {
|
||||
@JvmStatic
|
||||
fun insertNewsEntity(newsEntity: NewsEntity) {
|
||||
newsEntity.orderTag = System.currentTimeMillis()
|
||||
runOnIoThread { HistoryDatabase.instance.newsDao().addNews(newsEntity) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.newsDao().addNews(newsEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteNewsEntity(newsId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.newsDao().deleteNews(NewsEntity().apply { id = newsId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.newsDao().deleteNews(NewsEntity().apply { id = newsId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteGameEntity(gameId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.gameDao().deleteGame(HistoryGameEntity(id = gameId)) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gameDao().deleteGame(HistoryGameEntity(id = gameId)) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteArticleEntity(articleId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.articleDao().deleteArticle(ArticleEntity(id = articleId)) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.articleDao().deleteArticle(ArticleEntity(id = articleId)) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteAnswerEntity(answerId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.answerDao().deleteAnswer(AnswerEntity().apply { primaryKey = answerId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.answerDao().deleteAnswer(AnswerEntity().apply { primaryKey = answerId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteVideoEntity(videoId: String) {
|
||||
runOnIoThread { HistoryDatabase.instance.videoHistoryDao().deleteVideo(MyVideoEntity().apply { id = videoId }) }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.videoHistoryDao().deleteVideo(MyVideoEntity().apply { id = videoId }) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun emptyDatabase() {
|
||||
runOnIoThread { HistoryDatabase.instance.clearAllTables() }
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.clearAllTables() } }
|
||||
}
|
||||
|
||||
private fun convertArticleDetailToArticle(articleDetailEntity: ArticleDetailEntity): ArticleEntity {
|
||||
val articleEntity = ArticleEntity()
|
||||
|
||||
articleEntity.id = articleDetailEntity.id
|
||||
articleEntity.brief = articleDetailEntity.content.
|
||||
removeVideoContent().
|
||||
removeInsertedContent().
|
||||
clearHtmlFormatCompletely().
|
||||
replace(" +".toRegex()," ")
|
||||
articleEntity.brief = articleDetailEntity.content.removeVideoContent().removeInsertedContent().clearHtmlFormatCompletely().replace(" +".toRegex(), " ")
|
||||
articleEntity.count = articleDetailEntity.count
|
||||
articleDetailEntity.community.id = articleDetailEntity.communityId
|
||||
articleEntity.community = articleDetailEntity.community
|
||||
@ -118,6 +114,9 @@ object HistoryHelper {
|
||||
articleEntity.title = articleDetailEntity.title
|
||||
articleEntity.user = articleDetailEntity.user
|
||||
articleEntity.orderTag = System.currentTimeMillis()
|
||||
articleEntity.images = articleDetailEntity.images
|
||||
articleEntity.imagesInfo = articleDetailEntity.imagesInfo
|
||||
articleEntity.videos = articleDetailEntity.videos
|
||||
|
||||
return articleEntity
|
||||
}
|
||||
@ -132,12 +131,11 @@ object HistoryHelper {
|
||||
answerEntity.vote = answerDetailEntity.vote
|
||||
answerEntity.user = answerDetailEntity.user
|
||||
answerEntity.orderTag = System.currentTimeMillis()
|
||||
answerEntity.brief = answerDetailEntity.content.
|
||||
removeVideoContent().
|
||||
removeInsertedContent().
|
||||
clearHtmlFormatCompletely().
|
||||
replace(" +".toRegex(), " ")
|
||||
answerEntity.brief = answerDetailEntity.content.removeVideoContent().removeInsertedContent().clearHtmlFormatCompletely().replace(" +".toRegex(), " ")
|
||||
answerEntity.time = answerDetailEntity.time
|
||||
answerEntity.images = answerDetailEntity.images
|
||||
answerEntity.imagesInfo = answerDetailEntity.imagesInfo
|
||||
answerEntity.videos = answerDetailEntity.videos
|
||||
|
||||
return answerEntity
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import com.gh.common.exposure.meta.Meta
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.loghub.LoghubHelper
|
||||
import org.json.JSONObject
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
object LoghubUtils {
|
||||
|
||||
@ -17,22 +17,25 @@ object LoghubUtils {
|
||||
private lateinit var mApplication: Application
|
||||
|
||||
private val loghubEventSet by lazy { hashSetOf<LoghubEvent>() }
|
||||
private val loghubEventExecutor by lazy { Executors.newSingleThreadExecutor() }
|
||||
private var loghubEventExecutor: ExecutorService? = null
|
||||
private val loghubEventDao by lazy { LoghubDatabase.buildDatabase(mApplication).logHubEventDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application) {
|
||||
fun init(application: Application, executor: ExecutorService) {
|
||||
mApplication = application
|
||||
|
||||
loghubEventExecutor.execute {
|
||||
val eventList = loghubEventDao.getAll()
|
||||
loghubEventSet.addAll(eventList)
|
||||
loghubEventExecutor = executor
|
||||
loghubEventExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
val eventList = loghubEventDao.getAll()
|
||||
loghubEventSet.addAll(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun log(logJson: JSONObject, logStore: String, forcedUpload: Boolean) {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
try {
|
||||
val event = LoghubEvent(time = (System.currentTimeMillis() / 1000L).toString(), content = logJson.toString(), logStore = logStore)
|
||||
loghubEventSet.add(event)
|
||||
@ -49,7 +52,7 @@ object LoghubUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun log(jsonString: String, logStore: String, forcedUpload: Boolean) {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
try {
|
||||
val event = LoghubEvent(time = (System.currentTimeMillis() / 1000L).toString(), content = jsonString, logStore = logStore)
|
||||
loghubEventSet.add(event)
|
||||
@ -69,7 +72,7 @@ object LoghubUtils {
|
||||
}
|
||||
|
||||
fun commitSavedLoghubEvents() {
|
||||
loghubEventExecutor.execute {
|
||||
loghubEventExecutor?.execute {
|
||||
// TODO 初始化 loghubHelper 去掉这个 tryCatch 块
|
||||
tryWithDefaultCatch {
|
||||
if (loghubEventSet.isEmpty()) return@execute
|
||||
|
||||
@ -20,7 +20,12 @@ class VolumeObserver(var context: Context, handler: Handler, var callback: MuteC
|
||||
super.onChange(selfChange)
|
||||
|
||||
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
val currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
var currentVolume = 0
|
||||
|
||||
tryCatchInRelease {
|
||||
// 部分设备(Meizu 7.1.2 M6 Note)的 audioManager getStreamVolume 内部会触发空指针 :(
|
||||
currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
|
||||
val delta = previousVolume - currentVolume
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.g00fy2.versioncompare.Version
|
||||
@ -14,7 +15,6 @@ import com.gh.common.AppExecutor.uiExecutor
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.dialog.TrackableDialog
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.PackageInstaller.getDownloadPath
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.ApkEntity
|
||||
@ -23,6 +23,7 @@ import com.gh.gamecenter.entity.TrackableEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.*
|
||||
import com.lightgame.utils.Utils
|
||||
import java.lang.ref.WeakReference
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class SimulatorDownloadManager private constructor() {
|
||||
@ -34,6 +35,7 @@ class SimulatorDownloadManager private constructor() {
|
||||
private var appProgressFilling: View? = null
|
||||
private var appProgressAnchor: View? = null
|
||||
private var downloadDialog: Dialog? = null
|
||||
private var mContextRef: WeakReference<Context>? = null
|
||||
|
||||
private var simulatorLocation: SimulatorLocation? = null
|
||||
private var simulator: SimulatorEntity? = null
|
||||
@ -71,7 +73,10 @@ class SimulatorDownloadManager private constructor() {
|
||||
val startTime = downloadEntity.getMetaExtra(Constants.SIMULATOR_DOWNLOAD_START_TIME)
|
||||
LogUtils.uploadSimulatorDownload("simulator_download_complete", fileName, simulator?.id, downloadEntity.name, gameId, locationStr, downloadType, startTime)
|
||||
DownloadManager.getInstance(HaloApp.getInstance().application).cancel(downloadEntity.url, false, true)
|
||||
downloadDialog?.dismiss()
|
||||
val activity = mContextRef?.get() as? AppCompatActivity
|
||||
if (activity?.isFinishing == false) {
|
||||
downloadDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
DownloadStatus.neterror == downloadEntity.status -> {
|
||||
ToastUtils.showToast("网络不稳定,下载任务已暂停")
|
||||
@ -94,7 +99,9 @@ class SimulatorDownloadManager private constructor() {
|
||||
showDownloadDialog(context, simulator, location, "", "", null)
|
||||
}
|
||||
|
||||
fun showDownloadDialog(context: Context, simulator: SimulatorEntity?, location: SimulatorLocation, gameId: String = "", gameName: String = "", cancelCallback: (() -> Unit)? = null) {
|
||||
fun showDownloadDialog(context: Context?, simulator: SimulatorEntity?, location: SimulatorLocation, gameId: String = "", gameName: String = "", cancelCallback: (() -> Unit)? = null) {
|
||||
if (context == null) return
|
||||
mContextRef = WeakReference(context)
|
||||
this.simulatorLocation = location
|
||||
this.simulator = simulator
|
||||
this.gameId = gameId
|
||||
@ -117,7 +124,7 @@ class SimulatorDownloadManager private constructor() {
|
||||
key = if (shouldShowUpdate && isInstalled) "更新弹窗" else "下载弹窗",
|
||||
logShowEvent = true
|
||||
)
|
||||
DialogUtils.showNewAlertDialog(context, title, message, negativeText, positiveText, trackableEntity, Gravity.LEFT, {
|
||||
DialogUtils.showNewAlertDialog(context, title, message, negativeText, positiveText, trackableEntity, Gravity.LEFT, false, {
|
||||
if (shouldShowUpdate && isInstalled) {
|
||||
cancelCallback?.invoke()
|
||||
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")
|
||||
@ -153,7 +160,10 @@ class SimulatorDownloadManager private constructor() {
|
||||
view.findViewById<View>(R.id.app_tv_cancel).setOnClickListener {
|
||||
DownloadManager.getInstance(context).pause(simulator?.apk?.url)
|
||||
MtaHelper.onEvent("模拟器下载", "下载中弹窗", "点击关闭")
|
||||
downloadDialog?.dismiss()
|
||||
val activity = mContextRef?.get() as? AppCompatActivity
|
||||
if (activity?.isFinishing == false) {
|
||||
downloadDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
downloadDialog?.setOnDismissListener {
|
||||
@ -184,6 +194,7 @@ class SimulatorDownloadManager private constructor() {
|
||||
DownloadManager.getInstance(context).pauseAll()
|
||||
|
||||
val entity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.url)
|
||||
HaloApp.put(simulator.name, simulator)
|
||||
if (entity != null) {
|
||||
when (entity.status) {
|
||||
DownloadStatus.pause, DownloadStatus.subscribe,
|
||||
@ -203,16 +214,18 @@ class SimulatorDownloadManager private constructor() {
|
||||
|
||||
private fun createDownload(context: Context, apkEntity: ApkEntity, simulator: SimulatorEntity) {
|
||||
DownloadManager.getInstance(context).addObserver(dataWatcher)
|
||||
val downloadId = PackageInstaller.createDownloadId(simulator.name)
|
||||
val downloadEntity = DownloadEntity()
|
||||
downloadEntity.url = apkEntity.url
|
||||
downloadEntity.name = simulator.name
|
||||
downloadEntity.path = getDownloadPath(simulator.name, apkEntity.format)
|
||||
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, apkEntity.format)
|
||||
downloadEntity.platform = apkEntity.getPlatform()
|
||||
downloadEntity.packageName = apkEntity.packageName
|
||||
downloadEntity.versionName = apkEntity.version
|
||||
|
||||
downloadEntity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, Constants.SIMULATOR_DOWNLOAD)
|
||||
downloadEntity.addMetaExtra(Constants.SIMULATOR_DOWNLOAD_START_TIME, (System.currentTimeMillis() / 1000).toString())
|
||||
downloadEntity.addMetaExtra(Constants.DOWNLOAD_ID, downloadId)
|
||||
uiExecutor.executeWithDelay(Runnable { DownloadManager.getInstance(context).add(downloadEntity) }, 200)
|
||||
|
||||
val locationStr = if (simulatorLocation == SimulatorLocation.LAUNCH) "${simulatorLocation?.value}《${gameName}》" else simulatorLocation?.value
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.common.simulator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import com.g00fy2.versioncompare.Version
|
||||
@ -18,9 +19,11 @@ import com.lightgame.download.DownloadDao
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.FileUtils
|
||||
import com.lightgame.utils.AppManager
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
|
||||
|
||||
@ -33,7 +36,7 @@ object SimulatorGameManager {
|
||||
|
||||
@JvmStatic
|
||||
fun deleteLocalGames(names: List<String>) {
|
||||
val downloadEntityList = DownloadDao.getInstance(HaloApp.getInstance().application).all
|
||||
val downloadEntityList = DownloadManager.getInstance(HaloApp.getInstance().application).allDownloadEntity
|
||||
names.forEach { name ->
|
||||
val downloadEntity = downloadEntityList.find { it.name == name }
|
||||
if (downloadEntity != null) {
|
||||
@ -48,7 +51,7 @@ object SimulatorGameManager {
|
||||
|
||||
@JvmStatic
|
||||
fun deleteLocalGame(name: String) {
|
||||
val downloadEntityList = DownloadDao.getInstance(HaloApp.getInstance().application).all
|
||||
val downloadEntityList = DownloadManager.getInstance(HaloApp.getInstance().application).allDownloadEntity
|
||||
val downloadEntity = downloadEntityList.find { it.name == name }
|
||||
if (downloadEntity != null) {
|
||||
val file = File(downloadEntity.path)
|
||||
@ -90,31 +93,62 @@ object SimulatorGameManager {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun jumpToSimulator(downloadEntity: DownloadEntity, gameEntity: GameEntity) {
|
||||
val intent = Intent()
|
||||
intent.data = Uri.fromFile(File(downloadEntity.path))
|
||||
if (gameEntity.simulatorType == "FBA") {
|
||||
val apkEntity = gameEntity.getApk()[0]
|
||||
intent.putExtra("rom_name", apkEntity.packageName)
|
||||
}
|
||||
intent.putExtra("default_path", downloadEntity.path.substring(0, downloadEntity.path.lastIndexOf('/')))
|
||||
intent.putExtra("game_type", gameEntity.simulatorType)
|
||||
intent.putExtra("title", downloadEntity.name)
|
||||
intent.putExtra("icon", gameEntity.icon ?: "")
|
||||
intent.putExtra("meta", LogUtils.getMetaObject().toString())
|
||||
intent.putExtra("simulatorId", gameEntity.simulator?.id)
|
||||
intent.putExtra("simulatorName", gameEntity.simulator?.name)
|
||||
intent.putExtra("gameId", gameEntity.id)
|
||||
Single.just(gameEntity.icon ?: "")
|
||||
.flatMap {
|
||||
getBitmapFormCache(it)
|
||||
}.map {
|
||||
BitmapUtils.compressBitmap(it, 100)
|
||||
}.map {
|
||||
val baos = ByteArrayOutputStream()
|
||||
it.compress(Bitmap.CompressFormat.WEBP, 100, baos)
|
||||
baos.toByteArray()
|
||||
}
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
val intent = Intent()
|
||||
intent.data = Uri.fromFile(File(downloadEntity.path))
|
||||
if (gameEntity.simulatorType == "FBA") {
|
||||
val apkEntity = gameEntity.getApk()[0]
|
||||
intent.putExtra("rom_name", apkEntity.packageName)
|
||||
}
|
||||
intent.putExtra("default_path", downloadEntity.path.substring(0, downloadEntity.path.lastIndexOf('/')))
|
||||
intent.putExtra("game_type", gameEntity.simulatorType)
|
||||
intent.putExtra("title", downloadEntity.name)
|
||||
intent.putExtra("icon", gameEntity.icon ?: "")
|
||||
intent.putExtra("iconStream", it)
|
||||
intent.putExtra("meta", LogUtils.getMetaObject().toString())
|
||||
intent.putExtra("simulatorId", gameEntity.simulator?.id)
|
||||
intent.putExtra("simulatorName", gameEntity.simulator?.name)
|
||||
intent.putExtra("gameId", gameEntity.id)
|
||||
|
||||
val destActivity = "com.gh.emu.RequestPermissionActivity"
|
||||
intent.setClassName(gameEntity.simulator?.apk?.packageName ?: "", destActivity)
|
||||
try {
|
||||
AppManager.getInstance().recentActiveActivity.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
ToastUtils.showToast("模拟器安装错误")
|
||||
}
|
||||
val destActivity = "com.gh.emu.RequestPermissionActivity"
|
||||
intent.setClassName(gameEntity.simulator?.apk?.packageName ?: "", destActivity)
|
||||
try {
|
||||
AppManager.getInstance().recentActiveActivity?.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
ToastUtils.showToast("模拟器安装错误")
|
||||
}
|
||||
|
||||
recordPlaySimulatorGames(gameEntity.id)
|
||||
recordPlaySimulatorGames(gameEntity.id)
|
||||
}, {
|
||||
ToastUtils.showToast("跳转失败")
|
||||
})
|
||||
}
|
||||
|
||||
private fun getBitmapFormCache(url: String): Single<Bitmap> {
|
||||
return Single.create {
|
||||
ImageUtils.getBitmap(url, object : BiCallback<Bitmap, Boolean> {
|
||||
override fun onFirst(first: Bitmap) {
|
||||
it.onSuccess(first)
|
||||
}
|
||||
|
||||
override fun onSecond(second: Boolean) {
|
||||
it.onError(Throwable("获取bitmap失败"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.gamecenter.entity.SettingsEntity
|
||||
import com.gh.gamecenter.entity.StartupAdEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
object AdHelper {
|
||||
|
||||
@ -12,6 +19,28 @@ object AdHelper {
|
||||
const val LOCATION_DISCOVER = "discover"
|
||||
const val LOCATION_SIMULATOR_GAME = "simulator_game"
|
||||
|
||||
@JvmField
|
||||
var startupAd = MutableLiveData<StartupAdEntity>()
|
||||
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun getStartUpAd() {
|
||||
if (!NetworkUtils.isNetworkConnected(HaloApp.getInstance())) {
|
||||
startupAd.postValue(null)
|
||||
return
|
||||
}
|
||||
|
||||
RetrofitManager.getInstance(HaloApp.getInstance())
|
||||
.api
|
||||
.getSplashAd(HaloApp.getInstance().channel)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<StartupAdEntity>() {
|
||||
override fun onSuccess(data: StartupAdEntity) {
|
||||
startupAd.postValue(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getAd(location: String): SettingsEntity.AD? {
|
||||
val adList = Config.getSettings()?.adList ?: return null
|
||||
|
||||
|
||||
@ -333,14 +333,19 @@ public class BitmapUtils {
|
||||
}
|
||||
|
||||
|
||||
public static Bitmap compressBitmap(Bitmap bitmap) {
|
||||
/**
|
||||
* 压缩图片
|
||||
*
|
||||
* @param bitmap
|
||||
* @param compressSize 压缩大小(单位k)
|
||||
*/
|
||||
public static Bitmap compressBitmap(Bitmap bitmap, int compressSize) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
Matrix matrix = new Matrix();
|
||||
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
result.compress(Bitmap.CompressFormat.WEBP, 100, bos);
|
||||
|
||||
while (bos.toByteArray().length > 400 * 1024) {
|
||||
System.out.println(bos.toByteArray().length);
|
||||
while (bos.toByteArray().length > compressSize * 1024) {
|
||||
matrix.setScale(0.9f, 0.9f);
|
||||
result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
|
||||
bos.reset();
|
||||
|
||||
148
app/src/main/java/com/gh/common/util/CalendarHelper.kt
Normal file
148
app/src/main/java/com/gh/common/util/CalendarHelper.kt
Normal file
@ -0,0 +1,148 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.CalendarContract.*
|
||||
import android.text.TextUtils
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* 签到日历提醒辅助类
|
||||
*/
|
||||
object CalendarHelper {
|
||||
|
||||
private val CALENDAR_URL = Calendars.CONTENT_URI
|
||||
private val CALENDAR_EVENT_URL = Events.CONTENT_URI
|
||||
private val CALENDAR_REMINDER_URL = Reminders.CONTENT_URI
|
||||
private const val CALENDARS_NAME = "guanghuan"
|
||||
private const val CALENDARS_ACCOUNT_TYPE = BuildConfig.APPLICATION_ID
|
||||
private const val CALENDARS_DISPLAY_NAME = "光环助手"
|
||||
|
||||
|
||||
private fun checkAndAddCalendarAccount(context: Context): Int {
|
||||
val oldId: Int = checkCalendarAccount(context)
|
||||
return if (oldId >= 0) {
|
||||
oldId
|
||||
} else {
|
||||
val addId: Long = addCalendarAccount(context)
|
||||
if (addId >= 0) {
|
||||
checkCalendarAccount(context)
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCalendarAccount(context: Context): Int {
|
||||
val userCursor: Cursor? = context.contentResolver.query(CALENDAR_URL,
|
||||
null, null, null, null)
|
||||
return userCursor?.use {
|
||||
val count: Int = it.count
|
||||
if (count > 0) { // 存在现有账户,取第一个账户的id返回
|
||||
it.moveToFirst()
|
||||
it.getInt(userCursor.getColumnIndex(Calendars._ID))
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
} ?: -1
|
||||
}
|
||||
|
||||
private fun addCalendarAccount(context: Context): Long {
|
||||
val value = ContentValues().apply {
|
||||
put(Calendars.NAME, CALENDARS_NAME)
|
||||
put(Calendars.ACCOUNT_NAME, CALENDARS_DISPLAY_NAME)
|
||||
put(Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
|
||||
put(Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME)
|
||||
put(Calendars.VISIBLE, 1)
|
||||
put(Calendars.CALENDAR_COLOR, R.color.theme.toColor())
|
||||
put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER)
|
||||
put(Calendars.SYNC_EVENTS, 1)
|
||||
put(Calendars.CALENDAR_TIME_ZONE, TimeZone.getDefault().id)
|
||||
put(Calendars.OWNER_ACCOUNT, CALENDARS_NAME)
|
||||
put(Calendars.CAN_ORGANIZER_RESPOND, 0)
|
||||
}
|
||||
|
||||
var calendarUri: Uri = CALENDAR_URL
|
||||
calendarUri = calendarUri.buildUpon()
|
||||
.appendQueryParameter(CALLER_IS_SYNCADAPTER, "true")
|
||||
.appendQueryParameter(Calendars.ACCOUNT_NAME, CALENDARS_NAME)
|
||||
.appendQueryParameter(Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
|
||||
.build()
|
||||
val result: Uri? = context.contentResolver.insert(calendarUri, value)
|
||||
return if (result == null) -1 else ContentUris.parseId(result)
|
||||
}
|
||||
|
||||
fun insertCalendarEvent(context: Context,
|
||||
title: String,
|
||||
description: String,
|
||||
beginTimeMillis: Long,
|
||||
endTimeMillis: Long,
|
||||
rRule: String?): Boolean {
|
||||
val calendarId = checkAndAddCalendarAccount(context) // 获取日历账户的id
|
||||
if (calendarId < 0) { // 获取账户id失败直接返回,添加日历事件失败
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
/** 插入日程 */
|
||||
val eventValues = ContentValues().apply {
|
||||
if (rRule != null) put(Events.RRULE, rRule)
|
||||
put(Events.DTSTART, beginTimeMillis)
|
||||
put(Events.DTEND, endTimeMillis)
|
||||
put(Events.TITLE, title)
|
||||
put(Events.DESCRIPTION, description)
|
||||
put(Events.CALENDAR_ID, calendarId)
|
||||
put(Events.EVENT_TIMEZONE, TimeZone.getDefault().id)
|
||||
}
|
||||
val eUri: Uri? = context.contentResolver.insert(CALENDAR_EVENT_URL, eventValues)
|
||||
val eventId = eUri?.let { ContentUris.parseId(it) }
|
||||
if (eventId == 0L) { // 插入失败
|
||||
return false
|
||||
}
|
||||
/** 插入提醒 - 依赖插入日程成功 */
|
||||
val reminderValues = ContentValues()
|
||||
// uri.getLastPathSegment();
|
||||
reminderValues.put(Reminders.EVENT_ID, eventId)
|
||||
reminderValues.put(Reminders.MINUTES, 0) // 准时提醒
|
||||
reminderValues.put(Reminders.METHOD, Reminders.METHOD_ALERT)
|
||||
val rUri: Uri? = context.contentResolver.insert(CALENDAR_REMINDER_URL, reminderValues)
|
||||
if (rUri == null || ContentUris.parseId(rUri) == 0L) {
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun deleteCalendarEvent(context: Context,
|
||||
title: String): Boolean {
|
||||
val eventCursor = context.contentResolver.query(CALENDAR_EVENT_URL,
|
||||
null, null, null, null)
|
||||
eventCursor?.use {
|
||||
if (it.count > 0) {
|
||||
// 遍历所有事件,找到title跟需要查询的title一样的项
|
||||
it.moveToFirst()
|
||||
while (!it.isAfterLast) {
|
||||
val eventTitle = eventCursor.getString(eventCursor.getColumnIndex("title"))
|
||||
if (!TextUtils.isEmpty(title) && title == eventTitle) {
|
||||
val id = eventCursor.getInt(eventCursor
|
||||
.getColumnIndex(Calendars._ID)) // 取得id
|
||||
val deleteUri = ContentUris.withAppendedId(CALENDAR_EVENT_URL, id.toLong())
|
||||
val rows = context.contentResolver.delete(deleteUri, null, null)
|
||||
return rows != -1
|
||||
}
|
||||
it.moveToNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -334,15 +334,16 @@ public class CommentUtils {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void voteVideoComment(final Context context,
|
||||
String answerId,
|
||||
String articleId,
|
||||
String articleCommunityId,
|
||||
String videoId,
|
||||
final CommentEntity commentEntity,
|
||||
final TextView commentLikeCountTv,
|
||||
final ImageView commentLikeIv,
|
||||
final OnVoteListener listener) {
|
||||
String answerId,
|
||||
String articleId,
|
||||
String articleCommunityId,
|
||||
String videoId,
|
||||
final CommentEntity commentEntity,
|
||||
final TextView commentLikeCountTv,
|
||||
final ImageView commentLikeIv,
|
||||
final OnVoteListener listener) {
|
||||
|
||||
String entrance = "视频流-评论-点赞";
|
||||
CheckLoginUtils.checkLogin(context, entrance, () -> {
|
||||
@ -389,10 +390,10 @@ public class CommentUtils {
|
||||
}
|
||||
|
||||
public static void unVoteVideoComment(final Context context,
|
||||
String videoId,
|
||||
final CommentEntity commentEntity,
|
||||
final TextView commentLikeCountTv,
|
||||
final ImageView commentLikeIv) {
|
||||
String videoId,
|
||||
final CommentEntity commentEntity,
|
||||
final TextView commentLikeCountTv,
|
||||
final ImageView commentLikeIv) {
|
||||
String entrance = "视频流-评论-取消点赞";
|
||||
CheckLoginUtils.checkLogin(context, entrance, () -> {
|
||||
RetrofitManager.getInstance(context).getApi()
|
||||
@ -434,10 +435,10 @@ public class CommentUtils {
|
||||
holder.commentLikeIv.setImageResource(R.drawable.comment_vote_unselect);
|
||||
|
||||
if (userDataEntity == null || !userDataEntity.isCommentOwner()) {
|
||||
holder.replyLine.setVisibility(View.VISIBLE);
|
||||
if (holder.replyLine != null) holder.replyLine.setVisibility(View.VISIBLE);
|
||||
holder.commentReply.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.replyLine.setVisibility(View.GONE);
|
||||
if (holder.replyLine != null) holder.replyLine.setVisibility(View.GONE);
|
||||
holder.commentReply.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ public class DataCollectionUtils {
|
||||
public static void uploadDownload(Context context, DownloadEntity downloadEntity, String status) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", StringsKt.removeSuffix(downloadEntity.getName(), Constants.GAME_NAME_DECORATOR));
|
||||
map.put("game_id", downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER));
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
if (downloadEntity.isPluggable()) {
|
||||
map.put("method", "插件化");
|
||||
map.put("btn_status", "插件化");
|
||||
|
||||
@ -52,8 +52,8 @@ public class DataLogUtils {
|
||||
String user = Installation.getUUID(context);
|
||||
String channel = HaloApp.getInstance().getChannel();
|
||||
map.put("version", version);
|
||||
map.put("user", user);
|
||||
map.put("device_id", MetaUtil.getIMEI());
|
||||
map.put("resu", user);
|
||||
map.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
map.put("channel", channel);
|
||||
|
||||
Map<String, String> params = new HashMap<>();
|
||||
|
||||
@ -7,6 +7,7 @@ import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.common.AppExecutor;
|
||||
import com.gh.common.constant.Constants;
|
||||
import com.gh.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
@ -19,9 +20,8 @@ import com.lightgame.utils.Utils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.android.core.SentryAndroid;
|
||||
import io.sentry.core.Sentry;
|
||||
import io.sentry.core.protocol.User;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/6/15.
|
||||
@ -40,34 +40,9 @@ public class DataUtils {
|
||||
if (CommonDebug.IS_DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
SentryAndroid.init(context, options -> {
|
||||
options.setDebug(BuildConfig.DEBUG);
|
||||
options.setEnableSessionTracking(true);
|
||||
options.setEnvironment(BuildConfig.FLAVOR);
|
||||
options.setDsn("https://6b1caf0d17c1408e8680f3f73ff80bd0@sentry.ghzs.com/22");
|
||||
|
||||
options.setBeforeSend((event, hint) -> {
|
||||
if (BuildConfig.DEBUG) {
|
||||
return null;
|
||||
} else {
|
||||
return event;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Sentry.configureScope(scope -> {
|
||||
User user = new User();
|
||||
user.setId(HaloApp.getInstance().getGid());
|
||||
scope.setUser(user);
|
||||
|
||||
if (BuildConfig.BUILD_TIME != 0L) {
|
||||
scope.setTag("alias", "内测版" + BuildConfig.VERSION_NAME + "_" + BuildConfig.BUILD_TIME);
|
||||
} else {
|
||||
scope.setTag("alias", "正式版" + BuildConfig.VERSION_NAME);
|
||||
scope.setTag("channel", channel);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化 Sentry 约占用 90ms,这里切换到子线程初始化
|
||||
AppExecutor.getIoExecutor().execute(() -> initSentry(context, channel));
|
||||
|
||||
//TalkingData
|
||||
// try {
|
||||
@ -137,6 +112,41 @@ public class DataUtils {
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
private static void initSentry(Context context, String channel) {
|
||||
SentryAndroid.init(context, options -> {
|
||||
// Sentry 疯狂报 ANR (很大一部分还是莫名奇妙的 ANR)严重影响到其它闪退日志的收集
|
||||
// 这里将它局限到只有官网渠道的包才统计 ANR
|
||||
if ("GH_206".equals(channel)) {
|
||||
options.setAnrEnabled(true);
|
||||
options.setAnrTimeoutIntervalMillis(6000);
|
||||
} else {
|
||||
options.setAnrEnabled(false);
|
||||
}
|
||||
|
||||
options.setDebug(BuildConfig.DEBUG);
|
||||
options.setEnableSessionTracking(true);
|
||||
options.setEnvironment(BuildConfig.FLAVOR);
|
||||
options.setDsn("https://6b1caf0d17c1408e8680f3f73ff80bd0@sentry.ghzs.com/22");
|
||||
|
||||
options.setBeforeSend((event, hint) -> {
|
||||
if (BuildConfig.DEBUG) {
|
||||
return null;
|
||||
} else {
|
||||
return event;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Sentry.configureScope(scope -> {
|
||||
if (BuildConfig.BUILD_TIME != 0L) {
|
||||
scope.setTag("alias", "内测版" + BuildConfig.VERSION_NAME);
|
||||
} else {
|
||||
scope.setTag("alias", "正式版" + BuildConfig.VERSION_NAME);
|
||||
scope.setTag("channel", channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// MTA ->【次数统计】Key-Value参数的事件
|
||||
public static void onMtaEvent(Context context, String eventId, String... kv) {
|
||||
@ -229,7 +239,7 @@ public class DataUtils {
|
||||
|
||||
kv.put("版本", platform);
|
||||
kv.put("用户机型", Build.MODEL);
|
||||
kv.put("设备IMEI", MetaUtil.getIMEI());
|
||||
kv.put("设备JNFJ", MetaUtil.getBase64EncodedIMEI());
|
||||
kv.put("网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().getApplication()));
|
||||
kv.put("光环助手版本", BuildConfig.VERSION_NAME);
|
||||
kv.put("位置", entrance);
|
||||
|
||||
@ -41,11 +41,11 @@ public class DeviceUtils {
|
||||
context = context.getApplicationContext();
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("os", "Android");
|
||||
object.put("imei", MetaUtil.getIMEI());
|
||||
object.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
object.put("mac", getMac(context));
|
||||
object.put("model", MODEL);
|
||||
object.put("manufacturer", MANUFACTURER);
|
||||
object.put("android_id", MetaUtil.getAndroidId());
|
||||
object.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
object.put("android_sdk", Build.VERSION.SDK_INT);
|
||||
object.put("android_version", android.os.Build.VERSION.RELEASE);
|
||||
object.put("ip", "");
|
||||
@ -57,8 +57,8 @@ public class DeviceUtils {
|
||||
public static JSONObject getUserDevice(Context context) { // 判断新老用户device数据
|
||||
JSONObject object = new JSONObject();
|
||||
try {
|
||||
object.put("IMEI", MetaUtil.getIMEI());
|
||||
object.put("ANDROID_ID", MetaUtil.getAndroidId());
|
||||
object.put("JNFJ", MetaUtil.getBase64EncodedIMEI());
|
||||
object.put("DIA", MetaUtil.getBase64EncodedAndroidId());
|
||||
object.put("MAC", getMac(context));
|
||||
// object.put("MTA_ID", StatConfig.getMid(context));
|
||||
object.put("MANUFACTURER", MANUFACTURER);
|
||||
@ -258,7 +258,7 @@ public class DeviceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static long getTotalRamSizeOfDevice(Context context) {
|
||||
public static long getTotalRamSizeOfDevice(Context context) {
|
||||
ActivityManager actManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
|
||||
if (actManager != null) {
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.common.util
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.TextView
|
||||
import com.gh.common.dialog.TrackableDialog
|
||||
@ -73,6 +74,65 @@ object DialogHelper {
|
||||
return dialog
|
||||
}
|
||||
|
||||
fun showRoundedCornerDialog(
|
||||
context: Context,
|
||||
title: String,
|
||||
content: CharSequence,
|
||||
hint: String? = "",
|
||||
confirmText: String,
|
||||
cancelText: String,
|
||||
confirmClickCallback: (() -> Unit)? = null,
|
||||
cancelClickCallback: (() -> Unit)? = null,
|
||||
hintClickCallback: (() -> Unit)? = null,
|
||||
trackMtaEvent: Boolean = false,
|
||||
mtaEvent: String = "",
|
||||
mtaKey: String = ""): Dialog {
|
||||
val solidContext = checkDialogContext(context)
|
||||
|
||||
val dialog = if (trackMtaEvent) {
|
||||
TrackableDialog(solidContext, R.style.GhAlertDialog, mtaEvent, mtaKey)
|
||||
} else {
|
||||
Dialog(solidContext, R.style.GhAlertDialog)
|
||||
}
|
||||
|
||||
val contentView = LayoutInflater.from(solidContext).inflate(R.layout.dialog_alert_default, null)
|
||||
val contentTv = contentView.findViewById<TextView>(R.id.contentTv)
|
||||
val titleTv = contentView.findViewById<TextView>(R.id.titleTv)
|
||||
val hintTv = contentView.findViewById<TextView>(R.id.hintTv)
|
||||
val cancelTv = contentView.findViewById<TextView>(R.id.cancelTv)
|
||||
val confirmTv = contentView.findViewById<TextView>(R.id.confirmTv)
|
||||
contentTv.text = content
|
||||
titleTv.text = title
|
||||
if (!hint.isNullOrEmpty()) {
|
||||
hintTv.visibility = View.VISIBLE
|
||||
hintTv.text = hint
|
||||
hintTv.setOnClickListener {
|
||||
hintClickCallback?.invoke()
|
||||
}
|
||||
}
|
||||
cancelTv.text = cancelText
|
||||
confirmTv.text = confirmText
|
||||
|
||||
cancelTv.setOnClickListener {
|
||||
if (trackMtaEvent) MtaHelper.onEvent(mtaEvent, mtaKey, "点击$cancelText")
|
||||
|
||||
cancelClickCallback?.invoke()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
confirmTv.setOnClickListener {
|
||||
if (trackMtaEvent) MtaHelper.onEvent(mtaEvent, mtaKey, "点击$confirmText")
|
||||
|
||||
confirmClickCallback?.invoke()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
dialog.setContentView(contentView)
|
||||
dialog.show()
|
||||
return dialog
|
||||
}
|
||||
|
||||
/**
|
||||
* For legacy java invocation
|
||||
*/
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
@ -14,12 +15,15 @@ import android.os.CountDownTimer;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
@ -33,6 +37,7 @@ import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -41,12 +46,15 @@ import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.common.AppExecutor;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.dialog.TrackableDialog;
|
||||
import com.gh.common.view.CustomLinkMovementMethod;
|
||||
import com.gh.common.view.DrawableView;
|
||||
import com.gh.common.view.FixLinearLayoutManager;
|
||||
import com.gh.common.view.LimitHeightLinearLayout;
|
||||
@ -56,6 +64,8 @@ import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.SuggestionActivity;
|
||||
import com.gh.gamecenter.adapter.viewholder.PrivacyPolicyItemViewHolder;
|
||||
import com.gh.gamecenter.databinding.DialogOverseaConfirmationBinding;
|
||||
import com.gh.gamecenter.databinding.DialogPackageParseErrorBinding;
|
||||
import com.gh.gamecenter.databinding.DialogReportReasonBinding;
|
||||
import com.gh.gamecenter.databinding.ImprintContentItemBinding;
|
||||
import com.gh.gamecenter.databinding.PrivacyItemBinding;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
@ -64,6 +74,7 @@ import com.gh.gamecenter.entity.BadgeEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.PrivacyPolicyEntity;
|
||||
import com.gh.gamecenter.entity.SettingsEntity;
|
||||
import com.gh.gamecenter.entity.SimpleGameEntity;
|
||||
import com.gh.gamecenter.entity.TrackableEntity;
|
||||
import com.gh.gamecenter.suggest.SuggestType;
|
||||
import com.halo.assistant.HaloApp;
|
||||
@ -350,7 +361,7 @@ public class DialogUtils {
|
||||
* @param cmListener 确认按钮监听
|
||||
*/
|
||||
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
|
||||
, String negative, String positive, TrackableEntity trackableEntity, int gravity, final CancelListener clListener, final ConfirmListener cmListener) {
|
||||
, String negative, String positive, TrackableEntity trackableEntity, int gravity, boolean shouldShowCloseBtn, final CancelListener clListener, final ConfirmListener cmListener) {
|
||||
context = checkDialogContext(context);
|
||||
final Dialog dialog;
|
||||
if (trackableEntity != null) {
|
||||
@ -373,6 +384,7 @@ public class DialogUtils {
|
||||
TextView cancelBtn = contentView.findViewById(R.id.cancel);
|
||||
TextView confirmBtn = contentView.findViewById(R.id.confirm);
|
||||
View middleLine = contentView.findViewById(R.id.middle_line);
|
||||
View closeIv = contentView.findViewById(R.id.closeIv);
|
||||
titleTv.setGravity(gravity);
|
||||
contentTv.setGravity(gravity);
|
||||
|
||||
@ -389,6 +401,8 @@ public class DialogUtils {
|
||||
confirmBtn.setVisibility(View.GONE);
|
||||
middleLine.setVisibility(View.GONE);
|
||||
}
|
||||
closeIv.setVisibility(shouldShowCloseBtn ? View.VISIBLE : View.GONE);
|
||||
closeIv.setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
cancelBtn.setOnClickListener(v -> {
|
||||
if (clListener != null) clListener.onCancel();
|
||||
@ -413,7 +427,12 @@ public class DialogUtils {
|
||||
|
||||
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
|
||||
, String negative, String positive, final CancelListener clListener, final ConfirmListener cmListener) {
|
||||
return showNewAlertDialog(context, title, message, negative, positive, null, Gravity.LEFT, clListener, cmListener);
|
||||
return showNewAlertDialog(context, title, message, negative, positive, null, Gravity.LEFT, false, clListener, cmListener);
|
||||
}
|
||||
|
||||
public static Dialog showNewAlertDialog(Context context, String title, CharSequence message
|
||||
, String negative, String positive, int gravity, boolean shouldShowCloseBtn, final CancelListener clListener, final ConfirmListener cmListener) {
|
||||
return showNewAlertDialog(context, title, message, negative, positive, null, gravity, shouldShowCloseBtn, clListener, cmListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -970,7 +989,7 @@ public class DialogUtils {
|
||||
|
||||
public static void showPrivacyPolicyDialog(Context context,
|
||||
@NonNull PrivacyPolicyEntity entity,
|
||||
EmptyCallback callback) {
|
||||
SimpleCallback<Boolean> callback) {
|
||||
|
||||
final Context activityContext = checkDialogContext(context);
|
||||
|
||||
@ -1086,19 +1105,31 @@ public class DialogUtils {
|
||||
|
||||
title.setText(entity.getTitle());
|
||||
linkContent.setText(skipText);
|
||||
linkContent.setMovementMethod(new LinkMovementMethod());
|
||||
linkContent.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
topContent.setText(entity.getTopContent());
|
||||
bottomContent.setText(entity.getBottomContent());
|
||||
bottomContent.setText(ExtensionsKt.fromHtml(entity.getBottomContent()));
|
||||
bottomContent.setMovementMethod(CustomLinkMovementMethod.getInstance());
|
||||
|
||||
// Remove underline
|
||||
Spannable sa = (Spannable) bottomContent.getText();
|
||||
for (URLSpan u : sa.getSpans(0, sa.length(), URLSpan.class)) {
|
||||
sa.setSpan(new UnderlineSpan() {
|
||||
public void updateDrawState(TextPaint tp) {
|
||||
tp.setUnderlineText(false);
|
||||
}
|
||||
}, sa.getSpanStart(u), sa.getSpanEnd(u), 0);
|
||||
}
|
||||
|
||||
allowButton.setOnClickListener(view -> {
|
||||
dialog.dismiss();
|
||||
callback.onCallback();
|
||||
callback.onCallback(true);
|
||||
// MtaHelper.onEvent("隐私政策弹窗", "隐私政策弹窗", "点击同意");
|
||||
});
|
||||
|
||||
disallowButton.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
showPrivacyPolicyDisallowDialog(activityContext, entity, callback);
|
||||
callback.onCallback(false);
|
||||
// showPrivacyPolicyDisallowDialog(activityContext, entity, callback);
|
||||
// MtaHelper.onEvent("隐私政策弹窗", "隐私政策弹窗", "不同意并退出App");
|
||||
});
|
||||
|
||||
@ -1902,6 +1933,234 @@ public class DialogUtils {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static Dialog showCancelOrDeleteReservationDialog(Context context, String title, String message
|
||||
, String positive, String negative, final ConfirmListener cmListener, final CancelListener clListener) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_cancel_reservation, null);
|
||||
TextView contentTv = contentView.findViewById(R.id.dialog_content);
|
||||
TextView titleTv = contentView.findViewById(R.id.dialog_title);
|
||||
TextView negativeTv = contentView.findViewById(R.id.dialog_negative);
|
||||
TextView positiveTv = contentView.findViewById(R.id.dialog_positive);
|
||||
contentTv.setText(message);
|
||||
titleTv.setText(title);
|
||||
negativeTv.setText(negative);
|
||||
positiveTv.setText(positive);
|
||||
|
||||
negativeTv.setOnClickListener(view -> {
|
||||
if (clListener != null) {
|
||||
clListener.onCancel();
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
positiveTv.setOnClickListener(view -> {
|
||||
if (cmListener != null) {
|
||||
cmListener.onConfirm();
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60f);
|
||||
window.setAttributes(params);
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static void showEnergyDialog(Context context, String userName, int energy) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_energy, null);
|
||||
((TextView) contentView.findViewById(R.id.userName)).setText("\"" + userName + "\"");
|
||||
((TextView) contentView.findViewById(R.id.energy)).setText(energy + "");
|
||||
|
||||
contentView.findViewById(R.id.dialog_positive).setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showEnergyTaskNoticeDialog(Context context, String title, String content) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_notice, null);
|
||||
TextView titleTv = contentView.findViewById(R.id.dialog_title);
|
||||
TextView contentTv = contentView.findViewById(R.id.dialog_content);
|
||||
TextView okTv = contentView.findViewById(R.id.dialog_ok);
|
||||
|
||||
if (title == null) {
|
||||
titleTv.setVisibility(View.GONE);
|
||||
} else {
|
||||
titleTv.setVisibility(View.VISIBLE);
|
||||
titleTv.setText(title);
|
||||
}
|
||||
contentTv.setText(content);
|
||||
|
||||
okTv.setTextColor(ContextCompat.getColor(context, R.color.theme_font));
|
||||
okTv.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showSimulatorParseErrorDialog(Context context, String gameId, String gameName, ConfirmListener confirmListener) {
|
||||
context = checkDialogContext(context);
|
||||
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
|
||||
|
||||
DialogPackageParseErrorBinding binding = DialogPackageParseErrorBinding.inflate(LayoutInflater.from(context), null, false);
|
||||
Context finalContext = context;
|
||||
SpannableStringBuilder builder = new SpanBuilder("您也可以点击提交反馈跟我们联系").click(6, 10, R.color.theme_font, true, new Function0<Unit>() {
|
||||
@Override
|
||||
public Unit invoke() {
|
||||
SimpleGameEntity entity = new SimpleGameEntity(gameId, gameName, "");
|
||||
SuggestionActivity.startSuggestionActivity(finalContext, SuggestType.gameQuestion, "notfound", "模拟器安装包解析错误", entity, "-");
|
||||
dialog.dismiss();
|
||||
return null;
|
||||
}
|
||||
}).build();
|
||||
binding.feedbackTv.setText(builder);
|
||||
binding.feedbackTv.setMovementMethod(new LinkMovementMethod());
|
||||
|
||||
binding.cancel.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
binding.confirm.setOnClickListener(v -> {
|
||||
confirmListener.onConfirm();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(binding.getRoot());
|
||||
dialog.show();
|
||||
Window window = dialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60f);
|
||||
window.setAttributes(params);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public static void showReportReasonDialog(Context context, ReportReasonCallBack callBack) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
|
||||
DialogReportReasonBinding binding = DialogReportReasonBinding.inflate(LayoutInflater.from(context));
|
||||
|
||||
binding.reasonOne.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
callBack.onResponse(binding.reasonOne.getText().toString(), "");
|
||||
});
|
||||
|
||||
binding.reasonTwo.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
callBack.onResponse(binding.reasonTwo.getText().toString(), "");
|
||||
});
|
||||
|
||||
binding.reasonThree.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
callBack.onResponse(binding.reasonThree.getText().toString(), "");
|
||||
});
|
||||
|
||||
binding.reasonFour.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
callBack.onResponse(binding.reasonFour.getText().toString(), "");
|
||||
});
|
||||
|
||||
binding.reasonOther.setOnClickListener(v -> {
|
||||
binding.reasonTitle.setText(R.string.report_reason_other_title);
|
||||
binding.normalReasonContainer.setVisibility(View.GONE);
|
||||
binding.otherReasonContainer.setVisibility(View.VISIBLE);
|
||||
});
|
||||
|
||||
binding.negativeBtn.setOnClickListener(v -> {
|
||||
binding.reasonTitle.setText(R.string.report_reason_title);
|
||||
binding.normalReasonContainer.setVisibility(View.VISIBLE);
|
||||
binding.otherReasonContainer.setVisibility(View.GONE);
|
||||
});
|
||||
|
||||
binding.positiveBtn.setOnClickListener(v -> {
|
||||
if (TextUtils.isEmpty(binding.otherReasonEt.getText().toString().trim())) {
|
||||
ToastUtils.INSTANCE.showToast("请填写举报原因");
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
callBack.onResponse("其它", binding.otherReasonEt.getText().toString());
|
||||
}
|
||||
});
|
||||
|
||||
Context finalContext = context;
|
||||
ExtensionsKt.setTextChangedListener(binding.otherReasonEt, (s, start, before, count) -> {
|
||||
int tvCount = s.length();
|
||||
if (tvCount >= 500) {
|
||||
binding.tvCount.setTextColor(ContextCompat.getColor(finalContext, R.color.text_FF4147));
|
||||
}
|
||||
binding.tvCount.setText(tvCount + "/500");
|
||||
return null;
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(binding.getRoot());
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showSignSuccessDialog(Context context, ConfirmListener listener) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_sign_success, null);
|
||||
|
||||
ImageView signBg = contentView.findViewById(R.id.signBg);
|
||||
RelativeLayout signContainer = contentView.findViewById(R.id.signContainer);
|
||||
|
||||
Animation animation1 = AnimationUtils.loadAnimation(context, R.anim.anim_badge_light_bg);
|
||||
signBg.startAnimation(animation1);
|
||||
|
||||
Animation animation2 = AnimationUtils.loadAnimation(context, R.anim.anim_sign_dialog_dismiss);
|
||||
signContainer.postDelayed(() -> signContainer.startAnimation(animation2), 2000);
|
||||
|
||||
signContainer.postDelayed(dialog::dismiss, 3000);
|
||||
|
||||
dialog.setOnDismissListener(dialogInterface -> listener.onConfirm());
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showSignRuleDialog(Context context) {
|
||||
context = checkDialogContext(context);
|
||||
|
||||
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
|
||||
|
||||
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_sign_rule, null);
|
||||
contentView.findViewById(R.id.dialog_ok).setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(contentView);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context may be is application context
|
||||
* @return activity context
|
||||
@ -1935,4 +2194,7 @@ public class DialogUtils {
|
||||
void onResponse(boolean isSubscribe);
|
||||
}
|
||||
|
||||
public interface ReportReasonCallBack {
|
||||
void onResponse(String reason, String desc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
@ -39,6 +40,8 @@ import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.mygame.PlayedGameActivity
|
||||
import com.gh.gamecenter.personalhome.UserHomeActivity
|
||||
import com.gh.gamecenter.personalhome.background.PersonalityBackgroundActivity
|
||||
import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
|
||||
import com.gh.gamecenter.qa.CommunityFragment
|
||||
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
|
||||
import com.gh.gamecenter.qa.article.SimpleArticleListActivity
|
||||
@ -48,10 +51,12 @@ import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
|
||||
import com.gh.gamecenter.qa.subject.CommunitySubjectActivity
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.security.BindPhoneActivity
|
||||
import com.gh.gamecenter.servers.GameServersActivity
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.gh.gamecenter.suggest.SuggestType
|
||||
import com.gh.gamecenter.tag.TagsActivity
|
||||
import com.gh.gamecenter.user.UserViewModel
|
||||
import com.gh.gamecenter.video.data.VideoDataActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailActivity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
@ -249,6 +254,68 @@ object DirectUtils {
|
||||
|
||||
"bbs_detail" -> directForumDetail(context, linkEntity.link ?: "", entrance)
|
||||
|
||||
"mobile_bind" -> {
|
||||
CheckLoginUtils.checkLogin(context, entrance) {
|
||||
context.startActivity(BindPhoneActivity.getNormalIntent(context, false))
|
||||
}
|
||||
}
|
||||
|
||||
"authentication" -> {
|
||||
CheckLoginUtils.checkLogin(context, entrance) {
|
||||
context.startActivity(UserInfoEditActivity.getIntent(context, UserViewModel.TYPE_ID_CARD))
|
||||
}
|
||||
}
|
||||
|
||||
"user_background" -> {
|
||||
CheckLoginUtils.checkLogin(context, entrance) {
|
||||
context.startActivity(PersonalityBackgroundActivity.getIntent(context))
|
||||
}
|
||||
}
|
||||
|
||||
"avatar_frame" -> {
|
||||
CheckLoginUtils.checkLogin(context, entrance) {
|
||||
context.startActivity(AvatarBorderActivity.getIntent(context))
|
||||
}
|
||||
}
|
||||
|
||||
"badge" -> {
|
||||
CheckLoginUtils.checkLogin(context, entrance) {
|
||||
UserManager.getInstance().userInfoEntity?.run {
|
||||
directToBadgeWall(context, userId, name, icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"etiquette_exam" -> directToRegulationTestPage(context)
|
||||
|
||||
"setting" -> context.startActivity(SettingActivity.getIntent(context, false, entrance))
|
||||
|
||||
"index_page" -> directToHomeTab(context)
|
||||
|
||||
"video_upload" -> context.startActivity(VideoManagerActivity.getIntent(context, "", entrance))
|
||||
|
||||
"bbs" -> directToForum(context)
|
||||
|
||||
"user_page" -> directToHomeActivity(context, UserManager.getInstance().userId, "", entrance)
|
||||
|
||||
"video_tab" -> directToVideoTab(context)
|
||||
|
||||
"toolkit" -> context.startActivity(ToolBoxActivity.getIntent(context, entrance))
|
||||
|
||||
"toolkit_detail" -> {
|
||||
linkEntity.toolkit?.run {
|
||||
if (url != null && url!!.contains(Config.URL_ARTICLE)) {
|
||||
val newsId = url!!.substring(url!!.lastIndexOf("/") + 1, url!!.length - 5) // 5: ".html"
|
||||
val intent = NewsDetailActivity.getIntentById(context, newsId, entrance)
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
context.startActivity(WebActivity.getWebByCollectionTools(context, this, false))
|
||||
}
|
||||
} ?: ""
|
||||
}
|
||||
|
||||
"halo_tab" -> directToPersonalTab(context)
|
||||
|
||||
//"h5_game_center" -> directLetoGameCenter(context)
|
||||
|
||||
"" -> {
|
||||
@ -578,12 +645,17 @@ object DirectUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun directToExternalBrowser(context: Context, url: String) {
|
||||
if (url.isEmpty()) return
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Uri.decode(url)))
|
||||
if (context !is AppCompatActivity) {
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
if (url.isEmpty()) return
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Uri.decode(url)))
|
||||
if (context !is AppCompatActivity) {
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
e.printStackTrace()
|
||||
Utils.toast(context, "跳转地址无效")
|
||||
}
|
||||
context.startActivity(browserIntent)
|
||||
}
|
||||
|
||||
// 跳转 QQ,qqNumber 为空选择默认客服 QQ
|
||||
@ -1030,7 +1102,28 @@ object DirectUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 到首页论坛 tab
|
||||
* 到首页-首页 tab
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToHomeTab(context: Context) {
|
||||
if (RunningUtils.isRunning(context)
|
||||
&& MainActivity::class.java.name == RunningUtils.getBaseActivity(context)) {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
context.startActivity(intent)
|
||||
|
||||
// 这里换个线程操作是为了做一点延时
|
||||
AppExecutor.ioExecutor.execute {
|
||||
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_HOME))
|
||||
}
|
||||
} else {
|
||||
jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_HOME) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 到首页-论坛 tab
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToForum(context: Context) {
|
||||
@ -1049,6 +1142,46 @@ object DirectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 到首页-视频 tab
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToVideoTab(context: Context) {
|
||||
if (RunningUtils.isRunning(context)
|
||||
&& MainActivity::class.java.name == RunningUtils.getBaseActivity(context)) {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
context.startActivity(intent)
|
||||
|
||||
// 这里换个线程操作是为了做一点延时
|
||||
AppExecutor.ioExecutor.execute {
|
||||
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_VIDEO))
|
||||
}
|
||||
} else {
|
||||
jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_VIDEO) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 到首页-我的光环 tab
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToPersonalTab(context: Context) {
|
||||
if (RunningUtils.isRunning(context)
|
||||
&& MainActivity::class.java.name == RunningUtils.getBaseActivity(context)) {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
context.startActivity(intent)
|
||||
|
||||
// 这里换个线程操作是为了做一点延时
|
||||
AppExecutor.ioExecutor.execute {
|
||||
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_PERSONAL))
|
||||
}
|
||||
} else {
|
||||
jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_PERSONAL) })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至使用帮助与反馈
|
||||
* @param position 使用帮助:[HelpAndFeedbackActivity.HELP_ITEM],意见反馈:[HelpAndFeedbackActivity.SUGGESTION_ITEM]
|
||||
@ -1060,4 +1193,95 @@ object DirectUtils {
|
||||
bundle.putInt(BaseActivity_TabLayout.PAGE_INDEX, position)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至商品详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToCommodityDetail(context: Context, commodityId: String) {
|
||||
var url: String = if ("internal" == BuildConfig.FLAVOR) {
|
||||
Constants.COMMODITY_DETAIL_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.COMMODITY_DETAIL_ADDRESS
|
||||
}
|
||||
|
||||
url = String.format(Locale.CHINA, "%s&shopid=%s×tamp=%d", url, commodityId, (Date().time / 1000 / 1000.toFloat()).roundToInt())
|
||||
directToFullScreenWebPage(context, url, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至光能记录(默认跳到光能记录第一个Tab)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToEnergyRecord(context: Context) {
|
||||
directToEnergyRecord(context, 0)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToEnergyRecord(context: Context, position: Int) {
|
||||
var url: String = if ("internal" == BuildConfig.FLAVOR) {
|
||||
Constants.ENERGY_RECORD_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.ENERGY_RECORD_ADDRESS
|
||||
}
|
||||
|
||||
url = String.format(Locale.CHINA, "%s&position=%s×tamp=%d", url, position, (Date().time / 1000 / 1000.toFloat()).roundToInt())
|
||||
directToFullScreenWebPage(context, url, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至订单中心
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToOrderCenter(context: Context) {
|
||||
var url: String = if ("internal" == BuildConfig.FLAVOR) {
|
||||
Constants.ORDER_CENTER_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.ORDER_CENTER_ADDRESS
|
||||
}
|
||||
directToFullScreenWebPage(context, url, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至订单详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToOrderDetail(context: Context, orderId: String) {
|
||||
var url: String = if ("internal" == BuildConfig.FLAVOR) {
|
||||
Constants.ORDER_DETAIL_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.ORDER_DETAIL_ADDRESS
|
||||
}
|
||||
|
||||
url = String.format(Locale.CHINA, "%s&order_id=%s×tamp=%d", url, orderId, (Date().time / 1000 / 1000.toFloat()).roundToInt())
|
||||
directToFullScreenWebPage(context, url, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至邀请好友
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToInviteFriends(context: Context) {
|
||||
var url: String = if ("internal" == BuildConfig.FLAVOR) {
|
||||
Constants.INVITE_FRIENDS_ADDRESS_DEV
|
||||
} else {
|
||||
Constants.INVITE_FRIENDS_ADDRESS
|
||||
}
|
||||
directToFullScreenWebPage(context, url, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至等级页面
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToLevelPage(context: Context) {
|
||||
var url: String = if (isPublishEnv()) {
|
||||
Constants.LEVEL_ADDRESS
|
||||
} else {
|
||||
Constants.LEVEL_ADDRESS_DEV
|
||||
}
|
||||
|
||||
url = String.format(Locale.CHINA, "%s?timestamp=%d", url, (Date().time / 1000 / 1000.toFloat()).roundToInt())
|
||||
directToFullScreenWebPage(context, url, true)
|
||||
}
|
||||
}
|
||||
@ -125,16 +125,17 @@ public class DownloadItemUtils {
|
||||
public static void updateItemWithReserveStatus(GameViewHolder holder, GameEntity gameEntity) {
|
||||
if ("download".equals(gameEntity.getReserveStatus())) {
|
||||
// 已上线
|
||||
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
|
||||
holder.gameDownloadBtn.setText("已上线");
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
holder.gameDownloadBtn.setBackground(ContextCompat.getDrawable(holder.gameDes.getContext(), R.drawable.game_item_btn_pause_dn));
|
||||
// holder.gameDownloadBtn.setVisibility(View.VISIBLE);
|
||||
// holder.gameDownloadBtn.setText("已上线");
|
||||
// holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
// holder.gameDownloadBtn.setBackground(ContextCompat.getDrawable(holder.gameDes.getContext(), R.drawable.game_item_btn_pause_dn));
|
||||
updateItem(holder.gameDes.getContext(), gameEntity, holder, false);
|
||||
} else if ("appointment".equals(gameEntity.getReserveStatus())) {
|
||||
// 已预约
|
||||
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
|
||||
holder.gameDownloadBtn.setText("已预约");
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_dn);
|
||||
holder.gameDownloadBtn.setTextColor(ContextCompat.getColor(holder.gameDes.getContext(), R.color.aaaaaa));
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.button_round_f5f5f5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,7 +580,9 @@ public class DownloadItemUtils {
|
||||
final String location,
|
||||
@Nullable final ExposureEvent traceEvent) {
|
||||
String str = downloadBtn.getText().toString();
|
||||
ApkEntity apk = gameEntity.getApk().get(0);
|
||||
if (gameEntity.getApk().isEmpty()) return;
|
||||
ApkEntity apk = ExtensionsKt.safelyGetInRelease(gameEntity.getApk(), 0);
|
||||
if (apk == null) return;
|
||||
|
||||
if (str.equals(context.getString(R.string.download))) {
|
||||
// 先弹下载弹窗(如果需要的话)
|
||||
@ -654,6 +657,8 @@ public class DownloadItemUtils {
|
||||
}
|
||||
DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
|
||||
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
|
||||
|
||||
EnergyTaskHelper.postEnergyTask("play_game", gameEntity.getId(), gameEntity.getApk().get(0).getPackageName());
|
||||
} else if (str.equals(context.getString(R.string.update))) {
|
||||
if (entrance.contains("我的游戏")) {
|
||||
MtaHelper.onEvent("我的游戏_启动", "更新", gameEntity.getName());
|
||||
@ -687,6 +692,8 @@ public class DownloadItemUtils {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
downloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
|
||||
DeviceRemindDialog.Companion.showDeviceRemindDialog(context, gameEntity);
|
||||
|
||||
EnergyTaskHelper.postEnergyTask("download_game", gameEntity.getId(), gameEntity.getApk().get(0).getPackageName());
|
||||
} else {
|
||||
Utils.toast(context, msg);
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.SuggestionActivity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SimpleGameEntity
|
||||
import com.gh.gamecenter.entity.SimulatorEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBShowDialog
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
@ -50,7 +51,7 @@ object DownloadObserver {
|
||||
val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
|
||||
if (!xapkStatus.isNullOrEmpty()) return
|
||||
|
||||
val gameId = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER)
|
||||
val gameId = downloadEntity.gameId
|
||||
val downloadManager = DownloadManager.getInstance(HaloApp.getInstance().application)
|
||||
|
||||
tryCatchInRelease {
|
||||
@ -111,6 +112,7 @@ object DownloadObserver {
|
||||
}
|
||||
if (DownloadStatus.done == downloadEntity.status) {
|
||||
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
|
||||
statDoneEvent(downloadEntity)
|
||||
MtaHelper.onEvent("软件更新", "下载完成")
|
||||
// 会有 ActivityNotFoundException 异常,catch 掉不管了
|
||||
tryWithDefaultCatch {
|
||||
@ -138,19 +140,20 @@ object DownloadObserver {
|
||||
}
|
||||
if (!downloadEntity.isPluggable) {
|
||||
if (downloadEntity.isSimulatorGame()) {
|
||||
val gameEntity = HaloApp.get(GameEntity::class.java.simpleName, true) as? GameEntity
|
||||
if (gameEntity?.simulator != null) {
|
||||
val isInstalled = PackageUtils.isInstalledFromAllPackage(HaloApp.getInstance().application, gameEntity.simulator!!.apk!!.packageName)
|
||||
if (!isInstalled) {
|
||||
val currentActivity = AppManager.getInstance().currentActivity() ?: return
|
||||
val simulatorJson = downloadEntity.getMetaExtra(Constants.SIMULATOR)
|
||||
val gameName = downloadEntity.getMetaExtra(Constants.GAME_NAME)
|
||||
if (simulatorJson.isEmpty()) return
|
||||
val simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
|
||||
val isInstalled = PackageUtils.isInstalledFromAllPackage(HaloApp.getInstance().application, simulator.apk?.packageName)
|
||||
if (!isInstalled) {
|
||||
val currentActivity = AppManager.getInstance().currentActivity()
|
||||
?: return
|
||||
|
||||
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, gameEntity.simulator,
|
||||
SimulatorDownloadManager.SimulatorLocation.LAUNCH, gameEntity.id, gameEntity.name
|
||||
?: "", null)
|
||||
}
|
||||
SimulatorGameManager.recordDownloadSimulatorGames(gameEntity.id)
|
||||
SimulatorGameManager.postPlayedGame(gameEntity.id, downloadEntity.packageName)
|
||||
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator,
|
||||
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null)
|
||||
}
|
||||
SimulatorGameManager.recordDownloadSimulatorGames(downloadEntity.gameId)
|
||||
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
|
||||
} else {
|
||||
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
|
||||
// 是否是自动安装
|
||||
@ -179,15 +182,33 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
// 下载过程分析统计
|
||||
// val pm = mApplication.packageManager
|
||||
// val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
|
||||
// if (packageInfo == null) {
|
||||
|
||||
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||||
// 这里为了让它能用就不判断是否解析包错误了
|
||||
if (PackageUtils.isDeviceUnableToHandleBigApkFile(downloadEntity.path)) {
|
||||
return
|
||||
}
|
||||
|
||||
val pm = mApplication.packageManager
|
||||
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
|
||||
if (packageInfo == null) {
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误分析",
|
||||
// "游戏名字", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
//
|
||||
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误_新",
|
||||
// "游戏", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
|
||||
// }
|
||||
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
|
||||
if (downloadType == Constants.SIMULATOR_DOWNLOAD) {
|
||||
val currentActivity = AppManager.getInstance().currentActivity()
|
||||
?: return
|
||||
DialogUtils.showSimulatorParseErrorDialog(currentActivity, downloadEntity.gameId, downloadEntity.name) {
|
||||
val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity
|
||||
?: return@showSimulatorParseErrorDialog
|
||||
DownloadManager.getInstance(currentActivity).cancel(downloadEntity.url, true, true)
|
||||
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator, SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadEntity.status == DownloadStatus.done) {
|
||||
@ -219,7 +240,7 @@ object DownloadObserver {
|
||||
kv1["版本"] = platform
|
||||
kv1["状态"] = "下载完成"
|
||||
kv1["用户机型"] = Build.MODEL
|
||||
kv1["设备IMEI"] = MetaUtil.getIMEI()
|
||||
kv1["设备JNFJ"] = MetaUtil.getBase64EncodedIMEI()
|
||||
kv1["网络状态"] = DeviceUtils.getNetwork(HaloApp.getInstance().application)
|
||||
kv1["光环助手版本"] = BuildConfig.VERSION_NAME
|
||||
if (downloadEntity.isUpdate) {
|
||||
@ -257,7 +278,7 @@ object DownloadObserver {
|
||||
}
|
||||
|
||||
ExposureUtils.logADownloadCompleteExposureEvent(
|
||||
GameEntity(id = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER),
|
||||
GameEntity(id = downloadEntity.gameId,
|
||||
mName = downloadEntity.name.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||||
gameVersion = downloadEntity.versionName ?: ""),
|
||||
downloadEntity.platform,
|
||||
@ -287,7 +308,7 @@ object DownloadObserver {
|
||||
params["game"] = id
|
||||
params["platform"] = platform ?: ""
|
||||
val body = RequestBody.create(MediaType.parse("application/json"),
|
||||
JSONObject(params).toString())
|
||||
JSONObject(params as Map<*, *>).toString())
|
||||
RetrofitManager.getInstance(mApplication).api.postDownload(body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
160
app/src/main/java/com/gh/common/util/EnergyTaskHelper.kt
Normal file
160
app/src/main/java/com/gh/common/util/EnergyTaskHelper.kt
Normal file
@ -0,0 +1,160 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.energy.EnergyCenterActivity
|
||||
import com.gh.gamecenter.entity.EnergyTaskCompleteEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.AppManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* 上报光能任务辅助类
|
||||
*/
|
||||
object EnergyTaskHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun postEnergyTask(action: String) {
|
||||
postEnergyTask(action, null, null, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun postEnergyTask(action: String, id: String) {
|
||||
postEnergyTask(action, id, null, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun postEnergyTask(action: String, id: String, packageName: String) {
|
||||
postEnergyTask(action, id, packageName, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun postEnergyTaskForWeb(action: String, url: String) {
|
||||
postEnergyTask(action, null, null, url)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun postEnergyTaskForShare(type: String, id: String, url: String) {
|
||||
when (type) {
|
||||
"游戏详情" -> postEnergyTask("share_game_detail", id)
|
||||
|
||||
"视频" -> postEnergyTask("share_video", id)
|
||||
|
||||
"资讯文章" -> postEnergyTask("share_article", id)
|
||||
|
||||
"问题详情" -> postEnergyTask("share_question", id)
|
||||
|
||||
"回答详情" -> postEnergyTask("share_answer", id)
|
||||
|
||||
"文章详情" -> postEnergyTask("share_community_article", id)
|
||||
|
||||
"工具箱" -> postEnergyTask("share_toolkit", id)
|
||||
|
||||
"web链接" -> postEnergyTaskForWeb("share_web", url)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun postEnergyTask(action: String, id: String? = null, packageName: String? = null, url: String? = null) {
|
||||
if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0L) {
|
||||
val taskParams = JSONObject()
|
||||
taskParams.put("action", action)
|
||||
|
||||
val actionParams = JSONObject()
|
||||
if (id != null) actionParams.put("_id", id)
|
||||
if (packageName != null) actionParams.put("package", packageName)
|
||||
if (url != null) actionParams.put("url", url)
|
||||
taskParams.put("action_param", actionParams)
|
||||
|
||||
debugOnly { Utils.log("EnergyTaskHelper -> $taskParams") }
|
||||
|
||||
val body = RequestBody.create(MediaType.parse("application/json"), taskParams.toString())
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.api.postEnergyTask(UserManager.getInstance().userId, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<EnergyTaskCompleteEntity>>() {
|
||||
override fun onSuccess(data: List<EnergyTaskCompleteEntity>) {
|
||||
data.forEach { showCompletePopup(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun postInviteCodeTask(code: String, from: String, successCallback: () -> Unit, failCallback: () -> Unit) {
|
||||
if (BuildConfig.DEBUG || BuildConfig.BUILD_TIME != 0L) {
|
||||
val taskParams = JSONObject()
|
||||
taskParams.put("action", "enter_invite_code")
|
||||
|
||||
val actionParams = JSONObject()
|
||||
actionParams.put("code", code)
|
||||
actionParams.put("type", "new")
|
||||
actionParams.put("from", from)
|
||||
taskParams.put("action_param", actionParams)
|
||||
|
||||
debugOnly { Utils.log("EnergyTaskHelper -> $taskParams") }
|
||||
|
||||
val body = RequestBody.create(MediaType.parse("application/json"), taskParams.toString())
|
||||
RetrofitManager.getInstance(HaloApp.getInstance().application)
|
||||
.api.postEnergyTask(UserManager.getInstance().userId, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<EnergyTaskCompleteEntity>>() {
|
||||
override fun onSuccess(data: List<EnergyTaskCompleteEntity>) {
|
||||
SPUtils.setBoolean(Constants.SP_HAS_COMPLETE_INVITE_CODE, true)
|
||||
successCallback.invoke()
|
||||
data.forEach { showCompletePopup(it) }
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
failCallback.invoke()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 完成弹窗
|
||||
@JvmStatic
|
||||
fun showCompletePopup(entity: EnergyTaskCompleteEntity) {
|
||||
val currentActivity = AppManager.getInstance().recentActiveActivity
|
||||
currentActivity?.run {
|
||||
val contentView = View.inflate(this, R.layout.popup_energy_task, null)
|
||||
contentView.run {
|
||||
findViewById<TextView>(R.id.taskDesc).text = "恭喜你!完成任务:${entity.name}"
|
||||
findViewById<TextView>(R.id.taskEnergy).text = "+${entity.energy}光能"
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
setOnClickListener {
|
||||
currentActivity.startActivity(EnergyCenterActivity.getIntent(currentActivity))
|
||||
}
|
||||
}
|
||||
val popWindow = PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, 88F.dip2px())
|
||||
popWindow.showAtLocation(currentActivity.window.decorView, Gravity.TOP, 0, 0)
|
||||
|
||||
countDownTimer(3) { finish, _ ->
|
||||
if (finish && popWindow != null && popWindow.isShowing) {
|
||||
popWindow.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,6 +221,9 @@ public class EntranceUtils {
|
||||
public static final String KEY_PRIMARY_CATALOG_NAME = "primaryCatalogName";
|
||||
public static final String KEY_CATALOG_TITLE = "catalog_title";
|
||||
public static final String KEY_CATALOG_INIT_TITLE = "catalog_init_title";
|
||||
public static final String KEY_CATEGORY_LIST = "categoty_list";
|
||||
public static final String KEY_IS_FREE = "is_free";
|
||||
public static final String KEY_IS_SIGN = "is_sign";
|
||||
|
||||
public static void jumpActivity(Context context, Bundle bundle) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
@ -6,6 +6,7 @@ import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.ErrorEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import retrofit2.HttpException
|
||||
|
||||
/**
|
||||
* 这个类用来管理一些错误,像弹窗、TOAST 什么的
|
||||
@ -17,6 +18,7 @@ object ErrorHelper {
|
||||
* [showHighPriorityHint] 用来标识有同样错误码可以触发两种处理时,为 true 时选择重要的
|
||||
* [customizedHandler] 返回 true 为已处理该错误码,false 则交由 [handleError] 处理
|
||||
*/
|
||||
@JvmStatic
|
||||
fun handleErrorWithCustomizedHandler(context: Context,
|
||||
errorString: String?,
|
||||
showHighPriorityHint: Boolean = false,
|
||||
@ -169,4 +171,23 @@ object ErrorHelper {
|
||||
}, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun handleLoginError(context: Context, httpException: HttpException?) {
|
||||
try {
|
||||
val errorEntity: ErrorEntity? = httpException?.response()?.errorBody()?.string()?.toObject()
|
||||
when {
|
||||
errorEntity?.code == 403099 -> {
|
||||
Utils.toast(context, "当前账号正在注销,禁止登录")
|
||||
}
|
||||
errorEntity?.toast?.isNotEmpty() == true -> {
|
||||
Utils.toast(context, errorEntity.toast)
|
||||
}
|
||||
else -> Utils.toast(context, R.string.login_failure)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Utils.toast(context, R.string.login_failure)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -424,7 +424,7 @@ fun String.copyTextAndToast(toastText: String = "复制成功") {
|
||||
val application = HaloApp.getInstance().application
|
||||
val cmb = application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(null, this)
|
||||
cmb.primaryClip = clip
|
||||
cmb.setPrimaryClip(clip)
|
||||
|
||||
if (!TextUtils.isEmpty(toastText)) {
|
||||
ToastUtils.showToast(toastText)
|
||||
@ -534,6 +534,14 @@ fun Fragment.checkStoragePermissionBeforeAction(action: (() -> Unit)) {
|
||||
})
|
||||
}
|
||||
|
||||
fun Fragment.checkCalendarPermissionBeforeAction(action: (() -> Unit)) {
|
||||
PermissionHelper.checkCalendarPermissionBeforeAction(requireContext(), object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
action.invoke()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun FragmentActivity.checkReadPhoneStateAndStoragePermissionBeforeAction(action: (() -> Unit)) {
|
||||
PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction(this, object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
@ -558,6 +566,14 @@ fun FragmentActivity.checkStoragePermissionBeforeAction(action: (() -> Unit)) {
|
||||
})
|
||||
}
|
||||
|
||||
fun FragmentActivity.checkCalendarPermissionBeforeAction(action: (() -> Unit)) {
|
||||
PermissionHelper.checkCalendarPermissionBeforeAction(this, object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
action.invoke()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List related.
|
||||
*/
|
||||
@ -602,11 +618,11 @@ fun TextView.setTextChangedListener(action: (s: CharSequence, start: Int, before
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayList related
|
||||
* List related
|
||||
*/
|
||||
fun <T> ArrayList<T>.safelyGetInRelease(index: Int): T? {
|
||||
fun <T> List<T>.safelyGetInRelease(index: Int): T? {
|
||||
return if (index >= size) {
|
||||
throwExceptionInDebug("这里触发了数组越界,请检查")
|
||||
throwExceptionInDebug("这里触发了数组越界,请检查 (index $index >= size $size)")
|
||||
toastInInternalRelease("这个操作可能触发闪退,请确定复现方式并联系开发处理")
|
||||
null
|
||||
} else {
|
||||
@ -706,11 +722,16 @@ fun SimpleDraweeView.display(url: String) {
|
||||
* DownloadEntity extension
|
||||
*/
|
||||
fun DownloadEntity.addMetaExtra(key: String, value: String?) {
|
||||
value?.let { meta[key] = value }
|
||||
value?.let {
|
||||
if (meta == null) {
|
||||
meta = hashMapOf()
|
||||
}
|
||||
meta[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
fun DownloadEntity.getMetaExtra(key: String): String {
|
||||
return meta[key] ?: ""
|
||||
return meta?.get(key) ?: ""
|
||||
}
|
||||
|
||||
fun DownloadEntity.isSilentUpdate(): Boolean {
|
||||
@ -732,6 +753,9 @@ fun Context.doOnMainProcessOnly(callback: EmptyCallback) {
|
||||
doOnMainProcessOnly { callback.onCallback() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 虽然现在我们没有了友盟以后只是单进程APP,但在 debug 模式下还有 whatTheStack 这个进程如果不限定主进程会出现奇奇怪怪的问题 (BroadcastReceiver相关)
|
||||
*/
|
||||
inline fun Context.doOnMainProcessOnly(f: () -> Unit) {
|
||||
val processName = PackageUtils.obtainProcessName(this)
|
||||
if (processName == null || BuildConfig.APPLICATION_ID == processName) {
|
||||
|
||||
@ -78,6 +78,9 @@ public class GameUtils {
|
||||
int pluginCount = 0; // 可插件化数量
|
||||
int updateCount = 0; // 可更新数量
|
||||
int installCount = 0; // 已安装数量
|
||||
|
||||
boolean isRelatedEmulatorInstalled = false; // 若该游戏是模拟器游戏时其对应的模拟器是否已经安装
|
||||
|
||||
DownloadEntity downloadEntity;
|
||||
Object gh_id;
|
||||
apkFor:
|
||||
@ -113,7 +116,10 @@ public class GameUtils {
|
||||
&& !PackageUtils.isSignedByGh(context, apkEntity.getPackageName())
|
||||
&& apkEntity.isShowPlugin(pluginLocation)) {
|
||||
pluginCount++;
|
||||
} else if (gh_id == null || gh_id.equals(gameEntity.getId())) {
|
||||
} else if (gh_id == null
|
||||
|| gh_id.equals(gameEntity.getId())
|
||||
// 当使用镜像时,可能会存在镜像 gh_id 与当前游戏 id 不一样的情况,这时也让它通过,避免装完还是显示`下载`
|
||||
|| gameEntity.shouldUseMirrorInfo()) {
|
||||
installCount++;
|
||||
}
|
||||
}
|
||||
@ -123,20 +129,26 @@ public class GameUtils {
|
||||
boolean isInstalled = PackageUtils.isInstalledFromAllPackage(context, gameEntity.getSimulator().getApk().getPackageName());
|
||||
if (isInstalled) {
|
||||
installCount++;
|
||||
isRelatedEmulatorInstalled = true;
|
||||
} else {
|
||||
doneCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (installCount != 0) {
|
||||
|
||||
if (isRelatedEmulatorInstalled && doneCount != 0) {
|
||||
return context.getString(R.string.launch);
|
||||
}
|
||||
|
||||
if (doneCount != 0) {
|
||||
return context.getString(R.string.install);
|
||||
} else if (pluginCount != 0 && !SimulatorGameManager.isSimulatorGame(gameEntity)) {
|
||||
return context.getString(R.string.pluggable);
|
||||
} else if (updateCount != 0 && !SimulatorGameManager.isSimulatorGame(gameEntity)) {
|
||||
return context.getString(R.string.update);
|
||||
} else if (doneCount != 0) {
|
||||
return context.getString(R.string.install);
|
||||
} else if (installCount != 0) {
|
||||
return context.getString(R.string.launch);
|
||||
} else if (gameEntity.getVersionNumber().contains("无版号") && Config.isGameDomeSwitchOpen() && !SimulatorGameManager.isSimulatorGame(gameEntity)) {
|
||||
return context.getString(R.string.attempt);
|
||||
} else {
|
||||
|
||||
@ -38,13 +38,18 @@ object HomePluggableHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun activationFilterData() {
|
||||
val filterList = mHomePluggableFilterDao.getDataByActive(false)
|
||||
try {
|
||||
val filterList = mHomePluggableFilterDao.getDataByActive(false)
|
||||
|
||||
if (filterList != null) {
|
||||
for (entity in filterList) {
|
||||
entity.active = true
|
||||
if (filterList != null) {
|
||||
for (entity in filterList) {
|
||||
entity.active = true
|
||||
}
|
||||
mHomePluggableFilterDao.addData(filterList)
|
||||
}
|
||||
tryCatchInRelease { mHomePluggableFilterDao.addData(filterList) }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
toastInInternalRelease("数据库/磁盘已满,插件筛选失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,7 @@ import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.facebook.imagepipeline.request.ImageRequest
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import com.facebook.imagepipeline.request.Postprocessor
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.structure.FixedSizeLinkedHashSet
|
||||
import com.gh.gamecenter.R
|
||||
@ -85,8 +86,13 @@ object ImageUtils {
|
||||
@JvmStatic
|
||||
fun addLimitWidth(imageUrl: String?, width: Int?): String? {
|
||||
val jpegConfig = Config.getSettings()?.image?.oss?.jpeg
|
||||
val webpConfig = Config.getSettings()?.image?.oss?.webp ?: Config.getSettings()?.image?.oss?.gif
|
||||
if (jpegConfig != null) {
|
||||
return "$imageUrl$jpegConfig,w_$width"
|
||||
return if (width == 0 || width == null) {
|
||||
"$imageUrl$webpConfig"
|
||||
} else {
|
||||
"$imageUrl$jpegConfig,w_$width"
|
||||
}
|
||||
}
|
||||
return imageUrl
|
||||
}
|
||||
@ -286,12 +292,13 @@ object ImageUtils {
|
||||
|
||||
/**
|
||||
* 规则 width>0 Wifi/4G:x2 traffic:x1
|
||||
* 统一加载 webp 忽略掉第二种方案
|
||||
* 第一种方案:通过LayoutParams获取 可以快速(无延迟)获取宽高,但是无法获取wrap_content和match_parent的View
|
||||
* 第二种方案(备用方案):有延迟,View的宽高需要在Measure过程后才能确定,能够在这里获取到正确的宽高
|
||||
* ~~第二种方案(备用方案):有延迟,View的宽高需要在Measure过程后才能确定,能够在这里获取到正确的宽高~~
|
||||
* @param isAutoPlayGif 是否禁止播放动图
|
||||
*/
|
||||
@JvmStatic
|
||||
fun display(view: SimpleDraweeView?, url: String?, isAutoPlayGif: Boolean = true) {
|
||||
fun display(view: SimpleDraweeView?, url: String?, isAutoPlayGif: Boolean = true, processor: Postprocessor? = null) {
|
||||
if (url == null) return
|
||||
|
||||
val width = view?.layoutParams?.width
|
||||
@ -310,40 +317,47 @@ object ImageUtils {
|
||||
}
|
||||
|
||||
val loadImageClosure: (autoPlay: Boolean, highResUrl: String, lowResUrl: String) -> Unit = { autoPlay, hUrl, lUrl ->
|
||||
view?.controller = Fresco.newDraweeControllerBuilder()
|
||||
.setImageRequest(ImageRequest.fromUri(hUrl))
|
||||
val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(hUrl))
|
||||
.setPostprocessor(processor)
|
||||
.build()
|
||||
val controller = Fresco.newDraweeControllerBuilder()
|
||||
.setImageRequest(imageRequest)
|
||||
.apply {
|
||||
if (lUrl.isNotEmpty()
|
||||
&& lUrl != hUrl
|
||||
&& hUrl != view?.getTag(R.string.highResImageTag)) {
|
||||
lowResImageRequest = ImageRequest.fromUri(lUrl)
|
||||
lowResImageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(lUrl))
|
||||
.setPostprocessor(processor)
|
||||
.build()
|
||||
}
|
||||
autoPlayAnimations = autoPlay
|
||||
}
|
||||
.build()
|
||||
view?.controller = controller
|
||||
|
||||
view?.setTag(R.string.highResImageTag, highResUrl)
|
||||
}
|
||||
|
||||
val shouldLoadAsGif = url.endsWith(".gif") && isAutoPlayGif && view?.getTag(R.id.tag_show_gif) != false
|
||||
// 低于 2G 运行内存的不加载 gif
|
||||
val shouldLoadAsGif = url.endsWith(".gif")
|
||||
&& isAutoPlayGif
|
||||
&& (HaloApp.getInstance().deviceRamSize == 0L || HaloApp.getInstance().deviceRamSize > 2500)
|
||||
&& view?.getTag(R.id.tag_show_gif) != false
|
||||
|
||||
if (shouldLoadAsGif && view?.tag == url) return
|
||||
|
||||
if (width != null && width > 0) {
|
||||
highResUrl = if (shouldLoadAsGif) {
|
||||
resizeGif(url, width, height ?: 0)
|
||||
} else {
|
||||
getTransformLimitUrl(url, width, view.context) ?: ""
|
||||
}
|
||||
if (!shouldLoadAsGif) {
|
||||
highResUrl = getTransformLimitUrl(url, width ?: 0, view?.context) ?: ""
|
||||
loadImageClosure(shouldLoadAsGif, highResUrl, lowResUrl)
|
||||
} else {
|
||||
view?.post {
|
||||
highResUrl = if (shouldLoadAsGif) {
|
||||
resizeGif(url, view.width, height ?: 0)
|
||||
} else {
|
||||
getTransformLimitUrl(url, view.width, view.context) ?: ""
|
||||
}
|
||||
if (width != null && width > 0) {
|
||||
highResUrl = resizeGif(url, view.width, height ?: 0)
|
||||
loadImageClosure(shouldLoadAsGif, highResUrl, lowResUrl)
|
||||
} else {
|
||||
view?.post {
|
||||
highResUrl = resizeGif(url, view.width, height ?: 0)
|
||||
loadImageClosure(shouldLoadAsGif, highResUrl, lowResUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
view?.tag = url
|
||||
@ -352,25 +366,27 @@ object ImageUtils {
|
||||
// Wifi/4G:x2 traffic:x1
|
||||
@JvmStatic
|
||||
fun getTransformLimitUrl(url: String?, width: Int?, context: Context?): String? {
|
||||
var transformUrl: String? = url
|
||||
val transformUrl: String?
|
||||
if (width != null && width > 0) {
|
||||
// 加载大小是实际 ImageView 大小两倍的图片真的有意义吗?
|
||||
val transformUrlX2 = addLimitWidth(url, width * 2)
|
||||
val transformUrlX1 = addLimitWidth(url, width)
|
||||
// 当网络为 WIFI 或 4G, 且系统版本大于 5.0 && 手机内存大于 1G 才用高清图片
|
||||
if (NetworkUtils.isWifiOr4GConnected(context)
|
||||
// 当网络为 WIFI 或 4G, 且系统版本大于 5.0 && 手机内存大于 2G 才用高清图片
|
||||
transformUrl = if (NetworkUtils.isWifiOr4GConnected(context)
|
||||
&& Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP
|
||||
&& HaloApp.getInstance().deviceRamSize > 1024) {
|
||||
transformUrl = transformUrlX2
|
||||
&& HaloApp.getInstance().deviceRamSize > 2500) {
|
||||
transformUrlX2
|
||||
} else {
|
||||
// 检查X2大图是否被缓存
|
||||
if (Fresco.getImagePipeline().isInBitmapMemoryCache(Uri.parse(transformUrlX2)) ||
|
||||
Fresco.getImagePipeline().isInDiskCacheSync(Uri.parse(transformUrlX2))) {
|
||||
transformUrl = transformUrlX2
|
||||
transformUrlX2
|
||||
} else {
|
||||
transformUrl = transformUrlX1
|
||||
transformUrlX1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
transformUrl = addLimitWidth(url, null)
|
||||
}
|
||||
addCachedUrl(transformUrl ?: "")
|
||||
return transformUrl
|
||||
@ -383,9 +399,7 @@ object ImageUtils {
|
||||
if (width != null && width > 0) {
|
||||
view.setImageURI(addLimitWidth(url, width * 2))
|
||||
} else {
|
||||
view?.post {
|
||||
view.setImageURI(addLimitWidth(url, view.width * 2))
|
||||
}
|
||||
view?.setImageURI(addLimitWidth(url, view.width * 2))
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,6 +409,18 @@ object ImageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已缓存的图片地址,不区分质量,暂时只供查看大图页面使用
|
||||
*/
|
||||
fun getCachedUrl(url: String): String {
|
||||
for (decoratedUrl in mImageUrlCacheSet) {
|
||||
if (decoratedUrl.contains(url)) {
|
||||
return decoratedUrl
|
||||
}
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun bmpToByteArray(bmp: Bitmap, needRecycle: Boolean): ByteArray {
|
||||
val output = ByteArrayOutputStream()
|
||||
@ -444,7 +470,7 @@ object ImageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnImageloadListener {
|
||||
interface OnImageloadListener {
|
||||
fun onLoadFinal(imageInfo: ImageInfo?)
|
||||
}
|
||||
|
||||
|
||||
@ -18,11 +18,11 @@ public class Installation {
|
||||
private static String sID = null;
|
||||
|
||||
public synchronized static String getUUID(Context context) {
|
||||
String imei = MetaUtil.getIMEI();
|
||||
String imei = MetaUtil.getBase64EncodedIMEI();
|
||||
if (!TextUtils.isEmpty(imei)) {
|
||||
return imei;
|
||||
}
|
||||
String android_id = MetaUtil.getAndroidId();
|
||||
String android_id = MetaUtil.getBase64EncodedAndroidId();
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
return android_id;
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import com.gh.gamecenter.entity.CommunityEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.ShareResultEntity;
|
||||
import com.gh.gamecenter.entity.SpecialColumn;
|
||||
import com.gh.gamecenter.entity.StartupAdEntity;
|
||||
import com.gh.gamecenter.manager.UserManager;
|
||||
import com.gh.gamecenter.qa.entity.Questions;
|
||||
import com.gh.gamecenter.retrofit.EmptyResponse;
|
||||
@ -235,6 +236,7 @@ public class LogUtils {
|
||||
object.put("game_name", gameEntity.getName());
|
||||
object.put("game_id", gameEntity.getId());
|
||||
object.put("game_platform", gameEntity.getPlatform());
|
||||
object.put("download_open", gameEntity.getDownloadOffStatus() == null ? "true" : "false");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -255,13 +257,13 @@ public class LogUtils {
|
||||
try {
|
||||
object.put("version", PackageUtils.getVersionName());
|
||||
object.put("channel", HaloApp.getInstance().getChannel());
|
||||
object.put("android_id", MetaUtil.getAndroidId());
|
||||
object.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
object.put("time", Utils.getTime(context));
|
||||
object.put("network", DeviceUtils.getNetwork(context));
|
||||
object.put("user_id", UserManager.getInstance().getUserId());
|
||||
object.put("device_system", android.os.Build.VERSION.RELEASE);
|
||||
object.put("device_model", android.os.Build.MODEL);
|
||||
object.put("imei", MetaUtil.getIMEI());
|
||||
object.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
object.put("G_ID", UserManager.getInstance().getDeviceId());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
@ -279,13 +281,13 @@ public class LogUtils {
|
||||
try {
|
||||
object.put("version", PackageUtils.getVersionName());
|
||||
object.put("channel", HaloApp.getInstance().getChannel());
|
||||
object.put("android_id", MetaUtil.getAndroidId());
|
||||
object.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
object.put("time", Utils.getTime(context));
|
||||
object.put("network", DeviceUtils.getNetwork(context));
|
||||
object.put("user_id", UserManager.getInstance().getUserId());
|
||||
object.put("device_system", android.os.Build.VERSION.RELEASE);
|
||||
object.put("device_model", android.os.Build.MODEL);
|
||||
object.put("imei", MetaUtil.getIMEI());
|
||||
object.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
object.put("G_ID", UserManager.getInstance().getDeviceId());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
@ -298,13 +300,13 @@ public class LogUtils {
|
||||
Meta meta = MetaUtil.INSTANCE.getMeta();
|
||||
JSONObject metaObject = new JSONObject();
|
||||
try {
|
||||
metaObject.put("android_id", meta.getAndroid_id());
|
||||
metaObject.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
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("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
metaObject.put("mac", meta.getMac());
|
||||
metaObject.put("manufacturer", meta.getManufacturer());
|
||||
metaObject.put("model", meta.getModel());
|
||||
@ -376,6 +378,63 @@ public class LogUtils {
|
||||
uploadVideoStreaming(object);
|
||||
}
|
||||
|
||||
public static void uploadTopVideoStreamingPlaying(String action, String videoId, String videoTitle, String gameId, String gameName, String videoModel, String videoPlayStatus, double videoSize, int videoTotalTime, int videoPlayTs, int progress) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payloadObject = new JSONObject();
|
||||
try {
|
||||
object.put("event", "TOP_VIDEO_PLAYING");
|
||||
object.put("action", action);
|
||||
|
||||
payloadObject.put("video_mode", videoModel);//视频播放方式 ["自动播放"/"点击播放"/"全屏播放"]
|
||||
payloadObject.put("video_id", videoId);
|
||||
payloadObject.put("video_title", videoTitle);
|
||||
payloadObject.put("game_id", gameId);
|
||||
payloadObject.put("game_name", gameName);
|
||||
payloadObject.put("video_size", videoSize);
|
||||
payloadObject.put("video_total_time", videoTotalTime);
|
||||
payloadObject.put("progress", progress);
|
||||
payloadObject.put("video_play_ts", videoPlayTs);
|
||||
payloadObject.put("video_play_status", videoPlayStatus);
|
||||
|
||||
object.put("payload", payloadObject);
|
||||
object.put("meta", getMetaObject());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
LoghubUtils.log(object, "video_streaming", false);
|
||||
}
|
||||
|
||||
public static void uploadHomeVideoStreamingPlaying(String action, boolean videoShade, String videoId, String videoTitle, String gameId, String gameName, double videoSize, int videoTotalTime, int videoPlayTs, int progress) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payloadObject = new JSONObject();
|
||||
try {
|
||||
object.put("event", "HOME_VIDEO_PLAYING");
|
||||
object.put("action", action);
|
||||
|
||||
payloadObject.put("video_id", videoId);
|
||||
payloadObject.put("video_title", videoTitle);
|
||||
payloadObject.put("game_id", gameId);
|
||||
payloadObject.put("game_name", gameName);
|
||||
payloadObject.put("video_size", videoSize);
|
||||
payloadObject.put("video_total_time", videoTotalTime);
|
||||
payloadObject.put("progress", progress);
|
||||
payloadObject.put("video_play_ts", videoPlayTs);
|
||||
payloadObject.put("video_shade", String.valueOf(videoShade));//["true"/"false"]是否存在引导遮罩
|
||||
|
||||
object.put("payload", payloadObject);
|
||||
object.put("meta", getMetaObject());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
LoghubUtils.log(object, "video_streaming", false);
|
||||
}
|
||||
|
||||
public static void uploadWelcomeDialog(String action, String dialogId, String linkTitle) {
|
||||
ExposureEntity payload = new ExposureEntity();
|
||||
payload.setWelcomeDialogId(dialogId);
|
||||
@ -429,13 +488,13 @@ public class LogUtils {
|
||||
Meta meta = MetaUtil.INSTANCE.getMeta();
|
||||
JSONObject metaObject = new JSONObject();
|
||||
try {
|
||||
metaObject.put("android_id", meta.getAndroid_id());
|
||||
metaObject.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
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("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
metaObject.put("mac", meta.getMac());
|
||||
metaObject.put("manufacturer", meta.getManufacturer());
|
||||
metaObject.put("model", meta.getModel());
|
||||
@ -523,13 +582,13 @@ public class LogUtils {
|
||||
Meta meta = MetaUtil.INSTANCE.getMeta();
|
||||
JSONObject metaObject = new JSONObject();
|
||||
try {
|
||||
metaObject.put("android_id", meta.getAndroid_id());
|
||||
metaObject.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
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("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
metaObject.put("mac", meta.getMac());
|
||||
metaObject.put("manufacturer", meta.getManufacturer());
|
||||
metaObject.put("model", meta.getModel());
|
||||
@ -612,10 +671,16 @@ public class LogUtils {
|
||||
}
|
||||
|
||||
public static void uploadSearchGame(String event, String location, String key, String searchType) {
|
||||
uploadSearchClick(event, location, key, searchType, "", "");
|
||||
uploadSearchClick(event, location, key, searchType, "", "", false);
|
||||
}
|
||||
|
||||
public static void uploadSearchClick(String event, String location, String key, String searchType, String gameId, String gameName) {
|
||||
public static void uploadSearchClick(String event,
|
||||
String location,
|
||||
String key,
|
||||
String searchType,
|
||||
String gameId,
|
||||
String gameName,
|
||||
Boolean isMirrorData) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payload = new JSONObject();
|
||||
try {
|
||||
@ -628,6 +693,7 @@ public class LogUtils {
|
||||
payload.put("search_type", searchType); //搜索类型, 有四种取值 默认搜索/历史搜索/自动搜索/主动搜索
|
||||
payload.put("game_id", gameId); //event为search_click才取值
|
||||
payload.put("game_name", gameName); //event为search_click才取值
|
||||
payload.put("is_mirror_data", isMirrorData);
|
||||
object.put("payload", payload);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
@ -659,6 +725,25 @@ public class LogUtils {
|
||||
logCatalogEvent("click_content_list", entrance, key, seq1, -1, seqContent, seqContentList);
|
||||
}
|
||||
|
||||
public static void logSpecialCatalogBannerClickEvent(String key, int seq) {
|
||||
JSONObject object = new JSONObject();
|
||||
try {
|
||||
object.put("event", "category_lunbo_click");
|
||||
object.put("meta", getMetaObject());
|
||||
object.put("timestamp", System.currentTimeMillis() / 1000);
|
||||
|
||||
object.put("key", key);
|
||||
object.put("sequence", seq);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
LoghubUtils.log(object, "event", false);
|
||||
}
|
||||
|
||||
private static void logCatalogEvent(String event, String entrance, String key, int seq1, int seq2, int seqContent, int seqContentList) {
|
||||
JSONObject object = new JSONObject();
|
||||
JSONObject payload = new JSONObject();
|
||||
@ -683,4 +768,48 @@ public class LogUtils {
|
||||
}
|
||||
LoghubUtils.log(object, "event", false);
|
||||
}
|
||||
|
||||
public static void uploadPackageSkip(String event, String action, String gameId, String gameName) {
|
||||
JSONObject object = new JSONObject();
|
||||
try {
|
||||
object.put("event", event);//external_jump/external_show
|
||||
object.put("action", action);
|
||||
if (!TextUtils.isEmpty(gameId) && !TextUtils.isEmpty(gameName)) {
|
||||
object.put("game_id", gameId);
|
||||
object.put("game_name", gameName);
|
||||
}
|
||||
object.put("meta", getMetaObject());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
LoghubUtils.log(object, "event", false);
|
||||
}
|
||||
|
||||
public static void logStartAd(String event, StartupAdEntity adEntity) {
|
||||
JSONObject object = new JSONObject();
|
||||
try {
|
||||
object.put("event", event);
|
||||
object.put("timestamp", System.currentTimeMillis() / 1000);
|
||||
object.put("meta", getMetaObject());
|
||||
if (adEntity != null) {
|
||||
object.put("abs_id", adEntity.getId());
|
||||
object.put("abs_text", adEntity.getDesc());
|
||||
if (adEntity.getButton()) {
|
||||
object.put("abs_type", adEntity.getJump().getType());
|
||||
object.put("abs_link", adEntity.getJump().getLink());
|
||||
object.put("abs_link_title", adEntity.getJump().getText());
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
Utils.log("LogUtils->" + object.toString());
|
||||
}
|
||||
LoghubUtils.log(object, "event", false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -29,7 +29,10 @@ import android.widget.TextView;
|
||||
import com.gh.common.Base64ImageHolder;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WeiBoShareActivity;
|
||||
import com.lightgame.utils.Utils;
|
||||
import com.sina.weibo.sdk.WbSdk;
|
||||
import com.sina.weibo.sdk.auth.AuthInfo;
|
||||
import com.tencent.connect.auth.QQToken;
|
||||
import com.tencent.connect.share.QQShare;
|
||||
import com.tencent.mm.opensdk.modelmsg.SendMessageToWX;
|
||||
@ -48,7 +51,8 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gh.common.util.LoginHelper.WEIBO_SCOPE;
|
||||
|
||||
/**
|
||||
* Created by khy on 2016/11/8.
|
||||
@ -86,13 +90,17 @@ public class MessageShareUtils {
|
||||
@Override
|
||||
public void onError(UiError uiError) {
|
||||
// 单分享图片不支持显示未安装弹窗,手动调出
|
||||
Activity activity = mActivity.get();
|
||||
if (activity != null && !ShareUtils.isQQClientAvailable(activity.getApplication())) {
|
||||
new TDialog(activity,
|
||||
"",
|
||||
"http://openmobile.qq.com/oauth2.0/m_jump_by_version",
|
||||
null,
|
||||
new QQToken("")).show();
|
||||
if (mActivity != null) {
|
||||
Activity activity = mActivity.get();
|
||||
if (activity != null && !ShareUtils.isQQClientAvailable(activity.getApplication())) {
|
||||
new TDialog(activity,
|
||||
"",
|
||||
"http://openmobile.qq.com/oauth2.0/m_jump_by_version",
|
||||
null,
|
||||
new QQToken("")).show();
|
||||
} else {
|
||||
Utils.toast(mContext, "分享失败");
|
||||
}
|
||||
} else {
|
||||
Utils.toast(mContext, "分享失败");
|
||||
}
|
||||
@ -207,49 +215,54 @@ public class MessageShareUtils {
|
||||
}
|
||||
|
||||
public void shareFromWeb(Activity activity, String type) {
|
||||
// base64转bitmap
|
||||
byte[] decode = Base64.decode(Base64ImageHolder.INSTANCE.getImage(), Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
|
||||
// 转完后重新置位
|
||||
Base64ImageHolder.INSTANCE.setImage("");
|
||||
|
||||
this.picName = "shareImgPic.jpg";
|
||||
this.mActivity = new WeakReference<>(activity);
|
||||
this.shareBm = bitmap;
|
||||
|
||||
// 保存图片
|
||||
File file = new File(activity.getExternalCacheDir().getPath() + "/ShareImg");
|
||||
if (!file.isDirectory()) {
|
||||
file.delete();
|
||||
file.mkdirs();
|
||||
}
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
writeBitmap(file.getPath(), picName, bitmap, false);
|
||||
if ("weibo".equals(type)) {
|
||||
weiboShare();
|
||||
} else {
|
||||
// base64转bitmap
|
||||
byte[] decode = Base64.decode(Base64ImageHolder.INSTANCE.getImage(), Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
|
||||
// 转完后重新置位
|
||||
Base64ImageHolder.INSTANCE.setImage("");
|
||||
this.picName = "shareImgPic.jpg";
|
||||
this.shareBm = bitmap;
|
||||
|
||||
// 分享
|
||||
switch (type) {
|
||||
case "qq" :
|
||||
qqSahre();
|
||||
break;
|
||||
case "qq_zone" :
|
||||
qZoneSahre();
|
||||
break;
|
||||
case "wechat" :
|
||||
wechatSahre();
|
||||
break;
|
||||
case "wechat_moments" :
|
||||
wechatMomentsSahre();
|
||||
case "save" :
|
||||
String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/ghzhushou/";
|
||||
writeBitmap(savePath, "gh-" + new Date().getTime() + ".jpg", shareBm, true);
|
||||
break;
|
||||
// 保存图片
|
||||
File file = new File(activity.getExternalCacheDir().getPath() + "/ShareImg");
|
||||
if (!file.isDirectory()) {
|
||||
file.delete();
|
||||
file.mkdirs();
|
||||
}
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
writeBitmap(file.getPath(), picName, bitmap, false);
|
||||
|
||||
// 分享
|
||||
switch (type) {
|
||||
case "qq" :
|
||||
qqShare();
|
||||
break;
|
||||
case "qq_zone" :
|
||||
qZoneShare();
|
||||
break;
|
||||
case "wechat" :
|
||||
wechatShare();
|
||||
break;
|
||||
case "wechat_moments" :
|
||||
wechatMomentsShare();
|
||||
break;
|
||||
case "save" :
|
||||
String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/ghzhushou/";
|
||||
writeBitmap(savePath, "gh-" + new Date().getTime() + ".jpg", shareBm, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//QQ分享
|
||||
private void qqSahre() {
|
||||
private void qqShare() {
|
||||
Utils.toast(mContext, "分享跳转中...");
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE,
|
||||
@ -269,7 +282,7 @@ public class MessageShareUtils {
|
||||
}
|
||||
|
||||
//QQ空间分享
|
||||
private void qZoneSahre() {
|
||||
private void qZoneShare() {
|
||||
Utils.toast(mContext, "分享跳转中...");
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE,
|
||||
@ -289,7 +302,7 @@ public class MessageShareUtils {
|
||||
}
|
||||
|
||||
//微信好友分享
|
||||
private void wechatSahre() {
|
||||
private void wechatShare() {
|
||||
Utils.toast(mContext, "分享跳转中...");
|
||||
|
||||
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
|
||||
@ -325,7 +338,7 @@ public class MessageShareUtils {
|
||||
}
|
||||
|
||||
//微信朋友圈分享
|
||||
private void wechatMomentsSahre() {
|
||||
private void wechatMomentsShare() {
|
||||
Utils.toast(mContext, "分享跳转中...");
|
||||
|
||||
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
|
||||
@ -352,6 +365,20 @@ public class MessageShareUtils {
|
||||
mPopupWindow.dismiss();
|
||||
}
|
||||
|
||||
//新浪微博分享
|
||||
public void weiboShare() {
|
||||
WbSdk.install(mContext, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE));
|
||||
|
||||
Activity activity = mActivity.get();
|
||||
if (activity != null) {
|
||||
Intent intent = WeiBoShareActivity.getWeiboImageShareIntent(activity);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
if (mPopupWindow == null) return;
|
||||
mPopupWindow.dismiss();
|
||||
}
|
||||
|
||||
private String buildTransaction(final String type) {
|
||||
return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
|
||||
}
|
||||
@ -474,22 +501,22 @@ public class MessageShareUtils {
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
switch (holder.getPosition()) {
|
||||
case 0:
|
||||
wechatSahre();
|
||||
wechatShare();
|
||||
if (shareType != 2) {
|
||||
// activity.finish();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
wechatMomentsSahre();
|
||||
wechatMomentsShare();
|
||||
if (shareType != 2) {
|
||||
// activity.finish();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
qqSahre();
|
||||
qqShare();
|
||||
break;
|
||||
case 3:
|
||||
qZoneSahre();
|
||||
qZoneShare();
|
||||
break;
|
||||
case 4:
|
||||
String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/ghzhushou/";
|
||||
|
||||
@ -37,4 +37,15 @@ object NumberUtils {
|
||||
val minute = Math.round((totalMinute - hour * 60).toFloat())
|
||||
return hour.toString() + "小时" + if (minute == 0) "" else minute.toString() + "分钟"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findMax(lastPositions: IntArray): Int {
|
||||
var max = lastPositions[0]
|
||||
for (value in lastPositions) {
|
||||
if (value > max) {
|
||||
max = value
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
}
|
||||
@ -26,8 +26,8 @@ object PackageInstaller {
|
||||
* 为了兼容java代码
|
||||
*/
|
||||
@JvmStatic
|
||||
fun install(context: Context, downloadEntity: DownloadEntity) {
|
||||
install(context, downloadEntity, true)
|
||||
fun install(context: Context, downloadEntity: DownloadEntity?) {
|
||||
downloadEntity?.let { install(context, downloadEntity, true) }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,15 +44,19 @@ object PackageInstaller {
|
||||
|
||||
val currentActivity = AppManager.getInstance().currentActivity() ?: return
|
||||
|
||||
InstallPermissionDialogFragment.show(currentActivity as AppCompatActivity, downloadEntity) {
|
||||
// 取消状态栏下载完成的通知,若存在
|
||||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
// TODO 此处可能遇到 activity 是 WXEntryActivity
|
||||
// TODO 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装!
|
||||
if (currentActivity is AppCompatActivity) {
|
||||
InstallPermissionDialogFragment.show(currentActivity, downloadEntity) {
|
||||
// 取消状态栏下载完成的通知,若存在
|
||||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
|
||||
if (isXapk) {
|
||||
XapkInstaller.install(context, downloadEntity, showUnzipToast)
|
||||
} else {
|
||||
install(context, downloadEntity.path)
|
||||
if (isXapk) {
|
||||
XapkInstaller.install(context, downloadEntity, showUnzipToast)
|
||||
} else {
|
||||
install(context, downloadEntity.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,6 +71,8 @@ object PackageInstaller {
|
||||
fun install(context: Context, pkgPath: String) {
|
||||
try {
|
||||
if (PackageUtils.isCanLaunchSetup(context, pkgPath)) {
|
||||
HaloApp.put(Constants.LAST_INSTALL_GAME, pkgPath)
|
||||
|
||||
val installIntent = getInstallIntent(context, pkgPath)
|
||||
context.startActivity(installIntent)
|
||||
} else {
|
||||
@ -115,28 +121,43 @@ object PackageInstaller {
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uninstall(context: Context, path: String) {
|
||||
val packageName = PackageUtils.getPackageNameByPath(context, path)
|
||||
uninstallForPackageName(context, packageName)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据包名卸载应用
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uninstallForPackageName(context: Context, pkn: String) {
|
||||
if (pkn.isBlank()) return
|
||||
|
||||
val uninstallIntent = Intent()
|
||||
uninstallIntent.action = Intent.ACTION_DELETE
|
||||
uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT)
|
||||
if (context !is Activity) {
|
||||
uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
val packageName = PackageUtils.getPackageNameByPath(context, path)
|
||||
uninstallIntent.data = Uri.parse("package:$packageName")
|
||||
InstallUtils.getInstance(context).addUninstall(packageName)
|
||||
uninstallIntent.data = Uri.parse("package:$pkn")
|
||||
InstallUtils.getInstance(context).addUninstall(pkn)
|
||||
context.startActivity(uninstallIntent)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDownloadPath(gameName: String?, format: String?): String {
|
||||
return FileUtils.getDownloadPath(HaloApp.getInstance().application, getDownloadFileName(gameName, format))
|
||||
fun getDownloadPathWithId(id: String, format: String?): String {
|
||||
return FileUtils.getDownloadPath(HaloApp.getInstance().application, id + "." + getFileSuffixByFormat(format))
|
||||
}
|
||||
|
||||
private fun getDownloadFileName(gameName: String?, format: String?): String {
|
||||
var fileExtension = "apk"
|
||||
if (format == XapkInstaller.XAPK_EXTENSION_NAME) {
|
||||
fileExtension = XapkInstaller.XAPK_EXTENSION_NAME
|
||||
private fun getFileSuffixByFormat(format: String?): String {
|
||||
return if (format == XapkInstaller.XAPK_EXTENSION_NAME) {
|
||||
XapkInstaller.XAPK_EXTENSION_NAME
|
||||
} else {
|
||||
"apk"
|
||||
}
|
||||
return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis()) + "." + fileExtension
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createDownloadId(gameName: String?): String {
|
||||
return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis())
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.g00fy2.versioncompare.Version;
|
||||
import com.gh.common.xapk.XapkInstaller;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
@ -26,12 +27,16 @@ import com.gh.gamecenter.manager.PackagesManager;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
|
||||
import net.dongliu.apk.parser.ApkFile;
|
||||
import net.dongliu.apk.parser.bean.ApkMeta;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@ -183,16 +188,13 @@ public class PackageUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO 找一个更好的办法来比较签名并且不触发 ANR
|
||||
public static boolean compareSignatureBetweenInstalledAppWithApk(Context context, String packageName, String apkFilePath) {
|
||||
try {
|
||||
// 统计签名比较使用的频率 (大文件会触发 ANR)
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"SIGNATURE_COMPARE",
|
||||
"packageName", packageName);
|
||||
|
||||
// 据 Sentry 统计,刚上架一个周末的包里对这个方法有 700+ 次调用,然后其中一部分会造成 ANR
|
||||
Signature sig = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures[0];
|
||||
|
||||
// Fuck HUAWEI, 华为系统调用 getPackageArchiveInfo 获取魔羯 apk 的签名时会耗时超过5秒造成 ANR,没有找到解决方法
|
||||
// 调用 getPackageArchiveInfo 获取较大的 apk 的签名时会耗时超过5秒造成 ANR,没有找到解决方法
|
||||
// 如果可以的话尽量避免调用 getPackageArchiveInfo 方法
|
||||
Signature releaseSig = context.getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_SIGNATURES).signatures[0];
|
||||
return sig.hashCode() == releaseSig.hashCode();
|
||||
@ -293,14 +295,69 @@ public class PackageUtils {
|
||||
* 根据路径,获取apk的包名
|
||||
*/
|
||||
public static String getPackageNameByPath(Context context, String path) {
|
||||
|
||||
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||||
// 大于 1G 的 APK 就走另类方法
|
||||
if (isDeviceUnableToHandleBigApkFile(path)) {
|
||||
// XAPK 不存在 AndroidManifest
|
||||
if (path.contains(XapkInstaller.XAPK_EXTENSION_NAME)) {
|
||||
return null;
|
||||
}
|
||||
return getPackageNameByPathAlternative(path);
|
||||
}
|
||||
|
||||
PackageManager packageManager = context.getApplicationContext().getPackageManager();
|
||||
PackageInfo info = packageManager.getPackageArchiveInfo(path, 0);
|
||||
if (info != null) {
|
||||
ApplicationInfo appInfo = info.applicationInfo;
|
||||
return appInfo.packageName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 此设备是否不能调用 packageManager.getPackageArchiveInfo 来获取 APK 信息
|
||||
*
|
||||
* 部分设备 (已知 vivo 5.1.1 及 5.0.1 的设备) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||||
*/
|
||||
public static boolean isDeviceUnableToHandleBigApkFile(String path) {
|
||||
if ("vivo".equals(Build.MANUFACTURER)
|
||||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
File file = new File(path);
|
||||
|
||||
if (file != null && file.length() > 1024 * 1024 * 1024) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 APK 文件里读包名的另类方法
|
||||
* 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||||
*
|
||||
* 令人迷惑的点:
|
||||
* 1. 同样的代码,同样的 APK 在 demo 包里调用 packageManager.getPackageArchiveInfo 并不会 ANR
|
||||
* 2. 把 packageManager.getPackageArchiveInfo 放在子线程调用一样会出现 ANR
|
||||
* 3. demo 里 manifest 中 application 配置和 targetSdk 也改成与光环一样也不会出现 ANR
|
||||
*
|
||||
* 大概是光环的某个配置触发了系统的 bug ?
|
||||
*
|
||||
*/
|
||||
private static String getPackageNameByPathAlternative(String path) {
|
||||
ApkFile apkParser = null;
|
||||
try {
|
||||
apkParser = new ApkFile(new File(path));
|
||||
ApkMeta apkMeta = apkParser.getApkMeta();
|
||||
apkParser.close();
|
||||
return apkMeta.getPackageName();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 根据包名,判断是否已安装该游戏
|
||||
@ -485,9 +542,14 @@ public class PackageUtils {
|
||||
|
||||
if (gh_version != null && apkEntity.getGhVersion() != null && gh_id != null) {
|
||||
gh_version = gh_version.substring(2);
|
||||
return Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion())
|
||||
&& apkEntity.getForce()
|
||||
&& gh_id.equals(gameId);
|
||||
try {
|
||||
return Long.parseLong(gh_version) < Long.parseLong(apkEntity.getGhVersion()) && apkEntity
|
||||
.getForce() && gh_id.equals(gameId);
|
||||
} catch (NumberFormatException exception) {
|
||||
// gh_id 可能出錯
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -519,15 +581,21 @@ public class PackageUtils {
|
||||
* @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;
|
||||
try {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 遇到异常了让这次调用正常执行
|
||||
e.printStackTrace();
|
||||
return BuildConfig.APPLICATION_ID;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -553,6 +621,13 @@ public class PackageUtils {
|
||||
if (!pm.isScreenOn()) return false;
|
||||
}
|
||||
|
||||
// 上面的判断逻辑可能还是不够用,还是有少部分
|
||||
// Not allowed to start service Intent: app is in background 错误
|
||||
// 这里补充个自有的规则
|
||||
if (!HaloApp.getInstance().isRunningForeground) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String packageName = context.getApplicationContext().getPackageName();
|
||||
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
||||
// The name of the process that this object is associated with.
|
||||
|
||||
@ -5,10 +5,12 @@ import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.gh.common.constant.Constants
|
||||
import com.tbruyelle.rxpermissions2.RxPermissions
|
||||
|
||||
object PermissionHelper {
|
||||
@ -59,41 +61,41 @@ object PermissionHelper {
|
||||
@JvmStatic
|
||||
fun checkStoragePermissionBeforeAction(context: Context, emptyCallback: EmptyCallback) {
|
||||
if (context is FragmentActivity) {
|
||||
val rxPermission = RxPermissions(context)
|
||||
if (context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
&& context.checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
emptyCallback.onCallback()
|
||||
SPUtils.setBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION, false)
|
||||
} else {
|
||||
showDialogBeforeRequestingStorageDialog(context, emptyCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryWithDefaultCatch {
|
||||
rxPermission
|
||||
.requestEachCombined(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
DialogUtils.showPermissionDialog(context,
|
||||
"权限申请",
|
||||
"光环助手需要存储权限,以保证能正常使用相关功能",
|
||||
"重试",
|
||||
"放弃",
|
||||
{ checkStoragePermissionBeforeAction(context, emptyCallback) },
|
||||
null)
|
||||
}
|
||||
else -> {
|
||||
DialogUtils.showPermissionDialog(context,
|
||||
"权限申请",
|
||||
"在设置-应用-光环助手-权限中开启存储权限,以保证能正常使用相关功能",
|
||||
"去设置",
|
||||
"放弃",
|
||||
{
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.getPackageName())
|
||||
context.startActivity(intent)
|
||||
},
|
||||
null)
|
||||
private fun checkStoragePermissionBeforeAction(context: FragmentActivity, emptyCallback: EmptyCallback) {
|
||||
tryWithDefaultCatch {
|
||||
val rxPermission = RxPermissions(context)
|
||||
rxPermission
|
||||
.requestEachCombined(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
SPUtils.setBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION, false)
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
// do nothing
|
||||
}
|
||||
else -> {
|
||||
if (SPUtils.getBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION)) {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.packageName)
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
SPUtils.setBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,39 +103,37 @@ object PermissionHelper {
|
||||
@JvmStatic
|
||||
fun checkReadPhoneStateAndStoragePermissionBeforeAction(context: Context, emptyCallback: EmptyCallback) {
|
||||
if (context is FragmentActivity) {
|
||||
val rxPermission = RxPermissions(context)
|
||||
if (context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
&& context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
&& context.checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
emptyCallback.onCallback()
|
||||
} else {
|
||||
val rxPermission = RxPermissions(context)
|
||||
|
||||
tryWithDefaultCatch {
|
||||
rxPermission
|
||||
.requestEachCombined(
|
||||
Manifest.permission.READ_PHONE_STATE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
tryWithDefaultCatch {
|
||||
rxPermission
|
||||
.requestEachCombined(
|
||||
Manifest.permission.READ_PHONE_STATE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
|
||||
ActivationHelper.sendActivationInfo()
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
DialogUtils.showPermissionDialog(context, "权限申请",
|
||||
"光环助手需要获取存储权限和手机信息权限,以保证能正常使用相关功能", "重试", "放弃",
|
||||
{ checkStoragePermissionBeforeAction(context, emptyCallback) }, null)
|
||||
}
|
||||
else -> {
|
||||
DialogUtils.showPermissionDialog(context, "权限申请",
|
||||
"在设置-应用-光环助手-权限中开启获取存储权限和手机信息,以保证能正常使用相关功能",
|
||||
"去设置",
|
||||
"放弃",
|
||||
{
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.getPackageName())
|
||||
context.startActivity(intent)
|
||||
}, null)
|
||||
ActivationHelper.sendActivationInfo()
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
// do nothing
|
||||
}
|
||||
else -> {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.getPackageName())
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,34 +141,73 @@ object PermissionHelper {
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun checkReadPhoneStatePermissionBeforeAction(context: Context, emptyCallback: EmptyCallback) {
|
||||
if (context is FragmentActivity) {
|
||||
|
||||
if (context.checkCallingOrSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
emptyCallback.onCallback()
|
||||
} else {
|
||||
val rxPermission = RxPermissions(context)
|
||||
|
||||
tryWithDefaultCatch {
|
||||
rxPermission
|
||||
.requestEachCombined(Manifest.permission.READ_PHONE_STATE)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
|
||||
ActivationHelper.sendActivationInfo()
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
// do nothing
|
||||
}
|
||||
else -> {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.getPackageName())
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@JvmStatic
|
||||
fun checkCalendarPermissionBeforeAction(context: Context, emptyCallback: EmptyCallback) {
|
||||
if (context is FragmentActivity) {
|
||||
val rxPermission = RxPermissions(context)
|
||||
|
||||
tryWithDefaultCatch {
|
||||
rxPermission
|
||||
.requestEachCombined(Manifest.permission.READ_PHONE_STATE)
|
||||
.requestEachCombined(Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CALENDAR)
|
||||
.subscribe { permission ->
|
||||
when {
|
||||
permission.granted -> {
|
||||
emptyCallback.onCallback()
|
||||
|
||||
ActivationHelper.sendActivationInfo()
|
||||
}
|
||||
permission.shouldShowRequestPermissionRationale -> {
|
||||
DialogUtils.showPermissionDialog(context, "权限申请",
|
||||
"光环助手需要获取手机信息权限,以保证能正常使用相关功能", "重试", "放弃",
|
||||
{ checkStoragePermissionBeforeAction(context, emptyCallback) }, null)
|
||||
DialogUtils.showPermissionDialog(context,
|
||||
"权限申请",
|
||||
"光环助手需要日历权限,以保证能正常使用相关功能",
|
||||
"重试",
|
||||
"放弃",
|
||||
{ checkCalendarPermissionBeforeAction(context, emptyCallback) },
|
||||
null)
|
||||
}
|
||||
else -> {
|
||||
DialogUtils.showPermissionDialog(context, "权限申请",
|
||||
"在设置-应用-光环助手-权限中开启获取手机信息,以保证能正常使用相关功能",
|
||||
DialogUtils.showPermissionDialog(context,
|
||||
"权限申请",
|
||||
"在设置-应用-光环助手-权限中开启日历权限,以保证能正常使用相关功能",
|
||||
"去设置",
|
||||
"放弃",
|
||||
{
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.data = Uri.parse("package:" + context.getPackageName())
|
||||
context.startActivity(intent)
|
||||
}, null)
|
||||
},
|
||||
null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,7 +259,24 @@ object PermissionHelper {
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDialogBeforeRequestingStorageDialog(context: FragmentActivity, emptyCallback: EmptyCallback) {
|
||||
DialogHelper.showRoundedCornerDialog(
|
||||
context,
|
||||
title = "权限申请",
|
||||
content = "光环助手将向您申请开启设备的存储权限,以保证能正常使用相关功能。拒绝授权将无法正常使用部分功能。",
|
||||
hint = "查看权限应用场景",
|
||||
cancelText = "放弃",
|
||||
confirmText = "去授权",
|
||||
hintClickCallback = {
|
||||
DirectUtils.directToWebView(context, Constants.PERMISSION_SCENARIO_ADDRESS, "(权限弹窗)")
|
||||
},
|
||||
cancelClickCallback = null,
|
||||
confirmClickCallback = {
|
||||
checkStoragePermissionBeforeAction(context, emptyCallback)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,5 +292,4 @@ object PermissionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -64,34 +64,24 @@ object ReservationHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun showDeleteReservationDialog(context: Context, emptyCallback: EmptyCallback) {
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
DialogUtils.showCancelOrDeleteReservationDialog(context,
|
||||
"删除预约",
|
||||
"游戏已上线,你可以删除此预约记录,确定删除吗?",
|
||||
"确定删除",
|
||||
"暂不删除",
|
||||
{ emptyCallback.onCallback() },
|
||||
null,
|
||||
trackMtaEvent = true
|
||||
// , mtaEvent = "预约游戏",
|
||||
// mtaKey = "删除预约弹窗"
|
||||
)
|
||||
"暂不删除", {
|
||||
emptyCallback.onCallback()
|
||||
}, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showCancelReservationDialog(context: Context, emptyCallback: EmptyCallback) {
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
DialogUtils.showCancelOrDeleteReservationDialog(context,
|
||||
"取消预约",
|
||||
"取消之后你将无法收到游戏上线的通知,确定取消预约吗?",
|
||||
"确定取消",
|
||||
"暂不取消",
|
||||
{ emptyCallback.onCallback() },
|
||||
null,
|
||||
trackMtaEvent = true
|
||||
// , mtaEvent = "预约游戏",
|
||||
// mtaKey = "取消预约弹窗"
|
||||
)
|
||||
"暂不取消", {
|
||||
emptyCallback.onCallback()
|
||||
}, null)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,10 +2,10 @@ package com.gh.common.util
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.lightgame.utils.Utils
|
||||
import io.sentry.core.Sentry
|
||||
import io.sentry.core.SentryEvent
|
||||
import io.sentry.core.SentryLevel
|
||||
import io.sentry.core.protocol.Message
|
||||
import io.sentry.Sentry
|
||||
import io.sentry.SentryEvent
|
||||
import io.sentry.SentryLevel
|
||||
import io.sentry.protocol.Message
|
||||
|
||||
object SentryHelper {
|
||||
|
||||
@ -17,7 +17,7 @@ object SentryHelper {
|
||||
val message = Message()
|
||||
message.message = eventId
|
||||
sentryEvent.message = message
|
||||
sentryEvent.level = SentryLevel.LOG
|
||||
sentryEvent.level = SentryLevel.INFO
|
||||
|
||||
for (i in kv.indices) {
|
||||
if (i % 2 != 0) {
|
||||
|
||||
@ -90,7 +90,8 @@ public class ShareUtils {
|
||||
video("视频"),
|
||||
web("web链接"),
|
||||
userHome("个人主页"),
|
||||
qaDetail("QA内容详情");
|
||||
qaDetail("QA内容详情"),
|
||||
inviteFriends("邀请好友");
|
||||
|
||||
private String name;
|
||||
|
||||
@ -124,6 +125,7 @@ public class ShareUtils {
|
||||
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);
|
||||
EnergyTaskHelper.postEnergyTaskForShare(ShareUtils.shareEntrance.getName(), ShareUtils.resourceId, ShareUtils.shareEntity.getShareUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,6 +171,40 @@ public class ShareUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 邀请好友(专属链接)
|
||||
public void shareInviteFriends(Activity activity, String url, String way) {
|
||||
if (activity.isFinishing()) return;
|
||||
this.mActivity = new WeakReference<>(activity);
|
||||
this.shareIcon = activity.getString(R.string.gh_icon_url);
|
||||
this.shareUrl = url;
|
||||
this.mSummary = "卡牌神器,海量游戏下载,积分大礼,等你尝鲜";
|
||||
this.mTitle = "推荐一款好玩的游戏下载APP【光环助手】";
|
||||
this.mShareEntrance = ShareEntrance.inviteFriends;
|
||||
ShareUtils.shareEntrance = mShareEntrance;
|
||||
ShareUtils.resourceId = "";
|
||||
ShareUtils.shareEntity = new ShareEntity(shareUrl, mTitle, mSummary);
|
||||
LogUtils.uploadShareEnter(mShareEntrance.getName(), shareUrl, mTitle, mSummary, "");
|
||||
|
||||
// 分享
|
||||
switch (way) {
|
||||
case "qq" :
|
||||
qqShare();
|
||||
break;
|
||||
case "qq_zone" :
|
||||
qZoneShare();
|
||||
break;
|
||||
case "wechat" :
|
||||
wechatShare();
|
||||
break;
|
||||
case "wechat_moments" :
|
||||
wechatMomentsShare();
|
||||
break;
|
||||
case "weibo" :
|
||||
sinaWeiboShare();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void showShareUserHomeWindows(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);
|
||||
@ -318,13 +354,27 @@ public class ShareUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public void shareParamsDetail(Activity activity, 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.mShareEntrance = shareEntrance;
|
||||
ShareUtils.shareEntrance = mShareEntrance;
|
||||
ShareUtils.resourceId = id;
|
||||
ShareUtils.shareEntity = new ShareEntity(shareUrl, mTitle, mSummary);
|
||||
LogUtils.uploadShareEnter(mShareEntrance.getName(), shareUrl, mTitle, mSummary, id);
|
||||
}
|
||||
|
||||
|
||||
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分享
|
||||
private void qqShare() {
|
||||
public void qqShare() {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
Bundle params = new Bundle();
|
||||
|
||||
@ -359,7 +409,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//微信好友分享
|
||||
private void wechatShare() {
|
||||
public void wechatShare() {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
|
||||
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
|
||||
@ -476,7 +526,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//QQ空间分享
|
||||
private void qZoneShare() {
|
||||
public void qZoneShare() {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
Bundle params = new Bundle();
|
||||
|
||||
@ -515,7 +565,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//微信朋友圈分享
|
||||
private void wechatMomentsShare() {
|
||||
public void wechatMomentsShare() {
|
||||
Utils.toast(mContext, R.string.share_skip);
|
||||
|
||||
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
|
||||
@ -558,7 +608,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//新浪微博分享
|
||||
private void sinaWeiboShare() {
|
||||
public void sinaWeiboShare() {
|
||||
WbSdk.install(mContext, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE));
|
||||
|
||||
if (mShareEntrance == ShareEntrance.qaDetail) {
|
||||
@ -567,7 +617,7 @@ public class ShareUtils {
|
||||
}
|
||||
Activity activity = mActivity.get();
|
||||
if (activity != null) {
|
||||
Intent intent = WeiBoShareActivity.getWeiboshareIntent(
|
||||
Intent intent = WeiBoShareActivity.getWeiboShareIntent(
|
||||
activity,
|
||||
shareUrl,
|
||||
shareIcon,
|
||||
@ -584,7 +634,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//短信分享
|
||||
private void shortMessageShare() {
|
||||
public void shortMessageShare() {
|
||||
String smsBody;
|
||||
switch (mShareEntrance) {
|
||||
case news:
|
||||
@ -628,7 +678,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
//复制文字链接
|
||||
private void copyLink(String copyContent) {
|
||||
public void copyLink(String copyContent) {
|
||||
shareType = "copy_link";
|
||||
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
|
||||
if (mShareEntrance != ShareEntrance.shareGh) {
|
||||
@ -639,6 +689,10 @@ public class ShareUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
private class ShareRecyclerViewAdapter extends RecyclerView.Adapter<ShareRecyclerViewAdapter.ViewHolder> {
|
||||
|
||||
private OnItemClickListener listener;
|
||||
@ -664,6 +718,7 @@ public class ShareUtils {
|
||||
if (listener != null) {
|
||||
listener.onItemClick(holder.getAdapterPosition());
|
||||
}
|
||||
|
||||
switch (holder.getPosition()) {
|
||||
case 0:
|
||||
shareType = "wechat_friend";
|
||||
@ -749,7 +804,7 @@ public class ShareUtils {
|
||||
}
|
||||
|
||||
private void safelyDismiss() {
|
||||
if (popupWindow.get() != null) {
|
||||
if (popupWindow != null && popupWindow.get() != null) {
|
||||
popupWindow.get().dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ object SPUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun getString(key: String): String {
|
||||
return sp.getString(key, "")
|
||||
return sp.getString(key, "")?:""
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@ -71,7 +71,7 @@ object SyncDataBetweenPageHelper {
|
||||
val fields = syncData::class.java.declaredFields
|
||||
var isNeedNotify = false
|
||||
for (field in fields) {
|
||||
if (field.getAnnotation(Synchronize::class.java) != null) {
|
||||
if (field.getAnnotation(Synchronize::class.java) != null && resultData != null) {
|
||||
// 同步数据
|
||||
val resultField = resultData::class.java.getDeclaredField(field.name)
|
||||
resultField.isAccessible = true
|
||||
|
||||
@ -88,6 +88,17 @@ object TimeUtils {
|
||||
return String.format(Locale.CHINA, "%02d:%02d", minute, second)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时长(将秒数转化为HH:mm:ss格式)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun formatDuration(seconds: Long): String {
|
||||
val hour = seconds / 3600
|
||||
val minute = seconds % 3600 / 60
|
||||
val second = seconds % 60
|
||||
return String.format(Locale.CHINA, "%02d:%02d:%02d", hour, minute, second)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDayString(date: Long): String {
|
||||
val offSet = Calendar.getInstance().timeZone.rawOffset
|
||||
@ -117,6 +128,20 @@ object TimeUtils {
|
||||
return calendar.timeInMillis
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTimeOfToday(hour: Int) = getTimeOfToday(hour, 0)
|
||||
|
||||
@JvmStatic
|
||||
fun getTimeOfToday(hour: Int, minute: Int): Long {
|
||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"))
|
||||
calendar.timeInMillis = getJavaTimestamp(System.currentTimeMillis())
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour)
|
||||
calendar.set(Calendar.MINUTE, minute)
|
||||
calendar.set(Calendar.SECOND, 0)
|
||||
calendar.set(Calendar.MILLISECOND, 0)
|
||||
return calendar.timeInMillis
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getJavaTimestamp(timestamp: Long): Long {
|
||||
return if ((log10(timestamp.toDouble()) + 1).toInt() == 10) {
|
||||
|
||||
@ -6,6 +6,7 @@ import com.gh.common.constant.Constants;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.halo.assistant.HaloApp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.collection.ArrayMap;
|
||||
@ -17,10 +18,12 @@ public class TimestampUtils {
|
||||
|
||||
private static ArrayMap<String, Integer> intervalMap; // 间隔
|
||||
private static ArrayMap<String, Integer> cdMap; // cd
|
||||
private static ArrayList<String> whiteList; // 白名单
|
||||
|
||||
public static void initMap() {
|
||||
initIntervalMap();
|
||||
initCDMap();
|
||||
initWhiteList();
|
||||
}
|
||||
|
||||
private static void initIntervalMap() {
|
||||
@ -43,11 +46,25 @@ public class TimestampUtils {
|
||||
cdMap.put(".*packages.*", Constants.PACKAGES_CD);
|
||||
}
|
||||
|
||||
private static void initWhiteList() {
|
||||
whiteList = new ArrayList<>();
|
||||
whiteList.add(".*tasks:check.*");
|
||||
whiteList.add(".*novice_tasks.*");
|
||||
whiteList.add(".*daily_tasks.*");
|
||||
whiteList.add(".*energies.*");
|
||||
}
|
||||
|
||||
/*
|
||||
* 为url添加timestamp
|
||||
*/
|
||||
public static String addTimestamp(String url) {
|
||||
|
||||
for (String key : whiteList) {
|
||||
if (Pattern.matches(key, url)) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG || "GH_REFRESH".equals(HaloApp.getInstance().getChannel())) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return url;
|
||||
|
||||
@ -1,65 +1,23 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.widget.Toast
|
||||
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.toast.ToastHandler
|
||||
import com.lightgame.utils.toast.ToastHelper
|
||||
|
||||
object ToastUtils {
|
||||
/** 之前显示的内容 */
|
||||
private var mOldMsg: String? = null
|
||||
|
||||
/** Toast对象 */
|
||||
private var mToast: Toast? = null
|
||||
|
||||
/** 第一次时间 */
|
||||
private var mOneTime: Long = 0
|
||||
|
||||
/** 第二次时间 */
|
||||
private var mTwoTime: Long = 0
|
||||
|
||||
/**
|
||||
* 显示Toast
|
||||
* @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 = 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()
|
||||
}
|
||||
}
|
||||
mOneTime = mTwoTime
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toast(message: String) {
|
||||
if (mToast != null) mToast?.cancel()
|
||||
mToast = ToastHandler.INSTANCE.getToastInstance(HaloApp.getInstance().application, message, Toast.LENGTH_SHORT)
|
||||
mToast?.show()
|
||||
showToast(message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showToast(message: String) {
|
||||
ToastHelper.showToast(HaloApp.getInstance(), message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showToast(message: String, gravity: Int = -1, yOffset: Int = 0) {
|
||||
ToastHelper.showToast(HaloApp.getInstance(), message, gravity, yOffset)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
app/src/main/java/com/gh/common/util/TopCutProcess.kt
Normal file
21
app/src/main/java/com/gh/common/util/TopCutProcess.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package com.gh.common.util
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Matrix
|
||||
import com.facebook.common.references.CloseableReference
|
||||
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor
|
||||
|
||||
class TopCutProcess(private val cutRatio: Float) : BasePostprocessor() {
|
||||
|
||||
override fun process(sourceBitmap: Bitmap, bitmapFactory: PlatformBitmapFactory): CloseableReference<Bitmap>? {
|
||||
val viewWidth = sourceBitmap.width
|
||||
val viewHeight = sourceBitmap.height.toFloat()
|
||||
var newHeight = viewWidth / cutRatio
|
||||
if (newHeight > viewHeight) {
|
||||
newHeight = viewHeight
|
||||
}
|
||||
val bitmapRef = bitmapFactory.createBitmap(sourceBitmap, 0, 0, viewWidth, (newHeight).toInt())
|
||||
return CloseableReference.cloneOrNull(bitmapRef)
|
||||
}
|
||||
}
|
||||
@ -116,6 +116,7 @@ object UploadImageUtils {
|
||||
|
||||
Observable.create(ObservableOnSubscribe<Map<String, String>> {
|
||||
val compressList = compressImageList(imgs, compressGif)
|
||||
listener.onCompressSuccess(compressList.map { file -> file.absolutePath }.toList())
|
||||
var listTotal = 0L // 总大小
|
||||
var listProgress = 0L // 已上传的大小
|
||||
for (img in compressList) {
|
||||
@ -274,7 +275,7 @@ object UploadImageUtils {
|
||||
}
|
||||
|
||||
// 防止GIF图片文件后缀不是GIF,这个FileName只是告诉服务端后缀格式,没有其他用处
|
||||
fun getFileName(file: File): String {
|
||||
fun getFileName(file: File): String {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeFile(file.absolutePath, options)
|
||||
@ -292,6 +293,7 @@ object UploadImageUtils {
|
||||
|
||||
interface OnUploadImageListListener {
|
||||
fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) // key:sourceImage value:compressImage
|
||||
fun onCompressSuccess(imageUrls: List<String>) {}
|
||||
fun onError(errorMap: Map<String, Exception>) // 全部上传失败时回调
|
||||
fun onProgress(total: Long, progress: Long)
|
||||
}
|
||||
|
||||
@ -1,127 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import android.app.AppOpsManager.MODE_ALLOWED
|
||||
import android.app.AppOpsManager.OPSTR_GET_USAGE_STATS
|
||||
import android.app.usage.UsageEvents
|
||||
import android.app.usage.UsageStatsManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
@ -24,7 +25,6 @@ import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.ResponseBody
|
||||
@ -173,8 +173,6 @@ object UsageStatsHelper {
|
||||
|
||||
val body = RequestBody.create(MediaType.parse("application/json"), postBody.toString())
|
||||
mApi.postUsageStatus(body, UserManager.getInstance().userId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
debugOnly {
|
||||
@ -221,8 +219,6 @@ object UsageStatsHelper {
|
||||
}
|
||||
|
||||
mApi.getUsageStatusUpdateTime(HaloApp.getInstance().gid)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
val body = JSONObject(data.string())
|
||||
@ -257,10 +253,15 @@ object UsageStatsHelper {
|
||||
@JvmStatic
|
||||
fun skipToUsageStats(context: Context, requestCode: Int = -1) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (context is Activity && requestCode != -1) {
|
||||
context.startActivityForResult(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), requestCode)
|
||||
} else {
|
||||
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
|
||||
try {
|
||||
if (context is Activity && requestCode != -1) {
|
||||
context.startActivityForResult(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), requestCode)
|
||||
} else {
|
||||
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
|
||||
}
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Utils.toast(context, "当前设备不支持直接跳转查看应用使用情况页面,请手动进入并打开")
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else {
|
||||
Utils.toast(context, "当前设备不支持查看应用使用情况")
|
||||
|
||||
@ -2,7 +2,10 @@ package com.gh.common.videolog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.database.sqlite.SQLiteFullException
|
||||
import com.gh.common.util.toRequestBody
|
||||
import com.gh.common.util.toastInInternalRelease
|
||||
import com.gh.common.util.tryWithDefaultCatch
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -11,34 +14,40 @@ import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ExecutorService
|
||||
|
||||
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 var videoRecordExecutor: ExecutorService? = null
|
||||
private val videoRecordDao by lazy { AppDatabase.getInstance(mApplication).videoRecordDao() }
|
||||
|
||||
@JvmStatic
|
||||
fun init(application: Application) {
|
||||
fun init(application: Application, executor : ExecutorService) {
|
||||
mApplication = application
|
||||
|
||||
videoRecordExecutor.execute {
|
||||
val recordList = videoRecordDao.getAll()
|
||||
videoRecordSet.addAll(recordList)
|
||||
videoRecordExecutor = executor
|
||||
videoRecordExecutor?.execute {
|
||||
tryWithDefaultCatch {
|
||||
val recordList = videoRecordDao.getAll()
|
||||
videoRecordSet.addAll(recordList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun log(videoId: String) {
|
||||
videoRecordExecutor.execute {
|
||||
videoRecordExecutor?.execute {
|
||||
try {
|
||||
val entity = VideoRecordEntity(videoId = videoId, time = System.currentTimeMillis() / 1000L)
|
||||
videoRecordSet.add(entity)
|
||||
videoRecordDao.insert(entity)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
if (e is SQLiteFullException) {
|
||||
toastInInternalRelease("数据库/磁盘已满,写入视频新日志失败")
|
||||
}
|
||||
}
|
||||
if (videoRecordSet.size >= STORE_SIZE) {
|
||||
commitVideoRecord()
|
||||
@ -48,11 +57,15 @@ object VideoRecordUtils {
|
||||
|
||||
fun commitVideoRecord() {
|
||||
if (videoRecordSet.isEmpty() || !::mApplication.isInitialized) return
|
||||
videoRecordExecutor.execute {
|
||||
videoRecordExecutor?.execute {
|
||||
uploadVideoRecord()
|
||||
val exposureList = videoRecordSet.toList()
|
||||
videoRecordSet.removeAll(exposureList)
|
||||
videoRecordDao.deleteMany(exposureList)
|
||||
try {
|
||||
videoRecordDao.deleteMany(exposureList)
|
||||
} catch (e: SQLiteFullException) {
|
||||
toastInInternalRelease("数据库/磁盘已满,删除视频日志失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -99,4 +99,4 @@ class AutoScrollRecyclerViewContainerView(context: Context, @Nullable attrs: Att
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
//package com.gh.common.view
|
||||
//
|
||||
//import android.content.Context
|
||||
//import android.util.AttributeSet
|
||||
//import android.view.MotionEvent
|
||||
//import android.widget.LinearLayout
|
||||
//import androidx.annotation.Nullable
|
||||
//import androidx.core.view.children
|
||||
//import androidx.lifecycle.Lifecycle
|
||||
//import androidx.lifecycle.LifecycleObserver
|
||||
//import androidx.lifecycle.OnLifecycleEvent
|
||||
//import androidx.recyclerview.widget.RecyclerView
|
||||
//import com.gh.common.AppExecutor
|
||||
//import java.lang.ref.WeakReference
|
||||
//
|
||||
// TODO 多页面会出现问题
|
||||
//class AutoScrollRecyclerViewContainerView(context: Context, @Nullable attrs: AttributeSet?)
|
||||
// : LinearLayout(context, attrs), LifecycleObserver {
|
||||
//
|
||||
// var autoScrollTask: AutoScrollTask?
|
||||
//
|
||||
// private var mIsScrolling = false
|
||||
// private var mIsScrollable = false
|
||||
// private var mLastScrollTime = 0L
|
||||
//
|
||||
// private var mOnItemTouchListener: RecyclerView.OnItemTouchListener? = null
|
||||
//
|
||||
// init {
|
||||
// autoScrollTask = AutoScrollTask(this)
|
||||
// }
|
||||
//
|
||||
// override fun onAttachedToWindow() {
|
||||
// super.onAttachedToWindow()
|
||||
//
|
||||
// if (mOnItemTouchListener == null) {
|
||||
// mOnItemTouchListener = object : RecyclerView.OnItemTouchListener {
|
||||
// override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
|
||||
// override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent) = rv.scrollState == RecyclerView.SCROLL_STATE_DRAGGING
|
||||
// override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (child in children) {
|
||||
// if (child is RecyclerView) {
|
||||
// child.removeOnItemTouchListener(mOnItemTouchListener!!)
|
||||
// child.addOnItemTouchListener(mOnItemTouchListener!!)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// resumeScrolling()
|
||||
// }
|
||||
//
|
||||
// override fun onDetachedFromWindow() {
|
||||
// super.onDetachedFromWindow()
|
||||
//
|
||||
// pauseScrolling()
|
||||
// }
|
||||
//
|
||||
// @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
// fun resumeScrolling() {
|
||||
// if (mIsScrolling) return
|
||||
//
|
||||
// mIsScrollable = true
|
||||
// mIsScrolling = true
|
||||
//
|
||||
// AppExecutor.lightWeightIoExecutor.execute {
|
||||
// while (mIsScrolling && autoScrollTask != null) {
|
||||
// if (System.currentTimeMillis() - mLastScrollTime >= AUTO_SCROLL_INTERVAL) {
|
||||
// mLastScrollTime = System.currentTimeMillis()
|
||||
// AppExecutor.uiExecutor.execute(autoScrollTask!!)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||
// fun pauseScrolling() {
|
||||
// mIsScrolling = false
|
||||
// }
|
||||
//
|
||||
// fun setLifeCycleOwner(lifeCycleOwner: Lifecycle) {
|
||||
// lifeCycleOwner.addObserver(this)
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// private const val AUTO_SCROLL_INTERVAL: Long = 16
|
||||
// private const val SCROLL_SLOP = 1
|
||||
// }
|
||||
//
|
||||
// class AutoScrollTask(reference: AutoScrollRecyclerViewContainerView?) : Runnable {
|
||||
// private val mReference: WeakReference<AutoScrollRecyclerViewContainerView?>?
|
||||
//
|
||||
// init {
|
||||
// mReference = WeakReference(reference)
|
||||
// }
|
||||
//
|
||||
// override fun run() {
|
||||
// val containerView: AutoScrollRecyclerViewContainerView? = mReference?.get()
|
||||
// if (containerView != null && containerView.mIsScrolling && containerView.mIsScrollable) {
|
||||
// for (child in containerView.children) {
|
||||
// if (child is RecyclerView) {
|
||||
// child.scrollBy(SCROLL_SLOP, SCROLL_SLOP)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@ -5,7 +5,7 @@ import android.graphics.drawable.PaintDrawable
|
||||
import android.view.View
|
||||
import android.widget.PopupWindow
|
||||
|
||||
class BugFixedPopupWindow @JvmOverloads constructor(contentView: View? = null, width: Int = 0, height: Int = 0)
|
||||
open class BugFixedPopupWindow @JvmOverloads constructor(contentView: View? = null, width: Int = 0, height: Int = 0)
|
||||
: PopupWindow(contentView, width, height) {
|
||||
|
||||
init {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.RectF
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewConfiguration
|
||||
@ -37,7 +38,9 @@ class DraggableBigImageView @JvmOverloads constructor(context: Context, attrs: A
|
||||
MotionEvent.ACTION_DOWN -> if (mInitScale == null || mInitScale == 0F) mInitScale = ssiv?.scale
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> up()
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (mSingleTouch && mInitScale != 0F && ssiv?.scale == mInitScale) {
|
||||
val rectF = RectF()
|
||||
ssiv?.getPanRemaining(rectF)
|
||||
if (mSingleTouch && mInitScale != 0F && (rectF.top == 0f || rectF.bottom == 0f)) {
|
||||
if (mLastX == 0F) mLastX = event.rawX
|
||||
if (mLastY == 0F) mLastY = event.rawY
|
||||
val offsetX = event.rawX - mLastX
|
||||
|
||||
@ -2,24 +2,21 @@ package com.gh.common.view
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Shader
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.halo.assistant.HaloApp
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.gh.common.util.dip2px
|
||||
import com.j256.ormlite.stmt.query.In
|
||||
import android.graphics.Shader
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.drawable.shapes.RectShape
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.opengl.ETC1.getHeight
|
||||
import com.gh.gamecenter.R
|
||||
import com.lightgame.utils.Utils
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.graphics.drawable.shapes.RectShape
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.databind.DrawablesBindingAdapter
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.dip2px
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
|
||||
object DrawableView {
|
||||
@ -97,6 +94,17 @@ object DrawableView {
|
||||
return drawable
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCornerDrawable(color: Int, radiusLT: Float, radiusRT: Float, radiusRB: Float, radiusLB: Float):Drawable{
|
||||
val drawable = GradientDrawable()
|
||||
drawable.shape = GradientDrawable.RECTANGLE
|
||||
drawable.setColor(color)
|
||||
val radiusEach = floatArrayOf(radiusLT.dip2px().toFloat(), radiusLT.dip2px().toFloat(), radiusRT.dip2px().toFloat(), radiusRT.dip2px().toFloat(),
|
||||
radiusRB.dip2px().toFloat(), radiusRB.dip2px().toFloat(), radiusLB.dip2px().toFloat(), radiusLB.dip2px().toFloat())
|
||||
drawable.cornerRadii = radiusEach
|
||||
return drawable
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCornerGradientDrawable(startColor: Int, endColor: Int, radius: Float = 999F): Drawable {
|
||||
val drawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, intArrayOf(startColor, endColor))
|
||||
|
||||
@ -100,7 +100,8 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
|
||||
/**
|
||||
* 适用于不使用 maxLines 而是整段收起时的文字来确定“...更多”的位置的样式
|
||||
* @param shrankText 收起时的文字 (“...更多”跟在 shrankText 后)
|
||||
*
|
||||
* @param shrankText 收起时的文字 (“...更多”跟在 shrankText 后)
|
||||
* @param expandedText 展开时的文字
|
||||
*/
|
||||
public void setShrankTextAndExpandedText(CharSequence shrankText, CharSequence expandedText) {
|
||||
@ -122,6 +123,11 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
}
|
||||
|
||||
private void showExpandButton() {
|
||||
// 避免二次生成展开按钮 (有点粗暴,但是为了满足 recyclerView 复用后没问题,还是每次都用当前文字来判断比较好)
|
||||
if (getText().toString().endsWith(mExpandText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String finalEndText = "";
|
||||
TextPaint paint = getPaint();
|
||||
|
||||
@ -160,6 +166,10 @@ public class ExpandTextView extends AppCompatTextView {
|
||||
float subSequenceWidth;
|
||||
for (int i = lastLineText.length() - 1; i > 0; i--) {
|
||||
if (mUseGradientAlphaEndText) {
|
||||
|
||||
// 避免 additionalEntTextCount 大于 i 造成数组越界 (TODO 代码太多,有时间再检查)
|
||||
if (additionalEndTextCount > i) return;
|
||||
|
||||
subSequence = lastLineText.subSequence(0, i - additionalEndTextCount);
|
||||
subSequenceWidth = paint.measureText(subSequence.toString());
|
||||
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class FixGridLayoutManager extends GridLayoutManager {
|
||||
|
||||
|
||||
public FixGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public FixGridLayoutManager(Context context, int spanCount) {
|
||||
super(context, spanCount);
|
||||
}
|
||||
|
||||
public FixGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
|
||||
super(context, spanCount, orientation, reverseLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
try {
|
||||
super.onLayoutChildren(recycler, state);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.widget.ScrollView
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
/**
|
||||
* 不能滑动的 ScrollView
|
||||
@ -11,10 +12,16 @@ import android.widget.ScrollView
|
||||
class FixedScrollView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ScrollView(context, attrs) {
|
||||
|
||||
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
||||
return when (ev?.action) {
|
||||
MotionEvent.ACTION_DOWN -> true
|
||||
else -> super.onTouchEvent(ev)
|
||||
// super.onTouchEvent may cause IllegalArgumentException which is crazy!
|
||||
try {
|
||||
return when (ev?.action) {
|
||||
MotionEvent.ACTION_DOWN -> true
|
||||
else -> super.onTouchEvent(ev)
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
|
||||
102
app/src/main/java/com/gh/common/view/GameTagFlexLinearLayout.kt
Normal file
102
app/src/main/java/com/gh/common/view/GameTagFlexLinearLayout.kt
Normal file
@ -0,0 +1,102 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.TagStyleEntity
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* 首页游戏标签,最多显示三个,实际显示个数由控件宽度决定
|
||||
*/
|
||||
class GameTagFlexLinearLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) {
|
||||
private var mTotalCount = 0
|
||||
private var mTags = ArrayList<TagStyleEntity>()
|
||||
private var mItemHeight = DisplayUtils.dip2px(13F)
|
||||
private var mPadding = DisplayUtils.dip2px(4F)
|
||||
private var mMargin = DisplayUtils.dip2px(4F)
|
||||
private var mTextSize = 9F
|
||||
private var mTotalWidth = 0
|
||||
private val mMaxCount = 3
|
||||
|
||||
init {
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
}
|
||||
|
||||
fun setTags(datas: ArrayList<TagStyleEntity>) {
|
||||
val tags = datas.take(mMaxCount)
|
||||
mTags.clear()
|
||||
mTotalCount = tags.size
|
||||
mTotalWidth = measuredWidth
|
||||
val paint = Paint()
|
||||
paint.textSize = DisplayUtils.sp2px(context, mTextSize).toFloat()
|
||||
|
||||
var currentWidth = 0f
|
||||
tags.forEach {
|
||||
val strWidth = getTextWidth(paint, it.name)
|
||||
if (currentWidth + strWidth + mPadding * 2 + mMargin <= mTotalWidth) {
|
||||
currentWidth += strWidth + mPadding * 2 + mMargin
|
||||
mTags.add(it)
|
||||
}
|
||||
}
|
||||
if (mTags.isNotEmpty()) {
|
||||
addTags()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTextWidth(paint: Paint, str: String?): Int {
|
||||
var width = 0
|
||||
if (str != null && str.isNotEmpty()) {
|
||||
val len = str.length
|
||||
val widths = FloatArray(len)
|
||||
paint.getTextWidths(str, widths)
|
||||
for (i in 0 until len) {
|
||||
width += ceil(widths[i].toDouble()).toInt()
|
||||
}
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
private fun addTags() {
|
||||
removeAllViews()
|
||||
mTags.forEach {
|
||||
addView(createView(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createView(tag: TagStyleEntity): View {
|
||||
return TextView(context).apply {
|
||||
text = tag.name
|
||||
includeFontPadding = false
|
||||
textSize = mTextSize
|
||||
gravity = Gravity.CENTER
|
||||
setTextColor(Color.parseColor("#666666"))
|
||||
|
||||
val params = LayoutParams(LayoutParams.WRAP_CONTENT, mItemHeight)
|
||||
params.setMargins(0, 0, mMargin, 0)
|
||||
setPadding(mPadding, 0, mPadding, 0)
|
||||
layoutParams = params
|
||||
|
||||
val gradientDrawable = createBackgroundDrawable()
|
||||
background = gradientDrawable
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBackgroundDrawable(): GradientDrawable {
|
||||
val gradientDrawable = GradientDrawable()
|
||||
gradientDrawable.setColor(Color.TRANSPARENT)
|
||||
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 0.5f), Color.parseColor("#CCCCCC"))
|
||||
gradientDrawable.cornerRadius = DisplayUtils.dip2px(2f).toFloat()
|
||||
return gradientDrawable
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,7 @@ class GradientAlphaTextSpan(var textColor: Int) : ReplacementSpan() {
|
||||
originalColor, originalColorWithAlphaChanged, Shader.TileMode.CLAMP)
|
||||
paint.shader = mShader
|
||||
|
||||
canvas.drawText(text, start, end, x, y.toFloat(), paint)
|
||||
canvas.drawText(text ?: "", start, end, x, y.toFloat(), paint)
|
||||
|
||||
paint.shader = null
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.common.view
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
|
||||
|
||||
class GridSpacingItemDecoration(private val mSpanCount: Int,
|
||||
@ -19,7 +20,16 @@ class GridSpacingItemDecoration(private val mSpanCount: Int,
|
||||
}
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
|
||||
val isStaggeredGridLayout = parent.layoutManager is StaggeredGridLayoutManager
|
||||
|
||||
val position = if (!isStaggeredGridLayout) {
|
||||
parent.getChildAdapterPosition(view)
|
||||
} else {
|
||||
val params = view.layoutParams as (StaggeredGridLayoutManager.LayoutParams)
|
||||
// 当前位置
|
||||
params.spanIndex
|
||||
}
|
||||
val column = position % mSpanCount
|
||||
|
||||
if (mIncludeEdge) {
|
||||
@ -29,6 +39,8 @@ class GridSpacingItemDecoration(private val mSpanCount: Int,
|
||||
|
||||
if (position < mSpanCount) {
|
||||
outRect.top = mSpacing
|
||||
} else if (isStaggeredGridLayout) {
|
||||
outRect.top = mSpacing
|
||||
}
|
||||
|
||||
outRect.bottom = mSpacing
|
||||
@ -37,6 +49,8 @@ class GridSpacingItemDecoration(private val mSpanCount: Int,
|
||||
outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount
|
||||
if (position >= mSpanCount) {
|
||||
outRect.top = mSpacing
|
||||
} else if (isStaggeredGridLayout) {
|
||||
outRect.top = mSpacing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
221
app/src/main/java/com/gh/common/view/ImageContainerView.kt
Normal file
221
app/src/main/java/com/gh/common/view/ImageContainerView.kt
Normal file
@ -0,0 +1,221 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.facebook.drawee.drawable.ScalingUtils
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.ItemCommunityImageBinding
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.CommunityVideoEntity
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import java.util.ArrayList
|
||||
|
||||
class ImageContainerView : LinearLayout {
|
||||
private var mAnswerEntity: AnswerEntity? = null
|
||||
|
||||
//三图默认宽度
|
||||
private var mDefaultWidth = 0f
|
||||
|
||||
//单图固定宽度
|
||||
private var mFixdWidth = 0f
|
||||
|
||||
//最小比例
|
||||
private var mMinRatio = 3 / 4f
|
||||
|
||||
//最大比例
|
||||
private var mMaxRatio = 3 / 2f
|
||||
|
||||
//长图比例
|
||||
private var mLongPictureRatio = 9 / 18f
|
||||
|
||||
private var mEntrance = ""
|
||||
private var mPath = ""
|
||||
|
||||
//图片之间的间距
|
||||
private val mItemSpace = 4f.dip2px()
|
||||
private var index = 0
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
initView(attrs)
|
||||
}
|
||||
|
||||
private fun initView(attrs: AttributeSet?) {
|
||||
orientation = HORIZONTAL
|
||||
val ta = context.obtainStyledAttributes(attrs, R.styleable.ImageContainerView)
|
||||
val offset = ta.getDimensionPixelSize(R.styleable.ImageContainerView_offset, 0)
|
||||
mDefaultWidth = (DisplayUtils.getScreenWidth() - offset.toFloat() - mItemSpace * 2) / 3
|
||||
mFixdWidth = (DisplayUtils.getScreenWidth() - offset.toFloat() - mItemSpace * 2) * 2 / 3
|
||||
ta.recycle()
|
||||
}
|
||||
|
||||
fun bindData(entity: AnswerEntity, entrance: String = "", path: String = "") {
|
||||
mAnswerEntity = entity
|
||||
mEntrance = entrance
|
||||
mPath = path
|
||||
index = 0
|
||||
removeAllViews()
|
||||
if (entity.getPassVideos().isNullOrEmpty() && entity.images.isNullOrEmpty()) {
|
||||
visibility = View.GONE
|
||||
return
|
||||
}
|
||||
visibility = View.VISIBLE
|
||||
if (mAnswerEntity?.type == "community_article") {
|
||||
//若文章内容含有图片及视频,则信息流卡片,仅展示图片,且标题后带有‘有视频’标签
|
||||
//若文章内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
||||
//若文章内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
||||
when {
|
||||
entity.images.isNotEmpty() -> {
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val images = entity.images.take(3)
|
||||
images.forEachIndexed { index, url ->
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, images.size == 1)
|
||||
}
|
||||
}
|
||||
entity.getPassVideos().isNotEmpty() -> {
|
||||
val video = entity.getPassVideos()[0]
|
||||
bindVideo(video, video.width, video.height, true)
|
||||
}
|
||||
else -> {
|
||||
// do noting
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//若问答内容含有图片及视频,则信息流卡片,同时展示图片及视频,且参考以往排序逻辑(视频优先放置第一位),无标签
|
||||
//若问答内容仅含有图片,则信息流卡片,仅展示图片,无标签
|
||||
//若问答内容仅含有视频,则信息流卡片,仅展示视频,无标签
|
||||
if (entity.getPassVideos().isNotEmpty()) {
|
||||
val video = entity.getPassVideos()[0]
|
||||
bindVideo(video, video.width, video.height, mAnswerEntity?.images.isNullOrEmpty())
|
||||
entity.images.take(2).forEachIndexed { index, url ->
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, false)
|
||||
}
|
||||
} else {
|
||||
val images = entity.images.take(3)
|
||||
images.forEachIndexed { index, url ->
|
||||
val imagesInfo = entity.imagesInfo
|
||||
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
|
||||
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
|
||||
bindImage(url, width, height, images.size == 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindVideo(video: CommunityVideoEntity, width: Int, height: Int, isChangeRatio: Boolean) {
|
||||
val binding = ItemCommunityImageBinding.inflate(LayoutInflater.from(context), null, false)
|
||||
addView(binding.root)
|
||||
binding.durationOrNumTv.visibility = View.VISIBLE
|
||||
binding.videoPlay.visibility = View.VISIBLE
|
||||
binding.labelIcon.visibility = View.GONE
|
||||
binding.durationOrNumTv.text = video.duration
|
||||
displayImage(binding, video.poster, width.toFloat(), height.toFloat(), isChangeRatio, true)
|
||||
binding.root.setOnClickListener {
|
||||
debounceActionWithInterval(it.id, 1000) {
|
||||
if (mAnswerEntity == null) return@debounceActionWithInterval
|
||||
val videoEntity = mAnswerEntity!!.getPassVideos()[0]
|
||||
DirectUtils.directToVideoDetail(context,
|
||||
videoEntity.id,
|
||||
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
|
||||
showComment = false,
|
||||
entrance = mEntrance,
|
||||
path = mPath)
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
private fun bindImage(url: String, width: Int, height: Int, isChangeRatio: Boolean) {
|
||||
val binding = ItemCommunityImageBinding.inflate(LayoutInflater.from(context), null, false)
|
||||
binding.root.tag = index
|
||||
addView(binding.root)
|
||||
binding.durationOrNumTv.visibility = View.GONE
|
||||
binding.videoPlay.visibility = View.GONE
|
||||
displayImage(binding, url, width.toFloat(), height.toFloat(), isChangeRatio)
|
||||
binding.root.setOnClickListener {
|
||||
debounceActionWithInterval(it.id, 1000) {
|
||||
if (mAnswerEntity == null) return@debounceActionWithInterval
|
||||
val position = if (mAnswerEntity?.type == "community_article") {
|
||||
binding.root.tag as Int
|
||||
} else {
|
||||
if (mAnswerEntity!!.getPassVideos().isNullOrEmpty()) binding.root.tag as Int else (binding.root.tag as Int) - 1
|
||||
}
|
||||
val intent = ImageViewerActivity.getIntent(context, mAnswerEntity!!.images as ArrayList<String>, position, binding.root,
|
||||
if (mAnswerEntity?.type == "community_article") mAnswerEntity else null, mEntrance)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
private fun displayImage(binding: ItemCommunityImageBinding, url: String, width: Float, height: Float, isChangeRatio: Boolean, isVideo: Boolean = false) {
|
||||
val params = binding.root.layoutParams as LayoutParams
|
||||
val hierarchy = binding.image.hierarchy
|
||||
binding.labelIcon.visibility = View.GONE
|
||||
if (width != 0f && height != 0f) {
|
||||
val picRatio = width / height
|
||||
if (isChangeRatio) {
|
||||
when {
|
||||
picRatio <= mMinRatio -> {
|
||||
params.height = (mFixdWidth / mMinRatio).toInt()
|
||||
ImageUtils.display(binding.image, url, false, TopCutProcess(mMinRatio))
|
||||
}
|
||||
picRatio >= mMaxRatio -> {
|
||||
params.height = (mFixdWidth / mMaxRatio).toInt()
|
||||
ImageUtils.display(binding.image, url, false)
|
||||
}
|
||||
else -> {
|
||||
params.height = (mFixdWidth / picRatio).toInt()
|
||||
ImageUtils.display(binding.image, url, false)
|
||||
}
|
||||
}
|
||||
params.width = mFixdWidth.toInt()
|
||||
} else {
|
||||
params.width = mDefaultWidth.toInt()
|
||||
params.height = mDefaultWidth.toInt()
|
||||
ImageUtils.display(binding.image, url, false)
|
||||
}
|
||||
//长图
|
||||
if (!isVideo && picRatio <= mLongPictureRatio) {
|
||||
binding.labelIcon.visibility = View.VISIBLE
|
||||
binding.labelIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_long_pic_label))
|
||||
}
|
||||
} else {
|
||||
params.width = mDefaultWidth.toInt()
|
||||
params.height = mDefaultWidth.toInt()
|
||||
ImageUtils.display(binding.image, url, false)
|
||||
}
|
||||
|
||||
if (url.endsWith(".gif")) {
|
||||
binding.labelIcon.visibility = View.VISIBLE
|
||||
binding.labelIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_gif_label))
|
||||
}
|
||||
|
||||
val mediaCount = if (mAnswerEntity?.type == "community_article") {
|
||||
mAnswerEntity?.images?.size ?: 0
|
||||
} else {
|
||||
(mAnswerEntity?.images?.size ?: 0) + (mAnswerEntity?.getPassVideos()?.size ?: 0)
|
||||
}
|
||||
if (!isVideo && index == 2 && mediaCount > 3) {
|
||||
binding.labelIcon.visibility = View.GONE
|
||||
binding.durationOrNumTv.visibility = View.VISIBLE
|
||||
binding.durationOrNumTv.text = "+${mAnswerEntity?.images?.size}"
|
||||
}
|
||||
|
||||
hierarchy.actualImageScaleType = ScalingUtils.ScaleType.CENTER_CROP
|
||||
if (index != 0) params.leftMargin = mItemSpace
|
||||
binding.root.layoutParams = params
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.gh.common.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
@ -31,6 +32,7 @@ public class MarqueeView extends ViewFlipper {
|
||||
private int textSize = 14;
|
||||
private int textColor = 0xffffffff;
|
||||
private boolean useSingleLineText = false;
|
||||
private boolean isHtmlText = false;
|
||||
|
||||
public MarqueeView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@ -121,9 +123,13 @@ public class MarqueeView extends ViewFlipper {
|
||||
tv.setEllipsize(TextUtils.TruncateAt.END);
|
||||
}
|
||||
tv.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
|
||||
tv.setText(text);
|
||||
tv.setTextColor(textColor);
|
||||
tv.setTextSize(textSize);
|
||||
tv.setTextColor(textColor);
|
||||
if (isHtmlText) {
|
||||
tv.setText(Html.fromHtml(text));
|
||||
} else {
|
||||
tv.setText(text);
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
|
||||
@ -133,6 +139,12 @@ public class MarqueeView extends ViewFlipper {
|
||||
start();
|
||||
}
|
||||
|
||||
public void startWithListForHtmlText(List<String> notices) {
|
||||
isHtmlText = true;
|
||||
setNotices(notices);
|
||||
start();
|
||||
}
|
||||
|
||||
public void enableSingleLineText() {
|
||||
useSingleLineText = true;
|
||||
}
|
||||
|
||||
54
app/src/main/java/com/gh/common/view/RadiusCardView.kt
Normal file
54
app/src/main/java/com/gh/common/view/RadiusCardView.kt
Normal file
@ -0,0 +1,54 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.AttributeSet
|
||||
import androidx.cardview.widget.CardView
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class RadiusCardView : CardView {
|
||||
var topLeftRadius = 0f
|
||||
var topRightRadius = 0f
|
||||
var bottomLeftRadius = 0f
|
||||
var bottomRightRadius = 0f
|
||||
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
||||
radius = 0f
|
||||
val array = context.obtainStyledAttributes(attrs, R.styleable.RadiusCardView)
|
||||
topLeftRadius = array.getDimensionPixelSize(R.styleable.RadiusCardView_topLeftRadius, 0).toFloat()
|
||||
topRightRadius = array.getDimensionPixelSize(R.styleable.RadiusCardView_topRightRadius, 0).toFloat()
|
||||
bottomLeftRadius = array.getDimensionPixelSize(R.styleable.RadiusCardView_bottomLeftRadius, 0).toFloat()
|
||||
bottomRightRadius = array.getDimensionPixelSize(R.styleable.RadiusCardView_bottomRightRadius, 0).toFloat()
|
||||
background = ColorDrawable()
|
||||
array.recycle()
|
||||
}
|
||||
|
||||
@SuppressLint("DrawAllocation")
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
val path = Path()
|
||||
val rectF: RectF = getRectF()
|
||||
val radius = floatArrayOf(topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomLeftRadius, bottomLeftRadius)
|
||||
path.addRoundRect(rectF, radius, Path.Direction.CW)
|
||||
canvas?.clipPath(path, Region.Op.INTERSECT)
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
|
||||
fun setRadius(tLRadius: Float, tRRadius: Float, bLRadius: Float, bRRadius: Float) {
|
||||
topLeftRadius = tLRadius.dip2px().toFloat()
|
||||
topRightRadius = tRRadius.dip2px().toFloat()
|
||||
bottomLeftRadius = bLRadius.dip2px().toFloat()
|
||||
bottomRightRadius = bRRadius.dip2px().toFloat()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun getRectF(): RectF {
|
||||
val rect = Rect()
|
||||
getDrawingRect(rect)
|
||||
return RectF(rect)
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,9 @@ class ReserveDialog : BaseDialogFragment() {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding: DialogReserveBinding = DataBindingUtil.inflate(inflater, R.layout.dialog_reserve, container, false)
|
||||
|
||||
mReserveList = arguments?.getParcelableArrayList(RESERVE_LIST) ?: arrayListOf()
|
||||
|
||||
binding.title.text = resources.getString(R.string.dialog_reserve_title, mReserveList.size).fromHtml()
|
||||
binding.more.visibility = if (mReserveList.size > 4) {
|
||||
View.VISIBLE
|
||||
@ -74,8 +77,13 @@ class ReserveDialog : BaseDialogFragment() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val RESERVE_LIST = "reserve_list"
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(reserveList: List<SimpleGameEntity>) = ReserveDialog().apply { mReserveList = reserveList }
|
||||
fun getInstance(reserveList: List<SimpleGameEntity>) = ReserveDialog().apply {
|
||||
arguments = Bundle()
|
||||
arguments?.putParcelableArrayList(RESERVE_LIST, ArrayList(reserveList))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ import com.gh.common.util.HtmlUtils;
|
||||
import com.gh.common.util.ImageUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.RichEditorUtils;
|
||||
import com.gh.common.util.SPUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
@ -119,6 +120,7 @@ public class RichEditor extends WebView {
|
||||
private OnDecorationStateListener mDecorationStateListener;
|
||||
private AfterInitialLoadListener mLoadListener;
|
||||
private WebChromeClientListener mChromeClientListener;
|
||||
private PageFinishedListener mPageFinishedListener;
|
||||
|
||||
public RichEditor(Context context) {
|
||||
this(context, null);
|
||||
@ -177,6 +179,10 @@ public class RichEditor extends WebView {
|
||||
mChromeClientListener = chromeClientListener;
|
||||
}
|
||||
|
||||
public void setPageFinishedListener(PageFinishedListener pageFinishedListener) {
|
||||
mPageFinishedListener = pageFinishedListener;
|
||||
}
|
||||
|
||||
public void setLayoutCallback(EmptyCallback layoutCallback) {
|
||||
mInitialLayoutCallback = layoutCallback;
|
||||
}
|
||||
@ -321,6 +327,10 @@ public class RichEditor extends WebView {
|
||||
exec("javascript:RE.setBaseFontSize('" + px + "px');");
|
||||
}
|
||||
|
||||
public boolean hasPlaceholderImage() {
|
||||
return getHtml().contains("placeholder-image-container");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPadding(int left, int top, int right, int bottom) {
|
||||
super.setPadding(left, top, right, bottom);
|
||||
@ -444,6 +454,15 @@ public class RichEditor extends WebView {
|
||||
exec("javascript:RE.setTextColor('" + hex + "');");
|
||||
}
|
||||
|
||||
public void insertPlaceholderImage(String id) {
|
||||
exec("javascript:RE.prepareInsert();");
|
||||
exec("javascript:RE.insertPlaceholderImage('" + id + "');");
|
||||
}
|
||||
|
||||
public void replacePlaceholderImage(String list) {
|
||||
exec("javascript:RE.replacePlaceholderImage('" + list + "');");
|
||||
}
|
||||
|
||||
public void removeFormat() {
|
||||
exec("javascript:RE.removeFormat();");
|
||||
}
|
||||
@ -612,6 +631,10 @@ public class RichEditor extends WebView {
|
||||
boolean shouldOverrideUrlLoading(WebView view, String url);
|
||||
}
|
||||
|
||||
public interface PageFinishedListener {
|
||||
void onPageFinished();
|
||||
}
|
||||
|
||||
class NativeCallBack {
|
||||
|
||||
/**
|
||||
@ -649,6 +672,21 @@ public class RichEditor extends WebView {
|
||||
return DeviceUtils.getNetwork(HaloApp.getInstance().getApplication());
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void onPageFinished() {
|
||||
mPageFinishedListener.onPageFinished();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String getAppVersion() {
|
||||
return BuildConfig.VERSION_NAME;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public int getAppVersionCode() {
|
||||
return PackageUtils.getVersionCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示toast
|
||||
*
|
||||
@ -737,7 +775,7 @@ public class RichEditor extends WebView {
|
||||
public void invalidate() {
|
||||
super.invalidate();
|
||||
|
||||
if (mInitialLayoutCallback != null &&getHeight() > 0) {
|
||||
if (mInitialLayoutCallback != null && getHeight() > 0) {
|
||||
// 仅初次 layout 完成会有回调
|
||||
mInitialLayoutCallback.onCallback();
|
||||
mInitialLayoutCallback = null;
|
||||
|
||||
@ -23,30 +23,31 @@ import androidx.viewpager.widget.ViewPager;
|
||||
*/
|
||||
|
||||
public class TabIndicatorView extends View implements ViewPager.OnPageChangeListener {
|
||||
|
||||
|
||||
private TabLayout mTabLayout;
|
||||
|
||||
|
||||
private int mIndicatorSpace;
|
||||
|
||||
|
||||
private int mIndicatorHeight;
|
||||
|
||||
|
||||
private int mIndicatorWidth;
|
||||
|
||||
|
||||
private int mIndicatorColor = 0;
|
||||
|
||||
|
||||
private GradientDrawable mGradientDrawable;
|
||||
|
||||
private Drawable mCustomDrawable;
|
||||
|
||||
public TabIndicatorView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
|
||||
public TabIndicatorView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
|
||||
public TabIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabIndicatorView);
|
||||
if (a.hasValue(R.styleable.TabIndicatorView_indicatorColor)) {
|
||||
mIndicatorColor = a.getColor(R.styleable.TabIndicatorView_indicatorColor, 0);
|
||||
@ -55,45 +56,48 @@ public class TabIndicatorView extends View implements ViewPager.OnPageChangeList
|
||||
mGradientDrawable.setColor(mIndicatorColor);
|
||||
mGradientDrawable.setCornerRadius(Integer.MAX_VALUE);
|
||||
}
|
||||
if (a.hasValue(R.styleable.TabIndicatorView_indicatorDrawable)) {
|
||||
mCustomDrawable = a.getDrawable(R.styleable.TabIndicatorView_indicatorDrawable);
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
mIndicatorHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
}
|
||||
|
||||
|
||||
public void setupWithTabLayout(final TabLayout tableLayout) {
|
||||
mTabLayout = tableLayout;
|
||||
|
||||
|
||||
tableLayout.setSelectedTabIndicatorColor(Color.TRANSPARENT);
|
||||
tableLayout.getViewTreeObserver().addOnScrollChangedListener(() -> {
|
||||
if (mTabLayout.getScrollX() != getScrollX())
|
||||
scrollTo(mTabLayout.getScrollX(), mTabLayout.getScrollY());
|
||||
});
|
||||
|
||||
|
||||
ViewCompat.setElevation(this, ViewCompat.getElevation(mTabLayout));
|
||||
|
||||
|
||||
//清除Tab background
|
||||
for (int tab = 0; tab < tableLayout.getTabCount(); tab++) {
|
||||
View tabView = getTabViewByPosition(tab);
|
||||
if (tabView != null) tabView.setBackgroundResource(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setupWithViewPager(ViewPager viewPager) {
|
||||
viewPager.addOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
|
||||
public void setIndicatorWidth(int width) {
|
||||
if (width > 0) this.mIndicatorWidth = DisplayUtils.dip2px(getContext(), width);
|
||||
}
|
||||
|
||||
|
||||
public void setIndicatorSpace(int space) {
|
||||
this.mIndicatorSpace = DisplayUtils.dip2px(getContext(), space);
|
||||
}
|
||||
|
||||
|
||||
private int getIndicatorSpace() {
|
||||
if (mIndicatorSpace != 0) return mIndicatorSpace;
|
||||
if (mIndicatorWidth != 0) {
|
||||
@ -102,13 +106,15 @@ public class TabIndicatorView extends View implements ViewPager.OnPageChangeList
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Drawable drawable;
|
||||
if (mGradientDrawable != null) {
|
||||
if (mCustomDrawable != null) {
|
||||
drawable = mCustomDrawable;
|
||||
} else if (mGradientDrawable != null) {
|
||||
drawable = mGradientDrawable;
|
||||
} else {
|
||||
drawable = getResources().getDrawable(R.drawable.ask_tab_indicator_bg); // 固定Indicator背景 有需要可以自行更改
|
||||
@ -116,77 +122,77 @@ public class TabIndicatorView extends View implements ViewPager.OnPageChangeList
|
||||
drawable.setBounds(l, t, r, b);
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
|
||||
|
||||
int l;
|
||||
int t;
|
||||
int r;
|
||||
int b;
|
||||
|
||||
|
||||
public void generatePath(int position, float positionOffset) {
|
||||
RectF range = new RectF();
|
||||
View tabView = getTabViewByPosition(position);
|
||||
|
||||
|
||||
if (tabView == null) return;
|
||||
|
||||
|
||||
int left, top, right, bottom;
|
||||
left = top = right = bottom = 0;
|
||||
|
||||
|
||||
if (positionOffset > 0.f && position < mTabLayout.getTabCount() - 1) {
|
||||
View nextTabView = getTabViewByPosition(position + 1);
|
||||
if (nextTabView != null) {
|
||||
left += (int) (nextTabView.getLeft() * positionOffset + tabView.getLeft() * (1.f - positionOffset));
|
||||
right += (int) (nextTabView.getRight() * positionOffset + tabView.getRight() * (1.f - positionOffset));
|
||||
}
|
||||
|
||||
|
||||
left += getIndicatorSpace();
|
||||
right -= getIndicatorSpace();
|
||||
top = tabView.getTop() + getPaddingTop();
|
||||
bottom = tabView.getBottom() - getPaddingBottom();
|
||||
range.set(left, top, right, bottom);
|
||||
} else {
|
||||
|
||||
|
||||
left = tabView.getLeft() + getIndicatorSpace();
|
||||
right = tabView.getRight() - getIndicatorSpace();
|
||||
top = tabView.getTop() + getPaddingTop();
|
||||
bottom = tabView.getBottom() - getPaddingBottom();
|
||||
range.set(left, top, right, bottom);
|
||||
|
||||
|
||||
if (range.isEmpty()) return;
|
||||
}
|
||||
r = right;
|
||||
l = left;
|
||||
t = top;
|
||||
b = top + mIndicatorHeight;
|
||||
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
private View getTabViewByPosition(int position) {
|
||||
if (mTabLayout != null && mTabLayout.getTabCount() > 0) {
|
||||
ViewGroup tabStrip = (ViewGroup) mTabLayout.getChildAt(0);
|
||||
return tabStrip != null ? tabStrip.getChildAt(position) : null;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
generatePath(position, positionOffset);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (mTabLayout.getSelectedTabPosition() != position) {
|
||||
TabLayout.Tab tab = mTabLayout.getTabAt(position);
|
||||
if (tab != null) tab.select();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.InflateException;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.ConsoleMessage;
|
||||
@ -34,6 +35,7 @@ import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -284,6 +286,12 @@ public class DWebView extends WebView {
|
||||
// add dsbridge tag in lower android version
|
||||
settings.setUserAgentString(settings.getUserAgentString() + " _dsbridge");
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
removeJavascriptInterface("searchBoxJavaBridge_");
|
||||
removeJavascriptInterface("accessibility");
|
||||
removeJavascriptInterface("accessibilityTraversal");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -414,7 +422,7 @@ public class DWebView extends WebView {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
super.loadUrl("javascript:" + script);
|
||||
} catch (Exception ignored){
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -716,6 +724,8 @@ public class DWebView extends WebView {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Activity activity = (AppCompatActivity) getContext();
|
||||
if (activity.isFinishing()) return true;
|
||||
Dialog alertDialog = new AlertDialog.Builder(getContext()).
|
||||
setMessage(message).
|
||||
setCancelable(false).
|
||||
@ -742,6 +752,8 @@ public class DWebView extends WebView {
|
||||
if (webChromeClient != null && webChromeClient.onJsConfirm(view, url, message, result)) {
|
||||
return true;
|
||||
} else {
|
||||
Activity activity = (AppCompatActivity) getContext();
|
||||
if (activity.isFinishing()) return true;
|
||||
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@ -784,6 +796,8 @@ public class DWebView extends WebView {
|
||||
if (webChromeClient != null && webChromeClient.onJsPrompt(view, url, message, defaultValue, result)) {
|
||||
return true;
|
||||
} else {
|
||||
Activity activity = (AppCompatActivity) getContext();
|
||||
if (activity.isFinishing()) return true;
|
||||
final EditText editText = new EditText(getContext());
|
||||
editText.setText(defaultValue);
|
||||
if (defaultValue != null) {
|
||||
@ -1023,4 +1037,12 @@ public class DWebView extends WebView {
|
||||
mainHandler.post(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverScrollMode(int mode) {
|
||||
try {
|
||||
super.setOverScrollMode(mode);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +163,13 @@ public class PagerLayoutManager extends LinearLayoutManager {
|
||||
return 0;
|
||||
}
|
||||
this.mDrift = dy;
|
||||
return super.scrollVerticallyBy(dy, recycler, state);
|
||||
//fix for `java.lang.IndexOutOfBoundsException: Inconsistency detected.`
|
||||
try {
|
||||
return super.scrollVerticallyBy(dy, recycler, state);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -18,7 +18,7 @@ import java.util.*
|
||||
* 目前检验是否已解压的根据是存在相同文件名且大小一致(暂时想不到校验MD5值的方法)
|
||||
*
|
||||
* obb文件直接解压至根目录即可,无需操作文件
|
||||
* apk文件这解压的gh-download文件夹中
|
||||
* apk文件这解压的gh-files文件夹中
|
||||
*/
|
||||
object XapkInstaller : IXapkUnzipListener {
|
||||
private const val XAPK_PACKAGE_PATH_TAG = "xapk_package_path"
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.gh.download
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageInfo
|
||||
import android.text.TextUtils
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.exposure.meta.MetaUtil
|
||||
import com.gh.common.exposure.meta.MetaUtil.getMeta
|
||||
import com.gh.common.loghub.LoghubUtils
|
||||
import com.gh.common.util.*
|
||||
@ -52,11 +54,15 @@ object DownloadDataHelper {
|
||||
} else if (status == DownloadStatus.subscribe || status == DownloadStatus.neterror || status == DownloadStatus.timeout) {
|
||||
"暂停下载-连上WiFi自动下载"
|
||||
} else if (status == DownloadStatus.done) {
|
||||
if(downloadEntity.isSimulatorGame()){
|
||||
if (downloadEntity.isSimulatorGame()) {
|
||||
return "下载完成"
|
||||
}
|
||||
val pm = HaloApp.getInstance().application.applicationContext.packageManager
|
||||
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
|
||||
val packageInfo = if (PackageUtils.isDeviceUnableToHandleBigApkFile(downloadEntity.path)) {
|
||||
PackageInfo()
|
||||
} else {
|
||||
val pm = HaloApp.getInstance().application.applicationContext.packageManager
|
||||
pm.getPackageArchiveInfo(downloadEntity.path, 0)
|
||||
}
|
||||
if (packageInfo == null && XapkInstaller.PACKAGE_EXTENSION_NAME == downloadEntity.path.getExtension()) {
|
||||
"解析包错误"
|
||||
} else {
|
||||
@ -100,7 +106,7 @@ object DownloadDataHelper {
|
||||
payloadObject.put("gameName", downloadEntity.name)
|
||||
payloadObject.put("platform", downloadEntity.platform)
|
||||
payloadObject.put("package", downloadEntity.packageName)
|
||||
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
|
||||
payloadObject.put("filename", getFileName(downloadEntity))
|
||||
jsonObject.put("payload", payloadObject)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@ -182,7 +188,7 @@ object DownloadDataHelper {
|
||||
payloadObject.put("gameName", downloadEntity.name)
|
||||
payloadObject.put("platform", downloadEntity.platform)
|
||||
payloadObject.put("package", downloadEntity.packageName)
|
||||
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
|
||||
payloadObject.put("filename", getFileName(downloadEntity))
|
||||
payloadObject.put("launch_ms", startupTime)
|
||||
jsonObject.put("payload", payloadObject)
|
||||
} catch (e: Exception) {
|
||||
@ -213,7 +219,7 @@ object DownloadDataHelper {
|
||||
payloadObject.put("gameName", downloadEntity.name)
|
||||
payloadObject.put("platform", downloadEntity.platform)
|
||||
payloadObject.put("package", downloadEntity.packageName)
|
||||
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
|
||||
payloadObject.put("filename", getFileName(downloadEntity))
|
||||
payloadObject.put("total_size", downloadEntity.size / 1024 / 1024)
|
||||
payloadObject.put("completed_size", downloadEntity.progress / 1024 / 1024)
|
||||
if (downloadEntity.status == DownloadStatus.resume) {
|
||||
@ -257,7 +263,7 @@ object DownloadDataHelper {
|
||||
payloadObject.put("gameName", downloadEntity.name)
|
||||
payloadObject.put("platform", downloadEntity.platform)
|
||||
payloadObject.put("package", downloadEntity.packageName)
|
||||
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
|
||||
payloadObject.put("filename", getFileName(downloadEntity))
|
||||
payloadObject.put("speed_progress", JSONArray(averageSpeedList))
|
||||
payloadObject.put("is_finished", downloadEntity.status == DownloadStatus.done)
|
||||
jsonObject.put("payload", payloadObject)
|
||||
@ -292,7 +298,7 @@ object DownloadDataHelper {
|
||||
sheet.put("game_id", downloadEntity.gameId)
|
||||
sheet.put("platform", downloadEntity.platform)
|
||||
sheet.put("package", downloadEntity.packageName)
|
||||
sheet.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
|
||||
sheet.put("filename", getFileName(downloadEntity))
|
||||
sheet.put("total_size", downloadEntity.size / 1024 / 1024)
|
||||
sheet.put("current_progress_size", downloadEntity.progress / 1024)
|
||||
mDownloadHeartbeatSheet[downloadEntity.url] = sheet
|
||||
@ -339,13 +345,13 @@ object DownloadDataHelper {
|
||||
val meta = getMeta()
|
||||
// meta
|
||||
val metaObject = JSONObject()
|
||||
metaObject.put("android_id", meta.android_id)
|
||||
metaObject.put("dia", MetaUtil.getBase64EncodedAndroidId())
|
||||
metaObject.put("android_sdk", meta.android_sdk)
|
||||
metaObject.put("android_version", meta.android_version)
|
||||
metaObject.put("appVersion", meta.appVersion)
|
||||
metaObject.put("channel", meta.channel)
|
||||
metaObject.put("gid", meta.gid)
|
||||
metaObject.put("imei", meta.imei)
|
||||
metaObject.put("jnfj", MetaUtil.getBase64EncodedIMEI())
|
||||
metaObject.put("mac", meta.mac)
|
||||
metaObject.put("manufacturer", meta.manufacturer)
|
||||
metaObject.put("model", meta.model)
|
||||
@ -355,4 +361,13 @@ object DownloadDataHelper {
|
||||
metaObject.put("userId", meta.userId)
|
||||
return metaObject
|
||||
}
|
||||
|
||||
private fun getFileName(downloadEntity: DownloadEntity): String {
|
||||
val downloadId = downloadEntity.getMetaExtra(Constants.DOWNLOAD_ID)
|
||||
return if (!TextUtils.isEmpty(downloadId)) {
|
||||
downloadId + downloadEntity.path.substring(downloadEntity.path.lastIndexOf("."))
|
||||
} else {
|
||||
downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user