Compare commits

..

9 Commits

4867 changed files with 75795 additions and 312221 deletions

6
.gitignore vendored
View File

@ -5,8 +5,4 @@ local.properties
# sign.properties
.DS_Store
captures/
build/
release-app/
test-app/
scripts/apk-channel/
app/src/test/java/com/gh/gamecenter
build/

View File

@ -1,44 +0,0 @@
stages:
- analysis
- sendmail
## 代码检查
sonarqube_analysis:
tags:
- offline-test
stage: analysis
image: sonarsource/sonar-scanner-cli:latest
dependencies: [] #禁止传递来的artifact
script:
## 获取项目的一级组和二级组和项目名作为projectKey例如projectKey=platform-backend-eci-monitor
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- sonar-scanner
-Dsonar.host.url=http://sonarqube-server.sonarqube:9000/
-Dsonar.login=be43de7264ce4c4766eb0c020373c3e74e6df257
-Dsonar.jacoco.reportPaths=target/jacoco.exec
-Dsonar.projectKey=$group
-Dsonar.projectName=$CI_PROJECT_PATH
-Dsonar.sourceEncoding=UTF-8
-Dsonar.exclusions=**/vendor/**,**/errcode/**
-Dsonar.gitlab.project_id=$CI_PROJECT_ID
-Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
-Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
-Dsonar.gitlab.ci_merge_request_iid=$CI_MERGE_REQUEST_IID
-Dsonar.gitlab.merge_request_discussion=true
-Dsonar.java.binaries=. # 如果不使用Maven或Gradle进行分析则必须手动提供测试二进制文件
only:
- dev
## 发送简易检测结果报告
send_sonar_report:
tags:
- offline-test
stage: sendmail
image: hub.shanqu.cc/library/docker:latest
dependencies: [] #禁止传递来的artifact
script:
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- docker login -u "${HARBOR_REGISTRY_USERNAME}" -p "${HARBOR_REGISTRY_PASSWORD}" "${HARBOR_REGISTRY}"
- docker run -e PROJECTKEY=$group -e EMAIL=$GITLAB_USER_EMAIL --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest
only:
- dev

7
.gitmodules vendored
View File

@ -1,7 +0,0 @@
[submodule "libraries/LGLibrary"]
path = libraries/LGLibrary
url = git@git.shanqu.cc:android/common-library.git
branch = master
[submodule "assistant_flutter"]
path = assistant_flutter
url = git@git.shanqu.cc:halo/android/flutter-module.git

View File

@ -1,89 +0,0 @@
# 版本升级备忘
### Ver 2.5
* 此处写本次更新所做的业务和代码修改
### Ver 2.6
* xx
### Ver 3.0
* 升级账号系统(登录流程/用户信息相关/用户账号相关操作(评论,礼包...))
* 新增收藏功能(文章/工具箱)
* 删除用户相关的所有本地数据库
* 重做总开服表
* 重做首页插件化模块
* 礼包重复领取机制改变(可重复领取的礼包,领取后立刻显示再领一个/再淘一个)
* 游戏下载平台面板修改(加快弹出速度,不再读取本地平台图片)
* 接入bugly(tinker)
### VER 3.1
### VER 3.2
### VER 3.3
### VER 3.4
### VER 3.5
### Ver 3.6
* 首页游戏增加预览骨架,游戏ITEM样式微调和开服标签
* 首页问答增加关注页面
* 重构游戏更新管理(游戏更新/插件化/已安装的游戏),具体细节参考PackageRepository & PackageViewModel
* 重构APP更新管理(已VersionVode为更新基准,小版本更新改为光环后台控制)
- 删除TINKER_VERISON_NAME
- tinker打包方式变更(以小版本作为Base包,防止与数据后台小版本更新发生冲突)
* 社区增加版主功能(版主可以对存在的相关内容进行修改/隐藏操作,内容包括问题/回答/回答评论)
* 社区互动引导优化(问答推荐增加`推荐关注`,回答详情增加一些交互动效)
### Ver 3.6.1
* 可以后台控制关闭资讯功能
* 版块、分类、专题详情、游戏详情、礼包详情增加预览骨架
* 下载按钮状态可以通过接口屏蔽相应的包
### Ver 3.6.2
* 资讯/问答入口和插件功能线上控制(不可逆)
* 首页不显示已安装的游戏
* 插件求版本功能增加内部跳转
* 下载面板增加公告和版本说明功能
* 接入腾讯`广点通`(广告)
### ver 3.6.3
* 社区搜索修改
- 增加 `文章/用户` 模块
- 增加 `搜索置顶` 功能
* 回答详情/社区文章详情修改
- 支持文案样式(加粗/斜体/删除线)和段落样式(引用/标题)
- 支持关闭评论功能
- 回答详情新增上下切换回答
* 社区编辑框(回答/文章)修改
- 支持批量插入图片(使用知乎Matisse实现)
- 新增插入特殊样式,文案样式(加粗/斜体/删除线)和段落样式(引用/标题)
* 编辑框部分 JS/CSS 使用远程文件
### ver 3.6.4
* 增加浏览记录(回答/文章/资讯/游戏)
* 回答/社区文章 增加反对功能
* 社区编辑框增加插入文章/回答/游戏
- 低版本兼容方案: 插入的样式默认隐藏,只有在3.6.4及以上才会显示
* 游戏详情评分模块增加`小编评论`区域以及样式修改
* 游戏评分增加回复功能
### var 3.6.5
* 以补丁方式向外推出并没有增加需求只是单纯的修BUG
### var 3.6.6
* 游戏详情:
- 支持修改评分
- 评分列表增加排序/过滤功能
- 增加弹出系统
* 社区相关:
- 选择社区页面重做
- 首页社区推荐增加推荐入口
- 首页社区问题模块改版,名称改为全部,去除问题分类(统一为问题列表),增加社区文章列表
* 游戏搜索默认页面改版
* 权限系统更改,不授权也可以进去App,申请权限细分到功能(用到某个功能时才需要强制授予权限)
* 增加隐私系统
* 增加游戏预约功能
* 增加标签详情模块
* 进入今日头条广告SDK
* 图片上传压缩机制优化
- 支持从后台修改上传配置(本该在早些版本实现,由于代码问题无法引用后台配置)
- 对压缩失败是进行catch(由于后台对宽高配置放宽,很容易发生OOM),失败后直接上传原图(这步可能会出现问题)
* 游戏相关UI修改

View File

@ -1,65 +1,23 @@
# 光环助手Android客户端
### 概述
### 多渠道打包配置
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
光环助手Android客户端目前使用 Kotlin 作为主要开发语言,以 MVVM 作为参考架构模式进行开发
### 约束
为编写易读易维护且较健壮的代码,可参考以下约束
1. 尽量将逻辑代码放置于 ViewModel 中View 中只执行 UI 操作
2. 尽量使 View 在被销毁之后仍能恢复状态,处理方式可参考 [保存界面状态](https://developer.android.com/topic/libraries/architecture/saving-states)
3. 尽量参考原有文件结构及命名规范,即以 大模块 - 小模块 的形式生成包关系
4. 遵循最小改动原则,在提交代码前务必先检查变动的代码,尽量以可控的变动规模来构成一个 commit ,以便日后追踪问题
5. 代码规范可参考 [AOSP Java 风格](https://source.android.com/setup/contribute/code-style)
6. 尽量使用 Kotlin 来写新文件
7. 尽量不要使用 DataBinding
8. Commit 前请确保不带入非项目必须文件,可手动修改 [.gitignore](https://stackoverflow.com/questions/8527597/how-do-i-ignore-files-in-a-directory-in-git) 文件忽略
9. 优先使用 ViewBinding 获取 View 对象
10. No AsyncTask!
### 公用部分
本项目使用 LiveData 实现了一个简单通用的基础列表分页功能,具体可见 `ListFragment`, `ListViewModel` 等类,理想情况下只需少量代码即可新建一个简单分页列表
### 首次拉取项目代码
`git clone -b dev git@git.shanqu.cc:halo/android/assistant-android.git --recursive`
### git 版本管理
本项目使用简化版的 git flow 来管理分支,细节请看 [光环安卓简单 git 规范](https://git.ghzs.com/halo/android/assistant-android/-/wikis/%E5%85%89%E7%8E%AF%E5%AE%89%E5%8D%93%E7%AE%80%E5%8D%95-git-%E8%A7%84%E8%8C%83)
### API 环境配置
本项目使用 Build Variants 来切换 API 环境
* internal 为测试环境
* publish 为正式环境
### 图片资源配置
* 新增图片资源时,默认只添加最高规格的 xxxhdpi 文件
* 新增图片资源时,需要将其转换为 .webp 格式 (包括含透明图层的图片默认质量为90%) (转换后体积变大的文件除外)
### 第三方appkey等配置
* 修改 `gradle.properties` 文件将各种key填入其中实现统一管理
* 通过 gradle 文件内的 resValue/buildConfigField/manifestPlaceHolder 方式实现编译期间修改,具体情况请参考 ``./build.gradle`` 和 ``./app/build.gradle`` 配置
* 正式打包命令:请使用./gradlew channelPubRelease打包渠道包
### 混淆配置
* 配置文件Android默认配置+proguard-rules.txt等
* 参考libraries下每个项目独立的配置文件``proguard-project.txt``
* 本项目使用了微信的 [AndResGuard](https://github.com/shwenzhang/AndResGuard) 作为资源混淆压缩方案,新增需要使用 `getIdentifier` 获取的资源文件时需要添加至白名单
* 本项目默认使用 R8 作为混淆工具,往 proguard-rules.txt 添加 proguard 新配置项时请检查可用性(如语法等)
### apk大小优化
* 限制resConfig资源集
* 开启ShrinkResources
* 开启混淆使用minifyEnabled(仅在release开启
* pngquant对png压缩、png/jpg->webp(未尝试)
### APK打包配置
### 第三方appkey等配置
* 修改``gradle.properties``文件将各种key填入其中实现统一管理
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改具体情况请参考``./build.gradle``和``./app/build.gradle``配置
> 打内部测试包:`./scripts/test_build.sh`
> 打邮件测试包:`./scripts/jenkins_build.sh`
### TODO
* 把原有 EventBus 的消息 Type 统一到一个文件内
* 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
* 重构 MainActivity
### Ver 2.5
* 此处写本次更新所做的业务和代码修改

View File

@ -1,98 +1,67 @@
// This comment exists for a reason, do not delete
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' // kotlin
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'AndResGuard'
import groovy.xml.XmlUtil
apply plugin: 'com.neenbedankt.android-apt'
//tinker插件
//apply plugin: 'com.tencent.tinker.patch'
// apkChannelPackage
apply plugin: 'channel'
android {
buildFeatures {
viewBinding true
dataBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
dexOptions {
// jumboMode = true
javaMaxHeapSize "4g"
preDexLibraries true
maxProcessCount 8
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
kapt {
useBuildCache = true
javacOptions {
// 增加注解处理器的最大错误次数,默认为 100
option("-Xmaxerrs", 500)
}
jumboMode = true
}
defaultConfig {
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'com.gh.EventBusIndex']
arguments = [ eventBusIndex : 'com.bandeng.MyEventBusIndex' ]
}
}
/**
* 只支持两种架构减少apk大小有疑问请参考
* https://developer.android.com/ndk/guides/abis.html
* http://allenfeng.com/2016/11/06/what-you-should-know-about-android-abi-and-so/
* 为了性能考虑armeabi可以考虑替换成armeabi-v7a[需要先收集用户设备情况]
*/
ndk {
// 如果不添加 `arm64` 调用系统的 PackageManager 的方法读取安装包信息的时候会出现 native 层闪退,草
// 添加了 `arm64` 以后部分 5.0 的设备会报用错 so 的问题,
// couldn't find DSO to load: libimagepipeline.so caused by: dlopen failed: "/data/data/com.gh.gamecenter/lib-main/libimagepipeline.so" is 64-bit instead of 32-bit result: 0
// 以 OPPO R7PLUS 为例,明明设备是骁龙 615ARMv8-64 bit 的设备却不支持 arm64 的 abi限制了只使用 java 后还是报错,只有 5.05.1 设备无法复现 : (
// 惊了
abiFilters "armeabi-v7a", "arm64-v8a", "x86"
abiFilters "armeabi", "x86"
}
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
// 由于app只针对中文用户所以仅保留zh资源其他删掉
resConfigs "zh"
// jackOptions {
// enabled true
// }
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
applicationId rootProject.ext.applicationId
multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
/**
* All third-party appid/appkey
*/
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\""
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
/**
* Build Time 供区分 jenkins 打包时间用
* IS_NIGHT_MODE_ON 供区分夜间模式功能是否启用
*/
buildConfigField "long", "BUILD_TIME", "0"
buildConfigField "boolean", "IS_NIGHT_MODE_ON", "true"
}
// gradle 2.2以上默认同时启用v1和v2优先用于Android N
@ -112,12 +81,28 @@ android {
debuggable true
minifyEnabled false
zipAlignEnabled false
signingConfig signingConfigs.debug
versionNameSuffix "-debug"
}
release {
debuggable false
minifyEnabled true
zipAlignEnabled true
shrinkResources true
signingConfig signingConfigs.release
}
}
buildConfigField "String", "EXPOSURE_REPO", "\"test\""
buildConfigField "String", "EXPOSURE_VERSION", "\"E4\""
buildTypes {
debug {
debuggable true
minifyEnabled false
zipAlignEnabled false
versionNameSuffix "-debug"
multiDexKeepProguard file("tinker_multidexkeep.pro")
buildConfigField "String", "UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "MIPUSH_APPID", "\"${DEBUG_MIPUSH_APPID}\""
buildConfigField "String", "MIPUSH_APPKEY", "\"${DEBUG_MIPUSH_APPKEY}\""
}
release {
debuggable false
@ -126,236 +111,144 @@ android {
shrinkResources true
signingConfig signingConfigs.release
buildConfigField "String", "EXPOSURE_REPO", "\"exposure\""
buildConfigField "String", "EXPOSURE_VERSION", "\"E4\""
multiDexKeepProguard file("tinker_multidexkeep.pro")
}
}
// Ignore useless variant
variantFilter { variant ->
def names = variant.flavors*.name
def isDebugType = variant.buildType.name == "debug"
if ((names.contains("tea")) && isDebugType) {
setIgnore(true)
}
}
flavorDimensions("env")
sourceSets {
publish {
java.srcDirs = ['src/main/java']
}
internal {
java.srcDirs = ['src/main/java']
}
tea {
java.srcDirs = ['src/main/java', 'src/tea/java']
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
}
}
/**
* 多渠道打包,渠道请参考"channel.txt"文件所有渠道值均通过java code设置
*/
productFlavors {
// internal test dev host
internal {
dimension "env"
versionNameSuffix "-debug"
buildConfigField "String", "DEV_API_HOST", "\"${DEV_API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_DEV_API_HOST}\""
// public release host
pub {
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "USER_HOST", "\"${USER_HOST}\""
buildConfigField "String", "COMMENT_HOST", "\"${COMMENT_HOST}\""
buildConfigField "String", "LIBAO_HOST", "\"${LIBAO_HOST}\""
buildConfigField "String", "MESSAGE_HOST", "\"${MESSAGE_HOST}\""
buildConfigField "String", "DATA_HOST", "\"${DATA_HOST}\""
}
// publish release host˛
publish {
dimension "env"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
}
tea {
dimension "env"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase())
// internal dev host
dev {
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
buildConfigField "String", "USER_HOST", "\"${DEV_USER_HOST}\""
buildConfigField "String", "COMMENT_HOST", "\"${DEV_COMMENT_HOST}\""
buildConfigField "String", "LIBAO_HOST", "\"${DEV_LIBAO_HOST}\""
buildConfigField "String", "MESSAGE_HOST", "\"${DEV_MESSAGE_HOST}\""
buildConfigField "String", "DATA_HOST", "\"${DEV_DATA_HOST}\""
}
}
lintOptions {
// For flutter release build, see https://github.com/flutter/flutter/issues/58247
checkReleaseBuilds false
// productFlavors.all { flavor ->
// flavor.manifestPlaceholders = [CHANNEL_VALUE: name]//命令 gradlew assembleRelease
// }
}
// apkChannelPackage
channel {
//多渠道包的输出目录默认为new File(project.buildDir,"channel")
baseOutputDir = new File(project.buildDir, "channel")
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
}
apt {
arguments {
eventBusIndex "com.bandeng.MyEventBusIndex"
}
}
repositories {
flatDir {
dirs 'libs', 'libs/aars'
}
rebuildChannel {
// baseDebugApk = 已有Debug APK
// baseReleaseApk = 已有Release APK
// //默认为new File(project.buildDir, "rebuildChannel/debug")
// debugOutputDir = Debug渠道包输出目录
// //默认为new File(project.buildDir, "rebuildChannel/release")
// releaseOutputDir = Release渠道包输出目录
}
dependencies {
testCompile test.junit
compile fileTree(include: '*.jar', dir: 'libs')
compile libs.supportMultidex
compile libs.supportDesign
compile libs.supportAppCompat
compile libs.supportAnnotation
compile libs.supportPercent
compile libs.supportDesign
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
compile libs.switchButton
testImplementation 'junit:junit:4.12'
compile libs.systemBarTint
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
// debugImplementation "com.gu.android:toolargetool:${toolargetool}" // 需要使用调试时才启用
debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}"
debugImplementation "io.github.didi.dokit:dokitx:${dokit}"
compile libs.fresco
compile libs.frescoAnimatedGif
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}"
implementation "androidx.annotation:annotation:${annotation}"
implementation "androidx.constraintlayout:constraintlayout:${constraintLayout}"
implementation "androidx.recyclerview:recyclerview:${recyclerView}"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifeCycle"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifeCycle"
implementation "androidx.lifecycle:lifecycle-extensions:$lifeCycleExtensions"
implementation "androidx.room:room-runtime:${room}"
implementation "androidx.room:room-rxjava2:${room}"
implementation "androidx.core:core-ktx:${ktx}"
implementation "androidx.viewpager2:viewpager2:${viewpager2}"
implementation "androidx.webkit:webkit:${webkit}"
kapt "androidx.room:room-compiler:${room}"
compile libs.okHttp
compile libs.okHttpLogInterceptor
implementation "com.google.android.material:material:${material}"
compile libs.apkChannelPackage
implementation "com.kyleduo.switchbutton:library:${switchButton}"
// debugCompile libs.stetho
// debugCompile libs.stethoWithOkHttp
implementation "com.facebook.fresco:fresco:${fresco}"
implementation "com.facebook.fresco:animated-gif-lite:${fresco}"
implementation "com.facebook.fresco:animated-drawable:${fresco}"
implementation "com.facebook.fresco:animated-webp:${fresco}"
implementation "com.facebook.fresco:webpsupport:${fresco}"
compile libs.retrofit
compile libs.retrofitWithGson // include gson 2.7
compile libs.retrofitWithRxJava
// compile libs.gson
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
compile libs.ormliteAndroid
compile libs.ormliteCore
implementation "com.leon.channel:helper:${apkChannelPackage}"
compile libs.butterKnife
apt libs.butterKnifeApt
implementation "com.squareup.retrofit2:retrofit:${retrofit}"
implementation "com.squareup.retrofit2:converter-gson:${retrofit}" // include gson 2.7
implementation "com.squareup.retrofit2:adapter-rxjava2:${retrofit}"
compile libs.rxJava
compile libs.rxAndroid
compile libs.rxBinding
compile libs.zxing
compile libs.zxingAndroid
implementation "com.j256.ormlite:ormlite-android:${ormlite}"
implementation "com.j256.ormlite:ormlite-core:${ormlite}"
compile libs.swipeLayout
compile(libs.autoScrollViewPager) {
exclude module: 'support-v4'
}
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
// tinker
// provided libs.tinker_anno
// compile libs.tinker_lib
implementation "org.greenrobot:eventbus:${eventbus}"
kapt "org.greenrobot:eventbus-annotation-processor:${eventbusApt}"
compile libs.eventbus
apt libs.eventbusApt
// compile project(':libraries:EventBus')
compile project(':libraries:MiPush')
compile project(':libraries:MTA')
compile project(':libraries:QQShare')
compile project(':libraries:TalkingData')
compile project(':libraries:UmengPush')
compile project(':libraries:WechatShare')
compile project(':libraries:WeiboShare')
compile project(':libraries:iosched')
implementation "io.reactivex.rxjava2:rxjava:${rxJava2}"
implementation "io.reactivex.rxjava2:rxandroid:${rxAndroid2}"
implementation "com.jakewharton.rxbinding2:rxbinding:${rxBinding2}"
implementation "com.google.zxing:core:${zxing}"
implementation "com.google.zxing:android-core:${zxing}"
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
implementation "com.google.android:flexbox:${flexbox}"
implementation "pub.devrel:easypermissions:${easypermissions}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.contrarywind:Android-PickerView:${pickerView}"
implementation "com.scwang.smartrefresh:SmartRefreshLayout:${smartRefreshLayout}"
implementation "net.cachapa.expandablelayout:expandablelayout:${expandableLayout}"
// 用于比较 versionName 是大于小于或等于
implementation "com.g00fy2:versioncompare:${versioncompare}"
implementation "top.zibin:Luban:${luban}"
implementation "com.squareup.picasso:picasso:${picasso}"
// for video streaming
implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:$gsyVideo", {
exclude module: "gsyvideoplayer-androidvideocache"
exclude group: "tv.danmaku.ijk.media"
})
implementation "com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo"
implementation "androidx.work:work-runtime:${workManager}"
implementation "com.llew.huawei:verifier:${verifier}"
implementation "com.github.tbruyelle:rxpermissions:${rxPermissions}"
implementation "com.lg:skeleton:${skeleton}"
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
implementation "com.github.nichbar:AndroidRomChecker:${romChecker}"
debugImplementation "com.github.nichbar.chucker:library:${chucker}"
releaseImplementation "com.github.nichbar.chucker:library-no-op:${chucker}"
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}"
implementation "com.aliyun.dpa:oss-android-sdk:${oss}"
implementation "com.airbnb.android:lottie:${lottie}"
implementation "net.lingala.zip4j:zip4j:${zip4j}"
implementation "io.sentry:sentry-android:4.3.0"
implementation("com.github.piasy:BigImageViewer:${bigImageViewer}", {
exclude group: 'com.squareup.okhttp3'
exclude group: 'androidx.swiperefreshlayout'
exclude group: 'com.github.bumptech.glide'
exclude group: 'com.facebook.fresco'
})
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
implementation "com.lahm.library:easy-protector-release:${easyProtector}"
implementation "com.github.hsiafan:apk-parser:${apkParser}"
implementation "org.nanohttpd:nanohttpd:${nanohttpd}"
implementation "com.aliyun.openservices:aliyun-log-android-sdk:${aliyunLog}"
implementation "com.lg:easyfloat:${easyFloat}"
implementation "io.github.florent37:shapeofview:${shapeOfView}"
implementation "io.github.sinaweibosdk:core:${weiboSDK}"
implementation "com.lg:apksig:${apksig}"
implementation "com.lg:gid:${gid}"
implementation "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl:${splitties}"
compileOnly "com.github.axen1314.lancet:lancet-base:${lancet_version}"
implementation project(':libraries:LGLibrary')
implementation project(':libraries:QQShare')
implementation project(':libraries:Matisse')
}
File propFile = file('sign.properties')
File propFile = file('sign.properties');
if (propFile.exists()) {
Properties props = new Properties()
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('keyAlias') && props.containsKey('keyPassword') &&
props.containsKey('storeFile') && props.containsKey('storePassword')) {
android.signingConfigs {
// debug 不要使用正式签名这样tinker才不会打补丁。
debug {
keyAlias props.get('keyAlias')
keyPassword props.get('keyPassword')
storeFile file(props.get('storeFile'))
storePassword props.get('storePassword')
}
debug {
keyAlias props.get('keyAlias')
keyPassword props.get('keyPassword')
storeFile file(props.get('storeFile'))
storePassword props.get('storePassword')
}
release {
keyAlias props.get('keyAlias')
keyPassword props.get('keyPassword')
@ -369,230 +262,3 @@ if (propFile.exists()) {
} else {
android.buildTypes.release.signingConfig = null
}
// 用于测试读取 META-INF 里的文件
//task generateMetaJson {
// def resDir = new File(buildDir, 'generated/FILES_FOR_META_INF/')
// def destDir = new File(resDir, 'META-INF/')
// // Add resDir as a resource directory so that it is automatically included in the APK.
// android {
// sourceSets {
// main.resources {
// srcDir resDir
// }
// }
// }
//
// doLast {
// if (!destDir.exists()) destDir.mkdirs()
// copy {
// into destDir
// from new File('generated/FILES_FOR_META_INF/META-INF/halo_skip.json')
// }
// }
//}
//// Specify when put_files_in_META_INF should run
//project.afterEvaluate {
// tasks.findAll { task ->
// task.name.startsWith('merge') && task.name.endsWith('Resources')
// }.each { t -> t.dependsOn generateMetaJson }
//}
andResGuard {
mappingFile = null
use7zip = true
useSign = true
// 打开这个开关会keep住所有资源的原始路径只混淆资源的名字
keepRoot = false
// 设置这个值会把arsc name列混淆成相同的名字减少string常量池的大小
fixedResName = "arg"
// 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
mergeDuplicatedRes = true
whiteList = [
"R.drawable.icon",
"R.drawable.ic_bar_back",
"R.drawable.toolbar_search_icon",
"R.drawable.bg_notification_answer_style_1",
"R.drawable.bg_notification_answer_style_2",
"R.drawable.bg_notification_article_style_1",
"R.drawable.bg_notification_article_style_2",
"R.drawable.bg_notification_feedback_style_1",
"R.drawable.bg_notification_feedback_style_2",
"R.drawable.bg_notification_gift_style_1",
"R.drawable.bg_notification_gift_style_2",
"R.drawable.bg_notification_login_style_1",
"R.drawable.bg_notification_login_style_2",
"R.drawable.bg_notification_question_style_1",
"R.drawable.bg_notification_question_style_2",
"R.drawable.bg_notification_rating_style_1",
"R.drawable.bg_notification_rating_style_2",
"R.drawable.bg_notification_reserve_game_style_1",
"R.drawable.bg_notification_reserve_game_style_2",
"R.drawable.bg_notification_video_style_1",
"R.drawable.bg_notification_video_style_2",
"R.drawable.ic_search_no_1",
"R.drawable.ic_search_no_2",
"R.drawable.ic_search_no_3",
"R.drawable.ic_search_no_4",
"R.drawable.ic_search_no_5",
"R.drawable.ic_search_no_6",
"R.drawable.ic_search_no_7",
"R.drawable.ic_search_no_8",
"R.drawable.ic_search_no_9",
"R.drawable.ic_search_no_10",
"R.drawable.ic_search_no_11",
"R.drawable.ic_search_no_12",
"R.drawable.ic_search_no_13",
"R.drawable.ic_search_no_14",
"R.drawable.ic_search_no_15",
"R.drawable.ic_search_no_16",
"R.drawable.ic_search_no_17",
"R.drawable.ic_search_no_18",
"R.drawable.ic_search_no_19",
"R.drawable.ic_search_no_20",
"R.drawable.ic_recommend_activity",
"R.drawable.ic_recommend_discount",
"R.drawable.ic_recommend_function",
"R.drawable.ic_recommend_gift",
"R.drawable.ic_recommend_role",
"R.drawable.login_btn_bg",
"R.drawable.ic_quick_login_check",
"R.drawable.ic_quick_login_uncheck",
"R.anim.anim_auth_in",
"R.anim.anim_auth_out",
"R.id.download_speed",
"R.id.download_percentage",
"R.id.comment",
"R.id.vote",
"R.id.watermark_hint",
"R.id.watermark_sb",
"R.id.bottomShareIv",
"R.id.bottomShareTv",
"R.id.recommendStarPref",
"R.id.recommendStar",
"R.drawable.help_search_delete",
"R.drawable.suggest_type_normal",
"R.drawable.suggest_type_crash",
"R.drawable.suggest_type_game_question",
"R.drawable.suggest_type_game_collect",
"R.drawable.suggest_type_function_suggest",
"R.drawable.suggest_type_article_collect",
"R.drawable.suggest_type_copyright",
"R.drawable.help_result_empty",
"R.drawable.news_comment_detail_read",
"R.drawable.news_comment_detail_comment",
"R.drawable.news_comment_detail_share",
"R.drawable.ic_libao",
"R.drawable.ic_link",
"R.drawable.concern_message_icon",
"R.drawable.reuse_blank_hint",
"R.drawable.ic_concern",
"R.drawable.concern_down",
"R.drawable.concern_up",
"R.drawable.ic_libao_more",
"R.drawable.ic_libao_delete",
"R.drawable.ic_dialog_close",
"R.drawable.occupy2",
"R.drawable.kc_checkbox_unselect",
"R.drawable.kc_checkbox_select",
"R.drawable.ic_type_unselect",
"R.drawable.ic_type_selected",
"R.drawable.suggest_add_pic_icon",
"R.drawable.icon_pic_add",
"R.drawable.ask_search_input_delete",
"R.drawable.suggest_pic_delete"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.20'
}
}
project.afterEvaluate {
def variants = null
try {
variants = android.applicationVariants
} catch (Throwable t) {
t.printStackTrace()
try {
variants = android.libraryVariants
} catch (Throwable tt) {
tt.printStackTrace()
}
}
if (variants != null) {
variants.all { variant ->
variant.outputs.each { output ->
def task = output.processManifestProvider.get()
if (task == null) {
return
}
/**
* 为 Manifest 的 Activity 的 configChanges 添加自己手动处理 configurationChanges 配置 [https://developer.android.com/guide/topics/resources/runtime-changes]
* AGP 4.1.0 从 ProcessManifest task 里拿 manifest 的 API 变更调整可以参考这里 [https://github.com/Tencent/tinker/pull/1476/commits/d71645729b13d545ca4ba6826f93fbf558751434]
* (搞半天还是不会抽离方法,有空再把 gradle 改成用 kotlin 实现吧)
*/
task.doLast {
def manifestFile = new File(multiApkManifestOutputDirectory.get().asFile, "AndroidManifest.xml")
if (manifestFile == null || !manifestFile.exists()) {
return
}
String[] configChanges = [
"density",
"fontScale",
"keyboard",
"keyboardHidden",
"layoutDirection",
"locale",
"mcc",
"mnc",
"navigation",
"orientation",
"screenLayout",
"screenSize",
"smallestScreenSize",
"touchscreen",
"uiMode"]
def parser = new XmlSlurper(false, true)
def manifest = parser.parse(manifestFile)
def app = manifest.'application'[0]
app.'activity'.each { act ->
String value = act.attributes()['android:configChanges']
if (value == null || value.isEmpty()) {
if (value == null) value = ""
configChanges.eachWithIndex { config, index ->
if (index != configChanges.length - 1) {
value += config + "|"
} else {
value += config
}
}
act.attributes()['androidconfigChanges'] = value
} else {
String[] valueSplit = value.split("\\|")
println configChanges
configChanges.eachWithIndex { config, index ->
if (!valueSplit.contains(config)) {
value += ("|" + config)
}
}
act.attributes()['android:configChanges'] = value
}
}
def tmpManifest = XmlUtil.serialize(manifest).replaceAll("androidconfigChanges", "android:configChanges")
manifest = parser.parseText(tmpManifest)
manifestFile.setText(XmlUtil.serialize(manifest), "utf-8")
}
}
}
}
}

View File

@ -1,266 +0,0 @@
# 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 {
private static String getLogTag(...);
private static String getMethodName();
public static void logMethodName(...);
public static void logParams(...);
public static void logFields(...);
public static void logMethodWithParams(...);
}
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
#-dontoptimize
#--------- remove logs end ----------------
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
-dontwarn InnerClasses
# 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.** { *; }
-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.** { *; }
-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>; }
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
-dontwarn butterknife.Views$InjectViewProcessor
-dontwarn com.gc.materialdesign.views.**
# eventbus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# 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.** { *; }
-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 com.gh.gamecenter.video.detail.** {*;}
-keep class * extends rx.Subscriber
#---------------------------------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);
}
-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
-keepclassmembers class * {
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
}
# 重命名文件为SourceFile再配合mapping符号表可以拿到真实的类名
-renamesourcefileattribute SourceFile
# 保留源文件行号
-keepattributes SourceFile,LineNumberTable
-ignorewarnings
-keep @androidx.annotation.Keep class *
-keepclassmembers class ** {
@androidx.annotation.Keep *;
}
-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.** { *; }
# 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
### AndroidX
-keep class androidx.core.app.CoreComponentFactory { *; }
#阿里云上传
-keep class com.alibaba.sdk.android.oss.** { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**
#视频相关
-keep class com.shuyu.gsyvideoplayer.video.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.**
-keep class com.shuyu.gsyvideoplayer.video.base.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
-keep class com.shuyu.gsyvideoplayer.utils.** { *; }
-dontwarn com.shuyu.gsyvideoplayer.utils.**
-keep class tv.danmaku.ijk.** { *; }
-dontwarn tv.danmaku.ijk.**
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
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.gh.gamecenter.TeaHelper { *; }

View File

@ -1,77 +1,132 @@
# 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
#--------- remove logs start ----------------
-assumenosideeffects class com.lightgame.config.CommonDebug {
private static String getLogTag(...);
private static String getMethodName();
public static void logMethodName(...);
public static void logParams(...);
public static void logFields(...);
public static void logMethodWithParams(...);
}
# Add any project specific keep options here:
-assumenosideeffects class com.lightgame.utils.Utils {
public static void log(...);
}
#--------- remove logs end ----------------
# 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 useless mtahelper class --------
-assumenosideeffects class com.gh.common.util.MtaHelper {
public static void onEvent(...);
public static void onEventWithTime(...);
public static void onEventWithBasicDeviceInfo(...);
}
#--------- remove useless mta class end ----
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
-dontwarn InnerClasses
-dontoptimize
# TODO Dicard sourceFile in final release build but remain in internal build.
-renamesourcefileattribute SourceFile
# Keep Attribute
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
# 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.* { *; }
# 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.** { *; }
-dontwarn com.j256.**
### AutoScrollViewPager
-keep class cn.trinea.android.* { *; }
-keepclassmembers class cn.trinea.android.* { *; }
#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.** { *; }
-dontwarn cn.trinea.android.**
### eventbus
-keepclassmembers class * {
## 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>; }
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
-dontwarn butterknife.Views$InjectViewProcessor
-dontwarn com.gc.materialdesign.views.**
# eventbus
-keepattributes *Annotation*
-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.** { *; }
-dontwarn android.webkit.WebView
-dontwarn android.webkit.WebViewClient
### wechatSdk
### TODO 这里用 com.tencent.*{*;} 不起效?但其它地方可以?
-keep class com.tencent.**{*;}
# 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.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.* {*;}
-keep class com.gh.gamecenter.home.gamecollection.* {*;}
###
#---------------------------------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);
@ -79,79 +134,27 @@
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
#----------------------------------------------------------------------------
### easypermission
-keepclassmembers class * {
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
}
# TODO What's this ?
-ignorewarnings
##---------------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
### Keep Annotation
-keep @androidx.annotation.Keep class *
-keepclassmembers class * {
@androidx.annotation.Keep *;
}
# For using GSON @Expose annotation
-keepattributes *Annotation*
### 阿里云上传
-keep class com.alibaba.sdk.android.oss.* { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
### 视频相关
-keep class com.shuyu.gsyvideoplayer.video.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.**
-keep class com.shuyu.gsyvideoplayer.video.base.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.video.base.**
-keep class com.shuyu.gsyvideoplayer.utils.* { *; }
-dontwarn com.shuyu.gsyvideoplayer.utils.**
-keep class tv.danmaku.ijk.* { *; }
-dontwarn tv.danmaku.ijk.**
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 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
-keep class com.alibaba.sdk.android.*{*;}
-keep class com.ut.*{*;}
-keep class com.ta.*{*;}
### TEA
-keep class com.gh.gamecenter.TeaHelper { *; }
### 阿里云日志
-keep class com.aliyun.sls.android.producer.* { *; }
-keep interface com.aliyun.sls.android.producer.* { *; }
### 中国移动一键登录
-dontwarn com.cmic.sso.sdk.**
-keep class com.cmic.sso.sdk.* { *; }
### EasyFloat
-keep class com.lzf.easyfloat.* {*;}
### 避免 WebChromeClient 被混淆
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
### emoji4j
-keep class emoji4j.* {*;}
### dokit
-keep class com.didichuxing.** {*;}
# Flutter模块
-keep class com.gh.common.util.DirectUtils {
public static void directToQa(...);
public static void directToQaCollection(...);
public static void directToGift(...);
public static void directToConcernInfo(...);
public static void directToFeedback(...);
public static void directToSuggestion(...);
}
-keepclassmembers enum * { *; }
##---------------End: proguard configuration for Gson ----------

View File

@ -0,0 +1,13 @@
package com.gh.gamecenter;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -1,32 +0,0 @@
package com.gh.gamecenter;
import android.app.Application;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
/**
* @author CsHeng
* @Date 03/09/2017
* @Time 4:34 PM
*/
public class Injection {
public static boolean appInit(Application application) {
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
// TooLargeTool.startLogging(application);
return true;
}
public static OkHttpClient.Builder provideRetrofitBuilder() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(interceptor);
return builder;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<title>光环助手</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
body {
font: 100%/1.0 'Microsoft YaHei','Helvetica Neue',Helvetica,Arial,sans-serif;
background-color: #fff;
margin: 0;
padding: 0;
}
header {
}
article {
width:100%;
max-width:720px;
clear: both;
margin: 0 auto;
margin-top: 20%;
text-align: center;
margin-bottom:20%;
}
.title{margin-top: 4%;font-size:1.7em;color:#191919;text-align:center;}
.info{margin-top: 18%;font-size:1.0em;color:#191919;line-height:1.3em;}
.download {text-align: center;}
.download a{font-size:1.8em;padding:0.2em; text-align:center;color:#ffffff;margin: 0 auto;width:56%;background-color:#2999f9;border-radius:8px; text-decoration:none;display:block;line-height:1.8em;}
@media only screen and (min-width: 1080px) {
article {
width:100%;
max-width:720px;
clear: both;
margin: 0 auto;
margin-top: 5%;
text-align: center;
margin-bottom:20%;
}
}
</style>
</head>
<body>
<header>
</header>
<article>
<img src="http://192.168.43.1:3100/image/gh_icon.png" width="28%">
<p class="title">光环助手</p>
<br class="info">乐于分享的人是最帅的^_^ </p>
<div class="download">
<a href="http://192.168.43.1:3100/download/ghzs.apk">免流量下载</a>
</div>
<p class="title"><font color="#9A9A9A" size="3em">仅限安卓系统 </font></p>
</article>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,163 +0,0 @@
function requestContentFocus() {
$("#editor").focus();
}
function setupWhenContentEditable() {
var editor = $("#editor");
if (!editor[0].hasAttribute("contenteditable")) {
return;
}
// paste
editor.on("paste", function(e) {
e.preventDefault();
var text = (e.originalEvent || e).clipboardData.getData("text/plain");
text = text.replace(/\n/g, "<br>");
if ("" != text) {
document.execCommand("insertHTML", false, text);
} else {
window.onPasteListener.onPaste();
}
});
requestContentFocus();
}
function getStyle(dom, name) {
return window.getComputedStyle(dom)[name];
}
function customLinkgo(self) {
var datas = self.dataset.datas;
console.log(datas)
window.OnLinkClickListener.onClick(datas);
}
var typeClassList = [
"community_article-container",
"answer-container",
"game-container"
];
function removeDomByParent(curDom) {
if (curDom.parentElement) {
curDom.parentElement.removeChild(curDom);
}
}
window.addEventListener("load", function() {
var EditorDom = document.querySelector("#editor");
setupWhenContentEditable();
document.addEventListener("keydown", function(e) {
var event = e || window.event;
if (event.keyCode === 8) {
var s = document.getSelection();
var r = s.getRangeAt(0);
if (r.startOffset === r.endOffset && r.endOffset === 0) {
var preDOM = s.focusNode.previousElementSibling;
if (
preDOM &&
preDOM instanceof Element &&
preDOM.nodeName === "IMG" &&
getStyle(preDOM, "display") === "block"
) {
preDOM.parentElement.removeChild(preDOM);
}
}
var customDom = s.focusNode;
if (customDom) {
if (
r.startContainer.nodeName.toLowerCase() === "blockquote" &&
r.startOffset === 0
) {
RE.formatBlock();
e.preventDefault();
} else if (
customDom.nodeName === "#text" &&
customDom.previousElementSibling &&
typeClassList.indexOf(customDom.previousElementSibling.className) >
-1 &&
r.startOffset === 1
) {
var needDeleteDom = customDom.previousElementSibling;
needDeleteDom.insertAdjacentElement(
"afterend",
document.createElement("br")
);
} else if (
customDom instanceof Element &&
customDom.childNodes[s.focusOffset] &&
customDom.childNodes[s.focusOffset].previousElementSibling &&
typeClassList.indexOf(
customDom.childNodes[s.focusOffset].previousElementSibling.className
) > -1
) {
customDom =
customDom.childNodes[s.focusOffset].previousElementSibling;
customDom.parentElement.removeChild(customDom);
}
}
}
});
document.addEventListener("keyup", function(e) {
var event = e || window.event;
if (event.keyCode === 13) {
var s = document.getSelection();
var curDom = s.focusNode;
var preDom = curDom.previousElementSibling;
if (
curDom.nodeName.toLowerCase() === "blockquote" &&
preDom.nodeName.toLowerCase() === "blockquote"
) {
if (
preDom.childNodes.length > 1 ||
(preDom.childNodes.length === 1 &&
preDom.childNodes[0].tagName !== "BR")
) {
curDom.style.marginTop = 0;
preDom.style.marginBottom = 0;
} else if (
(curDom.childNodes.length === 0 ||
(curDom.childNodes.length === 1 &&
curDom.childNodes[0].tagName === "BR")) &&
(preDom.childNodes.length === 0 ||
(preDom.childNodes.length === 1 &&
preDom.childNodes[0].tagName === "BR"))
) {
removeDomByParent(curDom);
var startQuoteDom = preDom.previousElementSibling;
startQuoteDom && startQuoteDom.nodeName.toLowerCase() === "blockquote"
? (startQuoteDom.style.marginBottom = "10px")
: null;
var range = document.createRange();
range.selectNode(preDom);
s.removeAllRanges();
s.addRange(range);
RE.formatBlock();
}
}
}
});
document.addEventListener("selectionchange", function(e) {
var event = e || window.event;
var targetDom = event.target.activeElement;
if (targetDom.id === "editor" && targetDom.lastElementChild) {
if (typeClassList.indexOf(targetDom.lastElementChild.className) > -1) {
var brDom = document.createElement("br");
EditorDom.appendChild(brDom);
}
}
});
});

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" type="text/css" href="normalize.css">
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="video-js.min.css">
<!-- <link rel="stylesheet" href="https://static-web.ghzs.com/website-static/lib/video-js.min.css">--> <!--在web页面播放视频-->
<!--<link rel="stylesheet" type="text/css" href="https://resource.ghzs.com/css/halo_app.css">-->
</head>
<body style="overflow-x: hidden; word-break: break-all;">
<div id="editor" contenteditable="false"></div>
<script type="text/javascript" src="zepto.min.js"></script>
<script type="text/javascript" src="rich_editor.js"></script>
<script type="text/javascript" src="video.min.js"></script>
<!--<script src="https://static-web.ghzs.com/website-static/lib/video.min.js"></script>--> <!--在web页面播放视频-->
<!--<script type="text/javascript" src="content.js"></script>-->
<!--<script type="text/javascript" src="https://resource.ghzs.com/js/halo_app.js"></script>-->
</body>
</html>

View File

@ -1 +0,0 @@
{"v":"5.5.9","fr":60,"ip":0,"op":90,"w":1080,"h":202,"nm":"click","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"椭圆形","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204,1455,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"椭圆形","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":63,"s":[10]},{"t":70,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[204,1455,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":39,"s":[100,100,100]},{"t":49,"s":[110,110,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"圆环","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.531],"y":[0]},"t":28,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":38,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.526],"y":[0]},"t":48,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.446],"y":[0]},"t":63,"s":[50]},{"t":82,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125.951,79.658,0],"ix":2},"a":{"a":0,"k":[205.951,1458.658,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.601,0.601,0.333],"y":[0,0,0]},"t":28,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.528,0.528,0.333],"y":[0,0,0]},"t":38,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.526,0.526,0.333],"y":[0,0,0]},"t":48,"s":[120,120,100]},{"t":63,"s":[100,100,100]}],"ix":6}},"ao":0,"w":1080,"h":1920,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"点击手","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.596],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.515],"y":[0]},"t":63,"s":[100]},{"t":83,"s":[0]}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.507],"y":[0]},"t":10,"s":[6]},{"t":30,"s":[2]}],"ix":10},"p":{"a":0,"k":[178.982,123.325,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.489,0.489,0.333],"y":[0,0,0]},"t":10,"s":[100,100,100]},{"t":30,"s":[90,90,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.33,-8.3],[3.89,0.27],[-4.4,-1.68],[-4.33,-0.67],[-4.08,9.32],[3.33,5.44],[3.39,4.6],[0.87,-3.7],[3.6,-0.86],[1.03,-0.21],[2.34,-0.53],[0.96,1.15],[4.22,5.48],[-1.18,-4.56]],"o":[[1.11,1.71],[-3.89,-0.27],[6.42,2.5],[4.33,0.66],[1.63,-5.32],[-3.34,-5.45],[-1.68,-2.1],[-0.71,3.14],[-3.43,0.95],[-0.57,0.08],[-3.86,1.12],[-3.23,-3.94],[-1.89,-2.28],[2.42,4.64]],"v":[[-5.387,9.698],[-10.717,8.498],[-12.327,15.628],[5.813,21.748],[23.313,11.778],[20.273,-1.202],[11.563,-13.962],[5.393,-12.362],[1.083,-13.722],[-2.087,-9.742],[-5.707,-11.752],[-8.777,-7.572],[-18.297,-20.542],[-23.827,-17.832]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径备份 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0}],"markers":[]}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"v":"5.6.4","fr":25,"ip":0,"op":35,"w":1080,"h":214,"nm":"点赞","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"椭圆形 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.588],"y":[0]},"t":17,"s":[15]},{"t":20,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[468.04,73.68,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.659,0.659,0.333],"y":[0,0,0]},"t":10,"s":[50,50,100]},{"t":19,"s":[150,150,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":1510,"st":10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"椭圆形 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.588],"y":[0]},"t":27,"s":[15]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[468.04,73.68,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.659,0.659,0.333],"y":[0,0,0]},"t":20,"s":[50,50,100]},{"t":28,"s":[140,140,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":20,"op":1520,"st":20,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"路径备份 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.602],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.663],"y":[0]},"t":24,"s":[100]},{"t":29,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[531.02,129.675,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.514,0.514,0.333],"y":[0,0,0]},"t":5,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.533,0.533,0.333],"y":[0,0,0]},"t":10,"s":[90,90,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.586,0.586,0.333],"y":[0,0,0]},"t":15,"s":[95,95,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.499,0.499,0.333],"y":[0,0,0]},"t":19,"s":[90,90,100]},{"t":24,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.33,-8.3],[3.89,0.27],[-4.4,-1.68],[-4.33,-0.67],[-4.08,9.32],[3.33,5.44],[3.39,4.6],[0.87,-3.7],[3.6,-0.86],[1.03,-0.21],[2.34,-0.53],[0.96,1.15],[4.22,5.48],[-1.18,-4.56]],"o":[[1.11,1.71],[-3.89,-0.27],[6.42,2.5],[4.33,0.66],[1.63,-5.32],[-3.34,-5.45],[-1.68,-2.1],[-0.71,3.14],[-3.43,0.95],[-0.57,0.08],[-3.86,1.12],[-3.23,-3.94],[-1.89,-2.28],[2.42,4.64]],"v":[[-5.387,9.698],[-10.717,8.498],[-12.327,15.628],[5.813,21.748],[23.313,11.778],[20.273,-1.202],[11.563,-13.962],[5.393,-12.362],[1.083,-13.722],[-2.087,-9.742],[-5.707,-11.752],[-8.777,-7.572],[-18.297,-20.542],[-23.827,-17.832]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径备份 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1500,"st":0,"bm":0}],"markers":[]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"v":"5.6.9","fr":60,"ip":0,"op":76,"w":90,"h":90,"nm":"loading","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"圆环1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[45,45,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[63,63],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.833]},"o":{"x":[0.25],"y":[0.167]},"t":28,"s":[0]},{"t":76,"s":[96]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.833]},"o":{"x":[0.25],"y":[0.167]},"t":0,"s":[4]},{"t":48,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.712]},"o":{"x":[0.25],"y":[0.288]},"t":0,"s":[-7.2]},{"t":76,"s":[367.2]}],"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":76,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"圆环2","sr":1,"ks":{"o":{"a":0,"k":40,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[45,45,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[63,63],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.861]},"o":{"x":[0.25],"y":[0.139]},"t":36,"s":[0]},{"t":76,"s":[96]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.806]},"o":{"x":[0.25],"y":[0.194]},"t":0,"s":[4]},{"t":56,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.75],"y":[0.712]},"o":{"x":[0.25],"y":[0.288]},"t":0,"s":[-7.2]},{"t":76,"s":[367.2]}],"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.141176477075,0.588235318661,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":76,"st":0,"bm":0}],"markers":[{"tm":-5940,"cm":"S:[false]","dr":0},{"tm":0.740234375,"cm":"FOCUSED -- TO SELECTION","dr":0}]}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"v":"5.5.9","fr":60,"ip":0,"op":120,"w":1080,"h":586,"nm":"上滑","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"手","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.642],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.558],"y":[0]},"t":60,"s":[100]},{"t":71,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.479,"y":0},"t":3,"s":[611,475,0],"to":[0,-62.75,0],"ti":[0,62.75,0]},{"t":40,"s":[611,98.5,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[90,90,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.78,-12.06],[5.68,0.39],[-6.42,-2.45],[-6.32,-0.97],[-5.95,13.55],[4.87,7.91],[4.94,6.69],[1.26,-5.37],[5.25,-1.25],[1.5,-0.3],[3.4,-0.77],[1.4,1.68],[6.15,7.98],[-1.73,-6.64]],"o":[[1.62,2.49],[-5.68,-0.39],[9.37,3.63],[6.31,0.98],[2.39,-7.74],[-4.87,-7.92],[-2.45,-3.05],[-1.05,4.57],[-4.99,1.39],[-0.83,0.13],[-5.63,1.63],[-4.71,-5.72],[-2.75,-3.31],[3.52,6.76]],"v":[[-7.86,14.26],[-15.64,12.52],[-17.99,22.89],[8.47,31.78],[33.98,17.29],[29.55,-1.59],[16.85,-20.15],[7.86,-17.83],[1.56,-19.8],[-3.05,-14.02],[-8.33,-16.94],[-12.44,-11.045],[-26.761,-30.19],[-34.75,-25.78]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"矩形","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.715],"y":[0]},"t":57,"s":[100]},{"t":67,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[525,228.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.491,"y":0},"t":3,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[24.328,180.5],[-22,180.5],[-22,204.5],[24.328,204.5]],"c":true}]},{"t":40,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[24.328,-207.5],[-22,-207.5],[-22,204.5],[24.328,204.5]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"蒙版 1"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[6,137],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.5,1,1,1,1,1,1,1,0,1,0.5,0.5,1,0],"ix":9}},"s":{"a":0,"k":[0,-68.5],"ix":5},"e":{"a":0,"k":[0,68.5],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":33,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":123,"st":3,"bm":0}],"markers":[]}

View File

@ -1 +0,0 @@
{"v":"5.6.9","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-关闭","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[87,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[31,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[33,33,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[6.5,6.5]},{"t":18,"s":[4.5,4.5]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.966666666667,0.966666666667,0.966666666667,0.420000005762],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,4]},{"t":18,"s":[1.5,6]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sy":[{"c":{"a":0,"k":[0,0,0,1],"ix":2},"o":{"a":0,"k":5,"ix":3},"a":{"a":0,"k":120,"ix":5},"s":{"a":0,"k":1,"ix":8},"d":{"a":0,"k":0,"ix":6},"ch":{"a":0,"k":100,"ix":7},"bm":{"a":0,"k":5,"ix":1},"no":{"a":0,"k":0,"ix":9},"ty":2,"nm":"内阴影"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.141176477075,0.588235318661,1,1]},{"t":18,"s":[0.933333337307,0.933333337307,0.933333337307,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]}

View File

@ -1 +0,0 @@
{"v":"5.6.9","fr":60,"ip":0,"op":36,"w":120,"h":66,"nm":"开关动画-打开","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"按钮手柄","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.667,"y":0},"t":0,"s":[33,33,0],"to":[7.682,0,0],"ti":[-13.443,0,0]},{"i":{"x":0.333,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[89,33,0],"to":[2.306,0,0],"ti":[-1.318,0,0]},{"t":24,"s":[87,33,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"指示器-on","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0]},{"t":18,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[4.5,4.5]},{"t":18,"s":[6.5,6.5]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.966666666667,0.966666666667,0.966666666667,0.420000005762],"ix":3},"o":{"a":0,"k":40,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"指示器-off","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[87,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.667,0.667],"y":[0,0]},"t":0,"s":[1.5,6]},{"t":18,"s":[1.5,4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0.75,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":90,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[100]},{"t":18,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"按钮背景","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[60,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sy":[{"c":{"a":0,"k":[0,0,0,1],"ix":2},"o":{"a":0,"k":5,"ix":3},"a":{"a":0,"k":120,"ix":5},"s":{"a":0,"k":1,"ix":8},"d":{"a":0,"k":0,"ix":6},"ch":{"a":0,"k":100,"ix":7},"bm":{"a":0,"k":5,"ix":1},"no":{"a":0,"k":0,"ix":9},"ty":2,"nm":"内阴影"}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.667],"y":[0]},"t":0,"s":[0.933332979679,0.933332979679,0.933332979679,1]},{"t":18,"s":[0.141176477075,0.588235318661,1,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"filling","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.118,0.006],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":37,"st":0,"bm":0}],"markers":[]}

View File

@ -1 +0,0 @@
{"v":"5.6.9","fr":30,"ip":0,"op":20,"w":66,"h":66,"nm":"bottom bar tab/论坛/选中/E","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"白-修正","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[110,110,100]},{"t":13,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,-0.66],[0,0],[1.38,0],[0,1.38],[0,0],[-0.66,0],[0,0]],"o":[[0,0],[0,1.38],[-1.38,0],[0,0],[0,-0.66],[0,0],[0.66,0]],"v":[[2.5,-1.3],[2.5,0],[0,2.5],[-2.5,0],[-2.5,-1.3],[-1.3,-2.5],[1.3,-2.5]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":4,"s":[{"i":[[0,-0.66],[0,0],[1.38,0],[0,1.38],[0,0],[-0.66,0],[0,0]],"o":[[0,0],[0,1.38],[-1.38,0],[0,0],[0,-0.66],[0,0],[0.66,0]],"v":[[2.5,-0.102],[2.5,0],[0,2.109],[-2.5,0],[-2.5,-0.102],[-1.3,-1.302],[1.3,-1.302]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":9,"s":[{"i":[[0,-0.66],[0,0],[1.38,0],[0,1.38],[0,0],[-0.66,0],[0,0]],"o":[[0,0],[0,1.38],[-1.38,0],[0,0],[0,-0.66],[0,0],[0.66,0]],"v":[[2.498,-1.845],[2.5,0],[0,2.734],[-2.5,0],[-2.502,-1.845],[-1.302,-3.045],[1.298,-3.045]],"c":true}]},{"t":13,"s":[{"i":[[0,-0.66],[0,0],[1.38,0],[0,1.38],[0,0],[-0.66,0],[0,0]],"o":[[0,0],[0,1.38],[-1.38,0],[0,0],[0,-0.66],[0,0],[0.66,0]],"v":[[2.5,-1.3],[2.5,0],[0,2.5],[-2.5,0],[-2.5,-1.3],[-1.3,-2.5],[1.3,-2.5]],"c":true}]}],"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"蓝","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33.76,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.8,0.42],[-3.44,-0.79],[-0.09,-1.71],[0,0],[0,0],[1.98,-0.1],[0,0],[0,0],[0,0],[0.62,0.57],[0,0],[0,0],[0,0],[0,0],[0,0],[0.2,1.89],[0,0],[0,0],[0,0]],"o":[[3.44,-0.79],[1.74,0.41],[0,0],[0,0],[0,2],[0,0],[0,0],[0,0],[-0.69,0.52],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.94,0],[0,0],[0,0],[0,0],[0,-1.8]],"v":[[-6.39,-8.971],[6.39,-8.971],[9.49,-5.471],[9.5,-5.271],[9.5,3.249],[5.95,6.989],[5.75,6.999],[3.25,6.999],[0.3,9.209],[-1.95,9.089],[-2.06,8.969],[-2.17,8.849],[-2.21,8.779],[-3.4,6.999],[-5.75,6.999],[-9.48,3.639],[-9.49,3.449],[-9.5,3.249],[-9.5,-5.271]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.266,0.638,1,0.5,0.242,0.595,1,1,0.217,0.552,1],"ix":9}},"s":{"a":0,"k":[-9.5,-9.561],"ix":5},"e":{"a":0,"k":[9.5,9.561],"ix":6},"t":1,"nm":"color","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":150,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"预合成 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2},"a":{"a":0,"k":[33,33,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":4,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":13,"s":[90,90,100]},{"t":16,"s":[100,100,100]}],"ix":6}},"ao":0,"w":66,"h":66,"ip":0,"op":20,"st":0,"bm":0}],"markers":[]}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"v":"5.5.9","fr":30,"ip":0,"op":20,"w":66,"h":66,"nm":"tab_index","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"椭圆形备份","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.596],"y":[0]},"t":0,"s":[0]},{"t":6,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,40.493,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.54,0.54,0.333],"y":[0,0,0]},"t":4,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.555,0.555,0.333],"y":[0,0,0]},"t":9,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.552,0.552,0.333],"y":[0,0,0]},"t":13,"s":[90,90,100]},{"t":16,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[5,5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.635],"y":[0]},"t":0,"s":[0]},{"t":8,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形备份","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-0.5,"op":59.5,"st":-0.5,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"路径备份","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,32.993,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.508,0.508,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.488,0.488,0.333],"y":[0,0,0]},"t":4,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.502,0.502,0.333],"y":[0,0,0]},"t":9,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.534,0.534,0.333],"y":[0,0,0]},"t":13,"s":[95,95,100]},{"t":16,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.24,-1.21],[-1.93,-1.89],[-0.24,-0.57],[0,-0.06],[0,-2.63],[1.76,0],[0,0],[0,1.72],[0,2.63],[-0.24,0.61],[-0.03,0.02],[-1.93,1.89]],"o":[[1.92,1.89],[0.02,0.02],[0.24,0.57],[0,2.63],[0,1.72],[0,0],[-1.76,0],[0,-2.62],[0,-0.07],[0.25,-0.61],[1.92,-1.89],[1.24,-1.21]],"v":[[2.26,-9.09],[8.03,-3.42],[8.76,-2.38],[9,-1.02],[9,6.88],[5.82,10],[-5.82,10],[-9,6.88],[-9,-0.99],[-8.71,-2.38],[-8.01,-3.43],[-2.23,-9.09]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.266,0.638,1,0.5,0.242,0.595,1,1,0.217,0.552,1],"ix":9}},"s":{"a":0,"k":[-4.902,-4.663],"ix":5},"e":{"a":0,"k":[8.159,8.646],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径备份","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0}],"markers":[]}

View File

@ -1 +0,0 @@
{"v":"5.5.9","fr":30,"ip":0,"op":20,"w":66,"h":66,"nm":"tab_video","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"形状图层 1","parent":2,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.771],"y":[0]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-3.742,6.835,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[30.937,31.042,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":29.286,"ix":7},"os":{"a":0,"k":75,"ix":9},"ix":1,"nm":"多边星形路径 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-20.75,-13.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[102.743,88.578],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"多边星形 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.657],"y":[0]},"t":0,"s":[0]},{"t":8,"s":[100]}],"ix":2},"o":{"a":0,"k":-115,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"路径 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33.004,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.508,0.508,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.488,0.488,0.333],"y":[0,0,0]},"t":4,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.502,0.502,0.333],"y":[0,0,0]},"t":9,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.534,0.534,0.333],"y":[0,0,0]},"t":13,"s":[95,95,100]},{"t":16,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[2.16,0.38],[3.22,-0.55],[0.38,-2.16],[-0.55,-3.22],[-2.16,-0.38],[-1.63,0],[-1.61,0.27],[-0.38,2.16],[0.55,3.22]],"o":[[-0.38,-2.16],[-3.22,-0.55],[-2.16,0.38],[-0.55,3.22],[0.38,2.16],[1.61,0.27],[1.63,0],[2.16,-0.38],[0.55,-3.22],[0,0]],"v":[[9.09,-4.86],[4.86,-9.09],[-4.86,-9.09],[-9.09,-4.86],[-9.09,4.86],[-4.86,9.09],[0,9.5],[4.86,9.09],[9.09,4.86],[9.09,-4.86]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.266,0.638,1,0.5,0.242,0.595,1,1,0.217,0.552,1],"ix":9}},"s":{"a":0,"k":[-5.174,-4.43],"ix":5},"e":{"a":0,"k":[8.612,8.214],"ix":6},"t":1,"nm":"Gradient Fill 3","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0}],"markers":[]}

View File

@ -1,413 +0,0 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

View File

@ -1,132 +0,0 @@
[
{
"login": {
"title": "开启消息通知",
"content": "新游上线、互动回复,重要推送不错过",
"image": "bg_notification_login_style_1",
"styleNo": "样式A",
"scenes": "场景1"
},
"question": {
"title": "开启消息通知",
"content": "及时查看大神回答",
"image": "bg_notification_question_style_1",
"styleNo": "样式A",
"scenes": "场景2"
},
"answer": {
"title": "开启消息通知",
"content": "及时查看点赞与评论",
"image": "bg_notification_answer_style_1",
"styleNo": "样式A",
"scenes": "场景3"
},
"article": {
"title": "开启消息通知",
"content": "及时查看点赞与评论",
"image": "bg_notification_article_style_1",
"styleNo": "样式A",
"scenes": "场景4"
},
"video": {
"title": "开启消息通知",
"content": "实时获取审核与推荐进度",
"image": "bg_notification_video_style_1",
"styleNo": "样式A",
"scenes": "场景5"
},
"rating": {
"title": "开启消息通知",
"content": "成功上墙立即知道",
"image": "bg_notification_rating_style_1",
"styleNo": "样式A",
"scenes": "场景6"
},
"gift": {
"title": "开启消息通知",
"content": "新上礼包不再错过",
"image": "bg_notification_gift_style_1",
"styleNo": "样式A",
"scenes": "场景7"
},
"reserveGame": {
"title": "开启消息通知",
"content": "新游上线即时体验",
"image": "bg_notification_reserve_game_style_1",
"styleNo": "样式A",
"scenes": "场景8"
},
"feedback": {
"title": "开启消息通知",
"content": "及时查看客服回复",
"image": "bg_notification_feedback_style_1",
"styleNo": "样式A",
"scenes": "场景9"
}
},
{
"login": {
"title": "咦!是新的小伙伴耶!",
"content": "打开<font color=\"#1383EB\">通知开关</font>,游戏、礼包、抽奖活动不错过",
"image": "bg_notification_login_style_2",
"styleNo": "样式B",
"scenes": "场景1"
},
"question": {
"title": "发布成功!答案马上来!",
"content": "为了第一时间通知您,需要打开<font color=\"#1383EB\">通知开关</font>",
"image": "bg_notification_question_style_2",
"styleNo": "样式B",
"scenes": "场景2"
},
"answer": {
"title": "精彩的回答!大佬牛啤!",
"content": "打开<font color=\"#1383EB\">通知开关</font>,可以第一时间收获赞美和感谢哟!",
"image": "bg_notification_answer_style_2",
"styleNo": "样式B",
"scenes": "场景3"
},
"article": {
"title": "发布成功!不愧是你!",
"content": "打开<font color=\"#1383EB\">通知开关</font>,可以第一时间收获赞美和互动哟!",
"image": "bg_notification_article_style_2",
"styleNo": "样式B",
"scenes": "场景4"
},
"video": {
"title": "“百万”播放预定!",
"content": "<font color=\"#1383EB\">打开通知!</font>第一时间知道审核结果和互动信息哟!",
"image": "bg_notification_video_style_2",
"styleNo": "样式B",
"scenes": "场景5"
},
"rating": {
"title": "这游戏超好玩,我说的!",
"content": "想知道有多少人吃下安利?<font color=\"#1383EB\">打开通知</font>,马上知道!",
"image": "bg_notification_rating_style_2",
"styleNo": "样式B",
"scenes": "场景6"
},
"gift": {
"title": "获得道具:神奇的游戏礼包!",
"content": "<font color=\"#1383EB\">打开通知!</font>礼包上线,马上知道!",
"image": "bg_notification_gift_style_2",
"styleNo": "样式B",
"scenes": "场景7"
},
"reserveGame": {
"title": "玩最新的游戏,做游戏圈最靓的仔",
"content": "<font color=\"#1383EB\">打开通知!</font>游戏上线,更快知道!",
"image": "bg_notification_reserve_game_style_2",
"styleNo": "样式B",
"scenes": "场景8"
},
"feedback": {
"title": "真是重要的反馈!",
"content": "感恩有你,光环更精彩!<font color=\"#1383EB\">打开通知</font>,客服回复,马上知道!",
"image": "bg_notification_feedback_style_2",
"styleNo": "样式B",
"scenes": "场景9"
}
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1,636 +0,0 @@
/**
* Copyright (C) 2017 Wasabeef
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// alert("")
var RE = {};
RE.currentSelection = {
"startContainer": 0,
"startOffset": 0,
"endContainer": 0,
"endOffset": 0};
var isDebug = false;
try {
isDebug = window.NativeCallBack.isNativeBuildDebug()
} catch(error) {
}
// 引用远端的JS 和 CSS
var script = document.createElement("script")
document.body.appendChild(script)
if (isDebug) {
script.src = "https://resource.ghzs.com/js/halo_app_test.js" + "?timestamp=" + Math.round(new Date().getTime() / 1000)
} else {
script.src = "https://resource.ghzs.com/js/halo.js" + "?timestamp=" + Math.round(new Date().getTime() / 1000 / 1000)
}
var style = document.createElement("link")
style.rel = "stylesheet"
style.type = "text/css"
if (isDebug) {
style.href = "https://resource.ghzs.com/css/halo_app_test.css" + "?timestamp=" + Math.round(new Date().getTime() / 1000)
} else {
style.href = "https://resource.ghzs.com/css/halo.css" + "?timestamp=" + Math.round(new Date().getTime() / 1000 / 1000)
}
document.head.appendChild(style)
RE.editor = document.getElementById('editor');
document.addEventListener("selectionchange", function() { RE.backuprange(); });
// Initializations
RE.callback = function() {
window.location.href = "re-callback://" + encodeURIComponent(RE.getHtml());
}
RE.setHtml = function(contents) {
RE.editor.innerHTML = decodeURIComponent(contents.replace(/\+/g, '%20'));
}
// 后续初始化html代码,都用该方法
RE.setHtmlByVideoStatus = function(contents) {
RE.editor.innerHTML = decodeURIComponent(contents.replace(/\+/g, '%20'));
}
// Deprecated
RE.getHtml = function() {
return RE.editor.innerHTML;
}
RE.getText = function() {
return RE.editor.innerText;
}
RE.setBaseTextColor = function(color) {
RE.editor.style.color = color;
}
RE.setBaseFontSize = function(size) {
RE.editor.style.fontSize = size;
}
RE.setPadding = function(left, top, right, bottom) {
RE.editor.style.paddingLeft = left;
RE.editor.style.paddingTop = top;
RE.editor.style.paddingRight = right;
RE.editor.style.paddingBottom = bottom;
}
RE.setBackgroundColor = function(color) {
document.body.style.backgroundColor = color;
}
RE.setBackgroundImage = function(image) {
RE.editor.style.backgroundImage = image;
}
RE.setWidth = function(size) {
RE.editor.style.minWidth = size;
}
RE.setHeight = function(size) {
RE.editor.style.height = size;
}
RE.setTextAlign = function(align) {
RE.editor.style.textAlign = align;
}
RE.setVerticalAlign = function(align) {
RE.editor.style.verticalAlign = align;
}
RE.setPlaceholder = function(placeholder) {
RE.editor.setAttribute("placeholder", placeholder);
}
RE.setEditorFocus = function() {
RE.editor.focus();
}
RE.setInputEnabled = function(inputEnabled) {
RE.editor.contentEditable = String(inputEnabled);
}
RE.formatBlock = function() {
document.execCommand('formatBlock', false, 'p');
}
RE.undo = function() {
document.execCommand('undo', false, null);
}
RE.redo = function() {
document.execCommand('redo', false, null);
}
RE.setBold = function() {
document.execCommand('bold', false, null);
}
RE.setItalic = function() {
document.execCommand('italic', false, null);
}
RE.setSubscript = function() {
document.execCommand('subscript', false, null);
}
RE.setSuperscript = function() {
document.execCommand('superscript', false, null);
}
RE.setStrikeThrough = function() {
document.execCommand('strikeThrough', false, null);
}
RE.setUnderline = function() {
document.execCommand('underline', false, null);
}
RE.setBullets = function() {
document.execCommand('insertUnorderedList', false, null);
}
RE.setNumbers = function() {
document.execCommand('insertOrderedList', false, null);
}
RE.setTextColor = function(color) {
RE.restorerange();
document.execCommand("styleWithCSS", null, true);
document.execCommand('foreColor', false, color);
document.execCommand("styleWithCSS", null, false);
}
RE.setTextBackgroundColor = function(color) {
RE.restorerange();
document.execCommand("styleWithCSS", null, true);
document.execCommand('hiliteColor', false, color);
document.execCommand("styleWithCSS", null, false);
}
RE.setFontSize = function(fontSize){
document.execCommand("fontSize", false, fontSize);
}
RE.setHeading = function(heading) {
document.execCommand('formatBlock', false, '<h'+heading+'>');
RE.sendElementNameToNative()
}
RE.setIndent = function() {
document.execCommand('indent', false, null);
}
RE.setOutdent = function() {
document.execCommand('outdent', false, null);
}
RE.setJustifyLeft = function() {
document.execCommand('justifyLeft', false, null);
}
RE.setJustifyCenter = function() {
document.execCommand('justifyCenter', false, null);
}
RE.setJustifyRight = function() {
document.execCommand('justifyRight', false, null);
}
RE.setBlockquote = function() {
document.execCommand('formatBlock', false, '<blockquote>');
// var blockId = window.getSelection().focusNode.parentNode;
// $(blockId).addClass("haloBlock")
RE.sendElementNameToNative()
}
RE.insertImage = function(url) {
var html = "<div><img src =\"" + url + "\" style=\" max-width: 100%; display:block; margin:15px auto; height: auto;\"></div><br>"
RE.insertHTML(html);
}
// 替换成缩略图
RE.replaceTbImage = function(imgRuleFlag, gifRuleFlag) {
var imgs = document.getElementsByTagName("img");
var index = 0
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
// console.log(imageClassName)
if (imageClassName == "image-link" || img.className == "poster") continue;
if(img.src.indexOf("?") > 0) continue;
// console.log(i)
var tbImg
if(img.src.indexOf(".gif") > 0) {
tbImg = img.src + gifRuleFlag
} else {
tbImg = img.src + imgRuleFlag
}
img.style.cssText = "max-width: 60%; display:block; margin:15px auto; height: auto;"
img.src = tbImg;
if (index == 0) {
var bigImg = document.createElement('img');
bigImg.src = "file:///android_asset/web_load_dfimg_icon.png";
bigImg.style.cssText = "max-width: 20%; margin:15px 0 0 0; height: auto;"
img.parentNode.insertBefore(bigImg, img.parentNode.childNodes[0]);
i++;
if(img.parentNode != null) {
img.parentNode.style.cssText += "text-align: left;"
}
if(img.parentNode != null && img.parentNode.parentNode != null) {
img.parentNode.parentNode.style.cssText += "text-align: left;"
}
}
index ++;
}
}
// 替换成默认图
RE.replaceAllDfImage = function(imgRuleFlag, gifRuleFlag) {
var imgs = document.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if(img.src.indexOf("web_load_dfimg_icon") > 0) {
img.parentNode.removeChild(img.parentNode.childNodes[0]);
i--;
} else {
if(img.src.indexOf(".gif") > 0) {
img.src = img.src.split("?")[0] + gifRuleFlag
} else {
img.src = img.src.split("?")[0] + imgRuleFlag
}
}
}
}
// 去除显示大图
RE.hideShowBigPic = function() {
var imgs = document.getElementsByTagName("img");
var j = 0;
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if (img.src.indexOf(".gif") == -1) {
j++;
}
}
// 去除显示大图
if (j == 0) {
var imgs = document.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if(img.src.indexOf("web_load_dfimg_icon") > 0) {
img.parentNode.removeChild(img.parentNode.childNodes[0]);
break;
}
}
}
}
RE.replaceDfImageByUrl = function(imgUrl, imgRuleFlag, gifRuleFlag) {
var imgs = document.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link" || img.className == "poster") continue;
if (img.src.indexOf(imgUrl) != -1) {
if(img.src.indexOf(".gif") > 0) {
img.src = img.src.split("?")[0] + gifRuleFlag
} else {
img.src = img.src.split("?")[0] + imgRuleFlag
}
}
}
RE.hideShowBigPic();
}
RE.ImageClickListener = function() {
var imgs = document.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link"|| img.className == "poster") continue;
window.imagelistener.imageArr(img.src);
img.onclick = function() {
window.imagelistener.imageClick(this.src);
}
}
}
RE.insertHTML = function(html) {
RE.restorerange();
document.execCommand('insertHTML', false, html);
}
RE.insertLink = function(url, title) {
RE.restorerange();
var sel = document.getSelection();
if (sel.toString().length == 0) {
document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
} else if (sel.rangeCount) {
var el = document.createElement("a");
el.setAttribute("href", url);
el.setAttribute("title", title);
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(el);
sel.removeAllRanges();
sel.addRange(range);
}
RE.callback();
}
RE.setTodo = function(text) {
var html = '<input type="checkbox" name="'+ text +'" value="'+ text +'"/> &nbsp;';
document.execCommand('insertHTML', false, html);
}
RE.prepareInsert = function() {
RE.backuprange();
}
RE.backuprange = function(){
var selection = window.getSelection();
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
RE.currentSelection = {
"startContainer": range.startContainer,
"startOffset": range.startOffset,
"endContainer": range.endContainer,
"endOffset": range.endOffset};
}
}
RE.restorerange = function(){
try {
var selection = window.getSelection();
selection.removeAllRanges();
var range = document.createRange();
range.setStart(RE.currentSelection.startContainer, RE.currentSelection.startOffset);
range.setEnd(RE.currentSelection.endContainer, RE.currentSelection.endOffset);
selection.addRange(range);
} catch(error) {
}
}
RE.enabledEditingItems = function(e) {
var items = [];
if (document.queryCommandState('bold')) {
items.push('bold');
}
if (document.queryCommandState('italic')) {
items.push('italic');
}
if (document.queryCommandState('subscript')) {
items.push('subscript');
}
if (document.queryCommandState('superscript')) {
items.push('superscript');
}
if (document.queryCommandState('strikeThrough')) {
items.push('strikeThrough');
}
if (document.queryCommandState('underline')) {
items.push('underline');
}
if (document.queryCommandState('insertOrderedList')) {
items.push('orderedList');
}
if (document.queryCommandState('insertUnorderedList')) {
items.push('unorderedList');
}
if (document.queryCommandState('justifyCenter')) {
items.push('justifyCenter');
}
if (document.queryCommandState('justifyFull')) {
items.push('justifyFull');
}
if (document.queryCommandState('justifyLeft')) {
items.push('justifyLeft');
}
if (document.queryCommandState('justifyRight')) {
items.push('justifyRight');
}
if (document.queryCommandState('insertHorizontalRule')) {
items.push('horizontalRule');
}
var formatBlock = document.queryCommandValue('formatBlock');
if (formatBlock.length > 0) {
items.push(formatBlock);
}
window.location.href = "re-state://" + encodeURI(items.join(','));
}
RE.focus = function() {
var range = document.createRange();
range.selectNodeContents(RE.editor);
range.collapse(false);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
RE.editor.focus();
}
RE.blurFocus = function() {
RE.editor.blur();
}
RE.removeFormat = function() {
document.execCommand('removeFormat', false, null);
}
RE.insertCustomStyleLink = function(data) {
var entity = JSON.parse(data)
var html = "<br/><div class='"+ entity.type +"-container'>\n" +
" <a class='"+ entity.type +"' href=\"javascript:void(0);\" contenteditable=\"false\" onclick=\"customLinkgo(this)\" data-datas='"+ data +"'>\n" +
" <div class='flex-container'>\n" +
" <div class='gh-internal-content img-left'>\n" +
" <img class = \"image-link\" src='"+ entity.icon +"' />\n" +
" </div>\n" +
" <div class='gh-internal-content content-right'>\n" +
" <p class='content-title'>"+ entity.title +"</p>\n" +
" <p class='contents'>"+ entity.brief +"</p>\n" +
" </div>\n" +
" </div>\n" +
" </a>\n" +
" </div><br/>"
var tags = "", gameHtml = ""
if (entity.tags != null) {
for (var i = 0; i < entity.tags.length; i++) {
tags += "<label>"+ entity.tags[i]+"</label>"
}
gameHtml = "<br/><div class='"+ entity.type +"-container'>\n" +
" <a class='"+ entity.type +"' href=\"javascript:void(0);\" contenteditable=\"false\" onclick=\"customLinkgo(this)\" data-datas='"+ data +"'>\n" +
" <div class='flex-container'>\n" +
" <div class='gh-internal-content img-left'>\n" +
" <img class='image-link' src='"+ entity.icon +"' />\n" +
" </div>\n" +
" <div class='gh-internal-content content-right'>\n" +
" <p class='content-title'>"+ entity.title +"</p>\n" +
" <p class='tags'>"+ tags +"</p>\n" +
" </div>\n" +
" </div>\n" +
" </a></div><br/>"
}
switch(entity.type) {
case "answer":
document.execCommand("insertHTML",false, html);
break
case "community_article":
document.execCommand("insertHTML",false, html);
break
case "game":
document.execCommand("insertHTML",false, gameHtml);
break
}
RE.callback();
}
RE.showLinkStyle = function() {
var answerElement = document.getElementsByClassName("answer-container");
for (var i=0;i<answerElement.length;i+=1){
answerElement[i].style.display = 'inline';
}
var articleElement = document.getElementsByClassName("community_article-container");
for (var i=0;i<articleElement.length;i+=1){
articleElement[i].style.display = 'inline';
}
var gameElement = document.getElementsByClassName("game-container");
for (var i=0;i<gameElement.length;i+=1){
gameElement[i].style.display = 'inline';
}
}
RE.hideLinkStyle = function() {
var answerElement = document.getElementsByClassName("answer-container");
for (var i=0;i<answerElement.length;i+=1){
answerElement[i].style.display = 'none';
}
var articleElement = document.getElementsByClassName("community_article-container");
for (var i=0;i<articleElement.length;i+=1){
articleElement[i].style.display = 'none';
}
var gameElement = document.getElementsByClassName("game-container");
for (var i=0;i<gameElement.length;i+=1){
gameElement[i].style.display = 'none';
}
}
// Event Listeners
RE.editor.addEventListener("input", RE.callback);
RE.editor.addEventListener("keyup", function(e) {
var KEY_LEFT = 37, KEY_RIGHT = 39;
if (e.which == KEY_LEFT || e.which == KEY_RIGHT) {
RE.enabledEditingItems(e);
}
RE.sendElementNameToNative()
});
RE.editor.addEventListener("click", function(e) {
RE.enabledEditingItems
RE.sendElementNameToNative()
var s = document.getSelection()
var isNeedRemoveR = RE.recursion(e.target)
if (isNeedRemoveR && s.rangeCount) {
s.removeAllRanges()
}
});
document.addEventListener("selectionchange", function(e) {
RE.sendElementNameToNative()
});
RE.recursion = function(dom) {
var parenDom = dom.parentElement
if (parenDom && parenDom instanceof Element &&
typeClassList.indexOf(parenDom.className) > -1) {
return parenDom
} else if(parenDom && parenDom instanceof Element &&
typeClassList.indexOf(parenDom.className) === -1 && parenDom.nodeName !== 'BODY') {
return RE.recursion(parenDom)
} else {
return null
}
}
// 返回组件标签 多个标签以"空格"划分
RE.sendElementNameToNative = function() {
if (window.getSelection) {
var selection = window.getSelection()
if (selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
var container = range.startContainer;
var elements = " " + container.localName + " ";
var parentElement;
while(true) {
if(parentElement != null) {
parentElement = parentElement.parentElement
} else {
parentElement = container.parentElement
}
if (parentElement == null || parentElement.localName == null) {
break;
}
elements = elements + " " + parentElement.localName + " "
}
// console.log(elements)
window.OnCursorChangeListener.onElements(elements);
}
}
}
// android function to open link
function customLinkgo(self) {
var datas = self.dataset.datas
// console.log(datas)
window.OnLinkClickListener.onClick(datas)
}
// 在web页面播放视频
//RE.initArticleVideo = function(){
// initArticleVideo()
//}
function showNativeDialog(title, message, positive, negative, callback) {
var jsCallbackCode = "(" + function (v) {
window.onNativeDialogCallback(v);
delete window.onNativeDialogCallback;
}.toString() + ")";
window.onNativeDialogCallback = callback;
window.NativeCallBack.showDialog(title, message, positive, negative, jsCallbackCode);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

View File

@ -1,43 +0,0 @@
/**
* Copyright (C) 2017 Wasabeef
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@charset "UTF-8";
html {
height: 100%;
}
body {
overflow: scroll;
display: table;
table-layout: fixed;
width: 100%;
min-height:100%;
}
#editor {
display: table-cell;
outline: 0px solid transparent;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
#editor[placeholder]:empty:not(:focus):before {
content: attr(placeholder);
opacity: .5;
}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,159 +0,0 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.swiperefreshlayout.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
/**
* Private class created to work around issues with AnimationListeners being
* called before the animation is actually complete and support shadows on older
* platforms.
*/
class CircleImageView extends ImageView {
private static final int KEY_SHADOW_COLOR = 0x1E000000;
private static final int FILL_SHADOW_COLOR = 0x3D000000;
// PX
private static final float X_OFFSET = 0f;
private static final float Y_OFFSET = 1.75f;
private static final float SHADOW_RADIUS = 3.5f;
private static final int SHADOW_ELEVATION = 4;
private Animation.AnimationListener mListener;
int mShadowRadius;
CircleImageView(Context context, int color) {
super(context);
final float density = getContext().getResources().getDisplayMetrics().density;
final int shadowYOffset = (int) (density * Y_OFFSET);
final int shadowXOffset = (int) (density * X_OFFSET);
mShadowRadius = (int) (density * SHADOW_RADIUS);
ShapeDrawable circle;
if (elevationSupported()) {
circle = new ShapeDrawable(new OvalShape());
ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
} else {
OvalShape oval = new OvalShadow(mShadowRadius);
circle = new ShapeDrawable(oval);
setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint());
circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
KEY_SHADOW_COLOR);
final int padding = mShadowRadius;
// set padding so the inner image sits correctly within the shadow.
setPadding(padding, padding, padding, padding);
}
circle.getPaint().setColor(color);
ViewCompat.setBackground(this, circle);
}
private boolean elevationSupported() {
return android.os.Build.VERSION.SDK_INT >= 21;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!elevationSupported()) {
setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()
+ mShadowRadius * 2);
}
}
public void setAnimationListener(Animation.AnimationListener listener) {
mListener = listener;
}
@Override
public void onAnimationStart() {
super.onAnimationStart();
if (mListener != null) {
mListener.onAnimationStart(getAnimation());
}
}
@Override
public void onAnimationEnd() {
super.onAnimationEnd();
if (mListener != null) {
mListener.onAnimationEnd(getAnimation());
}
}
/**
* Update the background color of the circle image view.
*
* @param colorRes Id of a color resource.
*/
public void setBackgroundColorRes(int colorRes) {
setBackgroundColor(ContextCompat.getColor(getContext(), colorRes));
}
@Override
public void setBackgroundColor(int color) {
if (getBackground() instanceof ShapeDrawable) {
((ShapeDrawable) getBackground()).getPaint().setColor(color);
}
}
private class OvalShadow extends OvalShape {
private RadialGradient mRadialGradient;
private Paint mShadowPaint;
OvalShadow(int shadowRadius) {
super();
mShadowPaint = new Paint();
mShadowRadius = shadowRadius;
updateRadialGradient((int) rect().width());
}
@Override
protected void onResize(float width, float height) {
super.onResize(width, height);
updateRadialGradient((int) width);
}
@Override
public void draw(Canvas canvas, Paint paint) {
final int viewWidth = CircleImageView.this.getWidth();
final int viewHeight = CircleImageView.this.getHeight();
canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint);
canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint);
}
private void updateRadialGradient(int diameter) {
mRadialGradient = new RadialGradient(diameter / 2, diameter / 2,
mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT },
null, Shader.TileMode.CLAMP);
mShadowPaint.setShader(mRadialGradient);
}
}
}

View File

@ -1,58 +0,0 @@
package androidx.swiperefreshlayout.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
public class ViewPagerSwipeRefreshLayout extends SwipeRefreshLayout {
private float startY;
private float startX;
// 记录viewPager是否拖拽的标记
private boolean mIsVpDragger;
private final int mTouchSlop;
public ViewPagerSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 记录手指按下的位置
startY = ev.getY();
startX = ev.getX();
// 初始化标记
mIsVpDragger = false;
break;
case MotionEvent.ACTION_MOVE:
// 如果viewpager正在拖拽中那么不拦截它的事件直接return false
if(mIsVpDragger) {
return false;
}
// 获取当前手指位置
float endY = ev.getY();
float endX = ev.getX();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
// 如果X轴位移大于Y轴位移那么将事件交给viewPager处理。
if(distanceX > mTouchSlop && distanceX > distanceY) {
mIsVpDragger = true;
return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 初始化标记
mIsVpDragger = false;
break;
}
// 如果是Y轴位移大于X轴事件交给swipeRefreshLayout处理。
return super.onInterceptTouchEvent(ev);
}
}

View File

@ -0,0 +1,53 @@
package com.gc.materialdesign.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class CustomView extends RelativeLayout {
final static String MATERIALDESIGNXML = "http://schemas.android.com/apk/res-auto";
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
final int disabledBackgroundColor = Color.parseColor("#E2E2E2");
// Indicate if user touched this view the last time
public boolean isLastTouch = false;
int beforeBackground;
boolean animation = false;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
setBackgroundColor(beforeBackground);
else
setBackgroundColor(disabledBackgroundColor);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (animation)
invalidate();
}
@Override
protected void onAnimationStart() {
super.onAnimationStart();
animation = true;
}
@Override
protected void onAnimationEnd() {
super.onAnimationEnd();
animation = false;
}
}

View File

@ -0,0 +1,174 @@
package com.gc.materialdesign.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
public class ProgressBarCircularIndeterminate extends CustomView {
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
int backgroundColor = Color.parseColor("#1E88E5");
float radius1 = 0;
float radius2 = 0;
int cont = 0;
boolean firstAnimationOver = false;
int arcD = 1;
int arcO = 0;
float rotateAngle = 0;
int limite = 0;
public ProgressBarCircularIndeterminate(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
// Set atributtes of XML to View
protected void setAttributes(AttributeSet attrs) {
setMinimumHeight(Utils.dpToPx(32, getResources()));
setMinimumWidth(Utils.dpToPx(32, getResources()));
// Set background Color
// Color by resource
int bacgroundColor = attrs.getAttributeResourceValue(ANDROIDXML, "background", -1);
if (bacgroundColor != -1) {
setBackgroundColor(ContextCompat.getColor(getContext(), bacgroundColor));
} else {
// Color by hexadecimal
int background = attrs.getAttributeIntValue(ANDROIDXML, "background", -1);
if (background != -1) {
setBackgroundColor(background);
} else {
setBackgroundColor(Color.parseColor("#1E88E5"));
}
}
setMinimumHeight(Utils.dpToPx(3, getResources()));
}
// Set color of background
public void setBackgroundColor(int color) {
super.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
if (isEnabled()) {
beforeBackground = backgroundColor;
}
this.backgroundColor = color;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!firstAnimationOver) {
drawFirstAnimation(canvas);
}
if (cont > 0) {
drawSecondAnimation(canvas);
}
invalidate();
}
/**
* Draw first animation of view
*
* @param canvas
*/
private void drawFirstAnimation(Canvas canvas) {
if (radius1 < getWidth() / 2) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
radius1 = (radius1 >= getWidth() / 2) ? (float) getWidth() / 2 : radius1 + 1;
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius1, paint);
} else {
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
temp.drawCircle(getWidth() / 2, getHeight() / 2, getHeight() / 2, paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
if (cont >= 50) {
radius2 = (radius2 >= getWidth() / 2) ? (float) getWidth() / 2 : radius2 + 1;
} else {
radius2 = (radius2 >= getWidth() / 2 - Utils.dpToPx(4, getResources())) ?
(float) getWidth() / 2 - Utils.dpToPx(4, getResources()) : radius2 + 1;
}
temp.drawCircle(getWidth() / 2, getHeight() / 2, radius2, transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
if (radius2 >= getWidth() / 2 - Utils.dpToPx(4, getResources())) {
cont++;
}
if (radius2 >= getWidth() / 2) {
firstAnimationOver = true;
}
}
}
/**
* Draw second animation of view
*
* @param canvas
*/
private void drawSecondAnimation(Canvas canvas) {
if (arcO == limite) {
arcD += 6;
}
if (arcD >= 290 || arcO > limite) {
arcO += 6;
arcD -= 6;
}
if (arcO > limite + 290) {
limite = arcO;
arcO = limite;
arcD = 1;
}
rotateAngle += 4;
canvas.rotate(rotateAngle, getWidth() / 2, getHeight() / 2);
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(backgroundColor);
// temp.drawARGB(0, 0, 0, 255);
temp.drawArc(new RectF(0, 0, getWidth(), getHeight()), arcO, arcD, true, paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
temp.drawCircle(getWidth() / 2, getHeight() / 2, (getWidth() / 2)
- Utils.dpToPx(4, getResources()), transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
}
/**
* Make a dark color to ripple effect
*
* @return
*/
protected int makePressColor() {
int r = (this.backgroundColor >> 16) & 0xFF;
int g = (this.backgroundColor >> 8) & 0xFF;
int b = (this.backgroundColor >> 0) & 0xFF;
// r = (r+90 > 245) ? 245 : r+90;
// g = (g+90 > 245) ? 245 : g+90;
// b = (b+90 > 245) ? 245 : b+90;
return Color.argb(128, r, g, b);
}
}

View File

@ -0,0 +1,34 @@
package com.gc.materialdesign.views;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.View;
public class Utils {
/**
* Convert Dp to Pixel
*/
public static int dpToPx(float dp, Resources resources) {
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());
return (int) px;
}
public static int getRelativeTop(View myView) {
// if (myView.getParent() == myView.getRootView())
if (myView.getId() == android.R.id.content)
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}
public static int getRelativeLeft(View myView) {
// if (myView.getParent() == myView.getRootView())
if (myView.getId() == android.R.id.content)
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}
}

View File

@ -0,0 +1,235 @@
package com.gh.base;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.support.multidex.MultiDex;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;
import android.util.Log;
import com.bandeng.MyEventBusIndex;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.gh.common.constant.Config;
import com.gh.common.util.DataUtils;
import com.gh.common.util.HttpsUtils;
import com.gh.common.util.StringUtils;
import com.gh.common.util.TokenUtils;
import com.gh.common.util.Utils;
import com.gh.gamecenter.BuildConfig;
import com.leon.channel.helper.ChannelReaderUtil;
import com.umeng.message.IUmengRegisterCallback;
import com.umeng.message.PushAgent;
import com.umeng.message.UTrack;
import com.xiaomi.channel.commonutils.logger.LoggerInterface;
import com.xiaomi.mipush.sdk.Logger;
import com.xiaomi.mipush.sdk.MiPushClient;
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
import org.greenrobot.eventbus.EventBus;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class AppController extends Application {
public static final String TAG = AppController.class.getSimpleName();
public static final String KEY_FILE_INFO = "FileInfo";
//快传文件发送单线程
public static Executor FILE_SENDER_EXECUTOR = Executors.newSingleThreadExecutor();
//快传文件发送主要的线程池
public static Executor MAIN_EXECUTOR = Executors.newCachedThreadPool();
private static AppController mInstance;
private static ArrayMap<String, Object> objectMap = new ArrayMap<>();
private String mChannel;
public static void put(String key, Object object) {
if (objectMap == null) {
objectMap = new ArrayMap<>();
}
objectMap.put(key, object);
}
public static Object get(String key, boolean isRemove) {
if (objectMap == null) {
return null;
}
if (isRemove) {
return objectMap.remove(key);
} else {
return objectMap.get(key);
}
}
public static void remove(String key) {
if (objectMap == null) {
return;
}
objectMap.remove(key);
}
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
public static synchronized AppController getInstance() {
return mInstance;
}
public String getChannel() {
return mChannel;
}
@Override
public void onCreate() {
super.onCreate();
MultiDex.install(this);
mInstance = this;
//TODO 强烈不建议开发阶段开启这个Handler必须处理错误
if (!BuildConfig.DEBUG) {
AppUncaughtHandler appUncaughtHandler = new AppUncaughtHandler(this);
Thread.setDefaultUncaughtExceptionHandler(appUncaughtHandler);
}
mChannel = ChannelReaderUtil.getChannel(this);
if (TextUtils.isEmpty(mChannel)) {
//默认用Android Studio run时并没有写入channel magic number到apk包里面所以需要fallback
mChannel = "GH_TEST";
}
Log.e("CHANNEL_ID", mChannel);
//初始化Fresco
Fresco.initialize(this);
DataUtils.init(this, BuildConfig.DEBUG, mChannel);
//测试MTA崩溃的坑爹
// if (BuildConfig.DEBUG) {
// StatConfig.setDebugEnable(true);
// StatConfig.setStatSendStrategy(StatReportStrategy.DEVELOPER);
// throw new RuntimeException("test again");
// }
HttpsUtils.initHttpsUrlConnection(this);
// if (BuildConfig.DEBUG) {
// Stetho.initializeWithDefaults(this);
// }
/**
* 注册push服务注册成功后会向{@link GHPushMessageReceiver}发送广播
* 可以从{@link GHPushMessageReceiver#onCommandResult(Context, MiPushCommandMessage)}
* 的{@link MiPushCommandMessage} 对象参数中获取注册信息
*/
if (shouldInit()) {
MiPushClient.registerPush(this, Config.MIPUSH_APPID, Config.MIPUSH_APPKEY);
}
if (BuildConfig.DEBUG) {
LoggerInterface newLogger = new LoggerInterface() {
@Override
public void setTag(String tag) {
// ignore
}
@Override
public void log(String content) {
Log.d(TAG, content);
}
@Override
public void log(String content, Throwable t) {
Log.d(TAG, content, t);
}
};
Logger.setLogger(this, newLogger);
}
//友盟推送
final PushAgent pushAgent = PushAgent.getInstance(this);
pushAgent.setAppkeyAndSecret(Config.UMENG_APPKEY, Config.UMENG_MESSAGE_SECRET);
//注册推送服务每次调用register方法都会回调该接口
pushAgent.register(new IUmengRegisterCallback() {
@Override
public void onSuccess(String deviceToken) {
//注册成功会返回device token
Utils.log("deviceToken::" + deviceToken);
//设置别名
pushAgent.addExclusiveAlias(TokenUtils.getDeviceId(getApplicationContext()),
"GHDID", new UTrack.ICallBack() {
@Override
public void onMessage(boolean b, String s) {
Utils.log(StringUtils.buildString("ExclusiveAlias::", String.valueOf(b), "==", s));
}
});
}
@Override
public void onFailure(String s, String s1) {
Utils.log("deviceToken::" + "注册失败");
}
});
// 友盟推送数据处理
pushAgent.setNotificationClickHandler(new GHUmengNotificationClickHandler());
// // 监听屏幕状态广播
// if (shouldInit()) {
// UnlockScreenReceiver unlockScreenReceiver = new UnlockScreenReceiver();
// IntentFilter intentFilter = new IntentFilter();
// intentFilter.addAction(Intent.ACTION_SCREEN_ON);
// intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
// registerReceiver(unlockScreenReceiver, intentFilter);
//
// // 用户App运行数据统计服务
// Intent intent = new Intent(getApplicationContext(), AppStaticService.class);
// startService(intent);
//
// AppRunTimeDao dao = new AppRunTimeDao(getApplicationContext());
// for (AppRunTimeInfo appRunTimeInfo : dao.getAll()) {
// Utils.log(appRunTimeInfo.getPackageName() + "====1111=====" + appRunTimeInfo.getRunTime());
// }
// }
// 启用EventBus3.0加速功能
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
}
private boolean shouldInit() {
ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
String mainProcessName = getPackageName();
Log.d(TAG, mainProcessName);
int myPid = Process.myPid();
for (RunningAppProcessInfo info : processInfos) {
if (info.pid == myPid && mainProcessName.equals(info.processName)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,51 @@
//package com.gh.base;
//
//
//import android.annotation.TargetApi;
//import android.app.Application;
//import android.content.Context;
//import android.content.Intent;
//import android.os.Build;
//import android.support.multidex.MultiDex;
//
//import com.tencent.tinker.lib.listener.DefaultPatchListener;
//import com.tencent.tinker.lib.listener.PatchListener;
//import com.tencent.tinker.lib.patch.AbstractPatch;
//import com.tencent.tinker.lib.patch.UpgradePatch;
//import com.tencent.tinker.lib.reporter.DefaultLoadReporter;
//import com.tencent.tinker.lib.reporter.DefaultPatchReporter;
//import com.tencent.tinker.lib.reporter.LoadReporter;
//import com.tencent.tinker.lib.reporter.PatchReporter;
//import com.tencent.tinker.lib.tinker.TinkerInstaller;
//import com.tencent.tinker.loader.app.DefaultApplicationLike;
//
//public class AppControllerLike extends DefaultApplicationLike {
//
// public AppControllerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
// long applicationStartElapsedTime, long applicationStartMillisTime,
// Intent tinkerResultIntent) {
// super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
// applicationStartMillisTime, tinkerResultIntent);
// }
//
// @Override
// public void onBaseContextAttached(Context base) {
// super.onBaseContextAttached(base);
// MultiDex.install(base);
//
// LoadReporter loadReporter = new DefaultLoadReporter(getApplication());
// PatchReporter patchReporter = new DefaultPatchReporter(getApplication());
// PatchListener patchListener = new DefaultPatchListener(getApplication());
// AbstractPatch upgradePatchProcessor = new UpgradePatch();
//
// TinkerInstaller.install(this, loadReporter, patchReporter, patchListener,
// AppTinkerResultService.class, upgradePatchProcessor);
//// TinkerInstaller.install(this);
// }
//
// @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
// public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
// getApplication().registerActivityLifecycleCallbacks(callback);
// }
//
//}

View File

@ -0,0 +1,29 @@
//package com.gh.base;
//
//import com.gh.common.util.Utils;
//import com.tencent.tinker.lib.service.DefaultTinkerResultService;
//import com.tencent.tinker.lib.service.PatchResult;
//import com.tencent.tinker.lib.util.TinkerServiceInternals;
//
//import java.io.File;
//
//
//public class AppTinkerResultService extends DefaultTinkerResultService {
//
// @Override
// public void onPatchResult(PatchResult result) {
// if (result == null) {
// return;
// }
// Utils.log(result);
//
// //first, we want to kill the recover process
// TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());
//
// if (result.isSuccess) {
// Utils.log("Tinkder Success");
// deleteRawPatchFile(new File(result.rawPatchFilePath));
// }
// }
//
//}

View File

@ -5,19 +5,16 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.gh.common.util.AppManager;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.FileUtils;
import com.gh.gamecenter.SplashScreenActivity;
import com.lightgame.config.CommonDebug;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import com.tencent.stat.StatService;
import java.io.File;
import java.io.FileWriter;
@ -26,70 +23,87 @@ 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.Sentry;
public class AppUncaughtHandler implements UncaughtExceptionHandler {
private Context mContext;
private UncaughtExceptionHandler mDefaultHandler;
private AppController mAppController;
public AppUncaughtHandler(Context context) {
public AppUncaughtHandler(AppController appController) {
// 获取系统默认的UncaughtException处理器
mContext = context;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
mAppController = appController;
}
@Override
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
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
new Thread(() -> {
Looper.prepare();
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
Looper.loop();
AppController.MAIN_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mAppController.getApplicationContext(),
"\"光环助手\"发生错误", Toast.LENGTH_SHORT).show();
Looper.loop();
}
});
saveLocalLog(mContext, e);
restart(mContext);
Sentry.captureException(e);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 防止重复奔溃导致助手一直重启20秒内不做处理
SharedPreferences sp = mAppController.getApplicationContext().getSharedPreferences(
Config.PREFERENCE, Context.MODE_PRIVATE);
long time = sp.getLong("last_restart_time", 0);
if (System.currentTimeMillis() - time > 20 * 1000) {
sp.edit().putLong("last_restart_time", System.currentTimeMillis()).apply();
Intent intent = new Intent(mAppController.getApplicationContext(), SplashScreenActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent restartIntent = PendingIntent.getActivity(
mAppController.getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 退出程序并重启
AlarmManager mgr = (AlarmManager) mAppController.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); // 1秒钟后重启应用
}
AppManager.getInstance().finishAllActivity();
}
}
public static void restart(final Context context) {
// S450 这机器会无限重启循环 : (
if ("S450".equals(Build.MODEL)) return;
// 防止重复奔溃导致助手一直重启20秒内不做处理
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long curTime = System.currentTimeMillis();
long time = sp.getLong("last_restart_time", 0);
if (curTime - time > 20 * 1000) {
sp.edit().putLong("last_restart_time", curTime).apply();
Intent intent = new Intent(context, SplashScreenActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 退出程序并重启
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, curTime + 3000, restartIntent); // 1秒钟后重启应用
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
//error restart
// System.exit(2);
AppManager.getInstance().finishAllActivity();
saveLog(ex);
return true;
}
// 保存log到本地
public static void saveLocalLog(Context context, Throwable ex) {
private void saveLog(Throwable ex) {
String errorMsg = Log.getStackTraceString(ex);
Config.setExceptionMsg(errorMsg);
// MTA主动上传错误
// StatService.reportError(mAppController.getApplicationContext(), errorMsg);
StatService.reportException(mAppController, ex);
// 上传错误数据
DataCollectionUtils.uploadError(mAppController, errorMsg);
// 保存到本地
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
File file = new File(FileUtils.getLogPath(context.getApplicationContext(),
File file = new File(FileUtils.getLogPath(mAppController.getApplicationContext(),
format.format(new Date()) + "_gh_assist" + ".log"));
FileWriter writer = null;
try {
@ -110,25 +124,4 @@ public class AppUncaughtHandler implements UncaughtExceptionHandler {
}
}
/**
* 下次应用启动再上报
*
* @param context
* @param throwable
*/
public static void reportException(Context context, Throwable throwable) {
CommonDebug.logMethodWithParams(context, "ERRMSG", throwable);
// 上传错误数据
try {
DataCollectionUtils.uploadError(context, Log.getStackTraceString(throwable));
} catch (Exception e) {
}
DataUtils.onError(context, throwable);
}
}

View File

@ -1,629 +1,186 @@
package com.gh.base;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.content.Context;
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;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.core.view.LayoutInflaterCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import com.gh.base.fragment.BaseFragment;
import com.gh.common.constant.Constants;
import com.gh.common.tracker.IBusiness;
import com.gh.common.util.DialogHelper;
import com.gh.common.constant.Config;
import com.gh.common.util.AppManager;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.EnvHelper;
import com.gh.common.util.ExtensionsKt;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.NetworkUtils;
import com.gh.common.util.NightModeUtils;
import com.gh.common.util.PackageFlavorHelper;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.QuickLoginHelper;
import com.gh.common.util.FileUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.SPUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.StringUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.LoginActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.gamecenter.eventbus.EBShowDialog;
import com.lightgame.BaseAppCompatActivity;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
import com.tencent.tauth.Tencent;
import com.gh.gamecenter.listener.OnCallBackListener;
import com.readystatesoftware.systembartint.SystemBarTintManager.SystemBarConfig;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.ArrayList;
import kotlin.Pair;
import pub.devrel.easypermissions.EasyPermissions;
import butterknife.ButterKnife;
/**
* 只提供基础的服务(EventBus/Share/GlobalDialog/Permissions)
* <p>
* 需要工具栏的页面请继承{@link ToolBarActivity}
*/
import static com.gh.common.util.EntranceUtils.KEY_DATA;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
public abstract class BaseActivity extends BaseAppCompatActivity implements EasyPermissions.PermissionCallbacks, IBusiness {
public abstract class BaseActivity extends BaseAppCompatToolBarActivity implements OnCallBackListener {
// global dialog key
public final static String DOWNLOAD_HIJACK = "hijack";
public final static String LOGIN_EXCEPTION = "loginException";
public final static String PLUGGABLE = "plugin";
public final static String SIGNATURE_CONFLICT = "signature_conflict";
public final static int ID_ROOT_INDICATOR = 999;
public final static int ID_NIGHT_INDICATOR = 998;
public final int MAX_BUNDLE_SIZE = 300;
@NonNull
protected String mEntrance;
private boolean mIsExistLogoutDialog;
protected boolean mNightMode;
public long startPageTime = 0;
protected final Handler mBaseHandler = new BaseHandler(this);
protected static class BaseHandler extends Handler {
private final WeakReference<BaseActivity> mActivityWeakReference;
BaseHandler(BaseActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
BaseActivity activity = mActivityWeakReference.get();
if (activity != null) activity.handleMessage(msg);
}
}
protected void handleMessage(Message msg) {
}
//接收QQ或者QQ空间分享回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ExtensionsKt.tryCatchInRelease(() -> {
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
}
return null;
});
}
private boolean mIsPause;
@Override
protected void onCreate(Bundle savedInstanceState) {
if (PackageFlavorHelper.IS_TEST_FLAVOR && isAutoResetViewBackgroundEnabled()) {
LayoutInflaterCompat.setFactory2(getLayoutInflater(), new CustomLayoutInflaterFactory(this));
}
super.onCreate(savedInstanceState);
if (useEventBus()) EventBus.getDefault().register(this);
init(mContentView);
AppManager.getInstance().addActivity(this);
EventBus.getDefault().register(this);
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
if (TextUtils.isEmpty(mEntrance)) {
mEntrance = Constants.ENTRANCE_UNKNOWN;
if (getIntent().getBundleExtra(KEY_DATA) != null) {
mEntrance = getIntent().getBundleExtra(KEY_DATA).getString(KEY_ENTRANCE);
}
}
if (BuildConfig.DEBUG) {
Utils.log("ACTIVITY_ENTRANCE -> " + mEntrance);
@Override
protected boolean onNavigationIconClicked() {
return false;
}
private void init(View contentView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
SystemBarConfig config = getTintManager().getConfig();
contentView.setPadding(0, config.getPixelInsetTop(false), 0, config.getPixelInsetBottom());
}
setContentView(contentView);
disableAutofill();
ButterKnife.bind(this);
if (savedInstanceState != null) {
String xapkUnzipActivity = SPUtils.getString(Constants.SP_XAPK_UNZIP_ACTIVITY);
String xapkUrl = SPUtils.getString(Constants.SP_XAPK_URL);
Utils.log("页面重建了--" + xapkUnzipActivity + "--" + xapkUrl);
if (this.getClass().isAssignableFrom(SplashScreenActivity.class)) {
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "");
SPUtils.setString(Constants.SP_XAPK_URL, "");
return;
}
if (this.getClass().getName().equals(xapkUnzipActivity) && !TextUtils.isEmpty(xapkUrl)) {
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntityByUrl(xapkUrl);
if (downloadEntity != null) {
PackageInstaller.install(this, downloadEntity, false);
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, "");
SPUtils.setString(Constants.SP_XAPK_URL, "");
View reuse_actionbar = findViewById(R.id.reuse_actionbar);
if (reuse_actionbar != null) {
int actionbar_height = getSharedPreferences(Config.PREFERENCE, Context.MODE_PRIVATE)
.getInt("actionbar_height", DisplayUtils.dip2px(getApplicationContext(), 55));
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, actionbar_height);
reuse_actionbar.setLayoutParams(lparams);
findViewById(R.id.actionbar_rl_back).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
protected void init(String title) {
TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
actionbar_tv_title.setText(title);
// setNavigationTitle(title);
}
public void toast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void toast(int msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
//如果是游戏分享newsTitle默认为空
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag, boolean isToolsBox) {
//判断是否是官方版
boolean isPlugin = false;
if (tag != null) {
for (String s : tag) {
if (!"官方版".equals(s)) {
isPlugin = true;
}
}
}
mNightMode = NightModeUtils.INSTANCE.isNightMode(this);
}
ShareUtils.getInstance(this).showShareWindows(getWindow().getDecorView(), url, gameName, icon, newsTitle, isPlugin, true, isToolsBox);
@Override
protected void onResume() {
super.onResume();
startPageTime = System.currentTimeMillis();
if (BuildConfig.IS_NIGHT_MODE_ON
&& !NightModeUtils.INSTANCE.getSystemMode()
&& mNightMode != NightModeUtils.INSTANCE.isNightMode(this)) {
onNightModeChange();
if (newsTitle == null) {
DataUtils.onEvent(this, "内容分享", gameName);
} else {
DataUtils.onEvent(this, "内容分享", newsTitle);
}
}
@SuppressWarnings("ConstantConditions")
@Override
public void setContentView(View view) {
if (!(this instanceof SplashScreenActivity) && PackageFlavorHelper.IS_TEST_FLAVOR) {
view = getRootViewWithEnvIndicator(view);
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(final EBShowDialog showDialog) {
if (!mIsPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if ("hijack".equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this, "2586716223");// 建议用户联系客服
} else if ("plugin".equals(showDialog.getType())) {
DialogUtils.showPluginDialog(this, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
Toast.makeText(BaseActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
} else {
startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath()));
}
}
});
}
}
super.setContentView(view);
}
@Override
protected void onDestroy() {
if (useEventBus()) EventBus.getDefault().unregister(this);
mBaseHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
public void toast(String msg) {
Utils.toast(this, msg);
}
public void toast(int msg) {
toast(getString(msg));
}
public void showShare(String url,
String icon,
String shareTitle,
String shareSummary,
ShareUtils.ShareEntrance shareEntrance, String id) {
ShareUtils.getInstance(this).showShareWindows(this,
getWindow().getDecorView(),
url,
icon,
shareTitle,
shareSummary,
shareEntrance, id);
if (shareEntrance == ShareUtils.ShareEntrance.game || shareEntrance == ShareUtils.ShareEntrance.plugin) {
MtaHelper.onEvent("内容分享", "内容分享", shareTitle + shareSummary);
} else {
MtaHelper.onEvent("内容分享", "内容分享", shareTitle);
}
}
/**
* 关闭 editText 自动填充帐号 (我们也用不上),开启的时候有小概率出发 TimeoutException
*/
private void disableAutofill() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getWindow().getDecorView().setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}
}
private View getRootViewWithEnvIndicator(View view) {
RelativeLayout screenRootView = new RelativeLayout(this);
screenRootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
LinearLayout ll = new LinearLayout(this);
TextView tv = new TextView(this);
String envText = "正式环境";
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme));
if (EnvHelper.isDevEnv()) {
envText = "测试环境";
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme_red));
}
tv.setText(envText);
tv.setGravity(Gravity.CENTER);
tv.setTextColor(Color.WHITE);
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);
ll.setTranslationX(DisplayUtils.dip2px(20));
ll.setRotation(45);
ll.addView(tv);
ll.setPadding(0, (width - height) / 2, 0, (width - height) / 2);
if (BuildConfig.DEBUG) {
tv.setOnLongClickListener(v -> {
EntranceUtils.saveShortcut(this.getClass().getName(), getIntent().getExtras());
return true;
});
}
screenRootView.addView(view);
screenRootView.addView(ll);
if (BuildConfig.IS_NIGHT_MODE_ON) {
screenRootView.addView(getNightModeIndicatorView());
}
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) ll.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
return screenRootView;
}
private View getNightModeIndicatorView() {
LinearLayout ll = new LinearLayout(this);
TextView tv = new TextView(this);
String envText = NightModeUtils.INSTANCE.isNightMode(this) ? "夜间模式" : "日间模式";
tv.setBackground(ContextCompat.getDrawable(this, R.color.theme));
tv.setText(envText);
tv.setGravity(Gravity.CENTER);
tv.setTextColor(Color.WHITE);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
tv.measure(0, 0);
tv.setAlpha(NightModeUtils.INSTANCE.isNightMode(this) ? 0.8F : 0.15F);
tv.setId(ID_NIGHT_INDICATOR);
int height = tv.getMeasuredHeight();
int width = tv.getMeasuredWidth();
tv.setPadding(DisplayUtils.dip2px(20), 0, DisplayUtils.dip2px(20), 0);
ll.setTranslationX(DisplayUtils.dip2px(-20));
ll.setRotation(-45);
ll.addView(tv);
ll.setPadding(0, (width - height) / 2, 0, (width - height) / 2);
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
tv.setOnClickListener(v -> {
//切换深色模式
String mode;
String positive;
String negative;
if (NightModeUtils.INSTANCE.getSystemMode()) {
mode = "跟随系统模式";
positive = "普通模式";
negative = "深色模式";
} else if (NightModeUtils.INSTANCE.getNightMode()) {
mode = "深色模式";
positive = "跟随系统模式";
negative = "普通模式";
} else {
mode = "普通模式";
positive = "跟随系统模式";
negative = "深色模式";
}
DialogHelper.showCenterDialog(this, "选择模式", "当前为 " + mode, positive, negative, () -> {
if (NightModeUtils.INSTANCE.getSystemMode()) {
NightModeUtils.INSTANCE.setNightMode(false);
NightModeUtils.INSTANCE.setSystemMode(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
} else {
NightModeUtils.INSTANCE.setSystemMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
}
NightModeUtils.INSTANCE.initNightMode();
}, () -> {
if (NightModeUtils.INSTANCE.getSystemMode()) {
NightModeUtils.INSTANCE.setNightMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
}
} else {
boolean nightMode = NightModeUtils.INSTANCE.getNightMode();
NightModeUtils.INSTANCE.setNightMode(!NightModeUtils.INSTANCE.getNightMode());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getDelegate().setLocalNightMode(nightMode ? AppCompatDelegate.MODE_NIGHT_NO : AppCompatDelegate.MODE_NIGHT_YES);
}
}
NightModeUtils.INSTANCE.setSystemMode(false);
NightModeUtils.INSTANCE.initNightMode();
});
});
}
return ll;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(final EBShowDialog showDialog) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
&& this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if (DOWNLOAD_HIJACK.equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this);// 建议用户联系客服
} else if (PLUGGABLE.equals(showDialog.getType())) {
DialogHelper.showPluginDialog(this, () -> {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
toast(R.string.install_failure_hint);
} else {
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
}
return null;
});
} else if (SIGNATURE_CONFLICT.equals(showDialog.getType())) {
DialogHelper.showSignatureConflictDialog(this, () -> {
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
return null;
});
} else if (LOGIN_EXCEPTION.equals(showDialog.getType())) {
if (mIsExistLogoutDialog) return;
mIsExistLogoutDialog = true;
try {
JSONObject object = new JSONObject(showDialog.getPath());
JSONObject device = object.getJSONObject("device");
String model = device.getString("model");
DialogHelper.showCenterDialog(this, "你的账号已在另外一台设备登录"
, StringUtils.buildString("", model, "")
, "知道了", "重新登录"
, () -> {
}
, () -> {
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(BaseActivity.this)) {
QuickLoginHelper.startLogin(BaseActivity.this, "你的账号已在另外一台设备登录多设备-重新登录");
} else {
startActivity(LoginActivity.getIntent(BaseActivity.this,
"你的账号已在另外一台设备登录多设备-重新登录"));
}
}
);
mBaseHandler.postDelayed(() -> mIsExistLogoutDialog = false, 5000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
EventBus.getDefault().unregister(this);
AppManager.getInstance().finishActivity(this);
}
@Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
onFinish();
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment.isAdded() && fragment instanceof BaseFragment) {
((BaseFragment) fragment).onParentActivityFinish();
}
}
}
DataUtils.onPause(this);
mIsPause = true;
}
/**
* 此回调可用于确认当前 activity 已经执行了 finish() 方法并处于 isFinishing 状态
*/
protected void onFinish() {
@Override
protected void onResume() {
super.onResume();
DataUtils.onResume(this);
mIsPause = false;
DownloadManager.getInstance(this).initGameMap();
}
@Override
public void loadDone() {
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
public void loadDone(Object obj) {
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
public void loadError() {
}
protected void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(color);
}
}
/**
* 提供当前 activity 的中文名 (不重载的话为类名)
*/
public String getActivityNameInChinese() {
return getClass().getSimpleName();
}
/**
* @param entrance 上一个页面的链式入口名称
* @param path 当前页面名称
* @return 完整的链式入口名称
*/
public static String mergeEntranceAndPath(String entrance, String path) {
if (TextUtils.isEmpty(entrance) && TextUtils.isEmpty(path)) return "";
if (TextUtils.isEmpty(entrance) && !TextUtils.isEmpty(path)) {
return StringUtils.buildString("(", path, ")");
}
if (!TextUtils.isEmpty(entrance) && TextUtils.isEmpty(path)) {
return entrance;
}
return StringUtils.buildString(entrance, "+(", path, ")");
}
protected boolean useEventBus() {
return true;
}
@Override
public Resources getResources() {
Resources resources = super.getResources();
if (resources.getConfiguration().fontScale != 1.0f) {
Configuration configuration = resources.getConfiguration();
configuration.fontScale = 1.0f;
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
}
return resources;
}
public void loadEmpty() {
/**
* ActivityThread每次调用onSaveInstanceState时outState大小都会累加最终会导致{@link TransactionTooLargeException}异常
* 解决方案判断每次获取到的outState大小当达到300k时手动clear掉
*/
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (preventRecreateFragmentByFragmentManager()) {
outState = discardFragmentFromSaveInstanceState(outState);
}
long bundleSize = getBundleSize(outState);
if (bundleSize > MAX_BUNDLE_SIZE * 1024) {
outState.clear();
}
}
/**
* 是否停用 Activity 重建时 FragmentManager 根据 saveState 自动重建保存的 Fragment 的功能
*/
protected boolean preventRecreateFragmentByFragmentManager() {
return false;
}
private Bundle discardFragmentFromSaveInstanceState(Bundle outState) {
if (outState != null) {
outState.remove("android:support:fragments");
}
return outState;
}
private long getBundleSize(Bundle bundle) {
long dataSize;
Parcel obtain = Parcel.obtain();
try {
obtain.writeBundle(bundle);
dataSize = obtain.dataSize();
} finally {
obtain.recycle();
}
return dataSize;
}
@Override
public Pair<String, String> getBusinessId() {
return null;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onNightModeChange();
}
protected void onNightModeChange() {
if (BuildConfig.IS_NIGHT_MODE_ON) {
mNightMode = NightModeUtils.INSTANCE.isNightMode(this);
TextView tv = findViewById(ID_NIGHT_INDICATOR);
if (tv != null) {
tv.setText(NightModeUtils.INSTANCE.isNightMode(this) ? "夜间模式" : "日间模式");
tv.setAlpha(NightModeUtils.INSTANCE.isNightMode(this) ? 0.8F : 0.15F);
}
if (isAutoResetViewBackgroundEnabled()) {
updateStaticViewBackground(getWindow().getDecorView());
}
}
}
protected boolean isAutoResetViewBackgroundEnabled() {
return false;
}
/**
* 自动重置部分 view 的背景颜色/资源
*
* @param view 父 view
*/
private void updateStaticViewBackground(View view) {
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View child = ((ViewGroup) view).getChildAt(i);
updateStaticViewBackground(child);
}
}
String backgroundString = (String) view.getTag(CustomLayoutInflaterFactory.TAG_BACKGROUND_ID);
String textColorString = (String) view.getTag(CustomLayoutInflaterFactory.TAG_TEXT_COLOR_ID);
if (backgroundString != null) {
if (backgroundString.startsWith("#")) return;
int backgroundId = Integer.parseInt(
backgroundString
.replace("@", "")
.replace("?", "")
);
if (backgroundId != 0) {
try {
TypedValue value = new TypedValue();
getResources().getValue(backgroundId, value, true);
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
view.setBackgroundColor(ExtensionsKt.toColor(backgroundId, this));
} else {
view.setBackground(ExtensionsKt.toDrawable(backgroundId, this));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (textColorString != null && view instanceof TextView) {
if (textColorString.startsWith("#")) return;
int textColorId = Integer.parseInt(
textColorString
.replace("@", "")
.replace("?", "")
);
if (textColorId != 0) {
try {
final ColorStateList colorStateList = ContextCompat.getColorStateList(this, textColorId);
((TextView) view).setTextColor(colorStateList);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -1,155 +0,0 @@
package com.gh.base;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.gh.base.adapter.FragmentAdapter;
import com.gh.base.fragment.BaseFragment_TabLayout;
import com.gh.common.view.TabIndicatorView;
import com.gh.gamecenter.R;
import com.google.android.material.tabs.TabLayout;
import com.lightgame.view.NoScrollableViewPager;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
/**
* Created by khy on 15/03/18.
*/
public abstract class BaseActivity_TabLayout extends ToolBarActivity implements ViewPager.OnPageChangeListener {
public static final String PAGE_INDEX = "PAGE_INDEX";
protected TabLayout mTabLayout;
protected NoScrollableViewPager mViewPager;
protected TabIndicatorView mTabIndicatorView;
protected View mDividerLineView;
protected List<Fragment> mFragmentsList;
protected List<String> mTabTitleList;
protected int mCheckedIndex = 0;
protected abstract void initFragmentList(List<Fragment> fragments);
protected abstract void initTabTitleList(List<String> tabTitleList);
protected int provideIndicatorWidth() {
return 20;
}
protected View provideTabView(int position, String tabTitle) {
return null;
}
@Override
protected int getLayoutId() {
return R.layout.activity_tablayout_viewpager;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTabLayout = findViewById(R.id.activity_tab_layout);
mViewPager = findViewById(R.id.activity_view_pager);
mTabIndicatorView = findViewById(R.id.activity_tab_indicator);
mDividerLineView = findViewById(R.id.dividerLine);
if (getIntent() != null) mCheckedIndex = getIntent().getIntExtra(PAGE_INDEX, 0);
mTabTitleList = new ArrayList<>();
initTabTitleList(mTabTitleList);
mFragmentsList = new ArrayList<>(restoreFragments());
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
mFragmentsList.clear();
initFragmentList(mFragmentsList);
}
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.addOnPageChangeListener(this);
mViewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), mFragmentsList, mTabTitleList));
mViewPager.setCurrentItem(mCheckedIndex);
mTabLayout.setupWithViewPager(mViewPager);
mTabIndicatorView.setupWithTabLayout(mTabLayout);
mTabIndicatorView.setupWithViewPager(mViewPager);
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth());
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab == null) continue;
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
View tabView = provideTabView(i, tabTitle);
if (tabView == null)
tabView = BaseFragment_TabLayout.createDefaultTabCustomView(this, tabTitle);
tab.setCustomView(tabView);
}
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex);
}
private ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
int childCount = mTabTitleList.size();
for (int index = 0; index < childCount; index++) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag + index);
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected void onNightModeChange() {
super.onNightModeChange();
View container = findViewById(R.id.activity_tab_container);
if (container != null) {
container.setBackgroundColor(ContextCompat.getColor(this, R.color.background_white));
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab != null) {
BaseFragment_TabLayout.updateTabStyle(tab, tab.isSelected());
}
}
}
if (mDividerLineView != null) {
mDividerLineView.setBackgroundColor(ContextCompat.getColor(this, R.color.divider));
}
}
}

View File

@ -0,0 +1,325 @@
package com.gh.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import com.gh.common.util.AppDebugConfig;
import com.gh.common.util.AppManager;
import com.gh.common.util.Util_System_Keyboard;
import com.gh.gamecenter.R;
/**
* @author CsHeng
*/
public abstract class BaseAppCompatActivity extends BaseAppCompatActivityLog implements FragmentNavigationDelegate,
OnBackPressedListener {
protected static final String ARGS_FRAGMENT_NAME = "frgName";
protected static final String ARGS_FRAGMENT_BUNDLE = "frgBundle";
protected View mContentView;
protected static Intent clearTop(Context context, Class<? extends Activity> cls) {
final Intent intent = getReorderToFrontIntent(context, cls);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return intent;
}
protected static Intent getReorderToFrontIntent(Context context, Class<? extends Activity> cls) {
final Intent intent = new Intent(context, cls);
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
protected static void showActivity(Context context, Class<? extends Activity> cls) {
final Intent intent = getReorderToFrontIntent(context, cls);
context.startActivity(intent);
}
protected static void startFragmentForResult(Context context, Class<? extends Activity> activity,
Class<? extends Fragment> fragment, Bundle bundle, int requestCode) {
if (context instanceof Activity) {
((Activity) context).startActivityForResult(getFragmentIntent(context, activity, fragment, bundle),
requestCode);
}
}
/**
* 根据传进来的fragment class和bundle extra来决定跳转到哪一个fragment
*
* @param context
* @param fragment fragment.getCanonicalName()
* @param bundle fragment的构造参数
* @return
*/
protected static Intent getFragmentIntent(Context context, Class<? extends Activity> activity,
Class<? extends Fragment> fragment, Bundle bundle) {
final Intent intent = getReorderToFrontIntent(context, activity);
intent.putExtra(ARGS_FRAGMENT_NAME, fragment.getCanonicalName());
intent.putExtra(ARGS_FRAGMENT_BUNDLE, bundle);
return intent;
}
protected static void startFragment(Context context, Class<? extends Activity> activity,
Class<? extends Fragment> fragment) {
startFragment(context, activity, fragment, null);
}
/**
* 启动Fragment
*
* @param context
* @param fragment
* @param bundle
*/
protected static void startFragment(Context context, Class<? extends Activity> activity,
Class<? extends Fragment> fragment, Bundle bundle) {
context.startActivity(getFragmentIntent(context, activity, fragment, bundle));
}
private void handleRedirectFromUri(Uri uri) {
try {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this, uri, uri.getHost(), uri.getPath(), uri.getEncodedQuery());
}
// switch (uri.getPath()) {
// case SchemeConstants.PATH_OPEN_GAME: {
// /**
// * 对于联运游戏传入的game_id为0game_url为对应的游戏链接aid传联运游戏的aid
// * 对于非联运游戏传入非联运游戏的game_idgame_url不传aid传0
// */
// GameModel game = new GameModel();
// int gameId = Integer.parseInt(uri.getQueryParameter(SchemeConstants.PARAMS_GAMEID));
// game.setId(gameId);
//// if (AppDebugConfig.IS_DEBUG) {
//// int debugId = 1049;
//// game.setId(debugId);
//// }
// if (gameId == 0) {
// // cooperate
// int aid = Integer.parseInt(uri.getQueryParameter(SchemeConstants.PARAMS_AID));
// game.setAid(aid);
// String gameUrl = uri.getQueryParameter(SchemeConstants.PARAMS_GAMEURL);
// game.setUrl(gameUrl);
// }
// ApiRequester.requestGameDetail(this, game);
//
// break;
// }
// case SchemeConstants.PATH_OPEN_ARTICLE: {
//
// String url = uri.getQueryParameter(SchemeConstants.PARAMS_ARTICLE_URL);
// String title = uri.getQueryParameter(SchemeConstants.PARAMS_ARTICLE_TITLE);
//
// if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(title)) {
// Bundle args = new Bundle();
// args.putString(Fragment_Browser_Web.ARGS_URL, url);
// args.putString(Fragment_Browser_Web.ARGS_TITLE, getString(R.string.title_article_detail));
// CommonActivity.startFragment(this, Fragment_Browser_Web.class, args);
// }
//
// break;
// }
// default:
// break;
// }
} catch (Exception e) {
if (AppDebugConfig.IS_DEBUG) {
// Debug_SDK.e(e);
}
}
}
@Override
public boolean onHandleBackPressed() {
onBackPressed();
return true;
}
protected boolean handleBackPressed() {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this);
}
final Fragment curFragment = getTopFragment();
if (curFragment instanceof OnBackPressedListener &&
((OnBackPressedListener) curFragment).onHandleBackPressed()) {
return true;
}
if (popFragment()) {
return true;
}
return false;
}
public Fragment getTopFragment() {
return getSupportFragmentManager().findFragmentById(R.id.layout_activity_content);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int layoutId = getLayoutId();
if (layoutId != 0) {
mContentView = getLayoutInflater().inflate(layoutId, null);
setContentView(mContentView);
}
if (savedInstanceState == null) {
handleRedirectIntent(getIntent());
}
AppManager.getInstance().addActivity(this);
}
protected abstract int getLayoutId();
/**
* 1、判断当前是否存在某Fragment例如下载管理器已经在栈中则不重新启动而将其带到最上层 (FindFragmentWithTag)
* 2、支持URI调用
* 是否需要重新导向到其他Activity
*/
protected void handleRedirectIntent(Intent intent) {
try {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this, intent.toUri(Intent.URI_INTENT_SCHEME));
}
// 1、根据从网页抓取到的Intent跳转进来处理custom uri
// final Uri uri = intent.getData();
// if (uri != null && SchemeConstants.SCHEME_FFSS.equals(uri.getScheme())) {
// handleRedirectFromUri(uri);
// return;
// }
//
// // 2、根据序列化的数据来重新构建Fragment实例若没有则是旧的形式来决定传参
// final String fragmentName = intent.getStringExtra(ARGS_FRAGMENT_NAME);
// if (AppDebugConfig.IS_DEBUG) {
// AppDebugConfig.logMethodWithParams(this, fragmentName);
// }
// if (!TextUtils.isEmpty(fragmentName)) {
// final Bundle args = intent.getBundleExtra(ARGS_FRAGMENT_BUNDLE);
// final Fragment fragment = Fragment.instantiate(this, fragmentName, args);
// if (fragment instanceof DialogFragment) {
// ((DialogFragment) fragment).show(getSupportFragmentManager(), fragmentName);
// } else {
// getSupportFragmentManager().beginTransaction().replace(R.id.layout_activity_content, fragment)
// .commitAllowingStateLoss();
// }
// }
} catch (Throwable e) {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this, e);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
AppManager.getInstance().finishActivity(this);
}
@Override
public void addFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().add(R.id.layout_activity_content, fragment)
.addToBackStack(fragment.toString()).commitAllowingStateLoss();
Util_System_Keyboard.hideSoftKeyboard(this);
}
@Override
public void replaceFragment(Fragment toReplace) {
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.layout_activity_content, toReplace).addToBackStack(toReplace.toString());
ft.commitAllowingStateLoss();
Util_System_Keyboard.hideSoftKeyboard(this);
}
@Override
public void removeFragment(Fragment toRemove) {
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.layout_activity_content, toRemove).addToBackStack(toRemove.toString());
ft.commitAllowingStateLoss();
Util_System_Keyboard.hideSoftKeyboard(this);
}
@Override
public void showFragment(Fragment toShow) {
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.show(toShow).addToBackStack(toShow.toString());
ft.commitAllowingStateLoss();
Util_System_Keyboard.hideSoftKeyboard(this);
}
@Override
public void hideFragment(Fragment toHide) {
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.show(toHide).addToBackStack(toHide.toString());
ft.commitAllowingStateLoss();
Util_System_Keyboard.hideSoftKeyboard(this);
}
@Override
public boolean popFragment() {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this, getSupportFragmentManager().getBackStackEntryCount());
}
final int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
if (backStackCount > 0) {
Util_System_Keyboard.hideSoftKeyboard(this);
getSupportFragmentManager().popBackStack();
return true;
}
return false;
}
@Override
public void popFragmentToBase() {
getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
/**
* forward ActivityResultListener to fragments this activity hosts
*
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this, requestCode, resultCode, data);
}
final Fragment fragment = getTopFragment();
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onBackPressed() {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(this);
}
if (handleBackPressed()) {
return;
}
// default finish activity
Util_System_Keyboard.hideSoftKeyboard(this);
finish();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleRedirectIntent(intent);
}
}

View File

@ -0,0 +1,94 @@
package com.gh.base;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.gh.common.util.AppDebugConfig;
/**
* @author: CsHeng (csheng1204[at]gmail[dot]com)
* Date: 13-7-25
* Time: 下午8:42
*/
public class BaseAppCompatActivityLog extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
protected void onStop() {
super.onStop();
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
public void onLowMemory() {
super.onLowMemory();
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
protected void onPause() {
super.onPause();
try {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
} catch (Throwable e) {
}
}
@Override
protected void onResume() {
super.onResume();
try {
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
} catch (Throwable e) {
}
}
@Override
protected void onStart() {
super.onStart();
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodName(this);
}
}
}

View File

@ -0,0 +1,138 @@
package com.gh.base;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;
import com.gh.gamecenter.R;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by csheng on 15-10-12.
*/
public abstract class BaseAppCompatToolBarActivity extends BaseAppCompatActivity implements ToolbarController {
private Toolbar mToolbar;
private SystemBarTintManager mTintManager;
// TODO 获取沉浸栏管理,要进行版本判断或者判断是否为空
protected SystemBarTintManager getTintManager() {
return mTintManager;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initToolbar();
// Util_Window.initStatusBarColor(getWindow(), ContextCompat.getColor(this, R.color.theme));
initStatusBar();
}
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar_navigation);
if (mToolbar != null) {
mToolbar.setTitle("");
setSupportActionBar(mToolbar);
}
}
private void initStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setTranslucentStatus(true);
mTintManager = new SystemBarTintManager(this);
mTintManager.setStatusBarTintEnabled(true);
// mTintManager.setNavigationBarTintEnabled(true);
if (Build.MANUFACTURER.equals("Meizu") || Build.MANUFACTURER.equals("Xiaomi")) {
mTintManager.setStatusBarTintColor(Color.WHITE);
} else {
mTintManager.setStatusBarTintColor(Color.BLACK);
}
switch (Build.MANUFACTURER) {
case "Meizu":
try {
Window window = getWindow();
if (window != null) {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
value |= bit;
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case "Xiaomi":
try {
Window window = getWindow();
if (window != null) {
Class<?> clazz = window.getClass();
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
protected void setTranslucentStatus(boolean status) {
Window window = getWindow();
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (status) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
window.setAttributes(winParams);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return onNavigationIconClicked();
}
return super.onOptionsItemSelected(item);
}
protected abstract boolean onNavigationIconClicked();
@Override
public void setNavigationTitle(int res) {
mToolbar.setTitle(res);
}
@Override
public void setNavigationTitle(CharSequence res) {
mToolbar.setTitle(res);
}
@Override
public Toolbar getToolBar() {
return mToolbar;
}
}

View File

@ -0,0 +1,394 @@
package com.gh.base;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.gh.common.constant.Config;
import com.gh.common.util.ApkActiveUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.GameUtils;
import com.gh.common.util.NetworkUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.view.DownloadDialog;
import com.gh.download.DataWatcher;
import com.gh.download.DownloadEntity;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.eventbus.EBPackage;
import com.gh.gamecenter.manager.PackageManager;
import com.tencent.tauth.Tencent;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* Created by Administrator on 2016/9/19.
* 游戏详情、新闻详情基类(控制底部下载栏)
*/
public abstract class BaseDetailActivity extends BaseActivity implements View.OnClickListener {
protected TextView actionbar_tv_title;
protected RecyclerView detail_rv_show;
protected LinearLayout detail_ll_bottom;
protected TextView detail_tv_download;
protected ProgressBar detail_pb_progressbar;
protected TextView detail_tv_per;
protected LinearLayout reuse_ll_loading;
protected LinearLayout reuse_no_connection;
protected LinearLayout reuse_none_data;
protected TextView reuse_tv_none_data;
protected ImageView iv_share;
protected GameEntity gameEntity;
protected DownloadEntity mDownloadEntity;
protected String name;
protected String title;
protected String downloadAddWord;
protected String downloadOffText;
private DataWatcher dataWatcher = new DataWatcher() {
@Override
public void onDataChanged(DownloadEntity downloadEntity) {
if (gameEntity != null && gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
if (url.equals(downloadEntity.getUrl())) {
if (!"pause".equals(DownloadManager.getInstance(BaseDetailActivity.this).
getStatus(downloadEntity.getUrl()))) {
mDownloadEntity = downloadEntity;
invalidate();
}
}
}
}
};
@Override
protected int getLayoutId() {
return R.layout.activity_detail;
}
//接收QQ或者QQ空间分享回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// View contentView = View.inflate(this, R.layout.activity_detail, null);
// 添加分享图标
iv_share = new ImageView(this);
iv_share.setImageResource(R.drawable.ic_share);
iv_share.setOnClickListener(this);
iv_share.setVisibility(View.GONE);
iv_share.setPadding(DisplayUtils.dip2px(this, 13), DisplayUtils.dip2px(this, 11)
, DisplayUtils.dip2px(this, 11), DisplayUtils.dip2px(this, 13));
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
DisplayUtils.dip2px(this, 48), DisplayUtils.dip2px(this, 48));
params.addRule(RelativeLayout.CENTER_VERTICAL);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
RelativeLayout reuse_actionbar = (RelativeLayout) mContentView.findViewById(
R.id.reuse_actionbar);
reuse_actionbar.addView(iv_share, params);
// init(contentView);
actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
detail_rv_show = (RecyclerView) findViewById(R.id.detail_rv_show);
detail_ll_bottom = (LinearLayout) findViewById(R.id.detail_ll_bottom);
detail_tv_download = (TextView) findViewById(R.id.detail_tv_download);
detail_pb_progressbar = (ProgressBar) findViewById(R.id.detail_pb_progressbar);
detail_tv_per = (TextView) findViewById(R.id.detail_tv_per);
reuse_ll_loading = (LinearLayout) findViewById(R.id.reuse_ll_loading);
reuse_no_connection = (LinearLayout) findViewById(R.id.reuse_no_connection);
reuse_none_data = (LinearLayout) findViewById(R.id.reuse_none_data);
reuse_tv_none_data = (TextView) findViewById(R.id.reuse_tv_none_data);
detail_ll_bottom.setOnClickListener(this);
detail_tv_download.setOnClickListener(this);
detail_pb_progressbar.setOnClickListener(this);
detail_tv_per.setOnClickListener(this);
reuse_no_connection.setOnClickListener(this);
}
@Override
protected void onPause() {
super.onPause();
DownloadManager.getInstance(this).removeObserver(dataWatcher);
}
@Override
protected void onResume() {
super.onResume();
if (gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
initDownload(true);
}
DownloadManager.getInstance(this).addObserver(dataWatcher);
}
protected void initDownload(boolean isCheck) {
if (Config.isShow(this)) {
detail_ll_bottom.setVisibility(View.VISIBLE);
detail_rv_show.setPadding(0, 0, 0,
DisplayUtils.dip2px(getApplicationContext(), 60));
} else {
detail_ll_bottom.setVisibility(View.GONE);
detail_rv_show.setPadding(0, 0, 0, 0);
}
if (gameEntity != null && "光环助手".equals(gameEntity.getName())) {
detail_ll_bottom.setVisibility(View.GONE);
detail_rv_show.setPadding(0, 0, 0, 0);
} else if (gameEntity == null || gameEntity.getApk().isEmpty()) {
detail_tv_download.setVisibility(View.VISIBLE);
detail_pb_progressbar.setVisibility(View.GONE);
detail_tv_per.setVisibility(View.GONE);
if (TextUtils.isEmpty(downloadOffText)) {
detail_tv_download.setText("暂无下载");
} else {
detail_tv_download.setText(downloadOffText);
}
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_pause_style);
detail_tv_download.setTextColor(0xFF999999);
detail_tv_download.setClickable(false);
} else {
detail_tv_download.setVisibility(View.VISIBLE);
detail_pb_progressbar.setVisibility(View.GONE);
detail_tv_per.setVisibility(View.GONE);
boolean isInstalled = false;
if (gameEntity.getApk() != null && gameEntity.getApk().size() == 1
&& PackageManager.isInstalled(gameEntity.getApk().get(0).getPackageName())) {
isInstalled = true;
}
if (isInstalled) {
if (PackageManager.isCanUpdate(gameEntity.getId(), gameEntity.getApk().get(0).getPackageName())) {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_download_style);
detail_tv_download.setText(String.format("更新《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_download_style);
detail_tv_download.setText(String.format("更新《%s》%s",
gameEntity.getName(), downloadAddWord));
}
} else {
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(gameEntity.getApk().get(0).getGhVersion())
&& !PackageUtils.isSignature(this, gameEntity.getApk().get(0).getPackageName())) {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_plugin_style);
detail_tv_download.setText(String.format("插件化《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_plugin_style);
detail_tv_download.setText(String.format("插件化《%s》%s",
gameEntity.getName(), downloadAddWord));
}
} else {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_launch_style);
detail_tv_download.setText(String.format("启动《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_launch_style);
detail_tv_download.setText(String.format("启动《%s》%s",
gameEntity.getName(), downloadAddWord));
}
}
}
} else {
String status = GameUtils.getDownloadBtnText(this, gameEntity);
if ("插件化".equals(status)) {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else if ("打开".equals(status)) {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_launch_style);
} else {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setText(String.format(status + "《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setText(String.format(status + "《%s》%s",
gameEntity.getName(), downloadAddWord));
}
}
}
if (isCheck && gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
DownloadEntity downloadEntity = DownloadManager.getInstance(getApplicationContext()).get(url);
if (downloadEntity != null) {
mDownloadEntity = downloadEntity;
detail_tv_download.setVisibility(View.GONE);
detail_pb_progressbar.setVisibility(View.VISIBLE);
detail_tv_per.setVisibility(View.VISIBLE);
invalidate();
}
}
}
private void invalidate() {
detail_pb_progressbar.setProgress((int) (mDownloadEntity.getPercent() * 10));
detail_tv_per.setTextColor(0xFFFFFFFF);
switch (mDownloadEntity.getStatus()) {
case downloading:
case pause:
case timeout:
case neterror:
case waiting:
detail_tv_per.setText("下载中");
break;
case done:
detail_tv_per.setText("安装");
if (mDownloadEntity.isPluggable()
&& PackageManager.isInstalled(mDownloadEntity.getPackageName())) {
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_plugin_radius_style));
} else {
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_normal_radius_style));
}
break;
case cancel:
case hijack:
case notfound:
initDownload(false);
break;
default:
break;
}
}
// 接收下载被删除消息
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(EBDownloadStatus status) {
if ("delete".equals(status.getStatus())
&& gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
if (url.equals(status.getUrl())) {
initDownload(false);
}
}
}
// 接受安装、卸载消息
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBPackage busFour) {
if (gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() > 0) {
for (ApkEntity apkEntity : gameEntity.getApk()) {
String packageName = apkEntity.getPackageName();
if (packageName.equals(busFour.getPackageName())) {
ApkActiveUtils.filterHideApk(gameEntity);
initDownload(false);
}
}
}
}
@Override
public void onClick(View v) {
if (v == detail_tv_download) {
if (gameEntity != null && !gameEntity.getApk().isEmpty()) {
if (gameEntity.getApk().size() == 1) {
if (NetworkUtils.isWifiConnected(this)) {
download();
} else {
DialogUtils.showDownloadDialog(this, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
download();
}
});
}
} else {
DownloadDialog.getInstance(this).showPopupWindow(v, gameEntity, mEntrance, name + ":" + title);
}
} else {
toast("稍等片刻~!游戏正在上传中...");
}
} else if (v == detail_pb_progressbar || v == detail_tv_per) {
String str = detail_tv_per.getText().toString();
if ("下载中".equals(str)) {
DownloadManagerActivity.startDownloadManagerActivity(this, gameEntity.getApk().get(0).getUrl()
, mEntrance + "+(" + name + "[" + title + "])");
} else if ("安装".equals(str)) {
PackageUtils.launchSetup(this, mDownloadEntity.getPath());
}
}
}
private void download() {
String str = detail_tv_download.getText().toString();
if (str.contains("启动")) {
DataUtils.onGameLaunchEvent(this, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), name);
PackageUtils.launchApplicationByPackageName(this, gameEntity.getApk().get(0).getPackageName());
} else {
String method;
if (str.contains("更新")) {
method = "更新";
} else if (str.contains("插件化")) {
method = "插件化";
} else {
method = "下载";
}
ApkEntity apkEntity = gameEntity.getApk().get(0);
String msg = FileUtils.isCanDownload(this, apkEntity.getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(this, gameEntity.getName(), apkEntity.getPlatform(), mEntrance, "下载开始");
DownloadManager.createDownload(this, apkEntity, gameEntity, method, mEntrance, name + ":" + title);
detail_tv_download.setVisibility(View.GONE);
detail_pb_progressbar.setVisibility(View.VISIBLE);
detail_tv_per.setVisibility(View.VISIBLE);
detail_pb_progressbar.setProgress(0);
detail_tv_per.setText("0.0%");
DownloadManager.getInstance(BaseDetailActivity.this).putStatus(apkEntity.getUrl(), "downloading");
} else {
toast(msg);
}
}
}
}

View File

@ -0,0 +1,100 @@
package com.gh.base;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.gh.common.util.EntranceUtils;
import com.gh.gamecenter.listener.OnCallBackListener;
import org.greenrobot.eventbus.EventBus;
import butterknife.ButterKnife;
/**
* Created by LGT on 2016/9/4.
* Fragment 基类
*/
public class BaseFragment extends Fragment implements OnCallBackListener {
protected View view;
protected boolean isEverpause;
protected String mEntrance;
protected void init(int layout) {
view = View.inflate(getActivity(), layout, null);
ButterKnife.bind(this, view);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEntrance = getActivity().getIntent().getStringExtra(EntranceUtils.KEY_ENTRANCE);
isEverpause = false;
EventBus.getDefault().register(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(view);
}
return view;
}
@Override
public void onResume() {
super.onResume();
isEverpause = false;
}
@Override
public void onPause() {
super.onPause();
isEverpause = true;
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public void toast(String msg) {
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
}
public boolean isEverpause() {
return isEverpause;
}
@Override
public void loadDone() {
}
@Override
public void loadDone(Object obj) {
}
@Override
public void loadError() {
}
@Override
public void loadEmpty() {
}
}

View File

@ -0,0 +1,206 @@
//package com.gh.base;
//
//import android.annotation.TargetApi;
//import android.content.Context;
//import android.graphics.Color;
//import android.os.Build;
//import android.os.Bundle;
//import android.support.v4.app.FragmentActivity;
//import android.view.*;
//import android.view.View.OnClickListener;
//import android.view.ViewGroup.LayoutParams;
//import android.widget.*;
//import butterknife.ButterKnife;
//import com.gh.common.constant.Config;
//import com.gh.common.util.*;
//import com.gh.download.DownloadManager;
//import com.gh.gamecenter.R;
//import com.gh.gamecenter.eventbus.EBShowDialog;
//import com.readystatesoftware.systembartint.SystemBarTintManager;
//import com.readystatesoftware.systembartint.SystemBarTintManager.SystemBarConfig;
//import de.greenrobot.event.EventBus;
//
//import java.lang.reflect.Field;
//import java.lang.reflect.Method;
//import java.util.ArrayList;
//
//public class BaseFragmentActivity extends FragmentActivity {
//
// protected String mEntrance;
//
// private boolean isPause;
//
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// AppManager.getInstance().addActivity(this);
// EventBus.getDefault().register(this);
// mEntrance = getIntent().getStringExtra("mEntrance");
// if (getIntent().getBundleExtra("data") != null) {
// mEntrance = getIntent().getBundleExtra("data").getString("mEntrance");
// }
// }
//
// public void init(View contentView, String title) {
// init(contentView);
// TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
// actionbar_tv_title.setText(title);
// }
//
// public void init(View contentView) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// setTheme(R.style.AppTheme);
// setTranslucentStatus(true);
// SystemBarTintManager tintManager = new SystemBarTintManager(this);
// tintManager.setStatusBarTintEnabled(true);
// if (Build.MANUFACTURER.equals("Meizu") || Build.MANUFACTURER.equals("Xiaomi")) {
// tintManager.setStatusBarTintColor(Color.WHITE);
// } else {
// tintManager.setStatusBarTintColor(Color.BLACK);
// }
// SystemBarConfig config = tintManager.getConfig();
// contentView.setPadding(0, config.getPixelInsetTop(false), 0,
// config.getPixelInsetBottom());
//
// if (Build.MANUFACTURER.equals("Meizu")) {
// try {
// Window window = getWindow();
// if (window != null) {
// WindowManager.LayoutParams lp = window.getAttributes();
// Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
// Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
// darkFlag.setAccessible(true);
// meizuFlags.setAccessible(true);
// int bit = darkFlag.getInt(null);
// int value = meizuFlags.getInt(lp);
// value |= bit;
// meizuFlags.setInt(lp, value);
// window.setAttributes(lp);
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// } else if (Build.MANUFACTURER.equals("Xiaomi")) {
// try {
// Window window = getWindow();
// if (window != null) {
// Class<?> clazz = window.getClass();
// Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
// Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
// int darkModeFlag = field.getInt(layoutParams);
// Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
// extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// }
//
// setContentView(contentView);
//
// ButterKnife.bind(this);
//
// int actionbar_height = getSharedPreferences(Config.PREFERENCE,
// Context.MODE_PRIVATE).getInt("actionbar_height",
// DisplayUtils.dip2px(getApplicationContext(), 48));
//
// RelativeLayout reuse_actionbar = (RelativeLayout) findViewById(R.id.reuse_actionbar);
// LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
// LayoutParams.MATCH_PARENT, actionbar_height);
// reuse_actionbar.setLayoutParams(params);
//
// findViewById(R.id.actionbar_rl_back).setOnClickListener(
// new OnClickListener() {
// @Override
// public void onClick(View v) {
// finish();
// }
// });
// }
//
// public void toast(String msg) {
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// }
//
// public void toast(int msg) {
// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// }
//
// @TargetApi(19)
// protected void setTranslucentStatus(boolean status) {
// Window window = getWindow();
// WindowManager.LayoutParams winParams = window.getAttributes();
// final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
// if (status) {
// winParams.flags |= bits;
// } else {
// winParams.flags &= ~bits;
// }
// window.setAttributes(winParams);
// }
//
// //如果是游戏分享newsTitle默认为空
// public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
//
// //判断是否是官方版
// boolean isPlugin = false;
// if (tag != null) {
// for (String s : tag) {
// if (!"官方版".equals(s)) {
// isPlugin = true;
// }
// }
// }
//
// ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
//
// if (newsTitle == null) {
// DataUtils.onEvent(this, "内容分享", gameName);
// } else {
// DataUtils.onEvent(this, "内容分享", newsTitle);
// }
// }
//
// public void onEventMainThread(final EBShowDialog showDialog) {
// if (!isPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
// if ("hijack".equals(showDialog.getType())) {
// DialogUtils.showQqSessionDialog(this, null);// 建议用户联系客服
// } else if ("plugin".equals(showDialog.getType())) {
// DialogUtils.showPluginDialog(this, new DialogUtils.ConfirmListener() {
// @Override
// public void onConfirm() {
// if (FileUtils.isEmptyFile(showDialog.getPath())) {
// Toast.makeText(BaseFragmentActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
// } else {
// startActivity(PackageUtils.getUninstallIntent(BaseFragmentActivity.this, showDialog.getPath()));
// }
// }
// });
// }
// }
// }
//
// @Override
// protected void onDestroy() {
// super.onDestroy();
// EventBus.getDefault().unregister(this);
// AppManager.getInstance().finishActivity(this);
// }
//
// @Override
// protected void onPause() {
// super.onPause();
// DataUtils.onPause(this);
// isPause = true;
// }
//
// @Override
// protected void onResume() {
// super.onResume();
// DataUtils.onResume(this);
// isPause = false;
// DownloadManager.getInstance(this).initGameMap();
// }
//
//}

View File

@ -1,54 +0,0 @@
package com.gh.base;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
/**
* 目前仅提供butterknife bind方法
*
* @author CsHeng
* @Date 16/06/2017
* @Time 9:55 AM
*/
public abstract class BaseRecyclerViewHolder<T> extends RecyclerView.ViewHolder implements View.OnClickListener {
private T mData;
private OnListClickListener mListClickListener;
public BaseRecyclerViewHolder(View itemView) {
super(itemView);
}
/**
* 具体的设置监听在childViewHolder 设置
*
* @param itemView
* @param data 一般情况下只传列表数据
* @param listClickListener 列表事件接口
*/
public BaseRecyclerViewHolder(View itemView, T data, OnListClickListener listClickListener) {
this(itemView);
this.mData = data;
this.mListClickListener = listClickListener;
}
public BaseRecyclerViewHolder(View itemView, OnListClickListener listClickListener) {
this(itemView);
this.mListClickListener = listClickListener;
}
public void setClickData(T clickData) {
this.mData = clickData;
}
@Override
public void onClick(View view) {
try {
mListClickListener.onListClick(view, getAdapterPosition(), mData);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -1,761 +0,0 @@
package com.gh.base
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.webkit.JavascriptInterface
import android.widget.CheckBox
import android.widget.FrameLayout
import android.widget.TextView
import androidx.lifecycle.Observer
import com.gh.common.AppExecutor
import com.gh.common.runOnIoThread
import com.gh.common.util.*
import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.qa.entity.AnswerEntity
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.lightgame.view.CheckableImageView
import io.reactivex.disposables.Disposable
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarActivity(),
KeyboardHeightObserver, UploadVideoListener {
lateinit var mRichEditor: RichEditor
private lateinit var mEditorTextNumTv: TextView
private lateinit var mEditorFont: CheckableImageView
private lateinit var mEditorLink: CheckableImageView
private lateinit var mEditorFontBold: CheckableImageView
private lateinit var mEditorFontItalic: CheckableImageView
private lateinit var mEditorFontStrikeThrough: CheckableImageView
private lateinit var mEditorFontUnderline: CheckableImageView
private lateinit var mEditorParagraphH1: CheckableImageView
private lateinit var mEditorParagraphH2: CheckableImageView
private lateinit var mEditorParagraphH3: CheckableImageView
private lateinit var mEditorParagraphH4: CheckableImageView
private lateinit var mEditorParagraphQuote: CheckableImageView
private lateinit var mEditorFontContainer: View
private lateinit var mEditorParagraphContainer: View
private lateinit var mEditorLinkContainer: View
private lateinit var mEditorInsertDetailContainer: View
private lateinit var mTagsContainer: FrameLayout
private lateinit var mUploadVideoGuideContainer: View
protected lateinit var mOriginalCb: CheckBox
private lateinit var mOriginalTipsContainer: View
private lateinit var mOriginalTipsClose: TextView
private var mCurrentParagraphStyle = ""
private var mIsExtendedKeyboardShow = false
private var mAgreePostPic: Boolean = false
private var mGuideDisposable: Disposable? = null
protected lateinit var mViewModel: VM
protected var mIsKeyBoardShow = false
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
private var mMaxUploadVideoGuideCount = 2
val FILE_HOST = "file:///"
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
DialogUtils.fixWebViewKeyboardNotWorking(this)
if (resultCode != Activity.RESULT_OK) return
val insertData: EditorInsertEntity?
when (requestCode) {
INSERT_ANSWER_CODE -> {
val answer =
data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
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) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(article)
mRichEditor.insertCustomStyleLink(insertData)
}
}
INSERT_GAME_CODE -> {
val game = data?.getParcelableExtra<GameEntity>(GameEntity::class.java.simpleName)
if (game != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(game)
mRichEditor.insertCustomStyleLink(insertData)
}
}
INSERT_GAME_COLLECTION_CODE -> {
val gameCollectionEntity = data?.getParcelableExtra<GamesCollectionEntity>(GamesCollectionEntity::class.java.simpleName)
if (gameCollectionEntity != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(gameCollectionEntity)
mRichEditor.insertCustomStyleLink(insertData)
}
}
REQUEST_CODE_IMAGE -> {
if (data != null) mViewModel.uploadPic(data)
}
INSERT_MEDIA_VIDEO_CODE -> {
val localVideoList = data?.getParcelableArrayListExtra<LocalVideoEntity>(LocalVideoEntity::class.java.name) ?: arrayListOf()
if (localVideoList.isNotEmpty()) {
mRichEditor.focusEditor()
uploadVideo(localVideoList)
}
}
REQUEST_CODE_IMAGE_CROP -> {
val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
if (!imagePath.isNullOrEmpty()) {
mViewModel.uploadPoster(imagePath)
}
}
INSERT_VIDEO_CODE -> {
val videoEntity = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
if (videoEntity != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(videoEntity)
mRichEditor.insertCustomStyleLink(insertData)
}
}
}
closeExtendedKeyboard()
AppExecutor.uiExecutor.executeWithDelay(Runnable {
Util_System_Keyboard.showSoftKeyboard(this)
}, 100)
}
private fun uploadVideo(localVideoList: ArrayList<LocalVideoEntity>) {
mViewModel.localVideoList.addAll(localVideoList)
runOnIoThread {
localVideoList.forEach {
if (it.poster.startsWith("http")) {
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, it.poster)
}
} else {
val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath)
val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp"
BitmapUtils.saveBitmap(videoThumbnail, filePath)
it.poster = filePath
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}")
}
}
}
mViewModel.uploadVideo()
}
}
private fun findView() {
mRichEditor = findViewById(R.id.rich_editor)
mEditorTextNumTv = findViewById(R.id.editorTextNumTv)
mEditorFont = findViewById(R.id.editor_font)
mEditorLink = findViewById(R.id.editor_link)
mEditorFontBold = findViewById(R.id.editor_font_bold)
mEditorFontItalic = findViewById(R.id.editor_font_italic)
mEditorFontStrikeThrough = findViewById(R.id.editor_font_strikethrough)
mEditorFontUnderline = findViewById(R.id.editor_font_underline)
mEditorParagraphH1 = findViewById(R.id.editor_paragraph_h1)
mEditorParagraphH2 = findViewById(R.id.editor_paragraph_h2)
mEditorParagraphH3 = findViewById(R.id.editor_paragraph_h3)
mEditorParagraphH4 = findViewById(R.id.editor_paragraph_h4)
mEditorParagraphQuote = findViewById(R.id.editor_paragraph_quote)
mEditorFontContainer = findViewById(R.id.editor_font_container)
mEditorParagraphContainer = findViewById(R.id.editor_paragraph_container)
mEditorLinkContainer = findViewById(R.id.editor_link_container)
mEditorInsertDetailContainer = findViewById(R.id.editor_insert_detail_container)
mTagsContainer = findViewById(R.id.tagsContainer)
mUploadVideoGuideContainer = findViewById(R.id.uploadVideoGuideContainer)
mOriginalCb = findViewById(R.id.originalCb)
mOriginalTipsContainer = findViewById(R.id.originalTipsContainer)
mOriginalTipsClose = findViewById(R.id.originalTipsClose)
}
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
findView()
onRichClick()
mViewModel = provideViewModel()
mViewModel.setUploadVideoListener(this)
mKeyboardHeightProvider = KeyboardHeightProvider(this)
mRichEditor.post { mKeyboardHeightProvider?.start() }
// 防止个别手机在Js里无法获取粘贴内容
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
mRichEditor.addJavascriptInterface(
OnEditorTextChangeListener(),
"OnEditorTextChangeListener"
)
mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener")
mRichEditor.addJavascriptInterface(
OnQuoteCountChangeListener(),
"OnQuoteCountChangeListener"
)
mRichEditor.setInputEnabled(true)
mRichEditor.setPadding(16, 12, 16, 12)
mRichEditor.setOnTouchListener { _, _ ->
if (mIsExtendedKeyboardShow) {
closeExtendedKeyboard()
Util_System_Keyboard.showSoftKeyboard(this)
//是否消费事件根据mRichEditor是否含有焦点决定mRichEditor没有焦点则不消费事件
mRichEditor.hasFocus()
} else false
}
mOriginalCb.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
mOriginalTipsContainer.alpha = 0f
mOriginalTipsContainer.visibility = View.VISIBLE
ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start()
}
}
observeData()
}
private fun observeData() {
mViewModel.chooseImagesUpload.observe(this, Observer {
mRichEditor.focusEditor()
for (key in it.keys) {
mRichEditor.insertPlaceholderImage(key)
}
})
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
val jsonArray = JSONArray()
for (key in it.keys) {
val jsonObject = JSONObject()
jsonObject.put("id", key)
jsonObject.put("url", it[key])
jsonArray.put(jsonObject)
}
mRichEditor.replacePlaceholderImage(jsonArray.toString())
})
}
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
mIsKeyBoardShow = height > 0
if (height > 0) {
closeExtendedKeyboard()
}
}
fun closeExtendedKeyboard() {
mEditorInsertDetailContainer.visibility = View.GONE
mEditorFont.isChecked = false
mEditorLink.isChecked = false
mIsExtendedKeyboardShow = false
}
protected fun controlEditorInsertContainerEnabled(isEnabled: Boolean) {
mEditorFont.isEnabled = isEnabled
}
private fun onRichClick() {
mEditorFont.setOnClickListener {
controlEditorFontContainer()
}
mEditorLink.setOnClickListener {
controlEditorLinkContainer()
}
mEditorFontBold.setOnClickListener {
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
mRichEditor.setBold()
if (mEditorFontBold.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-加粗")
}
}
mEditorFontItalic.setOnClickListener {
mEditorFontItalic.isChecked = !mEditorFontItalic.isChecked
mRichEditor.setItalic()
if (mEditorFontItalic.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-斜体")
}
}
mEditorFontStrikeThrough.setOnClickListener {
mEditorFontStrikeThrough.isChecked = !mEditorFontStrikeThrough.isChecked
mRichEditor.setStrikeThrough()
if (mEditorFontStrikeThrough.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-删除线")
}
}
mEditorFontUnderline.setOnClickListener {
mEditorFontUnderline.isChecked = !mEditorFontUnderline.isChecked
mRichEditor.setUnderline()
if (mEditorFontUnderline.isChecked) {
MtaHelper.onEvent(mtaEventName(), "文字样式", "文字样式-下滑线")
}
}
mEditorParagraphH1.setOnClickListener {
if (mEditorParagraphH1.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-1级标题")
mRichEditor.setHeading(1)
}
mEditorParagraphH1.isChecked = !mEditorParagraphH1.isChecked
}
mEditorParagraphH2.setOnClickListener {
if (mEditorParagraphH2.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-2级标题")
mRichEditor.setHeading(2)
}
mEditorParagraphH2.isChecked = !mEditorParagraphH2.isChecked
}
mEditorParagraphH3.setOnClickListener {
if (mEditorParagraphH3.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-3级标题")
mRichEditor.setHeading(3)
}
mEditorParagraphH3.isChecked = !mEditorParagraphH3.isChecked
}
mEditorParagraphH4.setOnClickListener {
if (mEditorParagraphH4.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-4级标题")
mRichEditor.setHeading(4)
}
mEditorParagraphH4.isChecked = !mEditorParagraphH4.isChecked
}
mEditorParagraphQuote.setOnClickListener {
if (mEditorParagraphQuote.isChecked) {
mRichEditor.formatBlock()
} else {
MtaHelper.onEvent(mtaEventName(), "段落样式", "段落样式-引用")
mRichEditor.setBlockquote()
}
mEditorParagraphQuote.isChecked = !mEditorParagraphQuote.isChecked
}
findViewById<View>(R.id.editor_link_answer).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
startActivityForResult(
InsertAnswerWrapperActivity.getIntent(this),
INSERT_ANSWER_CODE
)
}
findViewById<View>(R.id.editor_link_article).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
startActivityForResult(
InsertArticleWrapperActivity.getIntent(this),
INSERT_ARTICLE_CODE
)
}
findViewById<View>(R.id.editor_link_game).setOnClickListener {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
startActivityForResult(
GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
INSERT_GAME_CODE
)
}
findViewById<View>(R.id.editor_link_video).setOnClickListener {
startActivityForResult(
InsertVideoWrapperActivity.getIntent(this),
INSERT_VIDEO_CODE
)
}
findViewById<View>(R.id.editor_link_game_collection).setOnClickListener {
startActivityForResult(
InsertGameCollectionWrapperActivity.getIntent(this),
INSERT_GAME_COLLECTION_CODE
)
}
findViewById<View>(R.id.editor_video).setOnClickListener {
chooseVideo()
}
findViewById<View>(R.id.editor_image).setOnClickListener {
if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) {
mAgreePostPic = true
DialogHelper.showDialog(
this,
"警告",
"当前使用移动网络,上传图片会消耗手机流量",
"我知道了", "", { chooseImage() },
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return@setOnClickListener
}
chooseImage()
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"图片"
)
}
findViewById<View>(R.id.uploadVideoGuideClose).setOnClickListener {
hideUploadVideoGuide()
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
}
}
findViewById<View>(R.id.originalTipsClose).setOnClickListener {
val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200)
animator.doOnEnd {
mOriginalTipsContainer.visibility = View.GONE
}
animator.start()
}
}
private fun chooseVideo() {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
val videoCount = mViewModel.quoteCountEntity.videoCount
if (videoCount >= MAX_MEDIA_COUNT) {
toast(R.string.answer_edit_max_video_hint)
return
}
try {
PermissionHelper.checkStoragePermissionBeforeAction(this,
object : EmptyCallback {
override fun onCallback() {
val maxChooseCount = if (videoCount + 3 <= MAX_MEDIA_COUNT) 3 else MAX_MEDIA_COUNT - videoCount
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.VIDEO,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_MEDIA_VIDEO_CODE
)
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"视频"
)
}
})
} catch (e: Exception) {
toast(R.string.media_image_hint)
e.printStackTrace()
}
}
private fun chooseImage() {
MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片")
val imageCount = mViewModel.quoteCountEntity.imageCount
if (imageCount >= MAX_MEDIA_COUNT) {
toast(R.string.answer_edit_max_img_hint)
return
}
try {
PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback {
override fun onCallback() {
val maxChooseCount = if (imageCount + 10 <= MAX_MEDIA_COUNT) 10 else MAX_MEDIA_COUNT - imageCount
val intent = LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.IMAGE,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
)
startActivityForResult(intent, REQUEST_CODE_IMAGE)
}
})
} catch (e: Exception) {
toast(R.string.media_image_hint)
e.printStackTrace()
}
}
private fun controlEditorFontContainer() {
mEditorFont.isChecked = !mEditorFont.isChecked
mEditorLink.isChecked = 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
}, if (isShouldDelay) 200 else 0L)
}
private fun controlEditorLinkContainer() {
mEditorLink.isChecked = !mEditorLink.isChecked
mEditorFont.isChecked = 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
}, if (isShouldDelay) 200 else 0L)
}
override fun handleBackPressed(): Boolean {
if (mIsExtendedKeyboardShow) {
closeExtendedKeyboard()
return true
}
return super.handleBackPressed()
}
override fun onResume() {
super.onResume()
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
}
override fun onPause() {
super.onPause()
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
}
//视频上传功能引导
fun showUploadVideoGuide() {
mUploadVideoGuideContainer.postDelayed({
val count = SPUtils.getInt(getVideoGuideKey(), 0)
if (count >= mMaxUploadVideoGuideCount) return@postDelayed
mUploadVideoGuideContainer.alpha = 0f
mUploadVideoGuideContainer.visibility = View.VISIBLE
mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start()
mGuideDisposable = countDownTimer(3) { finish, _ ->
if (finish) {
hideUploadVideoGuide()
}
}
SPUtils.setInt(getVideoGuideKey(), count + 1)
}, 1000)
}
fun hideUploadVideoGuide() {
val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200)
animate.doOnEnd {
mUploadVideoGuideContainer.visibility = View.GONE
}
animate.start()
}
override fun onDestroy() {
super.onDestroy()
mKeyboardHeightProvider?.close()
val path = mViewModel.currentUploadingVideo?.filePath
if (path != null && UploadManager.isUploading(path)) {
UploadManager.cancelTask(path)
}
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
}
}
private inner class OnCursorChangeListener {
@JavascriptInterface
fun onElements(elements: String) {
Utils.log("-----------------------")
Utils.log(elements)
Utils.log(mRichEditor.html)
Utils.log("-----------------------")
mCurrentParagraphStyle = when {
elements.contains(ELEMENT_PARAGRAPH_QUOTE) -> ELEMENT_PARAGRAPH_QUOTE
elements.contains(ELEMENT_PARAGRAPH_P) -> ELEMENT_PARAGRAPH_P
else -> ""
}
mBaseHandler.post {
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)
mEditorParagraphH4.isChecked = elements.contains(ELEMENT_PARAGRAPH_H4)
mEditorParagraphQuote.isChecked = elements.contains(ELEMENT_PARAGRAPH_QUOTE)
}
}
}
private inner class OnPasteListener {
@JavascriptInterface
fun onPaste() {
val clipboard =
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipText = clipboard.text.toString()
if (!TextUtils.isEmpty(clipText)) {
// 替换换行符号否则 插入失败
val text = clipText.replace("[ ]".toRegex(), "&nbsp;")
.replace("[\r\n]".toRegex(), "<br/>")
mBaseHandler.post { mRichEditor.insertHtml(text) }
}
}
}
private inner class OnEditorTextChangeListener {
@JavascriptInterface
fun onTextChange(count: Int) {
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
mEditorTextNumTv.post {
mEditorTextNumTv.text = num.toString()
mViewModel.quoteCountEntity.textCount = num
}
}
}
private inner class OnQuoteCountChangeListener {
@JavascriptInterface
fun onQuoteCountChange(
imageCount: Int,
articleCount: Int,
answerCount: Int,
videoCount: Int,
gameCount: Int
) {
mEditorTextNumTv.post {
mViewModel.quoteCountEntity.apply {
this.imageCount = imageCount
this.articleCount = articleCount
this.answerCount = answerCount
this.videoCount = videoCount
this.gameCount = gameCount
}
}
}
}
private inner class OnVideoListener {
@JavascriptInterface
fun showDeleteDialog(id: String) {
DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗?", "确定", "取消", {
runOnUiThread {
mRichEditor.delPlaceholderVideo(id)
mViewModel.deleteVideo(id)
}
})
}
@JavascriptInterface
fun updatePoster(id: String, videoId: String, url: String) {
mViewModel.id = id
mViewModel.videoId = videoId
val videoEntity = VideoEntity(url = url)
val intent =
PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
}
@JavascriptInterface
fun deleteUploadingVideo(id: String) {
mViewModel.deleteVideo(id)
}
@JavascriptInterface
fun reUploadVideo(id: String) {
val video = mViewModel.uploadVideoErrorList.find { it.id == id }
if (video != null) {
mViewModel.localVideoList.add(video)
mViewModel.uploadVideoErrorList.remove(video)
mViewModel.uploadVideo()
}
}
}
override fun insertPlaceholderVideo(id: String, poster: String) {
mRichEditor.insertPlaceholderVideo(id, poster)
}
override fun updateVideoProgress(id: String, progress: String) {
mRichEditor.updateVideoProgress(id, progress)
}
override fun videoUploadFinished(id: String, url: String, msg: JsonObject) {
try {
val obj = JSONObject()
obj.put("poster", msg.get("poster").asString)
obj.put("url", msg.get("url").asString)
obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong))
obj.put("id", msg.get("_id").asString)
obj.put("status", "pending")
mRichEditor.videoUploadFinished(id, url, obj.toString())
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
override fun changePoster(id: String, poster: String) {
mRichEditor.changePoster(id, poster)
}
override fun videoUploadFailed(id: String) {
mRichEditor.videoUploadFailed(id)
}
open fun getSelectedLabel(): Int = 0
open fun onActivityDialogResult(requestCode: Int, resultCode: Int, data: Intent?) {}
abstract fun mtaEventName(): String
abstract fun provideViewModel(): VM
abstract fun getVideoGuideKey(): String
companion object {
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 "
const val ELEMENT_PARAGRAPH_H4 = " h4 "
const val ELEMENT_PARAGRAPH_P = " p "
const val ELEMENT_PARAGRAPH_QUOTE = " blockquote "
const val INSERT_ANSWER_CODE = 411
const val INSERT_ARTICLE_CODE = 412
const val INSERT_GAME_CODE = 413
const val INSERT_GAME_COLLECTION_CODE = 414
const val INSERT_VIDEO_CODE = 415
const val MAX_INPUT_TEXT_NUM = 10000
const val MAX_MEDIA_COUNT = 20
const val REQUEST_CODE_IMAGE = 120
const val INSERT_MEDIA_VIDEO_CODE = 121
const val REQUEST_CODE_IMAGE_CROP = 122
}
}

View File

@ -1,444 +0,0 @@
package com.gh.base
import android.app.Application
import android.content.Intent
import android.graphics.Bitmap
import android.media.ThumbnailUtils
import android.provider.MediaStore
import android.text.TextUtils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.gh.base.fragment.WaitingDialogFragment
import com.gh.common.runOnUiThread
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.ErrorEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.find
import kotlin.collections.forEach
import kotlin.collections.set
abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) {
val mApi: ApiService = RetrofitManager.getInstance().api
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
val uploadingImage = ArrayList<LinkedHashMap<String, String>>()
val chooseImagesUpload = MutableLiveData<LinkedHashMap<String, String>>()
val chooseImagesUploadSuccess = MutableLiveData<LinkedHashMap<String, String>>()
var uploadImageSubscription: Disposable? = null
val mapImages = HashMap<String, String>()
val localVideoList = ArrayList<LocalVideoEntity>()
val uploadVideoErrorList = ArrayList<LocalVideoEntity>()
var currentUploadingVideo: LocalVideoEntity? = null
var type: String = "" //游戏论坛game_bbs 官方论坛official_bbs
private var mUploadVideoListener: UploadVideoListener? = null
val TITLE_MIN_LENGTH = 6
val MIN_TEXT_LENGTH = 6
val MAX_TEXT_LENGTH = 10000
val FILE_HOST = "file:///"
var id = ""//视频标记
var videoId = ""//更改封面视频id
val quoteCountEntity = QuoteCountEntity()//数据上报用
fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) {
this.mUploadVideoListener = uploadVideoListener
}
//检查图片是否符合规则并上传图片
fun uploadPic(data: Intent) {
val uris = Matisse.obtainResult(data)
val pictureList = ArrayList<String>()
for (uri in uris) {
val picturePath = PathUtils.getPath(getApplication(), uri)
if (picturePath != null) {
if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
val application: Application = getApplication()
Utils.toast(
getApplication(),
application.getString(R.string.pic_max_hint, count)
)
continue
}
Utils.log("picturePath = $picturePath")
pictureList.add(picturePath)
} else {
Utils.log("picturePath is null")
}
}
if (pictureList.size == 0) return
val imageType = when (getRichType()) {
RichType.ARTICLE -> UploadImageUtils.UploadType.community_article
RichType.QUESTION -> UploadImageUtils.UploadType.question
else -> UploadImageUtils.UploadType.poster
}
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(
imageType,
pictureList,
false,
object : UploadImageUtils.OnUploadImageListListener {
override fun onProgress(total: Long, progress: Long) {}
override fun onCompressSuccess(imageUrls: List<String>) {
val chooseImageMd5Map = LinkedHashMap<String, String>()
imageUrls.forEach {
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
}
uploadingImage.add(chooseImageMd5Map)
chooseImagesUpload.postValue(chooseImageMd5Map)
}
override fun onSingleSuccess(imageUrl: Map<String, String>) {
val map = LinkedHashMap<String, String>()
for (key in imageUrl.keys) {
map[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI()
mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: ""
}
chooseImagesUploadSuccess.postValue(map)
}
override fun onSuccess(
imageUrl: LinkedHashMap<String, String>,
errorMap: Map<String, Exception>
) {
val uploadMap = uploadingImage.find {
it.containsKey(
MD5Utils.getUrlMD5(
imageUrl.entries.iterator().next().key
)
)
}
uploadMap?.let {
uploadingImage.remove(uploadMap)
}
val errorSize = pictureList.size - imageUrl.size
if (errorSize > 0) {
val map = LinkedHashMap<String, String>()
for (key in errorMap.keys) {
map[MD5Utils.getUrlMD5(key)] = ""
}
//value为空会删除PlaceholderImage
chooseImagesUploadSuccess.postValue(map)
for (error in errorMap.values) {
if (error is HttpException && error.code() == 403) {
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
return
}
}
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
}
}
override fun onError(errorMap: Map<String, Exception>) {
val errorSize = errorMap.size
if (errorSize > 0) {
val map = LinkedHashMap<String, String>()
for (key in errorMap.keys) {
map[MD5Utils.getUrlMD5(key)] = ""
}
//value为空会删除PlaceholderImage
chooseImagesUploadSuccess.postValue(map)
}
for (error in errorMap.values) {
if (error is HttpException && error.code() == 403) {
val e = error.response()?.errorBody()?.string()?.toObject<ErrorEntity>()
if (e != null && e.code == 403017) {
Utils.toast(
getApplication(),
errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传"
)
} else {
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
}
return
}
}
if (errorSize == 1) {
Utils.toast(getApplication(), "图片上传失败")
} else {
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
}
}
})
}
fun uploadPoster(picturePath: String) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", true))
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
picturePath,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
patchVideoPoster(imageUrl)
}
override fun onError(e: Throwable?) {
handleUploadPosterResult(true)
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun patchVideoPoster(poster: String) {
if (id.isEmpty() || videoId.isEmpty()) return
val map = hashMapOf("poster" to poster, "type" to getVideoType())
mApi.patchInsertVideo(videoId, map.toRequestBody())
.compose(observableToMain())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
mUploadVideoListener?.changePoster(id, poster)
handleUploadPosterResult(false)
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
handleUploadPosterResult(true)
}
})
}
private fun handleUploadPosterResult(isFailure: Boolean = false) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
if (isFailure) {
ToastUtils.showToast("封面更改失败")
}
id = ""
videoId = ""
}
fun deleteVideo(id: String) {
if (localVideoList.isNotEmpty()) {
val video = localVideoList.find { it.id == id }
if (video != null) {
if (UploadManager.isUploading(video.filePath)) {
UploadManager.cancelTask(video.filePath)
}
localVideoList.remove(video)
}
}
if (uploadVideoErrorList.isNotEmpty()) {
val video = uploadVideoErrorList.find { it.id == id }
if (video != null) {
uploadVideoErrorList.remove(video)
}
}
if (currentUploadingVideo?.id == id) {
currentUploadingVideo = null
uploadVideo()
}
}
fun uploadVideo() {
if (currentUploadingVideo != null) return
if (localVideoList.isEmpty()) return
currentUploadingVideo = localVideoList[0]
UploadManager.createUploadTask(currentUploadingVideo?.filePath
?: "", object : OnUploadListener {
override fun onProgressChanged(
uploadFilePath: String,
currentSize: Long,
totalSize: Long,
speed: Long
) {
runOnUiThread {
val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1)
currentUploadingVideo?.id?.let {
mUploadVideoListener?.updateVideoProgress(it, percent.toString())
}
}
}
override fun onUploadSuccess(uploadFilePath: String, url: String) {
if (currentUploadingVideo != null) {
postVideoPosterAndInfo(uploadFilePath, url)
}
}
override fun onUploadFailure(uploadFilePath: String, errorMsg: String) {
uploadVideoFailure()
}
})
}
private fun postVideoPosterAndInfo(uploadFilePath: String, url: String) {
val localVideoPoster =
getApplication<Application>().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
try {
val bmp = ThumbnailUtils.createVideoThumbnail(
uploadFilePath,
MediaStore.Images.Thumbnails.MINI_KIND
)
// bmp 可能为空
FileOutputStream(localVideoPoster).use { out ->
bmp?.compress(Bitmap.CompressFormat.PNG, 100, out)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
ToastUtils.showToast("视频封面操作失败")
uploadVideoFailure()
return
}
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
localVideoPoster,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
postVideoInfo(url, imageUrl)
}
override fun onError(e: Throwable?) {
uploadVideoFailure()
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun postVideoInfo(url: String, poster: String) {
val map = HashMap<String, Any>().apply {
put("poster", poster)
put("url", url)
put("format", currentUploadingVideo?.format ?: "")
put("size", currentUploadingVideo?.size ?: 0)
put("length", (currentUploadingVideo?.duration ?: 0) / 1000)
put("type", getVideoType())
}
val requestBody = map.toRequestBody()
mApi.insertVideo(requestBody)
.compose(observableToMain())
.subscribe(object : Response<JsonObject>() {
override fun onResponse(response: JsonObject?) {
super.onResponse(response)
if (response != null) {
uploadVideoSuccess(poster, url, response)
}
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
uploadVideoFailure()
}
})
}
private fun uploadVideoSuccess(poster: String, url: String, data: JsonObject) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
mUploadVideoListener?.changePoster(it.id, poster)
mUploadVideoListener?.videoUploadFinished(it.id, url, data)
UploadManager.cancelTask(it.filePath)
localVideoList.remove(it)
}
currentUploadingVideo = null
uploadVideo()
}
private fun uploadVideoFailure() {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
runOnUiThread {
mUploadVideoListener?.videoUploadFailed(it.id)
}
uploadVideoErrorList.add(it)
localVideoList.remove(it)
UploadManager.cancelTask(it.filePath)
}
currentUploadingVideo = null
uploadVideo()
}
fun checkIsAllUploadedAndToast(): Boolean {
if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) {
ToastUtils.showToast("视频未上传完成,视频内容保存失败")
return false
}
return true
}
private fun getVideoType(): String {
return when (type) {
BbsType.GAME_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value
else -> ""
}
}
BbsType.OFFICIAL_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value
else -> ""
}
}
else -> ""
}
}
abstract fun getRichType(): RichType
}
interface UploadVideoListener {
/**
* 插入视频占位图
*/
fun insertPlaceholderVideo(id: String, poster: String)
/**
* 更新视频进度条
*/
fun updateVideoProgress(id: String, progress: String)
/**
* 上传视频完成
*/
fun videoUploadFinished(id: String, url: String, msg: JsonObject)
/**
* 更换封面图
*/
fun changePoster(id: String, poster: String)
/**
* 上传失败
*/
fun videoUploadFailed(id: String)
}
enum class RichType {
ARTICLE,
QUESTION,
ANSWER
}

View File

@ -1,93 +0,0 @@
package com.gh.base
import android.content.Context
import android.content.SharedPreferences
import com.gh.common.util.SPUtils
import com.halo.assistant.HaloApp
/**
* 用 SP 实现的简单列表持久化结构
*/
abstract class BaseSimpleDao {
// 使用独有的 SP 文件
private val mSp: SharedPreferences by lazy {
HaloApp.getInstance().application.getSharedPreferences("SimpleDao", Context.MODE_PRIVATE)
}
fun add(key: String) {
val originString = SPUtils.getString(mSp, getSPKey())
if (originString.isEmpty()) {
// Insert keyword only for the very first time.
SPUtils.setString(mSp, getSPKey(), key)
} else {
getAll()?.let {
if (getMaxSize() != -1 && it.size > getMaxSize()) {
it.removeAt(it.size - 1)
}
// Move keyword to the very front if it exists.
if (it.contains(key)) {
it.remove(key)
}
it.add(0, key)
val builder = StringBuilder()
for ((index, k) in it.withIndex()) {
builder.append(k)
if (index != it.size - 1) {
builder.append(DIVIDER_KEY)
}
}
SPUtils.setString(mSp, getSPKey(), builder.toString())
}
}
}
fun delete(key: String) {
val originString = SPUtils.getString(mSp, getSPKey())
if (originString.isEmpty()) {
// do nothing
} else {
getAll()?.let {
if (it.contains(key)) {
it.remove(key)
}
val builder = StringBuilder()
for ((index, k) in it.withIndex()) {
builder.append(k)
if (index != it.size - 1) {
builder.append(DIVIDER_KEY)
}
}
SPUtils.setString(mSp, getSPKey(), builder.toString())
}
}
}
fun getAll(): ArrayList<String>? {
val list = SPUtils.getString(mSp, getSPKey()).split(DIVIDER_KEY)
return if (list.size == 1 && list[0].isEmpty()) null else ArrayList(list)
}
fun getRawString(): String = SPUtils.getString(mSp, getSPKey())
fun contains(key: String): Boolean {
return getAll()?.contains(key) == true
}
fun deleteAll() {
SPUtils.setString(mSp, getSPKey(), "")
}
open fun getMaxSize(): Int = -1
abstract fun getSPKey(): String
companion object {
private const val DIVIDER_KEY = "<-||->"
}
}

View File

@ -1,19 +0,0 @@
package com.gh.base
import android.app.Activity
object CurrentActivityHolder {
@JvmStatic
val activitySet = HashSet<Activity>()
@JvmStatic
fun getCurrentActivity(): Activity? {
return if (activitySet.isEmpty()) {
null
} else {
activitySet.iterator().next()
}
}
}

View File

@ -1,53 +0,0 @@
package com.gh.base
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.gh.gamecenter.R
class CustomLayoutInflaterFactory(
private val mAppCompatActivity: AppCompatActivity
) : LayoutInflater.Factory2 {
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? {
val view: View?
try {
view = mAppCompatActivity.delegate.createView(parent, name, context, attrs)
?: mAppCompatActivity.onCreateView(parent, name, context, attrs)
?: mAppCompatActivity.layoutInflater.createView(name, null, attrs)
} catch (e: Exception) {
return null
}
val n = attrs.attributeCount
for (i in 0 until n) {
val attributeName = attrs.getAttributeName(i).toString()
if (attributeName.contains("background")) {
view?.setTag(TAG_BACKGROUND_ID, attrs.getAttributeValue(i))
} else if (attributeName.contains("textColor")) {
view?.setTag(TAG_TEXT_COLOR_ID, attrs.getAttributeValue(i))
}
}
return view
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return mAppCompatActivity.onCreateView(name, context, attrs)
}
companion object {
const val TAG_BACKGROUND_ID = R.string.background_id
const val TAG_TEXT_COLOR_ID = R.string.text_color_id
}
}

View File

@ -0,0 +1,26 @@
package com.gh.base;
import android.support.v4.app.Fragment;
/**
* @author CsHeng
* @Date 15-9-29
* @Time 上午10:24
*/
public interface FragmentNavigationDelegate {
void addFragment(Fragment toAdd);
void replaceFragment(Fragment toReplace);
void removeFragment(Fragment toRemove);
void showFragment(Fragment toShow);
void hideFragment(Fragment toHide);
boolean popFragment();
void popFragmentToBase();
}

View File

@ -0,0 +1,54 @@
package com.gh.base;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
/**
* 1、写点针对生命周期的统计代码
* 2、写点通用的逻辑
* 3、接口解耦
*
* @author CsHeng
* @Date 09/05/2017
* @Time 6:22 PM
*/
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}

View File

@ -0,0 +1,312 @@
package com.gh.base;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;
import android.widget.RemoteViews;
import com.gh.common.util.AppDebugConfig;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.TokenUtils;
import com.gh.common.util.Utils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.R;
import com.xiaomi.mipush.sdk.ErrorCode;
import com.xiaomi.mipush.sdk.MiPushClient;
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
import com.xiaomi.mipush.sdk.MiPushMessage;
import com.xiaomi.mipush.sdk.PushMessageReceiver;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import static com.gh.common.util.EntranceUtils.ENTRANCE_MIPUSH;
import static com.gh.common.util.EntranceUtils.HOST_ARTICLE;
import static com.gh.common.util.EntranceUtils.HOST_GAME;
import static com.gh.common.util.EntranceUtils.HOST_WEB;
import static com.gh.common.util.EntranceUtils.HOSt_COLUMN;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
import static com.gh.common.util.EntranceUtils.KEY_GAMEID;
import static com.gh.common.util.EntranceUtils.KEY_ID;
import static com.gh.common.util.EntranceUtils.KEY_NEWSID;
import static com.gh.common.util.EntranceUtils.KEY_TARGET;
import static com.gh.common.util.EntranceUtils.KEY_TO;
import static com.gh.common.util.EntranceUtils.KEY_TYPE;
import static com.gh.common.util.EntranceUtils.KEY_URL;
/**
* 1、PushMessageReceiver是个抽象类该类继承了BroadcastReceiver。
* 2、需要将自定义的DemoMessageReceiver注册在AndroidManifest.xml文件中 <receiver
* android:exported="true"
* android:name="com.xiaomi.mipushdemo.DemoMessageReceiver"> <intent-filter>
* <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" /> </intent-filter>
* <intent-filter> <action android:name="com.xiaomi.mipush.ERROR" />
* </intent-filter> <intent-filter> <action
* android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" /></intent-filter>
* </receiver>
* 3、DemoMessageReceiver的onReceivePassThroughMessage方法用来接收服务器向客户端发送的透传消息
* 4、DemoMessageReceiver的onNotificationMessageClicked方法用来接收服务器向客户端发送的通知消息
* 这个回调方法会在用户手动点击通知后触发
* 5、DemoMessageReceiver的onNotificationMessageArrived方法用来接收服务器向客户端发送的通知消息
* 这个回调方法是在通知消息到达客户端时触发。另外应用在前台时不弹出通知的通知消息到达客户端也会触发这个回调函数
* 6、DemoMessageReceiver的onCommandResult方法用来接收客户端向服务器发送命令后的响应结果
* 7、DemoMessageReceiver的onReceiveRegisterResult方法用来接收客户端向服务器发送注册命令后的响应结果
* 8、以上这些方法运行在非UI线程中
*
* @author mayixiang
* //TODO 请勿更改此类路径,若需更改时请注意更改./libraries/MiPush/proguard-library.txt混淆对应配置
*/
public class GHPushMessageReceiver extends PushMessageReceiver {
private String mAlias;
@Override
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
// 1判断notifyid是否为4
try {
//TODO what is magic number 4?
if (message.getNotifyId() == 4) {
JSONObject jsonObject = new JSONObject(message.getContent());
Utils.log(jsonObject.toString());
String channel = jsonObject.getString("channel");
Utils.log("channel = " + channel);
// 1判断渠道号是否一致或是否为ALL
String packageChannel = AppController.getInstance().getChannel();
if ("ALL".equals(channel) || channel.equalsIgnoreCase(packageChannel)) {
String type = jsonObject.getString(KEY_TYPE);
Utils.log("type = " + type);
if ("NEWS".equals(type)) {
// 新闻推送
JSONArray jsonArray = jsonObject.getJSONArray("package");
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
Boolean b = map.get(jsonArray.getString(i));
if (b != null) {
// 显示推送的消息
showNotification(context, jsonObject, 0);
break;
}
}
} else if ("PLUGIN_UPDATE".equals(type)) {
// 插件更新推送
JSONArray jsonArray = jsonObject.getJSONArray("apk");
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject apk = jsonArray.getJSONObject(i);
String packageName = apk.getString("package");
Boolean b = map.get(packageName);
if (b != null) {
// 判断是否gh_version是否相同
String gh_version = (String) PackageUtils
.getMetaData(context, packageName, "gh_version");
if (gh_version != null) {
gh_version = gh_version.substring(2);
// 判断gh_version是否相同
if (gh_version.equals(apk.getString("gh_version"))) {
// 判断version是否相同
String version = PackageUtils.getVersionByPackage(context, packageName);
if (apk.getString("version").equals(version)) {
// 版本相同,无需显示插件更新,继续查看是否有可更新的游戏包
continue;
}
}
}
// 显示推送的消息
showNotification(context, jsonObject, 1);
break;
}
}
} else if ("NEW_GAME".equals(type)) {
// 新游推送
showNotification(context, jsonObject, 2);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
private ArrayMap<String, Boolean> getInstalledMapFromLocal(Context context) {
ArrayMap<String, Boolean> map = new ArrayMap<>();
ArrayList<String> list = getAllPackageName(context);
for (String str : list) {
map.put(str, true);
}
return map;
}
private void showNotification(Context context, JSONObject jsonObject, int id) throws JSONException {
Intent intent = new Intent();
intent.setAction("com.gh.gamecenter.NOTIFICATION");
intent.putExtra("notifyId", id);
intent.putExtra("notifyData", jsonObject.toString());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_ONE_SHOT);
NotificationManager nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.logo)
.setTicker(jsonObject.getString("pushTitle"))
.setContentTitle(jsonObject.getString("pushTitle"))
.setContentText(jsonObject.getString("pushDesc"))
.setContentIntent(pendingIntent).build();
RemoteViews remoteViews = null;
if (Build.MANUFACTURER.equals("Meizu") &&
(Build.MODEL.startsWith("m") || Build.MODEL.startsWith("MX"))) {
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_meizu);
SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("Xiaomi") &&
(Build.MODEL.startsWith("MI") || Build.MODEL.startsWith("HM") || Build.MODEL.startsWith("Redmi"))) {
// 小米系统
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_xiaomi);
SimpleDateFormat format = new SimpleDateFormat("ah:mm", Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("HUAWEI")) {
// 华为系统
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_huawei);
}
String url = jsonObject.getString("icon");
String path = context.getCacheDir() + File.separator + url.substring(url.lastIndexOf("/") + 1);
int result = FileUtils.downloadFile(url, path);
if (result != 200) {
// 下载出错使用光环logo
path = null;
}
if (remoteViews != null) {
if (path == null) {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeFile(path));
}
remoteViews.setTextViewText(R.id.title, jsonObject.getString("pushTitle"));
remoteViews.setTextViewText(R.id.intro, jsonObject.getString("pushDesc"));
notification.contentView = remoteViews;
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.logo_black)
.setTicker(jsonObject.getString("pushTitle"))
.setContentTitle(jsonObject.getString("pushTitle"))
.setContentText(jsonObject.getString("pushDesc"))
.setContentIntent(pendingIntent);
if (path == null) {
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
builder.setLargeIcon(BitmapFactory.decodeFile(path));
}
notification = builder.build();
}
notification.defaults = Notification.DEFAULT_SOUND;// 添加系统默认声音
notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时通知将被清除。
nManager.notify(((int) System.currentTimeMillis() / 1000), notification);// 通过通知管理器来发起通知。如果id不同则每click在status哪里增加一个提示
}
private ArrayList<String> getAllPackageName(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = context.getPackageManager().getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
list.add(packageInfo.packageName);
}
}
return list;
}
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
try {
String content = message.getContent();
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString(KEY_ENTRANCE, ENTRANCE_MIPUSH);
String type = response.getString(KEY_TYPE);
String target = response.getString(KEY_TARGET);
if (HOST_ARTICLE.equals(type)) {
bundle.putString(KEY_TO, "NewsDetailActivity");
bundle.putString(KEY_NEWSID, target);
} else if (HOST_GAME.equals(type)) {
bundle.putString(KEY_TO, "GameDetailActivity");
bundle.putString(KEY_GAMEID, target);
} else if (HOSt_COLUMN.equals(type)) {
bundle.putString(KEY_TO, "SubjectActivity");
bundle.putString(KEY_ID, target);
} else if (HOST_WEB.equals(type)) {
bundle.putString(KEY_TO, "WebActivity");
bundle.putString(KEY_URL, target);
}
EntranceUtils.jumpActivity(context, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onNotificationMessageArrived(Context context,
MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
}
@Override
public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
}
@Override
public void onCommandResult(Context context, MiPushCommandMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mAlias = arguments.get(0);
}
}
if (TextUtils.isEmpty(mAlias)) {
//添加别名
MiPushClient.setAlias(context, TokenUtils.getDeviceId(context), null);
}
}
}

View File

@ -1,17 +0,0 @@
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
}
}

View File

@ -0,0 +1,46 @@
package com.gh.base;
import android.content.Context;
import android.os.Bundle;
import com.gh.common.util.EntranceUtils;
import com.umeng.message.UmengNotificationClickHandler;
import com.umeng.message.entity.UMessage;
import org.json.JSONException;
import org.json.JSONObject;
public class GHUmengNotificationClickHandler extends UmengNotificationClickHandler {
@Override
public void launchApp(Context context, UMessage uMessage) {
// super.launchApp(context, uMessage);
try {
String content = uMessage.extra.get(EntranceUtils.KEY_DATA);
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG);
String type = response.getString(EntranceUtils.KEY_TYPE);
String target = response.getString(EntranceUtils.KEY_TARGET);
if (EntranceUtils.HOST_ARTICLE.equals(type)) {
bundle.putString(EntranceUtils.KEY_TO, "NewsDetailActivity");
bundle.putString(EntranceUtils.KEY_NEWSID, target);
} else if (EntranceUtils.HOST_GAME.equals(type)) {
bundle.putString(EntranceUtils.KEY_TO, "GameDetailActivity");
bundle.putString(EntranceUtils.KEY_GAMEID, target);
} else if (EntranceUtils.HOSt_COLUMN.equals(type)) {
bundle.putString(EntranceUtils.KEY_TO, "SubjectActivity");
bundle.putString(EntranceUtils.KEY_ID, target);
} else if (EntranceUtils.HOST_WEB.equals(type)) {
bundle.putString(EntranceUtils.KEY_TO, "WebActivity");
bundle.putString(EntranceUtils.KEY_URL, target);
}
EntranceUtils.jumpActivity(context, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
}

View File

@ -1,229 +0,0 @@
//package com.gh.base
//
//import android.app.Notification
//import android.app.NotificationChannel
//import android.app.NotificationManager
//import android.app.PendingIntent
//import android.content.Context
//import android.content.Intent
//import android.os.Build
//import android.os.Bundle
//import android.preference.PreferenceManager
//import android.text.TextUtils
//import android.view.View
//import androidx.core.app.NotificationCompat
//import androidx.core.text.htmlEncode
//import com.gh.common.notifier.Notifier
//import com.gh.common.util.*
//import com.gh.gamecenter.R
//import com.gh.gamecenter.entity.PushEntity
//import com.gh.gamecenter.entity.PushMessageEntity
//import com.gh.gamecenter.entity.PushMessageUnreadEntity
//import com.gh.gamecenter.entity.PushNotificationEntity
//import com.gh.gamecenter.manager.UserManager
//import com.gh.gamecenter.message.MessageUnreadRepository
//import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
//import com.gh.gamecenter.receiver.UmengMessageReceiver
//import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_CLICK
//import com.gh.gamecenter.receiver.UmengMessageReceiver.Companion.TYPE_REMOVE
//import com.gh.gamecenter.retrofit.Response
//import com.gh.gamecenter.retrofit.RetrofitManager
//import com.google.gson.Gson
//import com.umeng.message.UmengMessageService
//import io.reactivex.android.schedulers.AndroidSchedulers
//import io.reactivex.schedulers.Schedulers
//import okhttp3.MediaType
//import okhttp3.RequestBody
//import okhttp3.ResponseBody
//import org.android.agoo.common.AgooConstants
//import org.json.JSONObject
//import retrofit2.HttpException
//import java.util.*
//
//class GHUmengNotificationService : UmengMessageService() {
//
// companion object {
// const val ACTION_UMENG = "com.gh.gamecenter.UMENG"
// const val MESSAGE_FROM_SYSTEM = "message_from_system"
// const val HALO_MESSAGE_DIALOG = "HALO_MESSAGE_DIALOG"
// const val HALO_MESSAGE_CENTER = "HALO_MESSAGE_CENTER"
// const val ANSWER = "answer"
// const val FOLLOW_QUESTION = "follow_question"
// const val NOTIFICATION_ID = 2015
// const val DISPLAY_TYPE_NOTIFICATION = "notification"
// const val DISPLAY_TYPE_CUSTOM = "custom"
// const val MESSAGE_ID = "message_id"
// const val NOTIFICATION_MESSAGE_ID = "notification_message_id" // 通知中心消息 ID
// const val PUSH_ID = "push_id"
// }
//
// val notificationTags = arrayOf("GH_UMENG_TAG_1", "GH_UMENG_TAG_2", "GH_UMENG_TAG_3")
// val gson = Gson()
//
// override fun onMessage(context: Context, intent: Intent) {
// val message = intent.getStringExtra(AgooConstants.MESSAGE_BODY)
// val isMessageFromSystem = intent.getBooleanExtra(MESSAGE_FROM_SYSTEM, false)
//
// try {
// val pushData = message.toObject<PushEntity>()
// pushData?.let { handlePushData(context, it, message, isMessageFromSystem) }
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
//
// private fun handlePushData(context: Context, pushData: PushEntity, message: String, isMessageFromSystem: Boolean) {
// val notificationManager = context.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//
// if (pushData.displayType == DISPLAY_TYPE_NOTIFICATION) {
// // 其它类型的透传信息
// // 显示到通知栏
// val msg = message.toObject<PushNotificationEntity>()
// val data = msg?.extra?.data
//
// // 系统推送(非自定义信息),直接处理跳转
// if (isMessageFromSystem) {
// val intent = Intent()
// intent.setClass(context, UmengMessageReceiver::class.java)
// intent.putExtra(EntranceUtils.KEY_DATA, data?.link)
// intent.putExtra(EntranceUtils.KEY_TYPE, UmengMessageReceiver.DIRECT_ONLY)
// intent.putExtra(EntranceUtils.KEY_MESSAGE, message)
// intent.putExtra(NOTIFICATION_MESSAGE_ID, data?.messageId)
// context.sendBroadcast(intent)
// return
// }
//
// // 用户未登录的情况下不生成消息中心通知,避免用户掉登录了还收到跳转至消息中心的通知
// if (data != null
// && data.link?.link == "system"
// && !UserManager.getInstance().isLoggedIn) {
// return
// }
//
// val clickIntent = Intent()
// val removeIntent = Intent()
//
// clickIntent.setClass(context, UmengMessageReceiver::class.java)
// clickIntent.putExtra(EntranceUtils.KEY_DATA, data?.link)
// clickIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
// clickIntent.putExtra(MESSAGE_ID, msg?.msgId)
// clickIntent.putExtra(PUSH_ID, data?.pushId)
// clickIntent.putExtra(NOTIFICATION_MESSAGE_ID, data?.messageId)
// clickIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_CLICK)
//
// removeIntent.setClass(context, UmengMessageReceiver::class.java)
// removeIntent.putExtra(EntranceUtils.KEY_TYPE, TYPE_REMOVE)
// removeIntent.putExtra(EntranceUtils.KEY_MESSAGE, message)
//
// val clickPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(),
// clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//
// val deletePendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt() + 1,
// removeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// val channel = NotificationChannel("Halo_Push", "Halo_Push", NotificationManager.IMPORTANCE_DEFAULT)
// notificationManager.createNotificationChannel(channel)
// }
//
// val notification = NotificationCompat.Builder(context, "Halo_Push")
// .setSmallIcon(R.drawable.ic_notification)
// .setTicker(pushData.body?.ticker)
// .setContentTitle(pushData.body?.title)
// .setContentText(pushData.body?.text?.fromHtml())
// .setContentIntent(clickPendingIntent)
// .setDeleteIntent(deletePendingIntent)
// .build()
// notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL
//
// notificationManager.notify(getNotificationTag(context), NOTIFICATION_ID, notification)
// } else {
// if (UserManager.getInstance().isLoggedIn &&
// HALO_MESSAGE_DIALOG == pushData.body?.custom &&
// MessageUnreadRepository.unreadLiveData.value != null) {
// // 回答了问题或者关注了问题的消息
// val msg = gson.fromJson(message, PushMessageEntity::class.java)
// val data = msg?.extra?.data
//
// val type = if (ANSWER == data?.type) {
// "回答了你的问题"
// } else {
// "回答了你关注的问题"
// }
//
// val userName = StringUtils.shrinkStringWithDot(data?.userEntity?.name, 8)
// val displayText = userName + type
//
// if (Notifier.isActivityValid(CurrentActivityHolder.getCurrentActivity()) &&
// Notifier.shouldShowNotifier(data?.answer?.id + displayText)) {
// Notifier.create(CurrentActivityHolder.getCurrentActivity())
// .setText(displayText)
// .setDuration(5000)
// .setIcon(data?.userEntity?.icon)
// .setOnClickListener(View.OnClickListener {
// val bundle = Bundle()
// bundle.putString(EntranceUtils.KEY_ANSWER_ID, data?.answer?.id)
// bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG)
// bundle.putString(EntranceUtils.KEY_TO, AnswerDetailActivity::class.java.name)
// EntranceUtils.jumpActivity(context, bundle)
//
// MtaHelper.onEvent("消息弹窗", type, "Does not contains any parameter.")
//
// // 标记已读
// val jsonObject = JSONObject()
// jsonObject.put("type", type)
// val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString())
//
// RetrofitManager.getInstance().api.postMessageRead(UserManager.getInstance().userId, data?.id, body)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(object : Response<ResponseBody>() {
// override fun onResponse(response: ResponseBody?) {
// super.onResponse(response)
// MessageUnreadRepository.loadMessageUnreadData()
// }
//
// override fun onFailure(e: HttpException?) {
// e?.printStackTrace()
// }
// })
// Notifier.hide()
// })
// .show(false)
// Notifier.tagNotifierAsShowed(data?.answer?.id + displayText)
// }
// } else if (HALO_MESSAGE_CENTER == pushData.body?.custom) {
// // 消息中心逻辑
// val msg = gson.fromJson(message, PushMessageUnreadEntity::class.java)
// val data = msg?.extra?.data
// data?.let { MessageUnreadRepository.loadMessageUnreadData() }
// }
// }
// }
//
// /**
// * 规则:最多三条消息,以旧换新
// *
// * @return NotificationTag
// */
// private fun getNotificationTag(context: Context): String {
// val sp = PreferenceManager.getDefaultSharedPreferences(context)
// val edit = sp.edit()
//
// val timeTagMap = HashMap<Long, String>()
// for (tag in notificationTags) {
// val time = sp.getLong(tag, 0)
// if (time == 0L) {
// edit.putLong(tag, System.currentTimeMillis()).apply()
// return tag
// } else {
// timeTagMap[time] = tag
// }
// }
//
// val minTime = Collections.min(timeTagMap.keys)
// val tag = timeTagMap[minTime]
// edit.putLong(tag, System.currentTimeMillis()).apply()
// return if (TextUtils.isEmpty(tag)) notificationTags[0] else tag!!
// }
//}

View File

@ -1,97 +0,0 @@
package com.gh.base
import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.gh.common.notifier.Notifier
import com.gh.common.util.DataUtils
import com.gh.common.util.FloatingBackViewManager
import com.gh.download.DownloadManager
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.energy.EnergyCenterActivity
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.forum.list.ForumListActivity
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.halo.assistant.HaloApp
import com.lightgame.utils.AppManager
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
AppManager.getInstance().addActivity(activity)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
CurrentActivityHolder.activitySet.add(activity)
// 判断是否需要显示或隐藏返回小浮窗
if (FloatingBackViewManager.getType().isNotEmpty()) {
if (activity is EnergyCenterActivity
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_TASK
) {
FloatingBackViewManager.disableBackView()
} else if (!shouldShowActivityBackView(activity)
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_ACTIVITY
) {
FloatingBackViewManager.disableBackView()
} else {
FloatingBackViewManager.showBackView(activity)
}
}
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onResume(activity)
// FIXME 这里应该只是部分Activity需要
try {
// 初始化gameMap
if (activity !is SplashScreenActivity) {
DownloadManager.getInstance().initGameMap()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun shouldShowActivityBackView(activity: Activity): Boolean {
return (activity is MainActivity
|| activity is ArticleDetailActivity
|| activity is ForumVideoDetailActivity
|| activity is ForumDetailActivity
|| activity is ForumListActivity
|| activity is NewQuestionDetailActivity)
}
override fun onActivityPaused(activity: Activity) {
CurrentActivityHolder.activitySet.remove(activity)
FloatingBackViewManager.dismissBackView()
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onPause(activity)
}
if (activity.isFinishing) {
AppManager.getInstance().finishActivity(activity)
}
}
override fun onActivityStopped(activity: Activity) {
Notifier.hide()
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
AppManager.getInstance().finishActivity(activity)
}
}

View File

@ -0,0 +1,275 @@
package com.gh.base;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.gh.common.constant.Config;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.ConcernActivity;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SearchActivity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.manager.PackageManager;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
/**
* Created by LGT on 2016/9/9.
* 工具栏 搜索控制
*/
public class HomeFragment extends Fragment implements View.OnClickListener, SearchBarHint {
protected View view;
private TextView downloadHint;
private TextView searchHint;
private AlphaAnimation mAlphaAnimation;
private ArrayList<String> hintList;
private int hintIndex;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
hintList = savedInstanceState.getStringArrayList("hint");
}
hintIndex = 0;
view = View.inflate(getActivity(), R.layout.fragment_home, null);
// SharedPreferences sp = getActivity().getSharedPreferences(
// Config.PREFERENCE, Context.MODE_PRIVATE);
LinearLayout home_actionbar = (LinearLayout) view.findViewById(R.id.home_actionbar);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(getActivity(), 55));
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// int top = DisplayUtils.getStatusBarHeight(getResources());
// home_actionbar.setPadding(0, top, 0, 0);
// lparams.height += top;
// }
home_actionbar.setLayoutParams(lparams);
initActionBar();
final ScaleAnimation scaleAnimation = new ScaleAnimation(0.4f, 1.0f, 0.4f, 1.0f
, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(500);
mAlphaAnimation = new AlphaAnimation(1f, 0.2f);
mAlphaAnimation.setDuration(300);
mAlphaAnimation.setStartOffset(5000);
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (mAlphaAnimation != null) {
searchHint.setAnimation(mAlphaAnimation);
mAlphaAnimation.start();
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mAlphaAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
// 切换数据
if (hintIndex > hintList.size() - 1) {
hintIndex = 0;
}
searchHint.setHint(hintList.get(hintIndex));
hintIndex++;
if (scaleAnimation != null) {
searchHint.setAnimation(scaleAnimation);
scaleAnimation.start();
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
if (hintList != null && hintList.size() > 0) {
if (hintList.size() > 1) {
searchHint.setAnimation(mAlphaAnimation);
} else {
String hint = hintList.get(0);
searchHint.setHint(hint);
}
}
EventBus.getDefault().register(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(view);
}
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (hintList != null && !hintList.isEmpty()) {
outState.putStringArrayList("hint", hintList);
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
private void initActionBar() {
view.findViewById(R.id.actionbar_rl_download).setOnClickListener(this);
view.findViewById(R.id.actionbar_iv_search).setOnClickListener(this);
view.findViewById(R.id.actionbar_notification).setOnClickListener(this);
view.findViewById(R.id.actionbar_search_rl).setOnClickListener(this);
if (Config.isShow(getActivity())) {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
}
downloadHint = (TextView) view.findViewById(R.id.action_tip);
int updateSize = PackageManager.getUpdateListSize();
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
if (downloadSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(downloadSize));
} else if (updateSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(updateSize));
} else {
downloadHint.setVisibility(View.GONE);
}
searchHint = (TextView) view.findViewById(R.id.actionbar_search_input);
searchHint.setOnClickListener(this);
}
@Override
public void onClick(View v) {
final int id = v.getId();
if (id == R.id.actionbar_rl_download) {
DataUtils.onEvent(getActivity(), "主页", "下载图标");
DataCollectionUtils.uploadClick(getActivity(), "下载图标", "主页");
DownloadManagerActivity.startDownloadManagerActivity(getContext(), null, "(工具栏)");
} else if (id == R.id.actionbar_iv_search) {
DataUtils.onEvent(getActivity(), "主页", "搜索图标");
DataCollectionUtils.uploadClick(getActivity(), "搜索图标", "主页");
Intent intent = new Intent(getActivity(), SearchActivity.class);
intent.putExtra("clicked", true);
intent.putExtra("hint", searchHint.getHint().toString());
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
startActivity(intent);
} else if (id == R.id.actionbar_search_input || id == R.id.actionbar_search_rl) {
DataUtils.onEvent(getActivity(), "主页", "搜索框");
DataCollectionUtils.uploadClick(getActivity(), "搜索框", "主页");
Intent intent = new Intent(getActivity(), SearchActivity.class);
intent.putExtra("clicked", false);
intent.putExtra("hint", searchHint.getHint().toString());
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
startActivity(intent);
} else if (id == R.id.actionbar_notification) {
DataUtils.onEvent(getActivity(), "主页", "关注图标");
DataCollectionUtils.uploadClick(getActivity(), "关注图标", "主页");
Intent intent = new Intent(getActivity(), ConcernActivity.class);
intent.putExtra(EntranceUtils.KEY_ENTRANCE, "(工具栏)");
startActivity(intent);
}
}
// 打开下载按钮事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBReuse reuse) {
if ("Refresh".equals(reuse.getType())) {
if (Config.isShow(getActivity())) {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBDownloadStatus status) {
int updateSize = PackageManager.getUpdateListSize();
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
if (downloadSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(downloadSize));
} else if (updateSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(updateSize));
} else {
downloadHint.setVisibility(View.GONE);
}
}
@Override
public void setHint(ArrayList<String> hint) {
if (hint != null && hint.size() > 0) {
hintList = hint;
if (hint.size() == 1 && searchHint != null) {
searchHint.setHint(hintList.get(0));
} else if (mAlphaAnimation != null && searchHint != null) {
searchHint.setAnimation(mAlphaAnimation);
}
}
}
}

View File

@ -0,0 +1,9 @@
package com.gh.base;
/**
* Forward activity onBackPressed() events to fragment
* (If nested fragments need this, just forward again)
*/
public interface OnBackPressedListener {
public boolean onHandleBackPressed();
}

View File

@ -1,32 +0,0 @@
package com.gh.base;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by khy on 25/04/18.
*/
public abstract class OnDoubleTapListener implements View.OnTouchListener {
private GestureDetector mGestureDetector;
protected OnDoubleTapListener(Context context) {
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
OnDoubleTapListener.this.onDoubleTap();
return true;
}
});
}
@Override
public boolean onTouch(View v, MotionEvent event) {
mGestureDetector.onTouchEvent(event);
return false;
}
public abstract void onDoubleTap();
}

View File

@ -1,19 +0,0 @@
package com.gh.base;
import android.view.View;
/**
* Created by khy on 26/09/17.
*/
public interface OnListClickListener {
/**
*
* @param view
* @param position list position
* @param data list data (直接强转 如果列表传入不同数据类型 请做好判断)
* @param <T>
*/
<T> void onListClick(View view, int position, T data);
}

View File

@ -1,20 +0,0 @@
package com.gh.base;
/**
* Created by Administrator on 2016/9/8.
*
* 逐步移除
*/
@Deprecated
public interface OnRequestCallBackListener<T> {
void loadDone();
void loadDone(T obj);
void loadError();
void loadEmpty();
}

View File

@ -1,7 +0,0 @@
package com.gh.base;
import android.view.View;
public interface OnViewClickListener<T> {
void onClick(View v, T data);
}

View File

@ -0,0 +1,48 @@
package com.gh.base;
import java.io.Serializable;
public enum SuggestionType implements Serializable {
FEEDBACK("普通反馈", 1),
SUGGESTION("功能建议", 2),
CRASH("发生闪退", 3),
GAME("游戏问题", 4),
COLLECT("游戏收录", 5),
POST("文章投稿", 6);
private String mName;
private int mIndex;
private SuggestionType(String name, int index) {
mName = name;
mIndex = index;
}
public static String getName(int index) {
for (SuggestionType c : SuggestionType.values()) {
if (c.mIndex == index) {
return c.mName;
}
}
return "";
}
public static int getIndex(String name) {
for (SuggestionType c : SuggestionType.values()) {
if (c.mName == name) {
return c.mIndex;
}
}
return -1;
}
public int getIndex() {
return mIndex;
}
public String getName() {
return mName;
}
}

View File

@ -1,336 +0,0 @@
package com.gh.base;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.ActionMenuView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.common.constant.Constants;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.SPUtils;
import com.gh.common.view.GameIconView;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.GameUpdateEntity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.normal.ToolbarController;
import com.gh.gamecenter.packagehelper.PackageViewModel;
import com.lightgame.OnTitleClickListener;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* 需要用到工具栏的页面使用
* <p>
* 特殊页面请参考{@link BaseActivity}
*/
public abstract class ToolBarActivity extends BaseActivity implements ToolbarController, ActionMenuView.OnMenuItemClickListener {
@Nullable
private PackageViewModel mPackageViewModel;
protected View mToolbarContainer;
protected Toolbar mToolbar;
protected TextView mTitleTv;
protected LinearLayout mTitleContainer;
protected LinearLayout mIconTitleContainer;
protected FrameLayout mBackContainer;
protected ActionMenuView mActionMenuView;
protected View mBackBtn;
protected GameIconView mGameIconView;
protected SimpleDraweeView mUserAvatarIv;
protected TextView mIconTitle;
@Nullable
private TextView mDownloadCountHint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStatusBarDarkMode(true, this);
initToolbar();
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu()) {
mPackageViewModel = ViewModelProviders.of(this, new PackageViewModel.Factory()).get(PackageViewModel.class);
mPackageViewModel.getFilterSameUpdateLiveData().observe(this, this::updateDownloadCountHint);
}
}
// 小米沉浸式黑色字体
@SuppressLint("PrivateApi")
public void setStatusBarDarkMode(boolean darkmode, Activity activity) {
Class<? extends Window> clazz = activity.getWindow().getClass();
try {
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
} catch (Exception ignore) {
}
}
private void initToolbar() {
mToolbarContainer = findViewById(R.id.normal_toolbar_container);
mToolbar = findViewById(R.id.normal_toolbar);
mTitleTv = findViewById(R.id.normal_title);
mActionMenuView = findViewById(R.id.actionMenuView);
mTitleContainer = findViewById(R.id.titleContainer);
mIconTitleContainer = findViewById(R.id.iconTitleContainer);
mBackContainer = findViewById(R.id.backContainer);
mBackBtn = findViewById(R.id.backBtn);
mGameIconView = findViewById(R.id.gameIv);
mUserAvatarIv = findViewById(R.id.userAvatar);
mIconTitle = findViewById(R.id.iconTitle);
if (mToolbar != null) {
// setSupportActionBar(mToolbar); // 替换actionBar后 toolBar无法控制
// mToolbar.setNavigationIcon(provideNavigationIcon());
// mToolbar.setNavigationOnClickListener(provideNavigationItemClickListener());
if (mBackBtn != null) mBackBtn.setOnClickListener(provideNavigationItemClickListener());
if (mBackContainer != null) mBackContainer.setOnClickListener(provideNavigationItemClickListener());
if (mTitleTv != null) {
mTitleTv.setOnClickListener(view -> {
final List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if (fragment instanceof OnTitleClickListener) {
((OnTitleClickListener) fragment).onTitleClick();
}
}
});
}
}
}
@DrawableRes
public int provideNavigationIcon() {
return R.drawable.ic_bar_back; // default navigation icon
}
@Override
public void setNavigationTitle(String title) {
if (mTitleTv != null) mTitleTv.setText(title);
if (mIconTitle != null) mIconTitle.setText(title);
}
@Override
public void setNavigationTitle(@StringRes int res) {
setNavigationTitle(getString(res));
}
/**
* 重写此方法以将标题靠左显示
*/
public boolean showToolbarAtLeft() {
return false;
}
@Override
public void setToolbarMenu(int res) {
if (mActionMenuView == null) return;
// 青少年模式下要隐藏下载按钮
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && res == R.menu.menu_download) return;
// mToolbar.inflateMenu(res);
// mToolbar.setOnMenuItemClickListener(this);
getMenuInflater().inflate(res, mActionMenuView.getMenu());
mActionMenuView.setOnMenuItemClickListener(this);
if (showDownloadMenu()) {
createDownloadMenu(res);
}
Menu menu = mActionMenuView.getMenu();
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
// menu设置actionLayout后无法捕捉点击事件以icon为tag如果icon is null 手动设置menuItem点击事件
if (menuItem != null && menuItem.getIcon() == null) {
if (menuItem.getActionView() != null) {
menuItem.getActionView().setOnClickListener((v) -> this.onMenuItemClick(menuItem));
}
}
}
if (showToolbarAtLeft() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mTitleTv != null) {
mTitleTv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
}
setTitleCenter();
}
@Override
protected void onResume() {
super.onResume();
setTitleCenter();
}
// 设置标题居中
public void setTitleCenter() {
if (mActionMenuView != null && mTitleContainer != null && mBackContainer != null && !showToolbarAtLeft()) {
mActionMenuView.post(() -> {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mTitleContainer.getLayoutParams();
params.setMargins(mActionMenuView.getWidth() - mBackContainer.getWidth(), 0, 0, 0);
mTitleContainer.setLayoutParams(params);
});
}
}
public void setGameIconToolbar(String icon, String iconSubscript) {
mTitleContainer.setVisibility(View.GONE);
mGameIconView.displayGameIcon(icon, iconSubscript);
mGameIconView.setVisibility(View.VISIBLE);
mIconTitleContainer.setVisibility(View.VISIBLE);
}
public void setUserAvatarIconToolbar(String icon) {
mTitleContainer.setVisibility(View.GONE);
ImageUtils.display(mUserAvatarIv, icon);
mUserAvatarIv.setVisibility(View.VISIBLE);
mIconTitleContainer.setVisibility(View.VISIBLE);
}
private void createDownloadMenu(int res) {
if (res != R.menu.menu_download) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_download, mActionMenuView.getMenu());
}
if (mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
}
View downloadMenuView = mActionMenuView.getMenu().findItem(R.id.menu_download).getActionView();
mDownloadCountHint = downloadMenuView.findViewById(R.id.menu_download_count_hint);
}
private void updateDownloadCountHint(List<GameUpdateEntity> updateList) {
if (mDownloadCountHint == null) return;
String count = DownloadManager.getInstance().getDownloadOrUpdateCount(updateList);
if (count != null) {
mDownloadCountHint.setVisibility(View.VISIBLE);
mDownloadCountHint.setText(count);
ViewGroup.LayoutParams params = mDownloadCountHint.getLayoutParams();
if (TextUtils.isEmpty(count)) {
params.width = DisplayUtils.dip2px(6);
params.height = DisplayUtils.dip2px(6);
} else {
params.width = DisplayUtils.dip2px(12);
params.height = DisplayUtils.dip2px(12);
}
mDownloadCountHint.setLayoutParams(params);
} else {
mDownloadCountHint.setVisibility(View.GONE);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBDownloadStatus status) {
if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu() && mPackageViewModel != null) {
updateDownloadCountHint(mPackageViewModel.getFilterSameUpdateLiveData().getValue());
}
}
@Override
public MenuItem getMenuItem(int res) {
if (mToolbar == null) return null; //后续页面做好判断
return mActionMenuView.getMenu().findItem(res);
}
public void clearMenu() {
if (mToolbar != null) {
mActionMenuView.getMenu().clear();
setTitleCenter();
}
}
public Menu getMenu() {
return mActionMenuView.getMenu();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.menu_download) {
// MtaHelper.onEvent("下载管理", "下载管理入口", getActivityNameInChinese());
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(this, mEntrance);
startActivity(intent);
}
return false;
}
protected View.OnClickListener provideNavigationItemClickListener() {
return view -> onBackPressed();
}
protected boolean showDownloadMenu() {
return false;
}
@Override
public void hideToolbar(boolean isHide) {
if (mToolbarContainer != null) {
mToolbarContainer.setVisibility(isHide ? View.GONE : View.VISIBLE);
}
}
@Override
protected void onNightModeChange() {
super.onNightModeChange();
if (mToolbar != null) {
mToolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.background_white));
}
if (mBackBtn != null) {
if (mBackBtn instanceof ImageView) {
((ImageView) mBackBtn).setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_bar_back));
} else if (mBackBtn instanceof TextView) {
((TextView) mBackBtn).setTextColor(ContextCompat.getColor(this, R.color.text_subtitle));
}
}
if (mTitleTv != null) {
mTitleTv.setTextColor(ContextCompat.getColor(this, R.color.text_black));
}
if (showDownloadMenu() && getMenuItem(R.id.menu_download) != null) {
((ImageView) getMenuItem(R.id.menu_download).getActionView().findViewById(R.id.menu_download_iv)).setImageDrawable(ContextCompat.getDrawable(this, R.drawable.toolbar_download));
}
}
}

View File

@ -0,0 +1,17 @@
package com.gh.base;
import android.support.annotation.StringRes;
import android.support.v7.widget.Toolbar;
/**
* Created by csheng on 15-10-12.
*/
public interface ToolbarController {
void setNavigationTitle(@StringRes int res);
void setNavigationTitle(CharSequence res);
Toolbar getToolBar();
}

View File

@ -1,49 +0,0 @@
package com.gh.base.adapter;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.List;
/**
* Created by LGT on 2016/11/17.
* ViewPager FragmentAdapter
*/
public class FragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragmentList;
private List<String> mTitleList;
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
this.mFragmentList = fragmentList;
}
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
super(fm);
this.mFragmentList = fragmentList;
this.mTitleList = titleList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
if (mTitleList != null && mTitleList.size() > position) {
return mTitleList.get(position);
}
return super.getPageTitle(position);
}
}

View File

@ -1,31 +0,0 @@
package com.gh.base.adapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import java.util.List;
/**
* Created by khy on 7/12/28.
*/
public class FragmentStateAdapter extends FragmentStatePagerAdapter {
private List<Fragment> mFragmentList;
public FragmentStateAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
this.mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}

View File

@ -1,121 +0,0 @@
package com.gh.base.fragment;
import android.app.Dialog;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.KeyEvent;
import com.gh.common.util.ClickUtils;
import com.gh.common.util.NightModeUtils;
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;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
/**
* @author CsHeng
* @Date 17/05/2017
* @Time 4:30 PM
*/
public class BaseDialogFragment extends DialogFragment {
protected boolean mNightMode;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(getActivity(), getThemeRes());
dialog.setCanceledOnTouchOutside(false);
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK && !ClickUtils.isFastDoubleClick()) {
return onBack();
}
return false;
});
dialog.setCancelable(false);
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));
}
public void toast(String msg) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))
Utils.toast(getContext(), msg);
}
public void toastLong(@StringRes int msg) {
toastLong(getString(msg));
}
public void toastLong(String msg) {
RuntimeUtils.getInstance().toastLong(getContext(), msg);
}
public boolean onBack() {
return false;
}
@Override
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
Fragment fragment = manager.findFragmentByTag(tag);
if (fragment != null) {
FragmentTransaction transaction = manager.beginTransaction();
transaction.show(fragment);
transaction.commit();
} else {
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();
}
}
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onNightModeChange();
}
protected void onNightModeChange() {
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
}
}

View File

@ -1,65 +0,0 @@
package com.gh.base.fragment;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.gh.gamecenter.R;
/**
* Wrap another fragment with dialog fragment.
*/
public class BaseDialogWrapperFragment extends BaseDialogFragment {
private Fragment mFragmentToWrap;
public static BaseDialogWrapperFragment getInstance(Fragment fragmentToWrap) {
BaseDialogWrapperFragment fragment = new BaseDialogWrapperFragment();
fragment.mFragmentToWrap = fragmentToWrap;
return fragment;
}
public static BaseDialogWrapperFragment getInstance(Fragment fragmentToWrap, boolean isCancelable) {
BaseDialogWrapperFragment fragment = new BaseDialogWrapperFragment();
fragment.mFragmentToWrap = fragmentToWrap;
fragment.setCancelable(isCancelable);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_dialog_wrapper, null);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mFragmentToWrap != null) {
getChildFragmentManager().beginTransaction().replace(R.id.fragment_placeholder, mFragmentToWrap).commitNowAllowingStateLoss();
}
}
@Override
public void onStart() {
super.onStart();
getDialog().getWindow().setGravity(Gravity.BOTTOM);
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCanceledOnTouchOutside(true);
return dialog;
}
}

View File

@ -1,365 +0,0 @@
package com.gh.base.fragment;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.base.OnListClickListener;
import com.gh.base.OnRequestCallBackListener;
import com.gh.common.constant.Constants;
import com.gh.common.syncpage.ISyncAdapterHandler;
import com.gh.common.syncpage.SyncDataEntity;
import com.gh.common.syncpage.SyncPageRepository;
import com.gh.common.util.NightModeUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.R;
import com.gh.gamecenter.eventbus.EBMiPush;
import com.lightgame.OnTitleClickListener;
import com.lightgame.utils.RuntimeUtils;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import kotlin.Pair;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
/**
* Created by LGT on 2016/9/4.
* Fragment 基类
*/
public abstract class BaseFragment<T> extends Fragment implements OnRequestCallBackListener<T>,
View.OnClickListener, OnListClickListener, OnTitleClickListener {
public static final int RESULT_REFRESH = 9528;
protected View mCachedView;
protected boolean isEverPause;
protected boolean mNightMode;
@NonNull
protected String mEntrance = "";
public long startPageTime = 0;
protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this);
protected static class BaseHandler extends Handler {
private final WeakReference<BaseFragment> mFragmentWeakReference;
BaseHandler(BaseFragment fragment) {
mFragmentWeakReference = new WeakReference<>(fragment);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
BaseFragment fragment = mFragmentWeakReference.get();
if (fragment != null) fragment.handleMessage(msg);
}
}
protected void handleMessage(Message msg) {
}
@LayoutRes
protected abstract int getLayoutId();
/**
* 提供 Inflated 的 view ,可用于 data binding.
*/
protected View getInflatedLayout() {
return null;
}
/**
* 责任链谁处理了就返回true否则返回super.handleOnClick(View view)
*
* @return
*/
protected boolean handleOnClick(View view) {
return true;
}
@Override
public void onClick(View v) {
handleOnClick(v);
}
protected void initView(View view) {
View mBackBtn = view.findViewById(R.id.backBtn);
View mBackContainer = view.findViewById(R.id.backContainer);
if (mBackBtn != null && mBackContainer != null) {
mBackBtn.setOnClickListener(v -> requireActivity().onBackPressed());
mBackContainer.setOnClickListener(v -> requireActivity().onBackPressed());
}
}
protected void postRunnable(Runnable runnable) {
RuntimeUtils.getInstance().runOnUiThread(runnable);
}
// 定时任务全部改用这个方法, 在onDestroy做统一取消定时
protected void postDelayedRunnable(Runnable runnable, long delayMillis) {
RuntimeUtils.getInstance().runOnUiThread(runnable, delayMillis);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getActivity().getIntent();
mEntrance = intent.getStringExtra(KEY_ENTRANCE);
if (TextUtils.isEmpty(mEntrance) && getArguments() != null) {
mEntrance = getArguments().getString(KEY_ENTRANCE);
}
if (TextUtils.isEmpty(mEntrance)) {
mEntrance = Constants.ENTRANCE_UNKNOWN;
}
if (BuildConfig.DEBUG) {
Utils.log("FRAGMENT_ENTRANCE -> " + mEntrance);
}
isEverPause = false;
EventBus.getDefault().register(this);
// For data binding.
mCachedView = getInflatedLayout();
if (mCachedView == null) {
mCachedView = View.inflate(getContext(), getLayoutId(), null);
}
initView(mCachedView);
if (addSyncPageObserver()) {
initSyncPageObserver();
}
if (BuildConfig.IS_NIGHT_MODE_ON) {
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
} else {
mNightMode = false;
}
}
private void initSyncPageObserver() {
SyncPageRepository.INSTANCE.getSyncPageLiveData().observe(this, syncDataEntities -> {
try {
Utils.log("SyncPageRepository initSyncPageObserver->" + syncDataEntities);
List<SyncDataEntity> readyRemoveList = new ArrayList<>();
if (syncDataEntities == null || syncDataEntities.isEmpty()) return;
RecyclerView.Adapter adapter = provideSyncAdapter();
if (!(adapter instanceof ISyncAdapterHandler)) return;
for (int i = 0; i < adapter.getItemCount(); i++) {
Pair<String, Object> syncKey = ((ISyncAdapterHandler) adapter).getSyncData(i);
if (syncKey == null) continue;
for (SyncDataEntity syncDataEntity : syncDataEntities) {
if (syncDataEntity.getSyncId().equals(syncKey.getFirst())) {
boolean isSuccess = SyncPageRepository.INSTANCE.handleSyncData(syncKey.getSecond(), syncDataEntity);
if (isSuccess) {
if (BuildConfig.DEBUG) {
Utils.log("SyncPageRepository notify position->" + i);
}
adapter.notifyItemChanged(i);
if (syncDataEntity.getRemove()) {
readyRemoveList.add(syncDataEntity);
}
}
}
}
}
mBaseHandler.postDelayed(() -> SyncPageRepository.removeSyncData(readyRemoveList), 2000);
} catch (Exception e) {
if (BuildConfig.DEBUG) {
throw e;
} else {
e.printStackTrace();
}
}
});
}
// 必须的有subscribe才能register
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onDummyEvent(EBMiPush push) {
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(mCachedView);
// TODO 页面重建 (framgent 的重新获取) 有大问题,这里只是修修补补
if (mCachedView != null && mCachedView.getParent() instanceof ViewGroup) {
((ViewGroup) mCachedView.getParent()).removeView(mCachedView);
}
}
return mCachedView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
mCachedView = null;
}
@Override
public void onResume() {
super.onResume();
isEverPause = false;
startPageTime = System.currentTimeMillis();
if (BuildConfig.IS_NIGHT_MODE_ON
&& !NightModeUtils.INSTANCE.getSystemMode()
&& mNightMode != NightModeUtils.INSTANCE.isNightMode(requireContext())) {
onNightModeChange();
}
}
@Override
public void onPause() {
super.onPause();
isEverPause = true;
}
@Override
public void onDestroy() {
super.onDestroy();
mBaseHandler.removeCallbacksAndMessages(null);
RuntimeUtils.getInstance().removeRunnable();
EventBus.getDefault().unregister(this);
}
public void toast(@StringRes int res) {
toast(getString(res));
}
public void toast(String msg) {
Utils.toast(getContext(), msg);
}
public void toastLong(@StringRes int msg) {
toastLong(getString(msg));
}
public void toastLong(String msg) {
RuntimeUtils.getInstance().toastLong(getContext(), msg);
}
public boolean isEverPause() {
return isEverPause;
}
@Override
public void loadDone() {
}
@Override
public void loadDone(T obj) {
}
@Override
public void loadError() {
}
@Override
public void loadEmpty() {
}
@Override
public <LIST> void onListClick(View view, int position, LIST data) {
}
protected <K> Observable<K> asyncCall(Observable<K> observable) {
return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
// 将所有的Fragment都置为隐藏状态。
protected void hideFragments(FragmentTransaction transaction) {
List<Fragment> list = getChildFragmentManager().getFragments();
for (Fragment fragment : list) {
transaction.hide(fragment);
}
}
@Override
public void onTitleClick() {
List<Fragment> list = getChildFragmentManager().getFragments();
for (Fragment fragment : list) {
if (fragment instanceof OnTitleClickListener) {
((OnTitleClickListener) fragment).onTitleClick();
}
}
}
// 为 fragment 附加 bundle (setArgument())
public BaseFragment with(Bundle bundle) {
if (!isStateSaved()) {
this.setArguments(bundle);
}
return this;
}
public void onParentActivityFinish() {
}
@Nullable
protected RecyclerView.Adapter provideSyncAdapter() {
return null;
}
protected boolean addSyncPageObserver() {
return false;
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (BuildConfig.IS_NIGHT_MODE_ON) {
onNightModeChange();
}
}
protected void onNightModeChange() {
mNightMode = NightModeUtils.INSTANCE.isNightMode(requireContext());
}
}

View File

@ -1,222 +0,0 @@
package com.gh.base.fragment;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckedTextView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.gh.base.adapter.FragmentAdapter;
import com.gh.common.view.TabIndicatorView;
import com.gh.gamecenter.R;
import com.gh.gamecenter.normal.NormalFragment;
import com.google.android.material.tabs.TabLayout;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.Utils;
import com.lightgame.view.NoScrollableViewPager;
import java.util.ArrayList;
import java.util.List;
/**
* Created by khy on 15/03/18.
*/
public abstract class BaseFragment_TabLayout extends NormalFragment implements ViewPager.OnPageChangeListener {
public static final String PAGE_INDEX = "PAGE_INDEX";
protected TabLayout mTabLayout;
protected NoScrollableViewPager mViewPager;
protected TabIndicatorView mTabIndicatorView;
@Nullable
protected View mDividerLineView;
protected List<Fragment> mFragmentsList;
protected List<String> mTabTitleList;
protected int mCheckedIndex = 0;
protected abstract void initFragmentList(List<Fragment> fragments);
protected abstract void initTabTitleList(List<String> tabTitleList);
protected int provideIndicatorWidth() {
return 20;
}
protected View provideTabView(int position, String tabTitle) {
return null;
}
@Override
protected int getLayoutId() {
return R.layout.fragment_tablayout_viewpager;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
List<Fragment> fragments = getChildFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
private ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
int childCount = mTabTitleList.size();
for (int index = 0; index < childCount; index++) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(tag + index);
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mTabLayout = view.findViewById(R.id.fragment_tab_layout);
mViewPager = view.findViewById(R.id.fragment_view_pager);
mTabIndicatorView = view.findViewById(R.id.fragment_tab_indicator);
mDividerLineView = view.findViewById(R.id.dividerLine);
if (getArguments() != null) mCheckedIndex = getArguments().getInt(PAGE_INDEX, 0);
mTabTitleList = new ArrayList<>();
initTabTitleList(mTabTitleList);
mFragmentsList = new ArrayList<>(restoreFragments());
if (mFragmentsList.isEmpty() || mFragmentsList.size() != mTabTitleList.size()) {
mFragmentsList.clear();
initFragmentList(mFragmentsList);
}
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.addOnPageChangeListener(this);
mViewPager.setAdapter(new FragmentAdapter(getChildFragmentManager(), mFragmentsList, mTabTitleList));
mViewPager.setCurrentItem(mCheckedIndex);
mTabLayout.setupWithViewPager(mViewPager);
mTabIndicatorView.setupWithTabLayout(mTabLayout);
mTabIndicatorView.setupWithViewPager(mViewPager);
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth());
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab == null) continue;
String tabTitle = tab.getText() != null ? tab.getText().toString() : "";
View tabView = provideTabView(i, tabTitle);
if (tabView == null) tabView = createDefaultTabCustomView(requireContext(), tabTitle);
tab.setCustomView(tabView);
}
initTabStyle(mTabLayout, mCheckedIndex);
}
public static void initTabStyle(TabLayout tabLayout, int currentItem) {
// 默认选择addOnTabSelectedListener不会回调
int tabCount = tabLayout.getTabCount();
if (tabCount > 0) {
TabLayout.Tab tab = tabLayout.getTabAt(currentItem);
if (tab != null) updateTabStyle(tab, true);
}
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
updateTabStyle(tab, true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
updateTabStyle(tab, false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
updateTabStyle(tab, true);
}
});
}
public static void updateTabStyle(TabLayout.Tab tab, boolean isChecked) {
View tabView = tab.getCustomView();
if (tabView == null) {
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
return;
}
TextView tabTitle;
if (tabView instanceof TextView) {
tabTitle = (TextView) tabView;
} else {
tabTitle = tabView.findViewById(R.id.tab_title);
}
if (tabTitle == null) {
Utils.log("TabLayout->Tab样式不是通用样式,请检查");
return;
}
tabTitle.setTypeface(isChecked ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tabTitle.setTextColor(ContextCompat.getColorStateList(tabTitle.getContext(), R.color.text_tabbar_style));
}
// 如果不设置View的话无法动态设置字体样式
@NonNull
public static View createDefaultTabCustomView(Context context, String title) {
View view = LayoutInflater.from(context).inflate(R.layout.tab_item, null);
View tabTitle = view.findViewById(R.id.tab_title);
if (tabTitle instanceof CheckedTextView) {
((CheckedTextView) tabTitle).setText(title);
}
return view;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected void onNightModeChange() {
super.onNightModeChange();
View container = requireView().findViewById(R.id.fragment_tab_container);
if (container != null) {
container.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background_white));
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
if (tab != null) {
updateTabStyle(tab, tab.isSelected());
}
}
}
if (mDividerLineView != null) {
mDividerLineView.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.divider));
}
}
}

View File

@ -1,145 +0,0 @@
/**
* project: OPlay
* <p/>
* <p/>
* ========================================================================
* amend date amend user amend reason
* 2013-3-6 CsHeng
*/
package com.gh.base.fragment;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.view.View;
import com.gh.gamecenter.normal.NormalFragment;
import com.lightgame.adapter.BaseFragmentPagerAdapter;
import com.lightgame.config.CommonDebug;
import com.lightgame.view.DoubleTapTextView;
import java.util.ArrayList;
import java.util.List;
/**
* ViewPager 配合RadioGroup实现双切换<br/>
* 记得自己控制onCreateView返回和radioGroup里面radiobutton个数,Viewpager的布局<br/>
*
* @author CsHeng
* @date 2013-3-6
*/
public abstract class BaseFragment_ViewPager extends NormalFragment implements DoubleTapTextView.OnDoubleTapListener {
public static final String ARGS_INDEX = "index";
protected int mCheckedIndex = 0;
protected PagerAdapter mAdapter;
protected List<Fragment> mFragmentsList;
protected ViewPager mViewPager;
@LayoutRes
protected abstract int getLayoutId();
@IdRes
protected abstract int getViewPagerId();
protected abstract void initFragmentList(List<Fragment> fragments);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = view.findViewById(getViewPagerId());
mFragmentsList = restoreFragments();
if (mFragmentsList.size() == 0) {
initFragmentList(mFragmentsList);
}
mAdapter = BaseFragmentPagerAdapter.newInstance(getChildFragmentManager(), mFragmentsList);
final Bundle args = getArguments();
if (args != null) {
mCheckedIndex = args.getInt(ARGS_INDEX);
}
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.setAdapter(mAdapter);
if (mCheckedIndex < mFragmentsList.size()) {
mViewPager.setCurrentItem(mCheckedIndex, false);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (getArguments() != null) {
getArguments().putInt(ARGS_INDEX, mCheckedIndex);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mViewPager != null) {
mViewPager.setAdapter(null);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFragmentsList != null) {
mFragmentsList.clear();
}
}
@Override
public boolean onDoubleTap() {
final Fragment fragment = mFragmentsList.get(mViewPager.getCurrentItem());
return fragment instanceof DoubleTapTextView.OnDoubleTapListener && ((DoubleTapTextView.OnDoubleTapListener)
fragment).onDoubleTap();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (CommonDebug.IS_DEBUG) {
CommonDebug.logMethodWithParams(this, requestCode, resultCode, data);
}
List<Fragment> fragments = getChildFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
public ArrayList<Fragment> restoreFragments() {
String tag = "android:switcher:" + mViewPager.getId() + ":";
ArrayList<Fragment> fragments = new ArrayList<>();
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(tag + index);
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments;
}
public abstract int getChildCount();
public int getCurrentItem() {
return mViewPager != null ? mViewPager.getCurrentItem() : 0;
}
}

View File

@ -1,145 +0,0 @@
/**
* project: OPlay
* <p/>
* <p/>
* ========================================================================
* amend date amend user amend reason
* 2013-3-6 CsHeng
*/
package com.gh.base.fragment;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Checkable;
import com.gh.gamecenter.fragment.MainWrapperFragment;
import java.util.List;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
/**
* ViewPager 配合ViewGroup Checkable实现双切换<br/>
* 记得自己控制onCreateView返回和ViewGroup里面Checkable个数,ViewPager的布局<br/>
*
* @author CsHeng
* @date 2013-3-6
* @update 2014-09-29
*/
public abstract class BaseFragment_ViewPager_Checkable extends BaseFragment_ViewPager implements
ViewPager.OnPageChangeListener {
protected ViewGroup mCheckableGroup;
private int mLastPosition = MainWrapperFragment.INDEX_HOME;
@IdRes
protected abstract int getCheckableGroupId();
protected boolean getSmoothScroll() {
return false;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mCheckableGroup = (ViewGroup) view.findViewById(getCheckableGroupId());
for (int i = 0, size = mCheckableGroup.getChildCount(); i < size; i++) {
mCheckableGroup.getChildAt(i).setOnClickListener(this);
}
mViewPager.addOnPageChangeListener(this);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mViewPager.removeOnPageChangeListener(this);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
checkIndex(mCheckedIndex);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int index) {
onPageChanged(index);
try {
// 补充Viewpager Fragment的生命周期 先调用旧选中 fragment 的 onPause 再当前的 onResume
if (mFragmentsList.size() > mLastPosition) {
Fragment fragment = mFragmentsList.get(mLastPosition);
fragment.onPause();
FragmentManager childFragmentManager = fragment.getChildFragmentManager();
List<Fragment> fragments = childFragmentManager.getFragments();
for (Fragment childFragment : fragments) {
childFragment.onPause();
}
}
if (mFragmentsList.size() > index) {
Fragment fragment = mFragmentsList.get(index);
fragment.onResume();
FragmentManager childFragmentManager = fragment.getChildFragmentManager();
List<Fragment> fragments = childFragmentManager.getFragments();
for (Fragment childFragment : fragments) {
childFragment.onResume();
}
}
mLastPosition = index;
} catch (Exception ignore) {
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected boolean handleOnClick(View view) {
final int toCheck = mCheckableGroup.indexOfChild(view);
if (toCheck != -1) {
mViewPager.setCurrentItem(toCheck, getSmoothScroll());
return true;
}
return super.handleOnClick(view);
}
protected void checkIndex(int index) {
final int childCount = mCheckableGroup.getChildCount();
if (index < childCount && mCheckedIndex < childCount) {
final View toChecked = mCheckableGroup.getChildAt(index);
if (toChecked instanceof Checkable) {
((Checkable) toChecked).setChecked(true);
}
if (index != mCheckedIndex) {
final View checkedChild = mCheckableGroup.getChildAt(mCheckedIndex);
if (checkedChild instanceof Checkable) {
((Checkable) checkedChild).setChecked(false);
}
}
mCheckedIndex = index;
}
}
protected void onPageChanged(int index) {
checkIndex(index);
}
}

View File

@ -1,173 +0,0 @@
package com.gh.base.fragment
import android.os.Bundle
import com.gh.gamecenter.normal.NormalFragment
/**
* 懒加载(支持多层嵌套)
*/
abstract class BaseLazyFragment : NormalFragment() {
private var mIsFirstVisible = true
private var isViewCreated = false
protected var isSupportVisible = false
/**
* 用于分发可见时间的时候获取 父fragment 是否隐藏
*
* @return true fragment 不可见, false 父 fragment 可见
*/
private val isParentInvisible: Boolean
get() {
val parentFragment = parentFragment
return if (parentFragment is BaseLazyFragment) {
val fragment = parentFragment as BaseLazyFragment?
!fragment!!.isSupportVisible
} else {
false
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
// 对于默认 tab 和 间隔 checked tab 需要等到 isViewCreated = true 后才可以通过此通知用户可见
// 这种情况下第一次可见不是在这里通知 因为 isViewCreated = false 成立,等从别的界面回到这里后会使用 onFragmentResume 通知可见
// 对于非默认 tab mIsFirstVisible = true 会一直保持到选择则这个 tab 的时候,因为在 onActivityCreated 会返回 false
if (isViewCreated) {
if (isVisibleToUser && !isSupportVisible) {
dispatchUserVisibleHint(true)
} else if (!isVisibleToUser && isSupportVisible) {
dispatchUserVisibleHint(false)
}
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
isViewCreated = true
// !isHidden() 默认为 true 在调用 hide show 的时候可以使用
if (!isHidden && userVisibleHint) {
dispatchUserVisibleHint(true)
}
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
dispatchUserVisibleHint(false)
} else {
dispatchUserVisibleHint(true)
}
}
override fun onResume() {
super.onResume()
if (!mIsFirstVisible) {
if (!isHidden && !isSupportVisible && userVisibleHint) {
dispatchUserVisibleHint(true)
}
}
}
override fun onPause() {
super.onPause()
// 当前 Fragment 包含子 Fragment 的时候 dispatchUserVisibleHint 内部本身就会通知子 Fragment 不可见
// 子 fragment 走到这里的时候自身又会调用一遍
if (isSupportVisible && userVisibleHint) {
dispatchUserVisibleHint(false)
}
}
/**
* 统一处理 显示隐藏
*
* @param visible
*/
private fun dispatchUserVisibleHint(visible: Boolean) {
//当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment getUserVisibleHint = true
//但当父 fragment 不可见所以 currentVisibleState = false 直接 return 掉
// 这里限制则可以限制多层嵌套的时候子 Fragment 的分发
if (visible && isParentInvisible) return
//此处是对子 Fragment 不可见的限制,因为 子 Fragment 先于父 Fragment回调本方法 currentVisibleState 置位 false
// 当父 dispatchChildVisibleState 的时候第二次回调本方法 visible = false 所以此处 visible 将直接返回
if (isSupportVisible == visible) {
return
}
isSupportVisible = visible
if (visible) {
// TODO 当 fragment 重建时这里的被调用很奇怪onActivityCreated 回调触发,但此时的 view 是空的,原因是 createView 还没被调用
// TODO 这样就造成了 onFragmentResume 里可能用到 view 的地方出现空指针异常,所以这里遇到 view 为空的时候 return 等下一次被调用才进去
if (view == null) {
return
}
if (mIsFirstVisible) {
mIsFirstVisible = false
onFragmentFirstVisible()
}
onFragmentResume()
dispatchChildVisibleState(true)
} else {
// 当 fragment 重建时,这个代码块可能在第一次 view 为空的 visible 后调用导致在 onFragmentPause 里可能用到 view 的地方出现空指针异常
if (!mIsFirstVisible) {
dispatchChildVisibleState(false)
onFragmentPause()
}
}
}
/**
* 当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment 的唯一或者嵌套 VP 的第一 fragment 时 getUserVisibleHint = true
* 但是由于父 Fragment 还未进入可见状态所以自身也是不可见的, 这个方法可以存在是因为庆幸的是 父 fragment 的生命周期回调总是先于子 Fragment
* 所以在父 fragment 设置完成当前不可见状态后,需要通知子 Fragment 我不可见,你也不可见,
*
*
* 因为 dispatchUserVisibleHint 中判断了 isParentInvisible 所以当 子 fragment 走到了 onActivityCreated 的时候直接 return 掉了
*
*
* 当真正的外部 Fragment 可见的时候,走 setVisibleHint (VP 中)或者 onActivityCreated (hide show) 的时候
* 从对应的生命周期入口调用 dispatchChildVisibleState 通知子 Fragment 可见状态
*
* @param visible
*/
private fun dispatchChildVisibleState(visible: Boolean) {
val childFragmentManager = childFragmentManager
val fragments = childFragmentManager.fragments
if (!fragments.isEmpty()) {
for (child in fragments) {
if (child is BaseLazyFragment && !child.isHidden() && child.getUserVisibleHint()) {
child.dispatchUserVisibleHint(visible)
}
}
}
}
open fun onFragmentFirstVisible() {
//ULog.e("对用户第一次可见")
}
open fun onFragmentResume() {
//ULog.e("对用户可见")
}
open fun onFragmentPause() {
//ULog.e("对用户不可见")
}
override fun onDestroyView() {
super.onDestroyView()
isViewCreated = false
mIsFirstVisible = true
}
}

View File

@ -1,142 +0,0 @@
package com.gh.base.fragment
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.gh.base.adapter.FragmentAdapter
import com.gh.common.view.TabIndicatorView
import com.gh.gamecenter.R
import com.google.android.material.tabs.TabLayout
import com.lightgame.view.NoScrollableViewPager
abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeListener {
lateinit var mTabLayout: TabLayout
lateinit var mViewPager: NoScrollableViewPager
lateinit var mTabIndicatorView: TabIndicatorView
var mFragmentsList: MutableList<Fragment> = arrayListOf()
var mTabTitleList: MutableList<String> = arrayListOf()
var mCheckedIndex = 0
abstract fun initFragmentList(fragments: MutableList<Fragment>)
abstract fun initTabTitleList(tabTitleList: MutableList<String>)
protected open fun provideIndicatorWidth(): Int {
return 20
}
protected open fun provideTabView(position: Int, tabTitle: String?): View? {
return null
}
override fun getLayoutId(): Int {
return R.layout.fragment_tablayout_viewpager
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val fragments = childFragmentManager.fragments
if (fragments != null) {
for (fragment in fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) mCheckedIndex = requireArguments().getInt(PAGE_INDEX, 0)
}
open fun providePagerAdapter(): PagerAdapter? {
return null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mTabLayout = view.findViewById(R.id.fragment_tab_layout)
mViewPager = view.findViewById(R.id.fragment_view_pager)
mTabIndicatorView = view.findViewById(R.id.fragment_tab_indicator)
}
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
mTabTitleList.clear()
mFragmentsList.clear()
initTabTitleList(mTabTitleList)
mFragmentsList.addAll(restoreFragments())
if (mFragmentsList.isEmpty() || mFragmentsList.size != mTabTitleList.size) {
mFragmentsList.clear()
initFragmentList(mFragmentsList)
}
mViewPager.offscreenPageLimit = mFragmentsList.size
mViewPager.addOnPageChangeListener(this)
mViewPager.adapter = providePagerAdapter()
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
mViewPager.currentItem = mCheckedIndex
mTabLayout.setupWithViewPager(mViewPager)
mTabIndicatorView.setupWithTabLayout(mTabLayout)
mTabIndicatorView.setupWithViewPager(mViewPager)
mTabIndicatorView.setIndicatorWidth(provideIndicatorWidth())
for (i in 0 until mTabLayout.tabCount) {
val tab = mTabLayout.getTabAt(i) ?: continue
val tabTitle = if (tab.text != null) tab.text.toString() else ""
var tabView = provideTabView(i, tabTitle)
if (tabView == null) tabView = BaseFragment_TabLayout.createDefaultTabCustomView(requireContext(), tabTitle)
tab.customView = tabView
}
BaseFragment_TabLayout.initTabStyle(mTabLayout, mCheckedIndex)
}
private fun restoreFragments(): ArrayList<Fragment> {
val tag = "android:switcher:${mViewPager.id}:"
val fragments = ArrayList<Fragment>()
val childCount = mTabTitleList.size
for (index in 0 until childCount) {
val fragment = childFragmentManager.findFragmentByTag("$tag$index")
if (fragment != null) {
restoreFragment(fragment)
fragments.add(fragment)
}
}
return fragments
}
open fun restoreFragment(fragment: Fragment) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {}
override fun onPageScrollStateChanged(state: Int) {}
override fun onNightModeChange() {
super.onNightModeChange()
val container = requireView().findViewById<View>(R.id.fragment_tab_container)
if (container != null) {
container.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background_white))
for (i in 0 until mTabLayout.tabCount) {
val tab = mTabLayout.getTabAt(i)
if (tab != null) {
BaseFragment_TabLayout.updateTabStyle(tab, tab.isSelected)
}
}
}
}
companion object {
const val PAGE_INDEX = "PAGE_INDEX"
}
}

View File

@ -1,99 +0,0 @@
package com.gh.base.fragment
import android.os.Bundle
import android.view.View
import android.view.ViewStub
import com.gh.gamecenter.R
/**
* 这是在 BaseLazyFragment 之上添加了一些通用功能的抽象类
*
* 怎么将一个已有的 fragment 转化为懒加载 (延迟渲染) 的 fragment 呢?
* (继承 ListFragment 的类请改为继承 LazyListFragment)
*
* 0. 删掉旧的 getInflatedLayout() 的代码,现在由 getStubLayoutId() 提供 Stub 布局 (默认为 R.layout.fragment_stub若重写请注意提供 id 为 stub 的 ViewStub
* 1. 重写 getRealLayoutId(),提供实际要延迟渲染的 layout Id
* 1. 将原有在 onCreate() 的代码移动到 onFragmentFirstVisible()
* 2. 将原有在 onViewCreated() 的代码移动到 initRealView()
* 注意initRealView() 在 onFragmentFirstVisible() 中被调用,如果要初始化 viewModel 等非 UI 对象请在 super.onFragmentVisible() 调用)
* 3. 如需使用 ViewBinding ,在 onRealLayoutInflated() 的回调中初始化 ViewBinding 即可
* 4. onResume() 的代码移动到 onFragmentResume()onPause() 的代码移动到 onFragmentPause()
* 5. Done!
*/
abstract class LazyFragment : BaseLazyFragment() {
// ViewStub + ViewBinding 有莫名的 bug语法上没问题但编译时通不过。
private var mViewStub: ViewStub? = null
private var mIsRecreatedByFragmentManager = false
override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null) {
mIsRecreatedByFragmentManager = true
}
super.onCreate(savedInstanceState)
}
@Deprecated(
"使用了 LazyFragment 以后不要 override getLayoutId(),建议 override getRealLayoutId() 或者 getStubLayoutId()",
ReplaceWith("LazyFragment.getRealLayoutId()")
)
override fun getLayoutId() = if (isRecreatedByFragmentManager()) {
getRealLayoutId()
} else {
getStubLayoutId()
}
override fun initView(view: View?) {
super.initView(view)
if (!isRecreatedByFragmentManager()) {
mViewStub = mCachedView.findViewById(R.id.stub)
}
}
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
inflateRealView()
initRealView()
}
/**
* 真正 inflate View 的地方
*/
protected open fun inflateRealView() {
if (isRecreatedByFragmentManager()) {
onRealLayoutInflated(mCachedView)
} else {
mViewStub?.layoutResource = getRealLayoutId()
mViewStub?.setOnInflateListener { _, inflatedView -> onRealLayoutInflated(inflatedView) }
mViewStub?.inflate()?.let { mCachedView = it }
}
}
protected fun isRecreatedByFragmentManager() = mIsRecreatedByFragmentManager
/**
* 请在这个方法之后获取初始化后的各种 view
*
* 替换旧 fragment 实现时,等同于 onViewCreated
*/
protected open fun initRealView() {}
/**
* 提供要 stub inflate 的 layout
*/
protected abstract fun getRealLayoutId(): Int
/**
* 提供含有 stub 的 layout
*/
protected open fun getStubLayoutId(): Int {
return R.layout.fragment_stub
}
/**
* 真实 layout inflate 完成的回调,可用于 viewBinding
*/
protected open fun onRealLayoutInflated(inflatedView: View) {}
}

View File

@ -1,5 +0,0 @@
package com.gh.base.fragment;
public interface OnDialogBackListener {
void onBack();
}

View File

@ -1,134 +0,0 @@
package com.gh.base.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.ExtensionsKt;
import com.gh.gamecenter.R;
/**
* @author CsHeng
* @Date 17/05/2017
* @Time 4:27 PM
*/
public class WaitingDialogFragment extends BaseDialogFragment {
public static final String KEY_MSG = "msg";
private OnDialogBackListener mBackListener;
private TextView message;
public static WaitingDialogFragment newInstance(String message) {
Bundle args = new Bundle();
args.putString(KEY_MSG, message);
WaitingDialogFragment fragment = new WaitingDialogFragment();
fragment.setArguments(args);
return fragment;
}
public static WaitingDialogFragment newInstance(String message, boolean isCancelable) {
Bundle args = new Bundle();
args.putString(KEY_MSG, message);
WaitingDialogFragment fragment = new WaitingDialogFragment();
fragment.setArguments(args);
fragment.setCancelable(isCancelable);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.set_wait_dialog, null);
message = view.findViewById(R.id.set_wait_message);
message.setText(getArguments().getString(KEY_MSG));
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
WindowManager.LayoutParams layoutParams = getDialog().getWindow().getAttributes();
layoutParams.width = DisplayUtils.dip2px(160);
getDialog().getWindow().setAttributes(layoutParams);
}
@Override
public void show(FragmentManager manager, String tag) {
try {
super.show(manager, tag);
} catch (Exception e) {
e.printStackTrace();
}
}
public void show(FragmentManager manager, String tag, OnDialogBackListener backListener) {
show(manager, tag);
this.mBackListener = backListener;
}
public void uploadWaitingHint(String hint) {
if (message != null) message.setText(hint);
}
@Override
public boolean onBack() {
if (mBackListener != null) {
mBackListener.onBack();
return true;
}
return super.onBack();
}
@Override
public void dismiss() {
dismissAllowingStateLoss();
}
@Override
public void dismissAllowingStateLoss() {
mBackListener = null;
try {
super.dismissAllowingStateLoss();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class WaitingDialogData {
private String msg;
private boolean isShow;
public WaitingDialogData(String msg, boolean isShow) {
this.msg = msg;
this.isShow = isShow;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public boolean isShow() {
return isShow;
}
public void setShow(boolean show) {
isShow = show;
}
}
}

View File

@ -1,74 +0,0 @@
package com.gh.common
import android.os.Handler
import android.os.Looper
import com.gh.base.GHThreadFactory
import com.gh.common.AppExecutor.heavyWeightIoExecutor
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.*
/**
* APP 线程池管理类
*
* [ioExecutor] 是一个最大线程数固定的线程池,较为繁重的 IO 任务可以交给它
* [uiExecutor] 是主线程的包裹,需要切换至主线程执行可以用它
* [lightWeightIoExecutor] 是一个单线程的线程池,轻量级且需要保证同一线程的 IO 任务可以交给它
* [heavyWeightIoExecutor] 重量级的线程池,一些高频调用但不用保证结果的任务可以交给它
* [logExecutor] 只为上传 log 而使用的线程池
*/
object AppExecutor {
private val mCoreSize = Runtime.getRuntime().availableProcessors()
private val mMinimumPoolSize = 6.coerceAtLeast(mCoreSize)
private val mMaximumPoolSize = 24.coerceAtLeast(mCoreSize * 3)
@JvmStatic
val uiExecutor by lazy { MainThreadExecutor() }
@JvmStatic
val lightWeightIoExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LIGHT_WEIGHT_IO_THREAD")) }
@JvmStatic
val heavyWeightIoExecutor: ExecutorService by lazy { Executors.newFixedThreadPool(2, GHThreadFactory("GH_HEAVY_WEIGHT_IO_THREAD")) }
@JvmStatic
val logExecutor: ExecutorService by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_LOG_THREAD")) }
@JvmStatic
val ioExecutor = ThreadPoolExecutor(
mMinimumPoolSize,
mMaximumPoolSize,
20L, TimeUnit.SECONDS,
LinkedBlockingQueue(256),
GHThreadFactory("GH_IO_THREAD"))
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
class MainThreadExecutor : Executor {
private val mainThreadHandler = Handler(Looper.getMainLooper())
override fun execute(command: Runnable) {
mainThreadHandler.post(command)
}
fun executeWithDelay(command: Runnable, delay: Long) {
mainThreadHandler.postDelayed(command, delay)
}
}
}
fun runOnIoThread(isLightWeightTask: Boolean = false, isHeavyWightTask: Boolean = false, f: () -> Unit) {
when {
isLightWeightTask -> lightWeightIoExecutor.execute(f)
isHeavyWightTask -> heavyWeightIoExecutor.execute(f)
else -> ioExecutor.execute(f)
}
}
fun runOnUiThread(f: () -> Unit) {
uiExecutor.execute(f)
}

View File

@ -1,5 +0,0 @@
package com.gh.common
object Base64ImageHolder {
var image: String = ""
}

View File

@ -1,450 +0,0 @@
package com.gh.common
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.util.Base64
import android.webkit.JavascriptInterface
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import com.gh.base.CurrentActivityHolder
import com.gh.common.constant.Constants
import com.gh.common.loghub.LoghubUtils
import com.gh.common.tracker.Tracker
import com.gh.common.util.*
import com.gh.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.*
import com.gh.gamecenter.energy.EnergyCenterActivity
import com.gh.gamecenter.energy.EnergyHouseActivity
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.help.QaFeedbackDialogFragment
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
import com.gh.gamecenter.security.BindPhoneActivity
import com.gh.gamecenter.user.LoginTag
import com.gh.gamecenter.user.UserRepository
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.*
class DefaultJsApi(var context: Context) {
private var mLoginHandler: CompletionHandler<Any>? = null
@JavascriptInterface
fun isGhzs(msg: Any): String {
return "true"
}
@JavascriptInterface
fun toast(msg: Any) {
Utils.toast(HaloApp.getInstance().application, msg.toString())
}
@JavascriptInterface
fun logMtaEvent(event: Any) {
val mtaEvent = event.toString().toObject() ?: MtaEvent()
MtaHelper.onEvent(mtaEvent.name, mtaEvent.key, mtaEvent.value)
}
@JavascriptInterface
fun getUserInfo(msg: Any): String {
return UserManager.getInstance().userInfoEntity?.toJson() ?: ""
}
@JavascriptInterface
fun getUserToken(msg: Any): String {
return if (UserManager.getInstance().isLoggedIn) UserManager.getInstance().loginTokenEntity.accessToken.value else ""
}
@JavascriptInterface
fun login(msg: Any) {
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(context)) {
QuickLoginHelper.startLogin(context, "浏览器")
} else {
val intent = LoginActivity.getIntent(context, "浏览器")
context.startActivity(intent)
}
}
@JavascriptInterface
fun refreshUserInfoBadge(msg: Any) {
val userInfoEntity = UserManager.getInstance().userInfoEntity
if (msg.toString().isNotEmpty()) {
val badge = msg.toString().toObject() ?: Badge()
userInfoEntity?.badge = badge
} else {
userInfoEntity?.badge = null
}
UserManager.getInstance().userInfoEntity = userInfoEntity
}
@JavascriptInterface
fun getChannel(msg: Any): String {
return HaloApp.getInstance().channel
}
@JavascriptInterface
fun getAppVersion(msg: Any): String {
return BuildConfig.VERSION_NAME
}
@JavascriptInterface
fun getAppVersionCode(msg: Any): Int {
return PackageUtils.getGhVersionCode()
}
@JavascriptInterface
fun bindWechat(msg: Any, handler: CompletionHandler<Any>) {
context.ifLogin("浏览器") {
LoginHelper.loginWithWechat(object : LoginHelper.LoginCallback {
@SuppressLint("CheckResult")
override fun onLoginSuccess(loginType: LoginTag, jsonContent: JSONObject) {
val wechatLoginInfoMap = hashMapOf<String, String>()
wechatLoginInfoMap["openid"] = jsonContent.getString("openid")
wechatLoginInfoMap["unionid"] = jsonContent.getString("unionid")
wechatLoginInfoMap["access_token"] = jsonContent.getString("access_token")
wechatLoginInfoMap["refresh_token"] = jsonContent.getString("refresh_token")
WechatBindHelper.bindWechat(wechatLoginInfoMap, object : BiCallback<Boolean, Boolean> {
override fun onFirst(first: Boolean) {
EnergyTaskHelper.postEnergyTask("bind_wechat")
handler.complete(true)
}
override fun onSecond(second: Boolean) {
handler.complete(false)
}
})
LoginHelper.unregisterCallback()
}
override fun onLoginFailure(loginType: LoginTag, error: String) {
handler.complete(false)
LoginHelper.unregisterCallback()
}
})
}
}
@JavascriptInterface
fun refreshWechatBindData(msg: Any) {
WechatBindHelper.getWechatConfig(null)
}
@JavascriptInterface
fun copyText(msg: Any) {
runOnUiThread {
msg.toString().copyTextAndToast()
}
}
@JavascriptInterface
fun startApp(msg: Any) {
val packageName = msg.toString()
PackageUtils.launchApplicationByPackageName(HaloApp.getInstance().application, packageName)
}
@JavascriptInterface
fun openImage(event: Any) {
val imageEvent = event.toString().toObject() ?: ImageEvent()
val context = CurrentActivityHolder.getCurrentActivity()
context?.startActivity(ImageViewerActivity.getIntent(context, imageEvent.imageList, imageEvent.position, "浏览器"))
}
@JavascriptInterface
fun isInstalled(event: Any): String {
val localInstalledPackageList = PackageUtils.getAllPackageName(HaloApp.getInstance().application)
val packageNameList: ArrayList<String> = event.toString().toObject() ?: ArrayList()
for (packageName in packageNameList) {
if (!localInstalledPackageList.contains(packageName)) {
return "false"
}
}
return "true"
}
@JavascriptInterface
fun openBase64Image(event: Any) {
val context = CurrentActivityHolder.getCurrentActivity()
Base64ImageHolder.image = event.toString()
context?.startActivity(ImageViewerActivity.getBase64Intent(context, true))
}
@JavascriptInterface
fun openNotificationSetting(msg: Any) {
NotificationHelper.show(context as AppCompatActivity, NotificationUgc.LOGIN)
}
@JavascriptInterface
fun useDarkStatusBarText(msg: Any) {
runOnUiThread {
DisplayUtils.transparentStatusBar(context as AppCompatActivity)
DisplayUtils.setLightStatusBar(context as AppCompatActivity, msg.toString() == "true")
}
}
@JavascriptInterface
fun exitWebView(msg: Any) {
runOnUiThread { (context as Activity).finish() }
}
@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")
}
}
@JavascriptInterface
fun getStatusBarHeight(msg: Any): String {
val statusBarHeight = DisplayUtils.getStatusBarHeight(context.resources)
return "$statusBarHeight"
}
@JavascriptInterface
fun getGid(msg: Any): String {
return HaloApp.getInstance().gid
}
@JavascriptInterface
fun showIncompatibleVersionDialog(msg: Any) {
DialogHelper.showUpgradeDialog(context)
}
@JavascriptInterface
fun shareBase64Image(event: Any) {
val imageShareEvent = event.toString().toObject() ?: ImageShareEvent()
val context = CurrentActivityHolder.getCurrentActivity()
Base64ImageHolder.image = imageShareEvent.image.run {
if (this.startsWith("data:image/png;base64")) this.split(",")[1] else this
}
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).shareInviteFriends(context, inviteEvent.way)
} else {
ShareUtils.getInstance(context).shareInviteFriends(context, inviteEvent.url, inviteEvent.way)
}
}
@JavascriptInterface
fun bindPhone(msg: Any) {
val intent = BindPhoneActivity.getNormalIntent(context, false)
context.startActivity(intent)
}
@JavascriptInterface
fun updateTitle(msg: Any) {
if (context is WebActivity) {
runOnUiThread { (context as WebActivity).setNavigationTitle(msg.toString()) }
}
}
@JavascriptInterface
fun logoutExitWebViewAndRedirectToLogin() {
UserRepository.getInstance().logout()
if (context is Activity) {
AppExecutor.uiExecutor.executeWithDelay(Runnable {
context.ifLogin("内部网页")
(context as Activity).finish()
}, 100)
}
}
@JavascriptInterface
fun openInNewWebview(url: Any) {
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))
}
@JavascriptInterface
fun showQaFeedbackDialog(msg: Any) {
QaFeedbackDialogFragment.show(context as AppCompatActivity, msg.toString())
}
@JavascriptInterface
fun getMetaObject(msg: Any): String {
return LogUtils.getMetaObject().toString()
}
@JavascriptInterface
fun getLaunchId(msg: Any): String {
return Tracker.launchId
}
@JavascriptInterface
fun getSessionId(msg: Any): String {
return Tracker.sessionId
}
@JavascriptInterface
fun postLogEvent(event: Any) {
val logEvent = event.toString().toObject() ?: LogEvent()
debugOnly {
Utils.log("LogUtils->${logEvent.jsonString}")
}
LoghubUtils.log(logEvent.jsonString, logEvent.logStore, false)
}
@JavascriptInterface
fun startAvatarBorderPage(msg: Any) {
context.startActivity(AvatarBorderActivity.getIntent(context, msg.toString()))
}
@JavascriptInterface
fun isNetworkConnected(): Boolean {
return NetworkUtils.isNetworkConnected(context)
}
@JavascriptInterface
fun isWifiConnected(): Boolean {
return NetworkUtils.isWifiConnected(context)
}
@JavascriptInterface
fun enableBackToActivity(msg: Any) {
FloatingBackViewManager.enableBackView(FloatingBackViewManager.TYPE_ACTIVITY, msg.toString())
}
@JavascriptInterface
fun startBBSStayTimeCount(msg: Any) {
BbsStayTimeHelper.enableStayTimeCount(msg.toString().toInt())
}
@JavascriptInterface
fun saveBase64ImageToGallery(msg: Any) {
val base64StringData = msg.toString()
runOnUiThread {
(context as? FragmentActivity)?.checkStoragePermissionBeforeAction {
runOnIoThread {
val base64String = base64StringData.replace("data:image/png;base64", "")
tryWithDefaultCatch {
val imageFile = File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
val bos = BufferedOutputStream(FileOutputStream(imageFile))
bos.write(decodedString)
bos.flush()
bos.close()
ImageUtils.saveImageToFile(imageFile, "", true)
}
}
}
}
}
@JavascriptInterface
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
mLoginHandler = handler
login(msg)
}
fun onLogin() {
mLoginHandler?.complete(true)
mLoginHandler = null
}
@JavascriptInterface
fun openInNewFullWebview(url: Any) {
runOnUiThread { DirectUtils.directToFullScreenWebPage(context, url.toString(), true) }
}
@JavascriptInterface
fun startGameCollectionSquareBrowseTask(event: Any) {
val browseTimeEvent = event.toString().toObject() ?: BrowseTaskEvent()
GameCollectionSquareBrowseTaskHelper.enableBrowseTimeCount(
browseTimeEvent.timeout.toInt(),
browseTimeEvent.isFinished == "true"
)
}
@JavascriptInterface
fun checkUpdateGhzs(msg: Any) {
context.startActivity(AboutActivity.getIntent(context, true))
}
@JavascriptInterface
public fun clickGameActivityDownloadBtn(event: Any) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.start(context, gameActivityEvent)
}
@JavascriptInterface
fun isGameActivityTaskCompleted(event: Any, handler: CompletionHandler<Any>) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.checkTaskComplete(context, gameActivityEvent, handler)
}
@JavascriptInterface
fun postGameActivityExposureEvent(event: Any) {
val gameActivityEvent = event.toString().toObject() ?: GameActivityEvent()
GameActivityDownloadHelper.postExposureEvent(gameActivityEvent)
}
@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 = ""
)
@Keep
internal data class LogEvent(var jsonString: String = "", var logStore: String = "")
@Keep
internal data class BrowseTaskEvent(var timeout: String = "", var isFinished: String = "")
@Keep
data class GameActivityEvent(
var gameId: String = "",
var activityTitle: String = "",
var activityId: String = "",
var platform: String = ""
)
}

View File

@ -1,592 +0,0 @@
package com.gh.common
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import android.util.Base64
import com.gh.base.CurrentActivityHolder
import com.gh.common.constant.Constants
import com.gh.common.util.*
import com.gh.common.util.DirectUtils.directToFeedback
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
import com.gh.common.util.DirectUtils.directToGameServerCalendar
import com.gh.common.util.DirectUtils.directToGameVideo
import com.gh.common.util.DirectUtils.directToLegacyVideoDetail
import com.gh.common.util.DirectUtils.directToLinkPage
import com.gh.common.util.DirectUtils.directToQa
import com.gh.common.util.GsonUtils.gson
import com.gh.gamecenter.LibaoDetailActivity
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.NewsDetailActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
import com.gh.gamecenter.subject.SubjectActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.lightgame.utils.Utils
import java.nio.charset.Charset
object DefaultUrlHandler {
@JvmStatic
fun interceptUrl(context: Context, url: String, entrance: String): Boolean {
return interceptUrl(context, url, entrance, false)
}
/**
* @param bringAppToFront 是否需要在不匹配 host 的时候把 APP 调回到前台 (如微信调起)
*/
@JvmStatic
fun interceptUrl(context: Context, url: String, entrance: String, bringAppToFront: Boolean = false): Boolean {
val uri = Uri.parse(url)
if ("ghzhushou" == uri.scheme) {
Utils.log("url = $url")
Utils.log("url = " + uri.scheme!!)
val host = uri.host
val path = uri.path
var id = ""
if (!TextUtils.isEmpty(path)) {
id = path!!.substring(1)
}
if (TextUtils.isEmpty(id)) {
id = uri.getQueryParameter("id") ?: ""
}
val intent: Intent
when (host) {
"article" -> context.startActivity(NewsDetailActivity.getIntentById(context, id, entrance))
"game" -> DirectUtils.directToGameDetail(
context,
id = id,
tab = uri.getQueryParameter("to"),
autoDownload = uri.getQueryParameter("auto_download") == "true",
entrance = entrance
)
"column" -> SubjectActivity.startSubjectActivity(context, id, uri.getQueryParameter("name"), false, entrance)
"libao" -> context.startActivity(LibaoDetailActivity.getIntentById(context, id, entrance))
"qq" -> try {
DirectUtils.directToQqConversation(context, id)
} catch (e: Throwable) {
Utils.toast(context, "请检查是否已经安装手机QQ")
e.printStackTrace()
}
EntranceUtils.HOST_QQ_QUN -> {
val key = uri.getQueryParameter("key")
if (!DirectUtils.directToQqGroup(context, key)) {
Utils.toast(context, "请检查是否已经安装手机QQ")
}
}
"inurl" -> {
DirectUtils.directToWebView(context, uri.getQueryParameter("url") ?: "")
}
"outurl" -> {
intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.data = Uri.parse(uri.getQueryParameter("url"))
try {
context.startActivity(intent)
} catch (e: Exception) {
Utils.toast(context, "请检查是否已经安装手机浏览器")
e.printStackTrace()
}
}
"question" -> DirectUtils.directToQuestionDetail(context, id, entrance, "文章链接")
"community" -> {
val community = CommunityEntity()
community.id = id
community.name = uri.getQueryParameter("name") ?: ""
DirectUtils.directToCommunity(context, community)
}
"community_column" -> {
val community = CommunityEntity()
community.id = uri.getQueryParameter("community_id") ?: ""
community.name = uri.getQueryParameter("community_name") ?: ""
val columnId = uri.getQueryParameter("column_id") ?: ""
DirectUtils.directToCommunityColumn(context, community, columnId, entrance, "文章链接")
}
"answer" -> DirectUtils.directToAnswerDetail(context, id, entrance, "文章链接")
"communities" -> {
// ghzhushou://communities/5a32405b2397ab000f688de3/articles/5c99d262c140b321564f04e3
var communityId = ""
var type = ""
var typeId = ""
val split = id.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (text in split) {
if (TextUtils.isEmpty(communityId)) {
communityId = text
continue
}
if (TextUtils.isEmpty(type)) {
type = text
continue
}
if (TextUtils.isEmpty(typeId)) {
typeId = text
}
}
if ("articles" == type) {
DirectUtils.directToCommunityArticle(
context, typeId, communityId,
entrance, "文章链接"
)
}
}
EntranceUtils.HOST_UPLOAD_VIDEO -> {
val titleParameter = uri.getQueryParameter("title")
val title = if (titleParameter.isNullOrEmpty()) "" else "#$titleParameter#"
val categoryId = uri.getQueryParameter("category_id") ?: ""
val link = uri.getQueryParameter("link") ?: ""
val gameId = uri.getQueryParameter("gameId") ?: ""
val gameName = uri.getQueryParameter("gameName") ?: ""
val tagActivityId = uri.getQueryParameter("tagActivityId") ?: ""
val tagActivityName = uri.getQueryParameter("tagActivityName") ?: ""
val linkEntity = VideoLinkEntity(title, categoryId, link, tagActivityId, tagActivityName)
val simpleGameEntity = SimpleGameEntity(gameId, gameName)
CheckLoginUtils.checkLogin(context, null, true, EntranceUtils.ENTRANCE_BROWSER) {
DirectUtils.directToVideoManager(context, linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "")
}
}
EntranceUtils.HOST_USERHOME -> {
val position = uri.getQueryParameter("position")
val subtype = uri.getQueryParameter("sub_type") ?: ""
DirectUtils.directToHomeActivity(context, id, subtype, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
}
EntranceUtils.HOST_VIDEO_MORE -> {
val referer = uri.getQueryParameter("referer") ?: ""
val type = uri.getQueryParameter("type") ?: ""
val act = uri.getQueryParameter("act") ?: ""
val gameId = uri.getQueryParameter("gameId") ?: ""
val fieldId = uri.getQueryParameter("fieldId") ?: ""
val sectionName = uri.getQueryParameter("sectionName") ?: ""
val paginationType = uri.getQueryParameter("paginationType")
?: "page"//活动分页方式 page filter
val location = if (!TextUtils.isEmpty(act)) {
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
} else if (!TextUtils.isEmpty(fieldId)) {
VideoDetailContainerViewModel.Location.GAME_ZONE.value
} else {
id
}
directToLegacyVideoDetail(
context,
id,
location,
false,
gameId,
entrance,
"",
referer,
type,
act,
paginationType,
fieldId,
sectionName
)
}
EntranceUtils.HOST_VIDEO_DETAIL -> {
DirectUtils.directToVideoDetail(context, id, entrance, path)
}
EntranceUtils.HOST_VIDEO_SINGLE -> {
val referer = uri.getQueryParameter("referer") ?: ""
DirectUtils.directToVideoDetail(
context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer
)
}
EntranceUtils.HOST_VIDEO_STREAMING_HOME -> {
intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
intent.putExtra(MainActivity.SWITCH_TO_VIDEO, true)
context.startActivity(intent)
}
EntranceUtils.HOST_VIDEO_STREAMING_DESC -> {
directToGameDetailVideoStreaming(context, id, entrance)
}
EntranceUtils.HOST_VIDEO_COLLECTION -> {
directToGameVideo(context, id, entrance, "")
}
EntranceUtils.HOST_CATEGORY -> {
val title = uri.getQueryParameter("title")
DirectUtils.directCategoryDirectory(context, id, title ?: "", entrance, "")
}
EntranceUtils.HOST_COLUMN_COLLECTION -> {
val name = uri.getQueryParameter("name")
DirectUtils.directToColumnCollection(context, id, -1, entrance, name ?: "")
}
EntranceUtils.HOST_COLUMN -> {
DirectUtils.directToSubject(context, id, uri.getQueryParameter(EntranceUtils.KEY_NAME), entrance)
}
EntranceUtils.HOST_COMMUNITY_QUESTION_LABEL_DETAIL -> {
val community = CommunityEntity()
community.id = uri.getQueryParameter("community_id") ?: ""
community.name = uri.getQueryParameter("community_name") ?: ""
val tag = uri.getQueryParameter("tag") ?: ""
DirectUtils.directAskColumnLabelDetail(context, tag, community, entrance, "")
}
EntranceUtils.HOST_COMMUNITY_COLUMN_DETAIL -> {
val community = CommunityEntity()
community.id = uri.getQueryParameter("community_id") ?: ""
community.name = uri.getQueryParameter("community_name") ?: ""
val columnId = uri.getQueryParameter("column_id") ?: ""
DirectUtils.directAskColumnDetail(context, columnId, community, entrance, "")
}
EntranceUtils.HOST_BLOCK -> {
val name = uri.getQueryParameter("name")
?: ""
val entity = SubjectRecommendEntity(link = id, name = name, text = name)
DirectUtils.directToBlock(context, entity, entrance)
}
EntranceUtils.HOST_SERVER_BLOCK -> {
DirectUtils.directToGameServers(context, entrance = entrance, path = "")
}
EntranceUtils.HOST_AMWAY_BLOCK -> {
DirectUtils.directToAmway(context, entrance = entrance, path = "")
}
EntranceUtils.HOST_HELP -> {
val name = uri.getQueryParameter("name")
?: ""
DirectUtils.directToQa(context, name, id)
}
EntranceUtils.HOST_HELP_COLLECTION -> {
val name = uri.getQueryParameter("name")
?: ""
DirectUtils.directToQaCollection(context, name, id)
}
EntranceUtils.HOST_GAME_UPLOAD -> {
DirectUtils.directGameUpload(context, entrance = entrance, path = "")
}
EntranceUtils.HOST_GAME_ZONE -> {
val zoneUrl = uri.getQueryParameter("url") ?: ""
DirectUtils.directGameZone(context, id, zoneUrl, entrance)
}
EntranceUtils.HOST_LINK -> {
try {
val dataString = uri.getQueryParameter("data")
if (!TextUtils.isEmpty(dataString)) {
val linkData = Base64.decode(dataString, Base64.DEFAULT)
val linkDataString = String(linkData, Charset.defaultCharset())
val le = gson.fromJson(linkDataString, LinkEntity::class.java)
directToLinkPage(context, le, entrance, "")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
EntranceUtils.HOST_GAME_NEWS -> {
DirectUtils.directToGameNews(
context,
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
entrance
);
}
EntranceUtils.HOST_GAME_CALENDAR -> {
directToGameServerCalendar(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID))
}
EntranceUtils.HOST_HISTORY_APK -> {
DirectUtils.directToHistoryApk(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID))
}
EntranceUtils.HOST_FORUM_DETAIL -> {
DirectUtils.directForumDetail(context, id, entrance)
}
EntranceUtils.HOST_GAME_RATING_DETAIL -> {
DirectUtils.directToGameRatingDetail(
context,
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID),
EntranceUtils.ENTRANCE_BROWSER
)
}
EntranceUtils.HOST_FORUM -> {
val position = uri.getQueryParameter(EntranceUtils.KEY_POSITION)?.toInt()
DirectUtils.directToForum(context, position ?: 0)
}
EntranceUtils.HOST_UPLOAD_VIDEO_NEW -> {
val activityName = uri.getQueryParameter("activity_name") ?: ""
val activityId = uri.getQueryParameter("activity_id") ?: ""
val original = uri.getQueryParameter("original") ?: ""
val forumName = uri.getQueryParameter("forum_name") ?: ""
val forumId = uri.getQueryParameter("forum_id") ?: ""
val forumIcon = uri.getQueryParameter("forum_icon") ?: ""
val forumType = uri.getQueryParameter("forum_type") ?: BbsType.OFFICIAL_BBS.value
val gameId = uri.getQueryParameter("game_id") ?: ""
val gameName = uri.getQueryParameter("game_name") ?: ""
val icon = uri.getQueryParameter("game_icon") ?: ""
val iconSubscript = uri.getQueryParameter("game_icon_subscript") ?: ""
val gameEntity =
if (forumType == BbsType.OFFICIAL_BBS.value && gameId.isNotEmpty() && gameName.isNotEmpty() && icon.isNotEmpty()) {
GameEntity(id = gameId, mName = gameName, mIcon = icon, mIconSubscript = iconSubscript)
} else null
val activityLabelEntity = if (activityId.isNotEmpty() && activityName.isNotEmpty()) {
ActivityLabelEntity(id = activityId, name = activityName, original = original.ifEmpty { "false" }.toBoolean())
} else null
val communityEntity = if (forumId.isNotEmpty() && forumName.isNotEmpty() && forumIcon.isNotEmpty()) {
CommunityEntity(id = forumId, name = forumName, icon = forumIcon)
} else null
context.startActivity(
VideoPublishActivity.getIntent(
context,
communityEntity,
gameEntity,
activityLabelEntity,
forumType,
disableForumSelection = false,
isFromCommunityActivity = true,
entrance,
""
)
)
}
EntranceUtils.HOST_SUGGESTION -> {
val platform = uri.getQueryParameter(EntranceUtils.KEY_PLATFORM)
val platformName = PlatformUtils.getInstance(context).getPlatformName(platform)
val gameId = uri.getQueryParameter(EntranceUtils.KEY_GAMEID)
val packageMd5 = uri.getQueryParameter(EntranceUtils.KEY_PACKAGE_MD5)
val isQaFeedback = uri.getQueryParameter(EntranceUtils.KEY_IS_QA_FEEDBACK) == "true"
val content = if (TextUtils.isEmpty(gameId) || TextUtils.isEmpty(packageMd5)) String.format(
"%s-%s-V%s",
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceUtils.KEY_VERSION)
) else String.format(
"%s-%s-V%s\n游戏ID%s\n游戏包MD5%s\n",
uri.getQueryParameter(EntranceUtils.KEY_GAME_NAME),
if (TextUtils.isEmpty(platformName)) platform else platformName,
uri.getQueryParameter(EntranceUtils.KEY_VERSION), gameId, packageMd5
)
val qaId = uri.getQueryParameter("qa_id") ?: ""
val qaContentId = uri.getQueryParameter(EntranceUtils.KEY_QA_CONTENT_ID) ?: ""
val qaTitle = uri.getQueryParameter(EntranceUtils.KEY_QA_TITLE)
if (!TextUtils.isEmpty(qaId)) {
directToQa(context, qaTitle, qaId)
} else {
directToFeedback(context, content, null, isQaFeedback, qaContentId, EntranceUtils.ENTRANCE_BROWSER)
}
}
EntranceUtils.HOST_HELP_AND_FEEDBACK -> {
val position = uri.getQueryParameter("position") ?: ""
DirectUtils.directToHelpAndFeedback(context, position.toInt())
}
EntranceUtils.HOST_HELP_DETAIL -> {
var url = uri.getQueryParameter("url")
if (!url.isNullOrEmpty()) {
context.startActivity(WebActivity.getIntent(context, url, false))
} else {
url = if (EnvHelper.isDevEnv) {
Constants.HELP_ADDRESS_DEV
} else {
Constants.HELP_ADDRESS
}
val id = uri.getQueryParameter("id")
val name = uri.getQueryParameter("name")
val qaCollectionId = uri.getQueryParameter("collection_id")
context.startActivity(WebActivity.getIntent(context, "$url$id", name, true, !qaCollectionId.isNullOrEmpty()))
}
}
EntranceUtils.HOST_GAME_COLLECTION_DETAIL -> {
DirectUtils.directToGameCollectionDetail(context, id, entrance)
}
EntranceUtils.HOST_GAME_COLLECTION_SQUARE -> {
DirectUtils.directToGameCollectionSquare(context, entrance)
}
EntranceUtils.HOST_GAME_COLLECTION_EDIT -> {
context.startActivity(GameCollectionEditActivity.getIntent(context, entrance))
}
else -> {
if (bringAppToFront) {
DirectUtils.directToMainActivity(context)
if (!TextUtils.isEmpty(host)) {
AppExecutor.uiExecutor.executeWithDelay({
CurrentActivityHolder.getCurrentActivity()?.let {
DialogHelper.showUpgradeDialog(it)
}
}, 200)
}
} else {
DialogHelper.showUpgradeDialog(context)
}
}
}
return true
} else if ("zhiqu" == uri.scheme) {
if (PackageUtils.isInstalled(context, "com.beieryouxi.zqyxh")) {
val intent = Intent()
intent.data = Uri.parse(url)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
} else {
Utils.toast(context, "请安装指趣游戏盒")
}
}
if (url.startsWith("alipays:") || url.startsWith("alipay")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装支付宝客户端")
}
return true
} else if (url.startsWith("weixin")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装微信客户端")
}
return true
} else if (url.startsWith("mqqwpa")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装QQ客户端")
}
return true
}
// 处理内部页面逻辑
if (transformNormalScheme(context, url, entrance)) {
return true
}
if ("http" != uri.scheme && "https" != uri.scheme) return true
return false
}
@JvmStatic
fun transformNormalScheme(context: Context, url: String, entrance: String): Boolean {
val uri = Uri.parse(url)
if (uri.host == "www.ghzs666.com"
|| uri.host == "www.ghzs.com"
|| uri.host == "ask.ghzs.com"
|| uri.host == "m.ghzs.com"
|| uri.host == "m.ghzs666.com"
) {
Utils.log(uri.path)
uri.path?.apply {
when {
contains("game") -> {
val gameId = uri.getQueryParameter("gameId") ?: uri.pathSegments.last()
?: ""
DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, traceEvent = null)
}
contains("question") -> {
val questionId = split("/")[2]
val answerId = uri.getQueryParameter("answer")
if (answerId.isNullOrEmpty()) {
DirectUtils.directToQuestionDetail(context, questionId, entrance, "")
} else {
DirectUtils.directToAnswerDetail(context, answerId, entrance, "")
}
}
((contains("bbs")) && contains("article") ||
(contains("communities")) && contains("article")) -> {
var communityId = ""
var type = ""
var typeId = ""
val split =
replace("/communities", "").replace("/bbs", "").replace(".html", "").split("/".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
for (text in split) {
if (TextUtils.isEmpty(communityId)) {
communityId = text
continue
}
if (TextUtils.isEmpty(type)) {
type = text
continue
}
if (TextUtils.isEmpty(typeId)) {
typeId = text
}
}
if ("articles" == type || "article" == type) {
DirectUtils.directToCommunityArticle(
context, typeId, communityId,
entrance, "文章链接"
)
}
}
contains("article") -> {
val articleId = split("/")[2].replace(".html", "")
if (entrance == "隐私政策") {
DirectUtils.directToArticle(context, articleId, true, entrance)
} else {
DirectUtils.directToArticle(context, articleId, entrance)
}
}
contains("columns") -> {
val columnsId = split("/")[3]
val id = uri.getQueryParameter("communityId") ?: ""
val name = uri.getQueryParameter("communityName") ?: ""
DirectUtils.directToCommunityColumn(context, CommunityEntity(id, name), columnsId, entrance, "")
}
contains("zone") && split("/").size > 2 -> {
val gameId = split("/")[2]
DirectUtils.directGameZone(context, gameId, url, entrance)
}
else -> return false
}
}
return true
}
return false
}
/**
* 将 url 转换为 LinkEntity (实际只有 type 和 link 两个字段,仅供日志,不保证能用)
*/
fun urlToLinkEntity(url: String): LinkEntity? {
val uri = Uri.parse(url)
if ("ghzhushou" == uri.scheme) {
Utils.log("url = $url")
Utils.log("url = " + uri.scheme!!)
val host = uri.host
val path = uri.path
var id = ""
if (!TextUtils.isEmpty(path)) {
id = path!!.substring(1)
}
return LinkEntity(type = host, link = id)
}
return null
}
}

View File

@ -1,83 +0,0 @@
package com.gh.common
import com.gh.common.exposure.ExposureManager
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.loghub.LoghubUtils
import com.gh.common.util.doOnMainProcessOnly
import com.gh.common.util.tryCatchInRelease
import com.gh.common.videolog.VideoRecordUtils
import com.gh.download.DownloadDataHelper
import com.gh.gamecenter.entity.TimeEntity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
import kotlin.concurrent.fixedRateTimer
object FixedRateJobHelper {
private const val CHECKER_PERIOD: Long = 15 * 1000L
private const val TIME_PERIOD: Long = 600 * 1000L
private const val LOGHUB_PERIOD: Long = 120 * 1000L
private const val EXPOSURE_PERIOD: Long = 300 * 1000L
private const val REGION_SETTING_PERIOD: Long = 300 * 1000L
private const val VIDEO_RECORD_PERIOD: Long = 60 * 1000L
private const val DOWNLOAD_HEARTBEAT_PERIOD: Long = 60 * 1000L
private const val DOWNLOAD_HEARTBEAT_SHEET_PERIOD: Long = 15 * 1000L
private var mExecuteCount: Int = 0
var timeDeltaBetweenServerAndClient: Long = 0
@JvmStatic
fun begin() {
doOnMainProcessOnly {
fixedRateTimer("Global-Fixed-Rate-Timer", initialDelay = 100, period = CHECKER_PERIOD) {
// 时间校对10分钟一次
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
RetrofitManager.getInstance().api.time
.subscribeOn(Schedulers.io())
.subscribe(object : Response<TimeEntity>() {
override fun onResponse(response: TimeEntity?) {
val serverTime = response?.time
serverTime?.let { timeDeltaBetweenServerAndClient = it * 1000 - System.currentTimeMillis() }
}
})
}
// 提交曝光数据
if ((mExecuteCount * CHECKER_PERIOD) % EXPOSURE_PERIOD == 0L) {
ExposureManager.commitSavedExposureEvents(true)
}
// 分片检测下载进度
if ((mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_SHEET_PERIOD == 0L) {
tryCatchInRelease {
val upload = (mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_PERIOD == 0L
DownloadDataHelper.uploadDownloadHeartbeat(upload)
}
}
// 提交普通 loghub 数据
if ((mExecuteCount * CHECKER_PERIOD) % LOGHUB_PERIOD == 0L) {
LoghubUtils.commitSavedLoghubEvents()
}
// 更新游戏屏蔽信息
if ((mExecuteCount * CHECKER_PERIOD) % REGION_SETTING_PERIOD == 0L) {
if (HaloApp.getInstance().isRunningForeground) {
RegionSettingHelper.getRegionSetting()
}
}
// 提交视频浏览记录数据
if ((mExecuteCount * CHECKER_PERIOD) % VIDEO_RECORD_PERIOD == 0L) {
VideoRecordUtils.commitVideoRecord()
}
// ExposureUtils.logADownloadCompleteExposureEvent(GameEntity(id = mExecuteCount.toString(), name = "测试曝光上传"), platform = "", trace = null, downloadType = ExposureUtils.DownloadType.DOWNLOAD)
mExecuteCount++
}
}
}
}

View File

@ -1,39 +0,0 @@
//package com.gh.common
//
//import android.content.BroadcastReceiver
//import android.content.Context
//import android.content.Intent
//import com.gh.common.im.ImManager
//import com.gh.gamecenter.manager.UserManager
//import com.gh.gamecenter.retrofit.RetrofitManager
//import com.halo.assistant.HaloApp
//import com.m7.imkfsdk.chat.ChatActivity
//import io.reactivex.schedulers.Schedulers
//
///**
// * 可使用 [LocalBroadcastManager] 来进行简单的模块间消息通知
// */
//
//class LocalBroadcastReceiver : BroadcastReceiver() {
//
// override fun onReceive(context: Context?, intent: Intent?) {
// intent?.let {
// when (intent.action) {
// ChatActivity.ACTION_DISMISS_FLOATING_WINDOW -> {
// ImManager.dismissFloatingWindow()
//
// RetrofitManager.getInstance().api.postImEnding(UserManager.getInstance().userId)
// .subscribeOn(Schedulers.io())
// .subscribe()
// }
//
// ChatActivity.ACTION_HIDE_UNREAD_DOT -> {
// ImManager.updateShouldShowFloatingWindowDot(false)
// }
//
// else -> return
// }
// }
// }
//
//}

Some files were not shown because too many files have changed in this diff Show More