Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f64325f2b | |||
| de9c9cbdcb | |||
| 1fe9e259b4 | |||
| 6b4f68128b | |||
| 8b637fbeec | |||
| 7267535ccf | |||
| 8f99987696 | |||
| 515473e5dc | |||
| 8b83e939ce | |||
| c71bd36de2 | |||
| 9fdc8f0da3 | |||
| 8ba099024e | |||
| e656acec1d | |||
| 6fa4994e04 | |||
| a929706219 | |||
| 62effd1cc1 | |||
| 417b597d1d | |||
| 2fabb9013a | |||
| 835dcc6446 | |||
| fba0a6ec71 | |||
| 9a2f4c984c | |||
| d870e68001 | |||
| 8e7fcfb746 | |||
| a995e132f3 | |||
| 6b9921c5a7 | |||
| 75695812ee | |||
| eae0414cb4 | |||
| 7b203bedfc | |||
| da20b817d2 | |||
| ef1f2251b5 | |||
| c907835ce7 | |||
| 3be623dd63 | |||
| 045c1e0305 | |||
| c292e79d94 | |||
| cb54d87daf | |||
| a29c0bd466 | |||
| 575ba33e43 | |||
| 30bb750d94 | |||
| c045d4e2cc | |||
| b2d568d646 | |||
| 22c2b47506 | |||
| 2ddaf3ee66 | |||
| d4dbee023e | |||
| 560c34afed | |||
| b3fc66f6b9 | |||
| 0ca70316dc | |||
| dd0f475944 | |||
| 80bb4a1888 | |||
| d7e72a96f9 | |||
| e552987663 | |||
| 33dea97475 | |||
| 4cbaae1567 | |||
| a8a9e58fe9 | |||
| 93ec154b7e | |||
| ed71d3e348 | |||
| 62fb61a8bb | |||
| de75e67de7 | |||
| 4fc10d360a | |||
| 40615976e1 | |||
| c6bd428c46 | |||
| 95000d718a | |||
| 1e077a7b0c | |||
| 659d6fc263 | |||
| d56fa926d2 | |||
| 321121d5ef | |||
| a50b4ba041 | |||
| 94b4d730e3 | |||
| f4a946f5e2 | |||
| 23a062abea | |||
| 986f67adf6 | |||
| 750c73fef9 | |||
| 419a4d830f | |||
| 814daa5d3c | |||
| 308bd369bc | |||
| 80670a9c4b | |||
| c01db35297 | |||
| e8cbbbb4bc | |||
| b79955107b | |||
| 67e646408b | |||
| f2500aeb6e | |||
| 0df714b9ea | |||
| 3ebbb27c3a | |||
| 8e9cf4603c | |||
| be4fc50ae0 | |||
| bb8a33f1a8 | |||
| c9147dc64d | |||
| aabca8f771 | |||
| e2e4f53d3e | |||
| 4b2e3d876b | |||
| a294484fb9 | |||
| 8e0892498e | |||
| d571081625 | |||
| 46668e32f6 | |||
| 627f513da0 | |||
| 5b5c87a07b | |||
| df1eec07e5 | |||
| 3f98bcea33 | |||
| 11ac9f5f3e | |||
| 66816e1a4c | |||
| 920bacd5aa | |||
| f05f95dc41 | |||
| 8b0faa57aa | |||
| e29741cf17 | |||
| 3cbc850d12 | |||
| 70e12db7b7 | |||
| 3b5f921c01 | |||
| 3c0b56c99a | |||
| ce841476ae | |||
| 110e7c9c29 | |||
| 4f0f1672c7 | |||
| b18146f699 | |||
| c7ceb5cf82 |
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,8 +1,8 @@
|
||||
/.idea
|
||||
.idea/misc.xml
|
||||
.idea/
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
.gradle/
|
||||
local.properties
|
||||
# sign.properties
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
captures/
|
||||
build/
|
||||
1
.idea/.name
generated
1
.idea/.name
generated
@ -1 +0,0 @@
|
||||
GH-ASSISTv1.45
|
||||
22
.idea/compiler.xml
generated
22
.idea/compiler.xml
generated
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/copyright/profiles_settings.xml
generated
3
.idea/copyright/profiles_settings.xml
generated
@ -1,3 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
6
.idea/encodings.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
24
.idea/gradle.xml
generated
24
.idea/gradle.xml
generated
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.10" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="myModules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
46
.idea/misc.xml
generated
46
.idea/misc.xml
generated
@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
||||
<OptionsSetting value="true" id="Add" />
|
||||
<OptionsSetting value="true" id="Remove" />
|
||||
<OptionsSetting value="true" id="Checkout" />
|
||||
<OptionsSetting value="true" id="Update" />
|
||||
<OptionsSetting value="true" id="Status" />
|
||||
<OptionsSetting value="true" id="Edit" />
|
||||
<ConfirmationsSetting value="0" id="Add" />
|
||||
<ConfirmationsSetting value="0" id="Remove" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/modules.xml
generated
9
.idea/modules.xml
generated
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/GH-ASSISTv1.45.iml" filepath="$PROJECT_DIR$/GH-ASSISTv1.45.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/runConfigurations.xml
generated
12
.idea/runConfigurations.xml
generated
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1
app/.gitignore
vendored
1
app/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/build
|
||||
118
app/build.gradle
118
app/build.gradle
@ -1,39 +1,55 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
//butterknife
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.gh.gamecenter"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 21
|
||||
versionCode 17
|
||||
versionName "2.2"
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
// 默认的渠道
|
||||
// manifestPlaceholders = [CHANNEL_VALUE: "GH_TEST"]
|
||||
dexOptions {
|
||||
jumboMode = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名设置
|
||||
* 定位编译出错的图片
|
||||
*/
|
||||
aaptOptions.cruncherEnabled = false
|
||||
aaptOptions.useNewCruncher = false
|
||||
|
||||
defaultConfig {
|
||||
// jackOptions {
|
||||
// enabled true
|
||||
// }
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
applicationId rootProject.ext.applicationId
|
||||
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
debug {
|
||||
}
|
||||
release {
|
||||
storeFile file("gh.keystore")
|
||||
keyAlias "gh.keystore"
|
||||
keyPassword "20150318"
|
||||
storePassword "20150318"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
debuggable true
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
zipAlignEnabled false
|
||||
// versionNameSuffix "-debug"
|
||||
}
|
||||
release {
|
||||
debuggable false
|
||||
minifyEnabled false
|
||||
zipAlignEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
@ -50,6 +66,7 @@ android {
|
||||
GH_104 {}
|
||||
GH_106 {}
|
||||
GH_107 {}
|
||||
GH_108 {}
|
||||
GH_109 {}
|
||||
GH_110 {}
|
||||
GH_111 {}
|
||||
@ -57,6 +74,7 @@ android {
|
||||
GH_114 {}
|
||||
GH_115 {}
|
||||
GH_116 {}
|
||||
GH_117 {}
|
||||
GH_118 {}
|
||||
GH_119 {}
|
||||
GH_120 {}
|
||||
@ -78,22 +96,14 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
//butterknife
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
}
|
||||
}
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support:appcompat-v7:21.0.0'
|
||||
compile 'com.android.support:cardview-v7:21.0.0'
|
||||
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
|
||||
compile 'com.android.support:appcompat-v7:23.2.1'
|
||||
// compile 'com.android.support:cardview-v7:21.0.0'
|
||||
// fresco图片框架
|
||||
compile 'com.facebook.fresco:fresco:0.12.0'
|
||||
compile 'com.facebook.fresco:animated-gif:0.12.0'
|
||||
@ -104,7 +114,7 @@ dependencies {
|
||||
// ConverterFactory的Gson依赖包
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
|
||||
// ConverterFactory的String依赖包
|
||||
compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'
|
||||
// compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'
|
||||
// ConverterFactory的RxJava依赖包
|
||||
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
|
||||
// gson
|
||||
@ -122,4 +132,48 @@ dependencies {
|
||||
compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'
|
||||
// compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
|
||||
// compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'
|
||||
}
|
||||
|
||||
// zxing 二维码扫描以及生成
|
||||
compile 'com.google.zxing:core:3.2.1'
|
||||
compile 'com.google.zxing:android-core:3.2.1'
|
||||
//tinker
|
||||
// compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
|
||||
// compile "com.android.support:multidex:1.0.1"
|
||||
|
||||
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')
|
||||
|
||||
}
|
||||
File propFile = file('sign.properties');
|
||||
if (propFile.exists()) {
|
||||
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 {
|
||||
// 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')
|
||||
storeFile file(props.get('storeFile'))
|
||||
storePassword props.get('storePassword')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
android.buildTypes.release.signingConfig = null
|
||||
}
|
||||
} else {
|
||||
android.buildTypes.release.signingConfig = null
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
@ -15,3 +15,7 @@
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
-dontwarn InnerClasses
|
||||
-dontoptimize
|
||||
4
app/sign.properties
Normal file
4
app/sign.properties
Normal file
@ -0,0 +1,4 @@
|
||||
storeFile=gh.keystore
|
||||
storePassword=20150318
|
||||
keyAlias=gh.keystore
|
||||
keyPassword=20150318
|
||||
@ -38,6 +38,8 @@
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<!-- 小米推送需要的权限 -->
|
||||
<uses-permission android:name="com.gh.gamecenter.permission.MIPUSH_RECEIVE" />
|
||||
<!-- 修改系统设置的权限 -->
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
|
||||
|
||||
<permission
|
||||
android:name="com.gh.gamecenter.permission.MIPUSH_RECEIVE"
|
||||
@ -54,7 +56,7 @@
|
||||
android:name="com.gh.base.AppController"
|
||||
android:icon="@drawable/logo"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppThemeNormal" >
|
||||
android:theme="@style/AppNormalTheme" >
|
||||
<!-- TalkingData -->
|
||||
<meta-data
|
||||
android:name="TD_APP_ID"
|
||||
@ -64,20 +66,25 @@
|
||||
android:value="GH_TEST"/>
|
||||
<!--android:value="${CHANNEL_VALUE}"-->
|
||||
|
||||
<!-- MTA -->
|
||||
|
||||
|
||||
<!-- 友盟推送 -->
|
||||
<meta-data
|
||||
android:name="TA_APPKEY"
|
||||
android:value="APV567FTBS7J"/>
|
||||
android:name="UMENG_APPKEY"
|
||||
android:value="58e5b0b9c62dca35a00005e6">
|
||||
<!-- TODO 585a29fa8f4a9d327600023e -->
|
||||
</meta-data>
|
||||
<meta-data
|
||||
android:name="InstallChannel"
|
||||
android:value="GH_TEST"/>
|
||||
<!--android:value="${CHANNEL_VALUE}"-->
|
||||
android:name="UMENG_MESSAGE_SECRET"
|
||||
android:value="ca08596492f8a7fde2ab48dceab8c1f3">
|
||||
<!-- TODO 8bcce6bed547ee624f5c2cc64d39a9e9 -->
|
||||
</meta-data>
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SplashScreenActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme_Guide"
|
||||
android:theme="@style/AppGuideTheme"
|
||||
android:uiOptions="splitActionBarWhenNarrow" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -99,20 +106,13 @@
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SearchActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateVisible" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.GameDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
android:configChanges="keyboardHidden" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.NewsDetailActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SettingActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SuggestionActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateVisible" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ConcernActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -143,7 +143,67 @@
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.MessageDetailActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity android:name="com.gh.gamecenter.SkipActivity"
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.LibaoActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.LibaoDetailActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ShareGhWfifActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ShareGhActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.CleanApkActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.KcSelectGameActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ChooseReceiverActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.ReceiverWaitingActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.FileSenderActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.FileReceiverActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SelectUserIconActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.AboutActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.NewsNormalActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.KaiFuActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.CommentDetailActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.GameDetailActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SuggestSelectActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name=".SuggestionActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize" />
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.VoteActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize"/>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.SkipActivity"
|
||||
android:theme="@android:style/Theme.Translucent">
|
||||
<intent-filter>
|
||||
<data android:scheme="ghzhushou"/>
|
||||
@ -153,21 +213,6 @@
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.mob.tools.MobUIShell"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize" >
|
||||
<intent-filter>
|
||||
<data android:scheme="tencent100371282" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.wxapi.WXEntryActivity"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
@ -293,6 +338,20 @@
|
||||
|
||||
<service
|
||||
android:name="com.gh.download.DownloadService" />
|
||||
|
||||
<!--<service-->
|
||||
<!--android:name="com.gh.base.AppTinkerResultService"-->
|
||||
<!--android:exported="false" />-->
|
||||
|
||||
<service
|
||||
android:name="com.gh.gamecenter.statistics.AppStaticService" />
|
||||
|
||||
<service
|
||||
android:name="com.xiaomi.push.service.XMJobService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:process=":pushservice" />
|
||||
<service
|
||||
android:name="com.xiaomi.push.service.XMPushService"
|
||||
android:enabled="true"
|
||||
|
||||
67
app/src/main/assets/Home.html
Normal file
67
app/src/main/assets/Home.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!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
@ -1,30 +1,149 @@
|
||||
9u=\u4E5D\u6E38\u7248
|
||||
360=360\u7248
|
||||
baidu=\u767E\u5EA6\u7248
|
||||
dangle=\u5F53\u4E50\u7248
|
||||
ouwan=\u5076\u73A9\u7248
|
||||
gf=\u5B98\u65B9\u7248
|
||||
mi=\u5C0F\u7C73\u7248
|
||||
oppo=OPPO\u7248
|
||||
91=91\u7248
|
||||
wdj=\u8C4C\u8C46\u835A\u7248
|
||||
vivo=VIVO\u7248
|
||||
pps=PPS\u7248
|
||||
37wan=37\u73A9\u7248
|
||||
anzhi=\u5B89\u667A\u7248
|
||||
ewan=\u76CA\u73A9\u7248
|
||||
huawei=\u534E\u4E3A\u7248
|
||||
gf-h=\u5B98\u65B9-\u4E13\u670D\u7248
|
||||
gf-z=\u5B98\u65B9-\u6DF7\u670D\u7248
|
||||
shuizhu=\u6C34\u716E\u7248
|
||||
jifeng=\u673A\u950B\u7248
|
||||
azsc=\u5B89\u5353\u5E02\u573A\u7248
|
||||
lenvono=\u8054\u60F3\u7248
|
||||
jinshan=\u91D1\u5C71\u7248
|
||||
mumayi=\u6728\u8682\u8681\u7248
|
||||
gf-n=\u5B98\u7F51-\u5185\u7248
|
||||
gf-w=\u5B98\u7F51-\u5916\u7248
|
||||
duoku=\u591A\u9177\u7248
|
||||
pptv=PPTV\u7248
|
||||
\u5B98\u65B9\u7248=\u5B98\u65B9\u7248
|
||||
\u5B98\u65B9=\u5B98\u65B9\u7248
|
||||
9u=\u4e5d\u6e38
|
||||
4399=\u0034\u0033\u0039\u0039
|
||||
yyb=\u5e94\u7528\u5b9d
|
||||
xm=\u5c0f\u7c73
|
||||
dl=\u5f53\u4e50
|
||||
91=\u0039\u0031
|
||||
yw=\u76ca\u73a9
|
||||
gf-w=\u5b98\u65b9\u5916
|
||||
az=\u5b89\u667a
|
||||
oppo=\u006f\u0070\u0070\u006f
|
||||
wdj=\u8c4c\u8c46\u835a
|
||||
360=\u0033\u0036\u0030
|
||||
vivo=\u0076\u0069\u0076\u006f
|
||||
pps=\u0050\u0050\u0053
|
||||
hw=\u534e\u4e3a
|
||||
37=\u0033\u0037\u73a9
|
||||
baidu=\u767e\u5ea6
|
||||
ow=\u5076\u73a9
|
||||
gf=\u5b98\u65b9
|
||||
lenovo=\u8054\u60f3
|
||||
pptv=\u0050\u0050\u0054\u0056
|
||||
jf=\u673a\u950b
|
||||
mumayi=\u6728\u8682\u8681
|
||||
8868=\u0038\u0038\u0036\u0038
|
||||
19196=\u0031\u0039\u0031\u0039\u0036
|
||||
07073=\u0030\u0037\u0030\u0037\u0033
|
||||
gp=\u679c\u76d8
|
||||
mzw=\u62c7\u6307\u73a9
|
||||
af=\u5b89\u950b
|
||||
lb=\u730e\u5b9d
|
||||
ayx=\u963f\u6e38\u620f
|
||||
tt=\u0054\u0054
|
||||
xiongmao=\u718a\u732b\u73a9
|
||||
aq=\u5b89\u8da3
|
||||
ls=\u4e50\u89c6
|
||||
jl=\u91d1\u7acb
|
||||
lehh=\u4e50\u55e8\u55e8
|
||||
pyw=\u670b\u53cb\u73a9
|
||||
azsc=\u5b89\u5353\u5e02\u573a
|
||||
hyx=\u548c\u6e38\u620f
|
||||
aiyouxi=\u7231\u6e38\u620f
|
||||
woyouxi=\u6c83\u6e38\u620f
|
||||
lg=\u4e50\u8d2d\u0028\u70b9\u70b9\u0029
|
||||
gf-h=\u0028\u65e7\u0029\u5b98\u65b9\u4e13\u670d
|
||||
gf-z=\u0028\u65e7\u0029\u5b98\u65b9\u6df7\u670d
|
||||
gfzf=\u5b98\u65b9\u4e13\u670d
|
||||
gfhf=\u5b98\u65b9\u6df7\u670d
|
||||
owzf=\u5076\u73a9\u4e13\u670d
|
||||
gf-n=\u5b98\u65b9\u5185
|
||||
my=\u9b54\u9047
|
||||
jinshan=\u91d1\u5c71
|
||||
duoku=\u591a\u9177
|
||||
yk=\u4f18\u9177
|
||||
xl=\u65b0\u6d6a
|
||||
sougou=\u641c\u72d7
|
||||
dy=\u6597\u9c7c
|
||||
dw=\u591a\u73a9
|
||||
tf=\u53f0\u670d
|
||||
xunlei=\u8fc5\u96f7
|
||||
ky=\u5feb\u7528
|
||||
lenvono=\u0028\u65e7\u0029\u8054\u60f3
|
||||
jifeng=\u0028\u65e7\u0029\u673a\u950b
|
||||
anzhi=\u0028\u65e7\u0029\u5b89\u667a
|
||||
37wan=\u0028\u65e7\u0029\u0033\u0037\u73a9
|
||||
ewan=\u0028\u65e7\u0029\u76ca\u73a9
|
||||
mi=\u0028\u65e7\u0029\u5c0f\u7c73
|
||||
dangle=\u0028\u65e7\u0029\u5f53\u4e50
|
||||
ouwan=\u0028\u65e7\u0029\u5076\u73a9
|
||||
ttyy=\u0028\u65e7\u0029\u0054\u0054
|
||||
huawei=\u0028\u65e7\u0029\u534e\u4e3a
|
||||
shuizhu=\u0028\u65e7\u0029\u6c34\u716e
|
||||
43997=\u0034\u0033\u0039\u0039\u0037
|
||||
19196zf=\u0031\u0039\u0031\u0039\u0036\u4e13\u670d
|
||||
51508=\u0035\u0031\u0035\u0030\u0038
|
||||
zzb=\u81f3\u5c0a\u5b9d
|
||||
wkd=\u73a9\u5ba2\u7248
|
||||
yq=\u4f18\u8da3
|
||||
tiantian=\u5929\u5929
|
||||
ddw=\u70b9\u70b9\u73a9
|
||||
by=\u7206\u6e38
|
||||
as=\u7231\u4e0a
|
||||
flb=\u5c0f\u7b28\u6e38\u620f
|
||||
cf=\u695a\u98ce
|
||||
itools=\u0049\u0054\u004f\u004f\u004c\u0053
|
||||
ayw=\u7231\u7ea6\u73a9
|
||||
cc=\u866b\u866b
|
||||
kpzs=\u9760\u8c31\u52a9\u624b
|
||||
xtt=\u65b0\u0074\u0074
|
||||
yt=\u6e38\u9014\u7248
|
||||
9665=\u0039\u0036\u0036\u0035
|
||||
lehhkf=\u4e50\u55e8\u55e8\u006b\u0066
|
||||
lehhol=\u4e50\u55e8\u55e8\u006f\u006c
|
||||
afly=\u5b89\u950b\u006c\u0079
|
||||
360hf=\u0033\u0036\u0030\u6df7\u670d\u7248
|
||||
qql=\u9f50\u9f50\u4e50
|
||||
yehuo=\u91ce\u706b\u7248
|
||||
jizhi=\u6781\u81f4\u7248
|
||||
shengxun=\u76db\u8baf\u6e38\u620f
|
||||
9upt=\u4e5d\u6e38\u666e\u901a\u7248
|
||||
9ujs=\u4e5d\u6e38\u52a0\u901f\u7248
|
||||
xmpt=\u5c0f\u7c73\u666e\u901a\u7248
|
||||
xmjs=\u5c0f\u7c73\u52a0\u901f\u7248
|
||||
gfpt=\u5b98\u65b9\u666e\u901a\u7248
|
||||
gfjs=\u5b98\u65b9\u52a0\u901f\u7248
|
||||
wkd1=\u73a9\u5ba2\u65b0\u7248
|
||||
5288=\u0035\u0032\u0038\u0038
|
||||
7guo=\u4e03\u679c
|
||||
zhanyou=\u5c55\u6e38\u7248
|
||||
babie=\u5df4\u522b\u65f6\u4ee3\u7248
|
||||
yueyou=\u7ea6\u6e38\u7248
|
||||
kuniu=\u9177\u725b\u7248
|
||||
chukong=\u89e6\u63a7\u7248
|
||||
changxiang=\u7545\u60f3\u7248
|
||||
zhuohua=\u707c\u534e\u7248
|
||||
duokemeng=\u54c6\u53ef\u68a6\u7248
|
||||
xuanyun=\u7384\u4e91\u7248
|
||||
hanqu=\u701a\u8da3\u7248
|
||||
wapu=\u86d9\u6251\u7248
|
||||
shengli=\u80dc\u5229\u6e38\u620f\u7248
|
||||
heitao=\u9ed1\u6843\u4e92\u52a8\u7248
|
||||
xumei=\u65ed\u6885\u6e38\u620f\u7248
|
||||
weixun=\u5fae\u8baf\u7248
|
||||
tianxiang=\u5929\u8c61\u4e92\u52a8\u7248
|
||||
taiqi=\u6cf0\u5947\u7248
|
||||
chujian=\u521d\u89c1\u7248
|
||||
gaiya=\u76d6\u5a05\u7248
|
||||
wanmei=\u5b8c\u7f8e\u4e16\u754c\u7248
|
||||
zhuoyue=\u5353\u8d8a\u7248
|
||||
meifeng=\u7f8e\u5cf0\u7248
|
||||
xuanji=\u7384\u673a\u7248
|
||||
changyou=\u7545\u6e38\u7248
|
||||
syg=\u624b\u6e38\u72d7
|
||||
youzu=\u6e38\u65cf\u7248
|
||||
bili=\u0062\u0069\u006c\u0069\u0062\u0069\u006c\u0069\u7248
|
||||
ly=\u4e50\u6e38
|
||||
gfwy=\u7f51\u9875\u7248
|
||||
miqi=\u7c73\u5947\u73a9
|
||||
ayw=\u6e38\u620f\u0066\u0061\u006e
|
||||
ys=\u591c\u795e\u7248
|
||||
aofei=\u5965\u98de
|
||||
mgw=\u8611\u83c7\u73a9
|
||||
longc=\u9f99\u57ce\u7248
|
||||
16y=\u0031\u0036\u6e38
|
||||
xq=\u5c0f\u4e03
|
||||
yuwan=\u9c7c\u4e38\u7248
|
||||
jgp=\u679c\u76d8\u65e7
|
||||
lequ=\u6dd8\u8da3
|
||||
jianguo=\u575a\u679c\u7248
|
||||
yanmeng=\u5ef6\u68a6\u7248
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Error indicating that there was an authentication failure when performing a Request.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthFailureError extends VolleyError {
|
||||
/** An intent that can be used to resolve this exception. (Brings up the password dialog.) */
|
||||
private Intent mResolutionIntent;
|
||||
|
||||
public AuthFailureError() { }
|
||||
|
||||
public AuthFailureError(Intent intent) {
|
||||
mResolutionIntent = intent;
|
||||
}
|
||||
|
||||
public AuthFailureError(NetworkResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
public AuthFailureError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AuthFailureError(String message, Exception reason) {
|
||||
super(message, reason);
|
||||
}
|
||||
|
||||
public Intent getResolutionIntent() {
|
||||
return mResolutionIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (mResolutionIntent != null) {
|
||||
return "User needs to (re)enter credentials.";
|
||||
}
|
||||
return super.getMessage();
|
||||
}
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An interface for a cache keyed by a String with a byte array as data.
|
||||
*/
|
||||
public interface Cache {
|
||||
/**
|
||||
* Retrieves an entry from the cache.
|
||||
* @param key Cache key
|
||||
* @return An {@link Entry} or null in the event of a cache miss
|
||||
*/
|
||||
public Entry get(String key);
|
||||
|
||||
/**
|
||||
* Adds or replaces an entry to the cache.
|
||||
* @param key Cache key
|
||||
* @param entry Data to store and metadata for cache coherency, TTL, etc.
|
||||
*/
|
||||
public void put(String key, Entry entry);
|
||||
|
||||
/**
|
||||
* Performs any potentially long-running actions needed to initialize the cache;
|
||||
* will be called from a worker thread.
|
||||
*/
|
||||
public void initialize();
|
||||
|
||||
/**
|
||||
* Invalidates an entry in the cache.
|
||||
* @param key Cache key
|
||||
* @param fullExpire True to fully expire the entry, false to soft expire
|
||||
*/
|
||||
public void invalidate(String key, boolean fullExpire);
|
||||
|
||||
/**
|
||||
* Removes an entry from the cache.
|
||||
* @param key Cache key
|
||||
*/
|
||||
public void remove(String key);
|
||||
|
||||
/**
|
||||
* Empties the cache.
|
||||
*/
|
||||
public void clear();
|
||||
|
||||
/**
|
||||
* Data and metadata for an entry returned by the cache.
|
||||
*/
|
||||
public static class Entry {
|
||||
/** The data returned from cache. */
|
||||
public byte[] data;
|
||||
|
||||
/** ETag for cache coherency. */
|
||||
public String etag;
|
||||
|
||||
/** Date of this response as reported by the server. */
|
||||
public long serverDate;
|
||||
|
||||
/** TTL for this record. */
|
||||
public long ttl;
|
||||
|
||||
/** Soft TTL for this record. */
|
||||
public long softTtl;
|
||||
|
||||
/** Immutable response headers as received from server; must be non-null. */
|
||||
public Map<String, String> responseHeaders = Collections.emptyMap();
|
||||
|
||||
/** True if the entry is expired. */
|
||||
public boolean isExpired() {
|
||||
return this.ttl < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/** True if a refresh is needed from the original data source. */
|
||||
public boolean refreshNeeded() {
|
||||
return this.softTtl < System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.os.Process;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* Provides a thread for performing cache triage on a queue of requests.
|
||||
*
|
||||
* Requests added to the specified cache queue are resolved from cache.
|
||||
* Any deliverable response is posted back to the caller via a
|
||||
* {@link ResponseDelivery}. Cache misses and responses that require
|
||||
* refresh are enqueued on the specified network queue for processing
|
||||
* by a {@link NetworkDispatcher}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class CacheDispatcher extends Thread {
|
||||
|
||||
private static final boolean DEBUG = VolleyLog.DEBUG;
|
||||
|
||||
/** The queue of requests coming in for triage. */
|
||||
private final BlockingQueue<Request> mCacheQueue;
|
||||
|
||||
/** The queue of requests going out to the network. */
|
||||
private final BlockingQueue<Request> mNetworkQueue;
|
||||
|
||||
/** The cache to read from. */
|
||||
private final Cache mCache;
|
||||
|
||||
/** For posting responses. */
|
||||
private final ResponseDelivery mDelivery;
|
||||
|
||||
/** Used for telling us to die. */
|
||||
private volatile boolean mQuit = false;
|
||||
|
||||
/**
|
||||
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
|
||||
* in order to begin processing.
|
||||
*
|
||||
* @param cacheQueue Queue of incoming requests for triage
|
||||
* @param networkQueue Queue to post requests that require network to
|
||||
* @param cache Cache interface to use for resolution
|
||||
* @param delivery Delivery interface to use for posting responses
|
||||
*/
|
||||
public CacheDispatcher(
|
||||
BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
|
||||
Cache cache, ResponseDelivery delivery) {
|
||||
mCacheQueue = cacheQueue;
|
||||
mNetworkQueue = networkQueue;
|
||||
mCache = cache;
|
||||
mDelivery = delivery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces this dispatcher to quit immediately. If any requests are still in
|
||||
* the queue, they are not guaranteed to be processed.
|
||||
*/
|
||||
public void quit() {
|
||||
mQuit = true;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) VolleyLog.v("start new dispatcher");
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
||||
// Make a blocking call to initialize the cache.
|
||||
mCache.initialize();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// Get a request from the cache triage queue, blocking until
|
||||
// at least one is available.
|
||||
final Request request = mCacheQueue.take();
|
||||
request.addMarker("cache-queue-take");
|
||||
|
||||
// If the request has been canceled, don't bother dispatching it.
|
||||
if (request.isCanceled()) {
|
||||
request.finish("cache-discard-canceled");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt to retrieve this item from cache.
|
||||
Cache.Entry entry = mCache.get(request.getCacheKey());
|
||||
if (entry == null) {
|
||||
request.addMarker("cache-miss");
|
||||
// Cache miss; send off to the network dispatcher.
|
||||
mNetworkQueue.put(request);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it is completely expired, just send it to the network.
|
||||
if (entry.isExpired()) {
|
||||
request.addMarker("cache-hit-expired");
|
||||
request.setCacheEntry(entry);
|
||||
mNetworkQueue.put(request);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a cache hit; parse its data for delivery back to the request.
|
||||
request.addMarker("cache-hit");
|
||||
Response<?> response = request.parseNetworkResponse(
|
||||
new NetworkResponse(entry.data, entry.responseHeaders));
|
||||
request.addMarker("cache-hit-parsed");
|
||||
|
||||
if (!entry.refreshNeeded()) {
|
||||
// Completely unexpired cache hit. Just deliver the response.
|
||||
mDelivery.postResponse(request, response);
|
||||
} else {
|
||||
// Soft-expired cache hit. We can deliver the cached response,
|
||||
// but we need to also send the request to the network for
|
||||
// refreshing.
|
||||
request.addMarker("cache-hit-refresh-needed");
|
||||
request.setCacheEntry(entry);
|
||||
|
||||
// Mark the response as intermediate.
|
||||
response.intermediate = true;
|
||||
|
||||
// Post the intermediate response back to the user and have
|
||||
// the delivery then forward the request along to the network.
|
||||
mDelivery.postResponse(request, response, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mNetworkQueue.put(request);
|
||||
} catch (InterruptedException e) {
|
||||
// Not much we can do about this.
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// We may have been interrupted because it was time to quit.
|
||||
if (mQuit) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Default retry policy for requests.
|
||||
*/
|
||||
public class DefaultRetryPolicy implements RetryPolicy {
|
||||
/** The current timeout in milliseconds. */
|
||||
private int mCurrentTimeoutMs;
|
||||
|
||||
/** The current retry count. */
|
||||
private int mCurrentRetryCount;
|
||||
|
||||
/** The maximum number of attempts. */
|
||||
private final int mMaxNumRetries;
|
||||
|
||||
/** The backoff multiplier for for the policy. */
|
||||
private final float mBackoffMultiplier;
|
||||
|
||||
/** The default socket timeout in milliseconds */
|
||||
public static final int DEFAULT_TIMEOUT_MS = 2500;
|
||||
|
||||
/** The default number of retries */
|
||||
public static final int DEFAULT_MAX_RETRIES = 1;
|
||||
|
||||
/** The default backoff multiplier */
|
||||
public static final float DEFAULT_BACKOFF_MULT = 1f;
|
||||
|
||||
/**
|
||||
* Constructs a new retry policy using the default timeouts.
|
||||
*/
|
||||
public DefaultRetryPolicy() {
|
||||
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new retry policy.
|
||||
* @param initialTimeoutMs The initial timeout for the policy.
|
||||
* @param maxNumRetries The maximum number of retries.
|
||||
* @param backoffMultiplier Backoff multiplier for the policy.
|
||||
*/
|
||||
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
|
||||
mCurrentTimeoutMs = initialTimeoutMs;
|
||||
mMaxNumRetries = maxNumRetries;
|
||||
mBackoffMultiplier = backoffMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current timeout.
|
||||
*/
|
||||
@Override
|
||||
public int getCurrentTimeout() {
|
||||
return mCurrentTimeoutMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current retry count.
|
||||
*/
|
||||
@Override
|
||||
public int getCurrentRetryCount() {
|
||||
return mCurrentRetryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares for the next retry by applying a backoff to the timeout.
|
||||
* @param error The error code of the last attempt.
|
||||
*/
|
||||
@Override
|
||||
public void retry(VolleyError error) throws VolleyError {
|
||||
mCurrentRetryCount++;
|
||||
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
|
||||
if (!hasAttemptRemaining()) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this policy has attempts remaining, false otherwise.
|
||||
*/
|
||||
protected boolean hasAttemptRemaining() {
|
||||
return mCurrentRetryCount <= mMaxNumRetries;
|
||||
}
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Delivers responses and errors.
|
||||
*/
|
||||
public class ExecutorDelivery implements ResponseDelivery {
|
||||
/** Used for posting responses, typically to the main thread. */
|
||||
private final Executor mResponsePoster;
|
||||
|
||||
/**
|
||||
* Creates a new response delivery interface.
|
||||
* @param handler {@link Handler} to post responses on
|
||||
*/
|
||||
public ExecutorDelivery(final Handler handler) {
|
||||
// Make an Executor that just wraps the handler.
|
||||
mResponsePoster = new Executor() {
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
handler.post(command);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new response delivery interface, mockable version
|
||||
* for testing.
|
||||
* @param executor For running delivery tasks
|
||||
*/
|
||||
public ExecutorDelivery(Executor executor) {
|
||||
mResponsePoster = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postResponse(Request<?> request, Response<?> response) {
|
||||
postResponse(request, response, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
|
||||
request.markDelivered();
|
||||
request.addMarker("post-response");
|
||||
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postError(Request<?> request, VolleyError error) {
|
||||
request.addMarker("post-error");
|
||||
Response<?> response = Response.error(error);
|
||||
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* A Runnable used for delivering network responses to a listener on the
|
||||
* main thread.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private class ResponseDeliveryRunnable implements Runnable {
|
||||
private final Request mRequest;
|
||||
private final Response mResponse;
|
||||
private final Runnable mRunnable;
|
||||
|
||||
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
|
||||
mRequest = request;
|
||||
mResponse = response;
|
||||
mRunnable = runnable;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void run() {
|
||||
// If this request has canceled, finish it and don't deliver.
|
||||
if (mRequest.isCanceled()) {
|
||||
mRequest.finish("canceled-at-delivery");
|
||||
return;
|
||||
}
|
||||
|
||||
// Deliver a normal response or error, depending.
|
||||
if (mResponse.isSuccess()) {
|
||||
mRequest.deliverResponse(mResponse.result);
|
||||
} else {
|
||||
mRequest.deliverError(mResponse.error);
|
||||
}
|
||||
|
||||
// If this is an intermediate response, add a marker, otherwise we're done
|
||||
// and the request can be finished.
|
||||
if (mResponse.intermediate) {
|
||||
mRequest.addMarker("intermediate-response");
|
||||
} else {
|
||||
mRequest.finish("done");
|
||||
}
|
||||
|
||||
// If we have been provided a post-delivery runnable, run it.
|
||||
if (mRunnable != null) {
|
||||
mRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* An interface for performing requests.
|
||||
*/
|
||||
public interface Network {
|
||||
/**
|
||||
* Performs the specified request.
|
||||
* @param request Request to process
|
||||
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
|
||||
* @throws VolleyError on errors
|
||||
*/
|
||||
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
|
||||
}
|
||||
@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.net.TrafficStats;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* Provides a thread for performing network dispatch from a queue of requests.
|
||||
*
|
||||
* Requests added to the specified queue are processed from the network via a
|
||||
* specified {@link Network} interface. Responses are committed to cache, if
|
||||
* eligible, using a specified {@link Cache} interface. Valid responses and
|
||||
* errors are posted back to the caller via a {@link ResponseDelivery}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class NetworkDispatcher extends Thread {
|
||||
/** The queue of requests to service. */
|
||||
private final BlockingQueue<Request> mQueue;
|
||||
/** The network interface for processing requests. */
|
||||
private final Network mNetwork;
|
||||
/** The cache to write to. */
|
||||
private final Cache mCache;
|
||||
/** For posting responses and errors. */
|
||||
private final ResponseDelivery mDelivery;
|
||||
/** Used for telling us to die. */
|
||||
private volatile boolean mQuit = false;
|
||||
|
||||
/**
|
||||
* Creates a new network dispatcher thread. You must call {@link #start()}
|
||||
* in order to begin processing.
|
||||
*
|
||||
* @param queue Queue of incoming requests for triage
|
||||
* @param network Network interface to use for performing requests
|
||||
* @param cache Cache interface to use for writing responses to cache
|
||||
* @param delivery Delivery interface to use for posting responses
|
||||
*/
|
||||
public NetworkDispatcher(BlockingQueue<Request> queue,
|
||||
Network network, Cache cache,
|
||||
ResponseDelivery delivery) {
|
||||
mQueue = queue;
|
||||
mNetwork = network;
|
||||
mCache = cache;
|
||||
mDelivery = delivery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces this dispatcher to quit immediately. If any requests are still in
|
||||
* the queue, they are not guaranteed to be processed.
|
||||
*/
|
||||
public void quit() {
|
||||
mQuit = true;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
Request request;
|
||||
while (true) {
|
||||
try {
|
||||
// Take a request from the queue.
|
||||
request = mQueue.take();
|
||||
} catch (InterruptedException e) {
|
||||
// We may have been interrupted because it was time to quit.
|
||||
if (mQuit) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
request.addMarker("network-queue-take");
|
||||
|
||||
// If the request was cancelled already, do not perform the
|
||||
// network request.
|
||||
if (request.isCanceled()) {
|
||||
request.finish("network-discard-cancelled");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tag the request (if API >= 14)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
|
||||
}
|
||||
|
||||
// Perform the network request.
|
||||
NetworkResponse networkResponse = mNetwork.performRequest(request);
|
||||
request.addMarker("network-http-complete");
|
||||
|
||||
// If the server returned 304 AND we delivered a response already,
|
||||
// we're done -- don't deliver a second identical response.
|
||||
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
|
||||
request.finish("not-modified");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the response here on the worker thread.
|
||||
Response<?> response = request.parseNetworkResponse(networkResponse);
|
||||
request.addMarker("network-parse-complete");
|
||||
|
||||
// Write to cache if applicable.
|
||||
// TODO: Only update cache metadata instead of entire record for 304s.
|
||||
if (request.shouldCache() && response.cacheEntry != null) {
|
||||
mCache.put(request.getCacheKey(), response.cacheEntry);
|
||||
request.addMarker("network-cache-written");
|
||||
}
|
||||
|
||||
// Post the response back.
|
||||
request.markDelivered();
|
||||
mDelivery.postResponse(request, response);
|
||||
} catch (VolleyError volleyError) {
|
||||
parseAndDeliverNetworkError(request, volleyError);
|
||||
} catch (Exception e) {
|
||||
VolleyLog.e(e, "Unhandled exception %s", e.toString());
|
||||
mDelivery.postError(request, new VolleyError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
|
||||
error = request.parseNetworkError(error);
|
||||
mDelivery.postError(request, error);
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that there was a network error when performing a Volley request.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NetworkError extends VolleyError {
|
||||
public NetworkError() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NetworkError(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public NetworkError(NetworkResponse networkResponse) {
|
||||
super(networkResponse);
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Data and headers returned from {@link Network#performRequest(Request)}.
|
||||
*/
|
||||
public class NetworkResponse {
|
||||
/**
|
||||
* Creates a new network response.
|
||||
* @param statusCode the HTTP status code
|
||||
* @param data Response body
|
||||
* @param headers Headers returned with this response, or null for none
|
||||
* @param notModified True if the server returned a 304 and the data was already in cache
|
||||
*/
|
||||
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
|
||||
boolean notModified) {
|
||||
this.statusCode = statusCode;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
this.notModified = notModified;
|
||||
}
|
||||
|
||||
public NetworkResponse(byte[] data) {
|
||||
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
|
||||
}
|
||||
|
||||
public NetworkResponse(byte[] data, Map<String, String> headers) {
|
||||
this(HttpStatus.SC_OK, data, headers, false);
|
||||
}
|
||||
|
||||
/** The HTTP status code. */
|
||||
public final int statusCode;
|
||||
|
||||
/** Raw data from this response. */
|
||||
public final byte[] data;
|
||||
|
||||
/** Response headers. */
|
||||
public final Map<String, String> headers;
|
||||
|
||||
/** True if the server returned a 304 (Not Modified). */
|
||||
public final boolean notModified;
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Error indicating that no connection could be established when performing a Volley request.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NoConnectionError extends NetworkError {
|
||||
public NoConnectionError() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NoConnectionError(Throwable reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that the server's response could not be parsed.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ParseError extends VolleyError {
|
||||
public ParseError() { }
|
||||
|
||||
public ParseError(NetworkResponse networkResponse) {
|
||||
super(networkResponse);
|
||||
}
|
||||
|
||||
public ParseError(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@ -1,543 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.net.TrafficStats;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.volley.VolleyLog.MarkerLog;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base class for all network requests.
|
||||
*
|
||||
* @param <T> The type of parsed response this request expects.
|
||||
*/
|
||||
public abstract class Request<T> implements Comparable<Request<T>> {
|
||||
|
||||
/**
|
||||
* Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
|
||||
*/
|
||||
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
|
||||
|
||||
/**
|
||||
* Supported request methods.
|
||||
*/
|
||||
public interface Method {
|
||||
int DEPRECATED_GET_OR_POST = -1;
|
||||
int GET = 0;
|
||||
int POST = 1;
|
||||
int PUT = 2;
|
||||
int DELETE = 3;
|
||||
}
|
||||
|
||||
/** An event log tracing the lifetime of this request; for debugging. */
|
||||
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
|
||||
|
||||
/** Request method of this request. Currently supports GET, POST, PUT, and DELETE. */
|
||||
private final int mMethod;
|
||||
|
||||
/** URL of this request. */
|
||||
private final String mUrl;
|
||||
|
||||
/** Default tag for {@link TrafficStats}. */
|
||||
private final int mDefaultTrafficStatsTag;
|
||||
|
||||
/** Listener interface for errors. */
|
||||
private final Response.ErrorListener mErrorListener;
|
||||
|
||||
/** Sequence number of this request, used to enforce FIFO ordering. */
|
||||
private Integer mSequence;
|
||||
|
||||
/** The request queue this request is associated with. */
|
||||
private RequestQueue mRequestQueue;
|
||||
|
||||
/** Whether or not responses to this request should be cached. */
|
||||
private boolean mShouldCache = true;
|
||||
|
||||
/** Whether or not this request has been canceled. */
|
||||
private boolean mCanceled = false;
|
||||
|
||||
/** Whether or not a response has been delivered for this request yet. */
|
||||
private boolean mResponseDelivered = false;
|
||||
|
||||
// A cheap variant of request tracing used to dump slow requests.
|
||||
private long mRequestBirthTime = 0;
|
||||
|
||||
/** Threshold at which we should log the request (even when debug logging is not enabled). */
|
||||
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
|
||||
|
||||
/** The retry policy for this request. */
|
||||
private RetryPolicy mRetryPolicy;
|
||||
|
||||
/**
|
||||
* When a request can be retrieved from cache but must be refreshed from
|
||||
* the network, the cache entry will be stored here so that in the event of
|
||||
* a "Not Modified" response, we can be sure it hasn't been evicted from cache.
|
||||
*/
|
||||
private Cache.Entry mCacheEntry = null;
|
||||
|
||||
/** An opaque token tagging this request; used for bulk cancellation. */
|
||||
private Object mTag;
|
||||
|
||||
/**
|
||||
* Creates a new request with the given URL and error listener. Note that
|
||||
* the normal response listener is not provided here as delivery of responses
|
||||
* is provided by subclasses, who have a better idea of how to deliver an
|
||||
* already-parsed response.
|
||||
*
|
||||
* @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
|
||||
*/
|
||||
public Request(String url, Response.ErrorListener listener) {
|
||||
this(Method.DEPRECATED_GET_OR_POST, url, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request with the given method (one of the values from {@link Method}),
|
||||
* URL, and error listener. Note that the normal response listener is not provided here as
|
||||
* delivery of responses is provided by subclasses, who have a better idea of how to deliver
|
||||
* an already-parsed response.
|
||||
*/
|
||||
public Request(int method, String url, Response.ErrorListener listener) {
|
||||
mMethod = method;
|
||||
mUrl = url;
|
||||
mErrorListener = listener;
|
||||
setRetryPolicy(new DefaultRetryPolicy());
|
||||
|
||||
mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the method for this request. Can be one of the values in {@link Method}.
|
||||
*/
|
||||
public int getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a tag on this request. Can be used to cancel all requests with this
|
||||
* tag by {@link RequestQueue#cancelAll(Object)}.
|
||||
*/
|
||||
public void setTag(Object tag) {
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this request's tag.
|
||||
* @see Request#setTag(Object)
|
||||
*/
|
||||
public Object getTag() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
|
||||
*/
|
||||
public int getTrafficStatsTag() {
|
||||
return mDefaultTrafficStatsTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the retry policy for this request.
|
||||
*/
|
||||
public void setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
mRetryPolicy = retryPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event to this request's event log; for debugging.
|
||||
*/
|
||||
public void addMarker(String tag) {
|
||||
if (MarkerLog.ENABLED) {
|
||||
mEventLog.add(tag, Thread.currentThread().getId());
|
||||
} else if (mRequestBirthTime == 0) {
|
||||
mRequestBirthTime = SystemClock.elapsedRealtime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the request queue that this request has finished (successfully or with error).
|
||||
*
|
||||
* <p>Also dumps all events from this request's event log; for debugging.</p>
|
||||
*/
|
||||
void finish(final String tag) {
|
||||
if (mRequestQueue != null) {
|
||||
mRequestQueue.finish(this);
|
||||
}
|
||||
if (MarkerLog.ENABLED) {
|
||||
final long threadId = Thread.currentThread().getId();
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
// If we finish marking off of the main thread, we need to
|
||||
// actually do it on the main thread to ensure correct ordering.
|
||||
Handler mainThread = new Handler(Looper.getMainLooper());
|
||||
mainThread.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mEventLog.add(tag, threadId);
|
||||
mEventLog.finish(this.toString());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
mEventLog.add(tag, threadId);
|
||||
mEventLog.finish(this.toString());
|
||||
} else {
|
||||
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
|
||||
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
|
||||
VolleyLog.d("%d ms: %s", requestTime, this.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates this request with the given queue. The request queue will be notified when this
|
||||
* request has finished.
|
||||
*/
|
||||
public void setRequestQueue(RequestQueue requestQueue) {
|
||||
mRequestQueue = requestQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sequence number of this request. Used by {@link RequestQueue}.
|
||||
*/
|
||||
public final void setSequence(int sequence) {
|
||||
mSequence = sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sequence number of this request.
|
||||
*/
|
||||
public final int getSequence() {
|
||||
if (mSequence == null) {
|
||||
throw new IllegalStateException("getSequence called before setSequence");
|
||||
}
|
||||
return mSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of this request.
|
||||
*/
|
||||
public String getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache key for this request. By default, this is the URL.
|
||||
*/
|
||||
public String getCacheKey() {
|
||||
return getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates this request with an entry retrieved for it from cache.
|
||||
* Used for cache coherency support.
|
||||
*/
|
||||
public void setCacheEntry(Cache.Entry entry) {
|
||||
mCacheEntry = entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the annotated cache entry, or null if there isn't one.
|
||||
*/
|
||||
public Cache.Entry getCacheEntry() {
|
||||
return mCacheEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this request as canceled. No callback will be delivered.
|
||||
*/
|
||||
public void cancel() {
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this request has been canceled.
|
||||
*/
|
||||
public boolean isCanceled() {
|
||||
return mCanceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of extra HTTP headers to go along with this request. Can
|
||||
* throw {@link AuthFailureError} as authentication may be required to
|
||||
* provide these values.
|
||||
* @throws AuthFailureError In the event of auth failure
|
||||
*/
|
||||
public Map<String, String> getHeaders() throws AuthFailureError {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of POST parameters to be used for this request, or null if
|
||||
* a simple GET should be used. Can throw {@link AuthFailureError} as
|
||||
* authentication may be required to provide these values.
|
||||
*
|
||||
* <p>Note that only one of getPostParams() and getPostBody() can return a non-null
|
||||
* value.</p>
|
||||
* @throws AuthFailureError In the event of auth failure
|
||||
*
|
||||
* @deprecated Use {@link #getParams()} instead.
|
||||
*/
|
||||
protected Map<String, String> getPostParams() throws AuthFailureError {
|
||||
return getParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which encoding should be used when converting POST parameters returned by
|
||||
* {@link #getPostParams()} into a raw POST body.
|
||||
*
|
||||
* <p>This controls both encodings:
|
||||
* <ol>
|
||||
* <li>The string encoding used when converting parameter names and values into bytes prior
|
||||
* to URL encoding them.</li>
|
||||
* <li>The string encoding used when converting the URL encoded parameters into a raw
|
||||
* byte array.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @deprecated Use {@link #getParamsEncoding()} instead.
|
||||
*/
|
||||
protected String getPostParamsEncoding() {
|
||||
return getParamsEncoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBodyContentType()} instead.
|
||||
*/
|
||||
public String getPostBodyContentType() {
|
||||
return getBodyContentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw POST body to be sent.
|
||||
*
|
||||
* @throws AuthFailureError In the event of auth failure
|
||||
*
|
||||
* @deprecated Use {@link #getBody()} instead.
|
||||
*/
|
||||
public byte[] getPostBody() throws AuthFailureError {
|
||||
// Note: For compatibility with legacy clients of volley, this implementation must remain
|
||||
// here instead of simply calling the getBody() function because this function must
|
||||
// call getPostParams() and getPostParamsEncoding() since legacy clients would have
|
||||
// overridden these two member functions for POST requests.
|
||||
Map<String, String> postParams = getPostParams();
|
||||
if (postParams != null && postParams.size() > 0) {
|
||||
return encodeParameters(postParams, getPostParamsEncoding());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of parameters to be used for a POST or PUT request. Can throw
|
||||
* {@link AuthFailureError} as authentication may be required to provide these values.
|
||||
*
|
||||
* <p>Note that you can directly override {@link #getBody()} for custom data.</p>
|
||||
*
|
||||
* @throws AuthFailureError in the event of auth failure
|
||||
*/
|
||||
protected Map<String, String> getParams() throws AuthFailureError {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which encoding should be used when converting POST or PUT parameters returned by
|
||||
* {@link #getParams()} into a raw POST or PUT body.
|
||||
*
|
||||
* <p>This controls both encodings:
|
||||
* <ol>
|
||||
* <li>The string encoding used when converting parameter names and values into bytes prior
|
||||
* to URL encoding them.</li>
|
||||
* <li>The string encoding used when converting the URL encoded parameters into a raw
|
||||
* byte array.</li>
|
||||
* </ol>
|
||||
*/
|
||||
protected String getParamsEncoding() {
|
||||
return DEFAULT_PARAMS_ENCODING;
|
||||
}
|
||||
|
||||
public String getBodyContentType() {
|
||||
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw POST or PUT body to be sent.
|
||||
*
|
||||
* @throws AuthFailureError in the event of auth failure
|
||||
*/
|
||||
public byte[] getBody() throws AuthFailureError {
|
||||
Map<String, String> params = getParams();
|
||||
if (params != null && params.size() > 0) {
|
||||
return encodeParameters(params, getParamsEncoding());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
|
||||
*/
|
||||
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
|
||||
StringBuilder encodedParams = new StringBuilder();
|
||||
try {
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
|
||||
encodedParams.append('=');
|
||||
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
|
||||
encodedParams.append('&');
|
||||
}
|
||||
return encodedParams.toString().getBytes(paramsEncoding);
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not responses to this request should be cached.
|
||||
*/
|
||||
public final void setShouldCache(boolean shouldCache) {
|
||||
mShouldCache = shouldCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if responses to this request should be cached.
|
||||
*/
|
||||
public final boolean shouldCache() {
|
||||
return mShouldCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Priority values. Requests will be processed from higher priorities to
|
||||
* lower priorities, in FIFO order.
|
||||
*/
|
||||
public enum Priority {
|
||||
LOW,
|
||||
NORMAL,
|
||||
HIGH,
|
||||
IMMEDIATE
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
|
||||
*/
|
||||
public Priority getPriority() {
|
||||
return Priority.NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
|
||||
* per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
|
||||
* attempts remaining, this will cause delivery of a {@link TimeoutError} error.
|
||||
*/
|
||||
public final int getTimeoutMs() {
|
||||
return mRetryPolicy.getCurrentTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the retry policy that should be used for this request.
|
||||
*/
|
||||
public RetryPolicy getRetryPolicy() {
|
||||
return mRetryPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this request as having a response delivered on it. This can be used
|
||||
* later in the request's lifetime for suppressing identical responses.
|
||||
*/
|
||||
public void markDelivered() {
|
||||
mResponseDelivered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this request has had a response delivered for it.
|
||||
*/
|
||||
public boolean hasHadResponseDelivered() {
|
||||
return mResponseDelivered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this to parse the raw network response
|
||||
* and return an appropriate response type. This method will be
|
||||
* called from a worker thread. The response will not be delivered
|
||||
* if you return null.
|
||||
* @param response Response from the network
|
||||
* @return The parsed response, or null in the case of an error
|
||||
*/
|
||||
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to parse 'networkError' and return a more specific error.
|
||||
*
|
||||
* <p>The default implementation just returns the passed 'networkError'.</p>
|
||||
*
|
||||
* @param volleyError the error retrieved from the network
|
||||
* @return an NetworkError augmented with additional information
|
||||
*/
|
||||
protected VolleyError parseNetworkError(VolleyError volleyError) {
|
||||
return volleyError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this to perform delivery of the parsed
|
||||
* response to their listeners. The given response is guaranteed to
|
||||
* be non-null; responses that fail to parse are not delivered.
|
||||
* @param response The parsed response returned by
|
||||
* {@link #parseNetworkResponse(NetworkResponse)}
|
||||
*/
|
||||
abstract protected void deliverResponse(T response);
|
||||
|
||||
/**
|
||||
* Delivers error message to the ErrorListener that the Request was
|
||||
* initialized with.
|
||||
*
|
||||
* @param error Error details
|
||||
*/
|
||||
public void deliverError(VolleyError error) {
|
||||
if (mErrorListener != null) {
|
||||
mErrorListener.onErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our comparator sorts from high to low priority, and secondarily by
|
||||
* sequence number to provide FIFO ordering.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Request<T> other) {
|
||||
Priority left = this.getPriority();
|
||||
Priority right = other.getPriority();
|
||||
|
||||
// High-priority requests are "lesser" so they are sorted to the front.
|
||||
// Equal priorities are sorted by sequence number to provide FIFO ordering.
|
||||
return left == right ?
|
||||
this.mSequence - other.mSequence :
|
||||
right.ordinal() - left.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
|
||||
return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
|
||||
+ getPriority() + " " + mSequence;
|
||||
}
|
||||
}
|
||||
@ -1,287 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A request dispatch queue with a thread pool of dispatchers.
|
||||
*
|
||||
* Calling {@link #add(Request)} will enqueue the given Request for dispatch,
|
||||
* resolving from either cache or network on a worker thread, and then delivering
|
||||
* a parsed response on the main thread.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class RequestQueue {
|
||||
|
||||
/** Used for generating monotonically-increasing sequence numbers for requests. */
|
||||
private AtomicInteger mSequenceGenerator = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* Staging area for requests that already have a duplicate request in flight.
|
||||
*
|
||||
* <ul>
|
||||
* <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
|
||||
* key.</li>
|
||||
* <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
|
||||
* is <em>not</em> contained in that list. Is null if no requests are staged.</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final Map<String, Queue<Request>> mWaitingRequests =
|
||||
new HashMap<String, Queue<Request>>();
|
||||
|
||||
/**
|
||||
* The set of all requests currently being processed by this RequestQueue. A Request
|
||||
* will be in this set if it is waiting in any queue or currently being processed by
|
||||
* any dispatcher.
|
||||
*/
|
||||
private final Set<Request> mCurrentRequests = new HashSet<Request>();
|
||||
|
||||
/** The cache triage queue. */
|
||||
private final PriorityBlockingQueue<Request> mCacheQueue =
|
||||
new PriorityBlockingQueue<Request>();
|
||||
|
||||
/** The queue of requests that are actually going out to the network. */
|
||||
private final PriorityBlockingQueue<Request> mNetworkQueue =
|
||||
new PriorityBlockingQueue<Request>();
|
||||
|
||||
/** Number of network request dispatcher threads to start. */
|
||||
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
|
||||
|
||||
/** Cache interface for retrieving and storing respones. */
|
||||
private final Cache mCache;
|
||||
|
||||
/** Network interface for performing requests. */
|
||||
private final Network mNetwork;
|
||||
|
||||
/** Response delivery mechanism. */
|
||||
private final ResponseDelivery mDelivery;
|
||||
|
||||
/** The network dispatchers. */
|
||||
private NetworkDispatcher[] mDispatchers;
|
||||
|
||||
/** The cache dispatcher. */
|
||||
private CacheDispatcher mCacheDispatcher;
|
||||
|
||||
/**
|
||||
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
|
||||
*
|
||||
* @param cache A Cache to use for persisting responses to disk
|
||||
* @param network A Network interface for performing HTTP requests
|
||||
* @param threadPoolSize Number of network dispatcher threads to create
|
||||
* @param delivery A ResponseDelivery interface for posting responses and errors
|
||||
*/
|
||||
public RequestQueue(Cache cache, Network network, int threadPoolSize,
|
||||
ResponseDelivery delivery) {
|
||||
mCache = cache;
|
||||
mNetwork = network;
|
||||
mDispatchers = new NetworkDispatcher[threadPoolSize];
|
||||
mDelivery = delivery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
|
||||
*
|
||||
* @param cache A Cache to use for persisting responses to disk
|
||||
* @param network A Network interface for performing HTTP requests
|
||||
* @param threadPoolSize Number of network dispatcher threads to create
|
||||
*/
|
||||
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
|
||||
this(cache, network, threadPoolSize,
|
||||
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
|
||||
*
|
||||
* @param cache A Cache to use for persisting responses to disk
|
||||
* @param network A Network interface for performing HTTP requests
|
||||
*/
|
||||
public RequestQueue(Cache cache, Network network) {
|
||||
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the dispatchers in this queue.
|
||||
*/
|
||||
public void start() {
|
||||
stop(); // Make sure any currently running dispatchers are stopped.
|
||||
// Create the cache dispatcher and start it.
|
||||
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
|
||||
mCacheDispatcher.start();
|
||||
|
||||
// Create network dispatchers (and corresponding threads) up to the pool size.
|
||||
for (int i = 0; i < mDispatchers.length; i++) {
|
||||
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
|
||||
mCache, mDelivery);
|
||||
mDispatchers[i] = networkDispatcher;
|
||||
networkDispatcher.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the cache and network dispatchers.
|
||||
*/
|
||||
public void stop() {
|
||||
if (mCacheDispatcher != null) {
|
||||
mCacheDispatcher.quit();
|
||||
}
|
||||
for (int i = 0; i < mDispatchers.length; i++) {
|
||||
if (mDispatchers[i] != null) {
|
||||
mDispatchers[i].quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a sequence number.
|
||||
*/
|
||||
public int getSequenceNumber() {
|
||||
return mSequenceGenerator.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Cache} instance being used.
|
||||
*/
|
||||
public Cache getCache() {
|
||||
return mCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple predicate or filter interface for Requests, for use by
|
||||
* {@link RequestQueue#cancelAll(RequestFilter)}.
|
||||
*/
|
||||
public interface RequestFilter {
|
||||
public boolean apply(Request<?> request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all requests in this queue for which the given filter applies.
|
||||
* @param filter The filtering function to use
|
||||
*/
|
||||
public void cancelAll(RequestFilter filter) {
|
||||
synchronized (mCurrentRequests) {
|
||||
for (Request<?> request : mCurrentRequests) {
|
||||
if (filter.apply(request)) {
|
||||
request.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all requests in this queue with the given tag. Tag must be non-null
|
||||
* and equality is by identity.
|
||||
*/
|
||||
public void cancelAll(final Object tag) {
|
||||
if (tag == null) {
|
||||
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
|
||||
}
|
||||
cancelAll(new RequestFilter() {
|
||||
@Override
|
||||
public boolean apply(Request<?> request) {
|
||||
return request.getTag() == tag;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Request to the dispatch queue.
|
||||
* @param request The request to service
|
||||
* @return The passed-in request
|
||||
*/
|
||||
public Request add(Request request) {
|
||||
// Tag the request as belonging to this queue and add it to the set of current requests.
|
||||
request.setRequestQueue(this);
|
||||
synchronized (mCurrentRequests) {
|
||||
mCurrentRequests.add(request);
|
||||
}
|
||||
|
||||
// Process requests in the order they are added.
|
||||
request.setSequence(getSequenceNumber());
|
||||
request.addMarker("add-to-queue");
|
||||
|
||||
// If the request is uncacheable, skip the cache queue and go straight to the network.
|
||||
if (!request.shouldCache()) {
|
||||
mNetworkQueue.add(request);
|
||||
return request;
|
||||
}
|
||||
|
||||
// Insert request into stage if there's already a request with the same cache key in flight.
|
||||
synchronized (mWaitingRequests) {
|
||||
String cacheKey = request.getCacheKey();
|
||||
if (mWaitingRequests.containsKey(cacheKey)) {
|
||||
// There is already a request in flight. Queue up.
|
||||
Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
|
||||
if (stagedRequests == null) {
|
||||
stagedRequests = new LinkedList<Request>();
|
||||
}
|
||||
stagedRequests.add(request);
|
||||
mWaitingRequests.put(cacheKey, stagedRequests);
|
||||
if (VolleyLog.DEBUG) {
|
||||
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
|
||||
}
|
||||
} else {
|
||||
// Insert 'null' queue for this cacheKey, indicating there is now a request in
|
||||
// flight.
|
||||
mWaitingRequests.put(cacheKey, null);
|
||||
mCacheQueue.add(request);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from {@link Request#finish(String)}, indicating that processing of the given request
|
||||
* has finished.
|
||||
*
|
||||
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if
|
||||
* <code>request.shouldCache()</code>.</p>
|
||||
*/
|
||||
void finish(Request request) {
|
||||
// Remove from the set of requests currently being processed.
|
||||
synchronized (mCurrentRequests) {
|
||||
mCurrentRequests.remove(request);
|
||||
}
|
||||
|
||||
if (request.shouldCache()) {
|
||||
synchronized (mWaitingRequests) {
|
||||
String cacheKey = request.getCacheKey();
|
||||
Queue<Request> waitingRequests = mWaitingRequests.remove(cacheKey);
|
||||
if (waitingRequests != null) {
|
||||
if (VolleyLog.DEBUG) {
|
||||
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
|
||||
waitingRequests.size(), cacheKey);
|
||||
}
|
||||
// Process all queued up requests. They won't be considered as in flight, but
|
||||
// that's not a problem as the cache has been primed by 'request'.
|
||||
mCacheQueue.addAll(waitingRequests);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Encapsulates a parsed response for delivery.
|
||||
*
|
||||
* @param <T> Parsed type of this response
|
||||
*/
|
||||
public class Response<T> {
|
||||
|
||||
/** Callback interface for delivering parsed responses. */
|
||||
public interface Listener<T> {
|
||||
/** Called when a response is received. */
|
||||
public void onResponse(T response);
|
||||
}
|
||||
|
||||
/** Callback interface for delivering error responses. */
|
||||
public interface ErrorListener {
|
||||
/**
|
||||
* Callback method that an error has been occurred with the
|
||||
* provided error code and optional user-readable message.
|
||||
*/
|
||||
public void onErrorResponse(VolleyError error);
|
||||
}
|
||||
|
||||
/** Returns a successful response containing the parsed result. */
|
||||
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
|
||||
return new Response<T>(result, cacheEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a failed response containing the given error code and an optional
|
||||
* localized message displayed to the user.
|
||||
*/
|
||||
public static <T> Response<T> error(VolleyError error) {
|
||||
return new Response<T>(error);
|
||||
}
|
||||
|
||||
/** Parsed response, or null in the case of error. */
|
||||
public final T result;
|
||||
|
||||
/** Cache metadata for this response, or null in the case of error. */
|
||||
public final Cache.Entry cacheEntry;
|
||||
|
||||
/** Detailed error information if <code>errorCode != OK</code>. */
|
||||
public final VolleyError error;
|
||||
|
||||
/** True if this response was a soft-expired one and a second one MAY be coming. */
|
||||
public boolean intermediate = false;
|
||||
|
||||
/**
|
||||
* Returns whether this response is considered successful.
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return error == null;
|
||||
}
|
||||
|
||||
|
||||
private Response(T result, Cache.Entry cacheEntry) {
|
||||
this.result = result;
|
||||
this.cacheEntry = cacheEntry;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
private Response(VolleyError error) {
|
||||
this.result = null;
|
||||
this.cacheEntry = null;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
public interface ResponseDelivery {
|
||||
/**
|
||||
* Parses a response from the network or cache and delivers it.
|
||||
*/
|
||||
public void postResponse(Request<?> request, Response<?> response);
|
||||
|
||||
/**
|
||||
* Parses a response from the network or cache and delivers it. The provided
|
||||
* Runnable will be executed after delivery.
|
||||
*/
|
||||
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
|
||||
|
||||
/**
|
||||
* Posts an error for the given request.
|
||||
*/
|
||||
public void postError(Request<?> request, VolleyError error);
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Retry policy for a request.
|
||||
*/
|
||||
public interface RetryPolicy {
|
||||
|
||||
/**
|
||||
* Returns the current timeout (used for logging).
|
||||
*/
|
||||
public int getCurrentTimeout();
|
||||
|
||||
/**
|
||||
* Returns the current retry count (used for logging).
|
||||
*/
|
||||
public int getCurrentRetryCount();
|
||||
|
||||
/**
|
||||
* Prepares for the next retry by applying a backoff to the timeout.
|
||||
* @param error The error code of the last attempt.
|
||||
* @throws VolleyError In the event that the retry could not be performed (for example if we
|
||||
* ran out of attempts), the passed in error is thrown.
|
||||
*/
|
||||
public void retry(VolleyError error) throws VolleyError;
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that the error responded with an error response.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ServerError extends VolleyError {
|
||||
public ServerError(NetworkResponse networkResponse) {
|
||||
super(networkResponse);
|
||||
}
|
||||
|
||||
public ServerError() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Indicates that the connection or the socket timed out.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TimeoutError extends VolleyError { }
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
/**
|
||||
* Exception style class encapsulating Volley errors
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class VolleyError extends Exception {
|
||||
public final NetworkResponse networkResponse;
|
||||
|
||||
public VolleyError() {
|
||||
networkResponse = null;
|
||||
}
|
||||
|
||||
public VolleyError(NetworkResponse response) {
|
||||
networkResponse = response;
|
||||
}
|
||||
|
||||
public VolleyError(String exceptionMessage) {
|
||||
super(exceptionMessage);
|
||||
networkResponse = null;
|
||||
}
|
||||
|
||||
public VolleyError(String exceptionMessage, Throwable reason) {
|
||||
super(exceptionMessage, reason);
|
||||
networkResponse = null;
|
||||
}
|
||||
|
||||
public VolleyError(Throwable cause) {
|
||||
super(cause);
|
||||
networkResponse = null;
|
||||
}
|
||||
}
|
||||
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Logging helper class. */
|
||||
public class VolleyLog {
|
||||
public static String TAG = "Volley";
|
||||
|
||||
public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Customize the log tag for your application, so that other apps
|
||||
* using Volley don't mix their logs with yours.
|
||||
* <br />
|
||||
* Enable the log property for your tag before starting your app:
|
||||
* <br />
|
||||
* {@code adb shell setprop log.tag.<tag>}
|
||||
*/
|
||||
public static void setTag(String tag) {
|
||||
d("Changing log tag to %s", tag);
|
||||
TAG = tag;
|
||||
|
||||
// Reinitialize the DEBUG "constant"
|
||||
DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
}
|
||||
|
||||
public static void v(String format, Object... args) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, buildMessage(format, args));
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String format, Object... args) {
|
||||
Log.d(TAG, buildMessage(format, args));
|
||||
}
|
||||
|
||||
public static void e(String format, Object... args) {
|
||||
Log.e(TAG, buildMessage(format, args));
|
||||
}
|
||||
|
||||
public static void e(Throwable tr, String format, Object... args) {
|
||||
Log.e(TAG, buildMessage(format, args), tr);
|
||||
}
|
||||
|
||||
public static void wtf(String format, Object... args) {
|
||||
Log.wtf(TAG, buildMessage(format, args));
|
||||
}
|
||||
|
||||
public static void wtf(Throwable tr, String format, Object... args) {
|
||||
Log.wtf(TAG, buildMessage(format, args), tr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the caller's provided message and prepends useful info like
|
||||
* calling thread ID and method name.
|
||||
*/
|
||||
private static String buildMessage(String format, Object... args) {
|
||||
String msg = (args == null) ? format : String.format(Locale.US, format, args);
|
||||
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
|
||||
|
||||
String caller = "<unknown>";
|
||||
// Walk up the stack looking for the first caller outside of VolleyLog.
|
||||
// It will be at least two frames up, so start there.
|
||||
for (int i = 2; i < trace.length; i++) {
|
||||
Class<?> clazz = trace[i].getClass();
|
||||
if (!clazz.equals(VolleyLog.class)) {
|
||||
String callingClass = trace[i].getClassName();
|
||||
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
|
||||
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
|
||||
|
||||
caller = callingClass + "." + trace[i].getMethodName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return String.format(Locale.US, "[%d] %s: %s",
|
||||
Thread.currentThread().getId(), caller, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple event log with records containing a name, thread ID, and timestamp.
|
||||
*/
|
||||
static class MarkerLog {
|
||||
public static final boolean ENABLED = VolleyLog.DEBUG;
|
||||
|
||||
/** Minimum duration from first marker to last in an marker log to warrant logging. */
|
||||
private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
|
||||
|
||||
private static class Marker {
|
||||
public final String name;
|
||||
public final long thread;
|
||||
public final long time;
|
||||
|
||||
public Marker(String name, long thread, long time) {
|
||||
this.name = name;
|
||||
this.thread = thread;
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Marker> mMarkers = new ArrayList<Marker>();
|
||||
private boolean mFinished = false;
|
||||
|
||||
/** Adds a marker to this log with the specified name. */
|
||||
public synchronized void add(String name, long threadId) {
|
||||
if (mFinished) {
|
||||
throw new IllegalStateException("Marker added to finished log");
|
||||
}
|
||||
|
||||
mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the log, dumping it to logcat if the time difference between
|
||||
* the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.
|
||||
* @param header Header string to print above the marker log.
|
||||
*/
|
||||
public synchronized void finish(String header) {
|
||||
mFinished = true;
|
||||
|
||||
long duration = getTotalDuration();
|
||||
if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
long prevTime = mMarkers.get(0).time;
|
||||
d("(%-4d ms) %s", duration, header);
|
||||
for (Marker marker : mMarkers) {
|
||||
long thisTime = marker.time;
|
||||
d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
|
||||
prevTime = thisTime;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
// Catch requests that have been collected (and hence end-of-lifed)
|
||||
// but had no debugging output printed for them.
|
||||
if (!mFinished) {
|
||||
finish("Request on the loose");
|
||||
e("Marker log finalized without finish() - uncaught exit point for request");
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the time difference between the first and last events in this log. */
|
||||
private long getTotalDuration() {
|
||||
if (mMarkers.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long first = mMarkers.get(0).time;
|
||||
long last = mMarkers.get(mMarkers.size() - 1).time;
|
||||
return last - first;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
|
||||
/**
|
||||
* An Authenticator that uses {@link AccountManager} to get auth
|
||||
* tokens of a specified type for a specified account.
|
||||
*/
|
||||
public class AndroidAuthenticator implements Authenticator {
|
||||
private final Context mContext;
|
||||
private final Account mAccount;
|
||||
private final String mAuthTokenType;
|
||||
private final boolean mNotifyAuthFailure;
|
||||
|
||||
/**
|
||||
* Creates a new authenticator.
|
||||
* @param context Context for accessing AccountManager
|
||||
* @param account Account to authenticate as
|
||||
* @param authTokenType Auth token type passed to AccountManager
|
||||
*/
|
||||
public AndroidAuthenticator(Context context, Account account, String authTokenType) {
|
||||
this(context, account, authTokenType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new authenticator.
|
||||
* @param context Context for accessing AccountManager
|
||||
* @param account Account to authenticate as
|
||||
* @param authTokenType Auth token type passed to AccountManager
|
||||
* @param notifyAuthFailure Whether to raise a notification upon auth failure
|
||||
*/
|
||||
public AndroidAuthenticator(Context context, Account account, String authTokenType,
|
||||
boolean notifyAuthFailure) {
|
||||
mContext = context;
|
||||
mAccount = account;
|
||||
mAuthTokenType = authTokenType;
|
||||
mNotifyAuthFailure = notifyAuthFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Account being used by this authenticator.
|
||||
*/
|
||||
public Account getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthToken() throws AuthFailureError {
|
||||
final AccountManager accountManager = AccountManager.get(mContext);
|
||||
AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount,
|
||||
mAuthTokenType, mNotifyAuthFailure, null, null);
|
||||
Bundle result;
|
||||
try {
|
||||
result = future.getResult();
|
||||
} catch (Exception e) {
|
||||
throw new AuthFailureError("Error while retrieving auth token", e);
|
||||
}
|
||||
String authToken = null;
|
||||
if (future.isDone() && !future.isCancelled()) {
|
||||
if (result.containsKey(AccountManager.KEY_INTENT)) {
|
||||
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
|
||||
throw new AuthFailureError(intent);
|
||||
}
|
||||
authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
|
||||
}
|
||||
if (authToken == null) {
|
||||
throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType);
|
||||
}
|
||||
|
||||
return authToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAuthToken(String authToken) {
|
||||
AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken);
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
|
||||
/**
|
||||
* An interface for interacting with auth tokens.
|
||||
*/
|
||||
public interface Authenticator {
|
||||
/**
|
||||
* Synchronously retrieves an auth token.
|
||||
*
|
||||
* @throws AuthFailureError If authentication did not succeed
|
||||
*/
|
||||
public String getAuthToken() throws AuthFailureError;
|
||||
|
||||
/**
|
||||
* Invalidates the provided auth token.
|
||||
*/
|
||||
public void invalidateAuthToken(String authToken);
|
||||
}
|
||||
@ -1,303 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
import com.android.volley.Cache;
|
||||
import com.android.volley.Network;
|
||||
import com.android.volley.NetworkError;
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.NoConnectionError;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RetryPolicy;
|
||||
import com.android.volley.ServerError;
|
||||
import com.android.volley.TimeoutError;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.VolleyLog;
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.GzipUtils;
|
||||
import com.gh.gamecenter.volley.extended.JsonArrayExtendedRequest;
|
||||
import com.gh.gamecenter.volley.extended.JsonObjectExtendedRequest;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.conn.ConnectTimeoutException;
|
||||
import org.apache.http.impl.cookie.DateUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A network performing Volley requests over an {@link HttpStack}.
|
||||
*/
|
||||
public class BasicNetwork implements Network {
|
||||
protected static final boolean DEBUG = VolleyLog.DEBUG;
|
||||
|
||||
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
|
||||
|
||||
private static int DEFAULT_POOL_SIZE = 4096;
|
||||
|
||||
protected final HttpStack mHttpStack;
|
||||
|
||||
protected final ByteArrayPool mPool;
|
||||
|
||||
/**
|
||||
* @param httpStack
|
||||
* HTTP stack to be used
|
||||
*/
|
||||
public BasicNetwork(HttpStack httpStack) {
|
||||
// If a pool isn't passed in, then build a small default pool that will
|
||||
// give us a lot of
|
||||
// benefit and not use too much memory.
|
||||
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param httpStack
|
||||
* HTTP stack to be used
|
||||
* @param pool
|
||||
* a buffer pool that improves GC performance in copy operations
|
||||
*/
|
||||
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
|
||||
mHttpStack = httpStack;
|
||||
mPool = pool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkResponse performRequest(Request<?> request)
|
||||
throws VolleyError {
|
||||
long requestStart = SystemClock.elapsedRealtime();
|
||||
while (true) {
|
||||
HttpResponse httpResponse = null;
|
||||
byte[] responseContents = null;
|
||||
Map<String, String> responseHeaders = new HashMap<String, String>();
|
||||
try {
|
||||
// Gather headers.
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
addCacheHeaders(headers, request.getCacheEntry());
|
||||
httpResponse = mHttpStack.performRequest(request, headers);
|
||||
StatusLine statusLine = httpResponse.getStatusLine();
|
||||
int statusCode = statusLine.getStatusCode();
|
||||
|
||||
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
|
||||
// Handle cache validation.
|
||||
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
||||
if (request.getUrl().startsWith(Config.HOST + "support/upgrade")) {
|
||||
if (request.getCacheEntry() != null) {
|
||||
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
|
||||
request.getCacheEntry().data, responseHeaders, true);
|
||||
} else {
|
||||
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
|
||||
GzipUtils.compressBytes("{}".getBytes()), responseHeaders, true);
|
||||
}
|
||||
} else {
|
||||
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
|
||||
request.getCacheEntry().data, responseHeaders, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Some responses such as 204s do not have content. We must
|
||||
// check.
|
||||
if (httpResponse.getEntity() != null) {
|
||||
responseContents = entityToBytes(httpResponse.getEntity());
|
||||
} else {
|
||||
// Add 0 byte response as a way of honestly representing a
|
||||
// no-content request.
|
||||
responseContents = new byte[0];
|
||||
}
|
||||
|
||||
// if the request is slow, log it.
|
||||
long requestLifetime = SystemClock.elapsedRealtime()
|
||||
- requestStart;
|
||||
logSlowRequests(requestLifetime, request, responseContents,
|
||||
statusLine);
|
||||
|
||||
if (statusCode != HttpStatus.SC_OK
|
||||
&& statusCode != HttpStatus.SC_NO_CONTENT) {
|
||||
throw new IOException();
|
||||
}
|
||||
return new NetworkResponse(statusCode, responseContents,
|
||||
responseHeaders, false);
|
||||
} catch (SocketTimeoutException e) {
|
||||
attemptRetryOnException("socket", request, new TimeoutError());
|
||||
} catch (ConnectTimeoutException e) {
|
||||
attemptRetryOnException("connection", request,
|
||||
new TimeoutError());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Bad URL " + request.getUrl(), e);
|
||||
} catch (IOException e) {
|
||||
int statusCode = 0;
|
||||
NetworkResponse networkResponse = null;
|
||||
if (httpResponse != null) {
|
||||
statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||
} else {
|
||||
// If there is no network connection, judge whether the url is cached
|
||||
// If have cached, return cached
|
||||
Cache.Entry entry = request.getCacheEntry();
|
||||
if (entry != null) {
|
||||
return new NetworkResponse(HttpStatus.SC_OK,
|
||||
entry.data, entry.responseHeaders, false);
|
||||
}
|
||||
// else throw NoConnectionError
|
||||
throw new NoConnectionError(e);
|
||||
}
|
||||
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
|
||||
if (responseContents != null) {
|
||||
networkResponse = new NetworkResponse(statusCode,
|
||||
responseContents, responseHeaders, false);
|
||||
if (statusCode == HttpStatus.SC_UNAUTHORIZED
|
||||
|| statusCode == HttpStatus.SC_FORBIDDEN) {
|
||||
attemptRetryOnException("auth", request,
|
||||
new AuthFailureError(networkResponse));
|
||||
} else if (statusCode == HttpStatus.SC_NOT_FOUND) {
|
||||
if (request.getClass().equals(JsonObjectExtendedRequest.class)) {
|
||||
return new NetworkResponse(HttpStatus.SC_OK,
|
||||
GzipUtils.compressBytes("{}".getBytes()), responseHeaders, true);
|
||||
} else if (request.getClass().equals(JsonArrayExtendedRequest.class)) {
|
||||
return new NetworkResponse(HttpStatus.SC_OK,
|
||||
GzipUtils.compressBytes("[]".getBytes()), responseHeaders, true);
|
||||
} else {
|
||||
// TODO: Only throw ServerError for 5xx status codes.
|
||||
throw new ServerError(networkResponse);
|
||||
}
|
||||
} else {
|
||||
// TODO: Only throw ServerError for 5xx status codes.
|
||||
throw new ServerError(networkResponse);
|
||||
}
|
||||
} else {
|
||||
throw new NetworkError(networkResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
|
||||
*/
|
||||
private void logSlowRequests(long requestLifetime, Request<?> request,
|
||||
byte[] responseContents, StatusLine statusLine) {
|
||||
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
|
||||
VolleyLog
|
||||
.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], "
|
||||
+ "[rc=%d], [retryCount=%s]", request,
|
||||
requestLifetime,
|
||||
responseContents != null ? responseContents.length
|
||||
: "null", statusLine.getStatusCode(),
|
||||
request.getRetryPolicy().getCurrentRetryCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to prepare the request for a retry. If there are no more
|
||||
* attempts remaining in the request's retry policy, a timeout exception is
|
||||
* thrown.
|
||||
*
|
||||
* @param request
|
||||
* The request to use.
|
||||
*/
|
||||
private static void attemptRetryOnException(String logPrefix,
|
||||
Request<?> request, VolleyError exception) throws VolleyError {
|
||||
RetryPolicy retryPolicy = request.getRetryPolicy();
|
||||
int oldTimeout = request.getTimeoutMs();
|
||||
|
||||
try {
|
||||
retryPolicy.retry(exception);
|
||||
} catch (VolleyError e) {
|
||||
request.addMarker(String.format("%s-timeout-giveup [timeout=%s]",
|
||||
logPrefix, oldTimeout));
|
||||
throw e;
|
||||
}
|
||||
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix,
|
||||
oldTimeout));
|
||||
}
|
||||
|
||||
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
|
||||
// If there's no cache entry, we're done.
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.etag != null) {
|
||||
headers.put("If-None-Match", entry.etag);
|
||||
}
|
||||
|
||||
if (entry.serverDate > 0) {
|
||||
Date refTime = new Date(entry.serverDate);
|
||||
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
|
||||
}
|
||||
}
|
||||
|
||||
protected void logError(String what, String url, long start) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start),
|
||||
url);
|
||||
}
|
||||
|
||||
/** Reads the contents of HttpEntity into a byte[]. */
|
||||
private byte[] entityToBytes(HttpEntity entity) throws IOException,
|
||||
ServerError {
|
||||
PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(
|
||||
mPool, (int) entity.getContentLength());
|
||||
byte[] buffer = null;
|
||||
try {
|
||||
InputStream in = entity.getContent();
|
||||
if (in == null) {
|
||||
throw new ServerError();
|
||||
}
|
||||
buffer = mPool.getBuf(1024);
|
||||
int count;
|
||||
while ((count = in.read(buffer)) != -1) {
|
||||
bytes.write(buffer, 0, count);
|
||||
}
|
||||
return bytes.toByteArray();
|
||||
} finally {
|
||||
try {
|
||||
// Close the InputStream and release the resources by
|
||||
// "consuming the content".
|
||||
entity.consumeContent();
|
||||
} catch (IOException e) {
|
||||
// This can happen if there was an exception above that left the
|
||||
// entity in
|
||||
// an invalid state.
|
||||
VolleyLog.v("Error occured when calling consumingContent");
|
||||
}
|
||||
mPool.returnBuf(buffer);
|
||||
bytes.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Headers[] to Map<String, String>.
|
||||
*/
|
||||
private static Map<String, String> convertHeaders(Header[] headers) {
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
result.put(headers[i].getName(), headers[i].getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 com.android.volley.toolbox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to
|
||||
* supply those buffers to consumers who need to use them for a short period of time and then
|
||||
* dispose of them. Simply creating and disposing such buffers in the conventional manner can
|
||||
* considerable heap churn and garbage collection delays on Android, which lacks good management of
|
||||
* short-lived heap objects. It may be advantageous to trade off some memory in the form of a
|
||||
* permanently allocated pool of buffers in order to gain heap performance improvements; that is
|
||||
* what this class does.
|
||||
* <p>
|
||||
* A good candidate user for this class is something like an I/O system that uses large temporary
|
||||
* <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants
|
||||
* the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
|
||||
* off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
|
||||
* account and also to maximize the odds of being able to reuse a recycled buffer, this class is
|
||||
* free to return buffers larger than the requested size. The caller needs to be able to gracefully
|
||||
* deal with getting buffers any size over the minimum.
|
||||
* <p>
|
||||
* If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
|
||||
* class will allocate a new buffer and return it.
|
||||
* <p>
|
||||
* This class has no special ownership of buffers it creates; the caller is free to take a buffer
|
||||
* it receives from this pool, use it permanently, and never return it to the pool; additionally,
|
||||
* it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
|
||||
* are no other lingering references to it.
|
||||
* <p>
|
||||
* This class ensures that the total size of the buffers in its recycling pool never exceeds a
|
||||
* certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
|
||||
* least-recently-used buffers are disposed.
|
||||
*/
|
||||
public class ByteArrayPool {
|
||||
/** The buffer pool, arranged both by last use and by buffer size */
|
||||
private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
|
||||
private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
|
||||
|
||||
/** The total size of the buffers in the pool */
|
||||
private int mCurrentSize = 0;
|
||||
|
||||
/**
|
||||
* The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
|
||||
* under this limit.
|
||||
*/
|
||||
private final int mSizeLimit;
|
||||
|
||||
/** Compares buffers by size */
|
||||
protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
|
||||
@Override
|
||||
public int compare(byte[] lhs, byte[] rhs) {
|
||||
return lhs.length - rhs.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param sizeLimit the maximum size of the pool, in bytes
|
||||
*/
|
||||
public ByteArrayPool(int sizeLimit) {
|
||||
mSizeLimit = sizeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a buffer from the pool if one is available in the requested size, or allocates a new
|
||||
* one if a pooled one is not available.
|
||||
*
|
||||
* @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
|
||||
* larger.
|
||||
* @return a byte[] buffer is always returned.
|
||||
*/
|
||||
public synchronized byte[] getBuf(int len) {
|
||||
for (int i = 0; i < mBuffersBySize.size(); i++) {
|
||||
byte[] buf = mBuffersBySize.get(i);
|
||||
if (buf.length >= len) {
|
||||
mCurrentSize -= buf.length;
|
||||
mBuffersBySize.remove(i);
|
||||
mBuffersByLastUse.remove(buf);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
return new byte[len];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
|
||||
* size.
|
||||
*
|
||||
* @param buf the buffer to return to the pool.
|
||||
*/
|
||||
public synchronized void returnBuf(byte[] buf) {
|
||||
if (buf == null || buf.length > mSizeLimit) {
|
||||
return;
|
||||
}
|
||||
mBuffersByLastUse.add(buf);
|
||||
int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
|
||||
if (pos < 0) {
|
||||
pos = -pos - 1;
|
||||
}
|
||||
mBuffersBySize.add(pos, buf);
|
||||
mCurrentSize += buf.length;
|
||||
trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes buffers from the pool until it is under its size limit.
|
||||
*/
|
||||
private synchronized void trim() {
|
||||
while (mCurrentSize > mSizeLimit) {
|
||||
byte[] buf = mBuffersByLastUse.remove(0);
|
||||
mBuffersBySize.remove(buf);
|
||||
mCurrentSize -= buf.length;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.android.volley.Cache;
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
|
||||
/**
|
||||
* A synthetic request used for clearing the cache.
|
||||
*/
|
||||
public class ClearCacheRequest extends Request<Object> {
|
||||
private final Cache mCache;
|
||||
private final Runnable mCallback;
|
||||
|
||||
/**
|
||||
* Creates a synthetic request for clearing the cache.
|
||||
* @param cache Cache to clear
|
||||
* @param callback Callback to make on the main thread once the cache is clear,
|
||||
* or null for none
|
||||
*/
|
||||
public ClearCacheRequest(Cache cache, Runnable callback) {
|
||||
super(Method.GET, null, null);
|
||||
mCache = cache;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
// This is a little bit of a hack, but hey, why not.
|
||||
mCache.clear();
|
||||
if (mCallback != null) {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postAtFrontOfQueue(mCallback);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
return Priority.IMMEDIATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<Object> parseNetworkResponse(NetworkResponse response) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deliverResponse(Object response) {
|
||||
}
|
||||
}
|
||||
@ -1,648 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.volley.Cache;
|
||||
import com.android.volley.VolleyLog;
|
||||
import com.gh.common.util.TimestampUtils;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Cache implementation that caches files directly onto the hard disk in the
|
||||
* specified directory. The default disk usage size is 5MB, but is configurable.
|
||||
*/
|
||||
public class DiskBasedCache implements Cache {
|
||||
|
||||
/** Map of the Key, CacheHeader pairs */
|
||||
private final Map<String, CacheHeader> mEntries = new LinkedHashMap<String, CacheHeader>(
|
||||
16, .75f, true);
|
||||
|
||||
/** Total amount of space currently used by the cache in bytes. */
|
||||
private long mTotalSize = 0;
|
||||
|
||||
/** The root directory to use for the cache. */
|
||||
private final File mRootDirectory;
|
||||
|
||||
/** The maximum size of the cache in bytes. */
|
||||
private final int mMaxCacheSizeInBytes;
|
||||
|
||||
/** Default maximum disk usage in bytes. */
|
||||
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
|
||||
|
||||
/** High water mark percentage for the cache */
|
||||
private static final float HYSTERESIS_FACTOR = 0.9f;
|
||||
|
||||
/** Magic number for current version of cache file format. */
|
||||
private static final int CACHE_MAGIC = 0x20120504;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the DiskBasedCache at the specified directory.
|
||||
*
|
||||
* @param rootDirectory
|
||||
* The root directory of the cache.
|
||||
* @param maxCacheSizeInBytes
|
||||
* The maximum size of the cache in bytes.
|
||||
*/
|
||||
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
|
||||
mRootDirectory = rootDirectory;
|
||||
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the DiskBasedCache at the specified directory
|
||||
* using the default maximum cache size of 5MB.
|
||||
*
|
||||
* @param rootDirectory
|
||||
* The root directory of the cache.
|
||||
*/
|
||||
public DiskBasedCache(File rootDirectory) {
|
||||
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache. Deletes all cached files from disk.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void clear() {
|
||||
File[] files = mRootDirectory.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
mEntries.clear();
|
||||
mTotalSize = 0;
|
||||
VolleyLog.d("Cache cleared.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache entry with the specified key if it exists, null
|
||||
* otherwise.
|
||||
*/
|
||||
@Override
|
||||
public synchronized Entry get(String key) {
|
||||
CacheHeader entry = mEntries.get(key);
|
||||
// if the entry does not exist, return.
|
||||
if (entry == null) {
|
||||
if (key.contains("timestamp")) {
|
||||
Log.i("result", "get entrey is null");
|
||||
entry = mEntries.get(TimestampUtils.removeTimestamp(key));
|
||||
if (entry == null){
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File file = getFileForKey(key);
|
||||
if (!file.exists() && key.contains("timestamp")) {
|
||||
Log.i("result", "file = " + file.getName() + " not exists");
|
||||
file = getFileForKey(TimestampUtils.removeTimestamp(key));
|
||||
}
|
||||
Log.i("result", "key = " + key);
|
||||
Log.i("result", "name = " + file.getName());
|
||||
CountingInputStream cis = null;
|
||||
try {
|
||||
cis = new CountingInputStream(new FileInputStream(file));
|
||||
CacheHeader.readHeader(cis); // eat header
|
||||
byte[] data = streamToBytes(cis,
|
||||
(int) (file.length() - cis.bytesRead));
|
||||
return entry.toCacheEntry(data);
|
||||
} catch (IOException e) {
|
||||
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
|
||||
remove(key);
|
||||
return null;
|
||||
} finally {
|
||||
if (cis != null) {
|
||||
try {
|
||||
cis.close();
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized byte[] getData(String key) {
|
||||
File file = getFileForKey(key);
|
||||
CountingInputStream cis = null;
|
||||
try {
|
||||
cis = new CountingInputStream(new FileInputStream(file));
|
||||
CacheHeader.readHeader(cis); // eat header
|
||||
return streamToBytes(cis, (int) (file.length() - cis.bytesRead));
|
||||
} catch (IOException e) {
|
||||
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
|
||||
return null;
|
||||
} finally {
|
||||
if (cis != null) {
|
||||
try {
|
||||
cis.close();
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void modify(String key, byte[] data) {
|
||||
File file = getFileForKey(key);
|
||||
try {
|
||||
CountingInputStream cis = new CountingInputStream(new FileInputStream(file));
|
||||
CacheHeader e = CacheHeader.readHeader(cis); // eat header
|
||||
Entry entry = e.toCacheEntry(data);
|
||||
cis.close();
|
||||
put(key, entry);
|
||||
} catch (IOException e) {
|
||||
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DiskBasedCache by scanning for all files currently in the
|
||||
* specified root directory. Creates the root directory if necessary.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void initialize() {
|
||||
if (!mRootDirectory.exists()) {
|
||||
if (!mRootDirectory.mkdirs()) {
|
||||
VolleyLog.e("Unable to create cache dir %s",
|
||||
mRootDirectory.getAbsolutePath());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
File[] files = mRootDirectory.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
CacheHeader entry = CacheHeader.readHeader(fis);
|
||||
entry.size = file.length();
|
||||
putEntry(entry.key, entry);
|
||||
} catch (IOException e) {
|
||||
if (file != null) {
|
||||
file.delete();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates an entry in the cache.
|
||||
*
|
||||
* @param key
|
||||
* Cache key
|
||||
* @param fullExpire
|
||||
* True to fully expire the entry, false to soft expire
|
||||
*/
|
||||
@Override
|
||||
public synchronized void invalidate(String key, boolean fullExpire) {
|
||||
Entry entry = get(key);
|
||||
if (entry != null) {
|
||||
entry.softTtl = 0;
|
||||
if (fullExpire) {
|
||||
entry.ttl = 0;
|
||||
}
|
||||
put(key, entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the entry with the specified key into the cache.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void put(String key, Entry entry) {
|
||||
pruneIfNeeded(entry.data.length);
|
||||
File file = getFileForKey(key);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
CacheHeader e = new CacheHeader(key, entry);
|
||||
e.writeHeader(fos);
|
||||
fos.write(entry.data);
|
||||
fos.close();
|
||||
putEntry(key, e);
|
||||
// 如果url包含timestamp参数,则去掉该参数再存一份缓存
|
||||
if (key.contains("timestamp")) {
|
||||
put(TimestampUtils.removeTimestamp(key), entry);
|
||||
}
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
boolean deleted = file.delete();
|
||||
if (!deleted) {
|
||||
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified key from the cache if it exists.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void remove(String key) {
|
||||
boolean deleted = getFileForKey(key).delete();
|
||||
removeEntry(key);
|
||||
if (!deleted) {
|
||||
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
|
||||
key, getFilenameForKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pseudo-unique filename for the specified cache key.
|
||||
*
|
||||
* @param key
|
||||
* The key to generate a file name for.
|
||||
* @return A pseudo-unique filename.
|
||||
*/
|
||||
private String getFilenameForKey(String key) {
|
||||
int firstHalfLength = key.length() / 2;
|
||||
String localFilename = String.valueOf(key.substring(0, firstHalfLength)
|
||||
.hashCode());
|
||||
localFilename += String.valueOf(key.substring(firstHalfLength)
|
||||
.hashCode());
|
||||
return localFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file object for the given cache key.
|
||||
*/
|
||||
public File getFileForKey(String key) {
|
||||
return new File(mRootDirectory, getFilenameForKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes the cache to fit the amount of bytes specified.
|
||||
*
|
||||
* @param neededSpace
|
||||
* The amount of bytes we are trying to fit into the cache.
|
||||
*/
|
||||
private void pruneIfNeeded(int neededSpace) {
|
||||
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
|
||||
return;
|
||||
}
|
||||
if (VolleyLog.DEBUG) {
|
||||
VolleyLog.v("Pruning old cache entries.");
|
||||
}
|
||||
|
||||
long before = mTotalSize;
|
||||
int prunedFiles = 0;
|
||||
long startTime = SystemClock.elapsedRealtime();
|
||||
|
||||
Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet()
|
||||
.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, CacheHeader> entry = iterator.next();
|
||||
CacheHeader e = entry.getValue();
|
||||
boolean deleted = getFileForKey(e.key).delete();
|
||||
if (deleted) {
|
||||
mTotalSize -= e.size;
|
||||
} else {
|
||||
VolleyLog.d(
|
||||
"Could not delete cache entry for key=%s, filename=%s",
|
||||
e.key, getFilenameForKey(e.key));
|
||||
}
|
||||
iterator.remove();
|
||||
prunedFiles++;
|
||||
|
||||
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes
|
||||
* HYSTERESIS_FACTOR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (VolleyLog.DEBUG) {
|
||||
VolleyLog.v("pruned %d files, %d bytes, %d ms", prunedFiles,
|
||||
(mTotalSize - before), SystemClock.elapsedRealtime()
|
||||
- startTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the entry with the specified key into the cache.
|
||||
*
|
||||
* @param key
|
||||
* The key to identify the entry by.
|
||||
* @param entry
|
||||
* The entry to cache.
|
||||
*/
|
||||
private void putEntry(String key, CacheHeader entry) {
|
||||
if (!mEntries.containsKey(key)) {
|
||||
mTotalSize += entry.size;
|
||||
} else {
|
||||
CacheHeader oldEntry = mEntries.get(key);
|
||||
mTotalSize += (entry.size - oldEntry.size);
|
||||
}
|
||||
mEntries.put(key, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the entry identified by 'key' from the cache.
|
||||
*/
|
||||
private void removeEntry(String key) {
|
||||
CacheHeader entry = mEntries.get(key);
|
||||
if (entry != null) {
|
||||
mTotalSize -= entry.size;
|
||||
mEntries.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of an InputStream into a byte[].
|
||||
* */
|
||||
private static byte[] streamToBytes(InputStream in, int length)
|
||||
throws IOException {
|
||||
byte[] bytes = new byte[length];
|
||||
int count;
|
||||
int pos = 0;
|
||||
while (pos < length
|
||||
&& ((count = in.read(bytes, pos, length - pos)) != -1)) {
|
||||
pos += count;
|
||||
}
|
||||
if (pos != length) {
|
||||
throw new IOException("Expected " + length + " bytes, read " + pos
|
||||
+ " bytes");
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles holding onto the cache headers for an entry.
|
||||
*/
|
||||
// Visible for testing.
|
||||
static class CacheHeader {
|
||||
/**
|
||||
* The size of the data identified by this CacheHeader. (This is not
|
||||
* serialized to disk.
|
||||
*/
|
||||
public long size;
|
||||
|
||||
/** The key that identifies the cache entry. */
|
||||
public String key;
|
||||
|
||||
/** ETag for cache coherence. */
|
||||
public String etag;
|
||||
|
||||
/** Date of this response as reported by the server. */
|
||||
public long serverDate;
|
||||
|
||||
/** TTL for this record. */
|
||||
public long ttl;
|
||||
|
||||
/** Soft TTL for this record. */
|
||||
public long softTtl;
|
||||
|
||||
/** Headers from the response resulting in this cache entry. */
|
||||
public Map<String, String> responseHeaders;
|
||||
|
||||
private CacheHeader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new CacheHeader object
|
||||
*
|
||||
* @param key
|
||||
* The key that identifies the cache entry
|
||||
* @param entry
|
||||
* The cache entry.
|
||||
*/
|
||||
public CacheHeader(String key, Entry entry) {
|
||||
this.key = key;
|
||||
this.size = entry.data.length;
|
||||
this.etag = entry.etag;
|
||||
this.serverDate = entry.serverDate;
|
||||
this.ttl = entry.ttl;
|
||||
this.softTtl = entry.softTtl;
|
||||
this.responseHeaders = entry.responseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the header off of an InputStream and returns a CacheHeader
|
||||
* object.
|
||||
*
|
||||
* @param is
|
||||
* The InputStream to read from.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static CacheHeader readHeader(InputStream is) throws IOException {
|
||||
CacheHeader entry = new CacheHeader();
|
||||
int magic = readInt(is);
|
||||
if (magic != CACHE_MAGIC) {
|
||||
// don't bother deleting, it'll get pruned eventually
|
||||
throw new IOException();
|
||||
}
|
||||
entry.key = readString(is);
|
||||
entry.etag = readString(is);
|
||||
if (entry.etag.equals("")) {
|
||||
entry.etag = null;
|
||||
}
|
||||
entry.serverDate = readLong(is);
|
||||
entry.ttl = readLong(is);
|
||||
entry.softTtl = readLong(is);
|
||||
entry.responseHeaders = readStringStringMap(is);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache entry for the specified data.
|
||||
*/
|
||||
public Entry toCacheEntry(byte[] data) {
|
||||
Entry e = new Entry();
|
||||
e.data = data;
|
||||
e.etag = etag;
|
||||
e.serverDate = serverDate;
|
||||
e.ttl = ttl;
|
||||
e.softTtl = softTtl;
|
||||
e.responseHeaders = responseHeaders;
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of this CacheHeader to the specified
|
||||
* OutputStream.
|
||||
*/
|
||||
public boolean writeHeader(OutputStream os) {
|
||||
try {
|
||||
writeInt(os, CACHE_MAGIC);
|
||||
writeString(os, key);
|
||||
writeString(os, etag == null ? "" : etag);
|
||||
writeLong(os, serverDate);
|
||||
writeLong(os, ttl);
|
||||
writeLong(os, softTtl);
|
||||
writeStringStringMap(responseHeaders, os);
|
||||
os.flush();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
VolleyLog.d("%s", e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CountingInputStream extends FilterInputStream {
|
||||
private int bytesRead = 0;
|
||||
|
||||
private CountingInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int result = super.read();
|
||||
if (result != -1) {
|
||||
bytesRead++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int count)
|
||||
throws IOException {
|
||||
int result = super.read(buffer, offset, count);
|
||||
if (result != -1) {
|
||||
bytesRead += result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Homebrewed simple serialization system used for reading and writing cache
|
||||
* headers on disk. Once upon a time, this used the standard Java
|
||||
* Object{Input,Output}Stream, but the default implementation relies heavily
|
||||
* on reflection (even for standard types) and generates a ton of garbage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple wrapper around {@link InputStream#read()} that throws EOFException
|
||||
* instead of returning -1.
|
||||
*/
|
||||
private static int read(InputStream is) throws IOException {
|
||||
int b = is.read();
|
||||
if (b == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static void writeInt(OutputStream os, int n) throws IOException {
|
||||
os.write((n >> 0) & 0xff);
|
||||
os.write((n >> 8) & 0xff);
|
||||
os.write((n >> 16) & 0xff);
|
||||
os.write((n >> 24) & 0xff);
|
||||
}
|
||||
|
||||
static int readInt(InputStream is) throws IOException {
|
||||
int n = 0;
|
||||
n |= (read(is) << 0);
|
||||
n |= (read(is) << 8);
|
||||
n |= (read(is) << 16);
|
||||
n |= (read(is) << 24);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void writeLong(OutputStream os, long n) throws IOException {
|
||||
os.write((byte) (n >>> 0));
|
||||
os.write((byte) (n >>> 8));
|
||||
os.write((byte) (n >>> 16));
|
||||
os.write((byte) (n >>> 24));
|
||||
os.write((byte) (n >>> 32));
|
||||
os.write((byte) (n >>> 40));
|
||||
os.write((byte) (n >>> 48));
|
||||
os.write((byte) (n >>> 56));
|
||||
}
|
||||
|
||||
static long readLong(InputStream is) throws IOException {
|
||||
long n = 0;
|
||||
n |= ((read(is) & 0xFFL) << 0);
|
||||
n |= ((read(is) & 0xFFL) << 8);
|
||||
n |= ((read(is) & 0xFFL) << 16);
|
||||
n |= ((read(is) & 0xFFL) << 24);
|
||||
n |= ((read(is) & 0xFFL) << 32);
|
||||
n |= ((read(is) & 0xFFL) << 40);
|
||||
n |= ((read(is) & 0xFFL) << 48);
|
||||
n |= ((read(is) & 0xFFL) << 56);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void writeString(OutputStream os, String s) throws IOException {
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
writeLong(os, b.length);
|
||||
os.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
static String readString(InputStream is) throws IOException {
|
||||
int n = (int) readLong(is);
|
||||
byte[] b = streamToBytes(is, n);
|
||||
return new String(b, "UTF-8");
|
||||
}
|
||||
|
||||
static void writeStringStringMap(Map<String, String> map, OutputStream os)
|
||||
throws IOException {
|
||||
if (map != null) {
|
||||
writeInt(os, map.size());
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
writeString(os, entry.getKey());
|
||||
writeString(os, entry.getValue());
|
||||
}
|
||||
} else {
|
||||
writeInt(os, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, String> readStringStringMap(InputStream is)
|
||||
throws IOException {
|
||||
int size = readInt(is);
|
||||
Map<String, String> result = (size == 0) ? Collections
|
||||
.<String, String> emptyMap()
|
||||
: new HashMap<String, String>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
String key = readString(is).intern();
|
||||
String value = readString(is).intern();
|
||||
result.put(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Request.Method;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An HttpStack that performs request over an {@link HttpClient}.
|
||||
*/
|
||||
public class HttpClientStack implements HttpStack {
|
||||
protected final HttpClient mClient;
|
||||
|
||||
private final static String HEADER_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
public HttpClientStack(HttpClient client) {
|
||||
mClient = client;
|
||||
}
|
||||
|
||||
private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
|
||||
for (String key : headers.keySet()) {
|
||||
httpRequest.setHeader(key, headers.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
|
||||
List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
|
||||
for (String key : postParams.keySet()) {
|
||||
result.add(new BasicNameValuePair(key, postParams.get(key)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
|
||||
throws IOException, AuthFailureError {
|
||||
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
|
||||
addHeaders(httpRequest, additionalHeaders);
|
||||
addHeaders(httpRequest, request.getHeaders());
|
||||
onPrepareRequest(httpRequest);
|
||||
HttpParams httpParams = httpRequest.getParams();
|
||||
int timeoutMs = request.getTimeoutMs();
|
||||
// TODO: Reevaluate this connection timeout based on more wide-scale
|
||||
// data collection and possibly different for wifi vs. 3G.
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
|
||||
return mClient.execute(httpRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the appropriate subclass of HttpUriRequest for passed in request.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
|
||||
Map<String, String> additionalHeaders) throws AuthFailureError {
|
||||
switch (request.getMethod()) {
|
||||
case Method.DEPRECATED_GET_OR_POST: {
|
||||
// This is the deprecated way that needs to be handled for backwards compatibility.
|
||||
// If the request's post body is null, then the assumption is that the request is
|
||||
// GET. Otherwise, it is assumed that the request is a POST.
|
||||
byte[] postBody = request.getPostBody();
|
||||
if (postBody != null) {
|
||||
HttpPost postRequest = new HttpPost(request.getUrl());
|
||||
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
|
||||
HttpEntity entity;
|
||||
entity = new ByteArrayEntity(postBody);
|
||||
postRequest.setEntity(entity);
|
||||
return postRequest;
|
||||
} else {
|
||||
return new HttpGet(request.getUrl());
|
||||
}
|
||||
}
|
||||
case Method.GET:
|
||||
return new HttpGet(request.getUrl());
|
||||
case Method.DELETE:
|
||||
return new HttpDelete(request.getUrl());
|
||||
case Method.POST: {
|
||||
HttpPost postRequest = new HttpPost(request.getUrl());
|
||||
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
|
||||
setEntityIfNonEmptyBody(postRequest, request);
|
||||
return postRequest;
|
||||
}
|
||||
case Method.PUT: {
|
||||
HttpPut putRequest = new HttpPut(request.getUrl());
|
||||
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
|
||||
setEntityIfNonEmptyBody(putRequest, request);
|
||||
return putRequest;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unknown request method.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
|
||||
Request<?> request) throws AuthFailureError {
|
||||
byte[] body = request.getBody();
|
||||
if (body != null) {
|
||||
HttpEntity entity = new ByteArrayEntity(body);
|
||||
httpRequest.setEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before the request is executed using the underlying HttpClient.
|
||||
*
|
||||
* <p>Overwrite in subclasses to augment the request.</p>
|
||||
*/
|
||||
protected void onPrepareRequest(HttpUriRequest request) throws IOException {
|
||||
// Nothing.
|
||||
}
|
||||
}
|
||||
@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.Cache;
|
||||
import com.android.volley.NetworkResponse;
|
||||
|
||||
import org.apache.http.impl.cookie.DateParseException;
|
||||
import org.apache.http.impl.cookie.DateUtils;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utility methods for parsing HTTP headers.
|
||||
*/
|
||||
public class HttpHeaderParser {
|
||||
|
||||
/**
|
||||
* Extracts a {@link Cache.Entry} from a {@link NetworkResponse}.
|
||||
*
|
||||
* @param response The network response to parse headers from
|
||||
* @return a cache entry for the given response, or null if the response is not cacheable.
|
||||
*/
|
||||
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Map<String, String> headers = response.headers;
|
||||
|
||||
long serverDate = 0;
|
||||
long serverExpires = 0;
|
||||
long softExpire = 0;
|
||||
long maxAge = 0;
|
||||
boolean hasCacheControl = false;
|
||||
|
||||
String serverEtag = null;
|
||||
String headerValue;
|
||||
|
||||
headerValue = headers.get("Date");
|
||||
if (headerValue != null) {
|
||||
serverDate = parseDateAsEpoch(headerValue);
|
||||
}
|
||||
|
||||
headerValue = headers.get("Cache-Control");
|
||||
if (headerValue != null) {
|
||||
hasCacheControl = true;
|
||||
String[] tokens = headerValue.split(",");
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String token = tokens[i].trim();
|
||||
if (token.equals("no-cache") || token.equals("no-store")) {
|
||||
return null;
|
||||
} else if (token.startsWith("max-age=")) {
|
||||
try {
|
||||
maxAge = Long.parseLong(token.substring(8));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
|
||||
maxAge = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headerValue = headers.get("Expires");
|
||||
if (headerValue != null) {
|
||||
serverExpires = parseDateAsEpoch(headerValue);
|
||||
}
|
||||
|
||||
serverEtag = headers.get("ETag");
|
||||
|
||||
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
|
||||
// is more restrictive.
|
||||
if (hasCacheControl) {
|
||||
softExpire = now + maxAge * 1000;
|
||||
} else if (serverDate > 0 && serverExpires >= serverDate) {
|
||||
// Default semantic for Expire header in HTTP specification is softExpire.
|
||||
softExpire = now + (serverExpires - serverDate);
|
||||
}
|
||||
|
||||
Cache.Entry entry = new Cache.Entry();
|
||||
entry.data = response.data;
|
||||
entry.etag = serverEtag;
|
||||
entry.softTtl = softExpire;
|
||||
entry.ttl = entry.softTtl;
|
||||
entry.serverDate = serverDate;
|
||||
entry.responseHeaders = headers;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse date in RFC1123 format, and return its value as epoch
|
||||
*/
|
||||
public static long parseDateAsEpoch(String dateStr) {
|
||||
try {
|
||||
// Parse date in RFC1123 format if this header contains one
|
||||
return DateUtils.parseDate(dateStr).getTime();
|
||||
} catch (DateParseException e) {
|
||||
// Date in invalid format, fallback to 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the charset specified in the Content-Type of this header,
|
||||
* or the HTTP default (ISO-8859-1) if none can be found.
|
||||
*/
|
||||
public static String parseCharset(Map<String, String> headers) {
|
||||
String contentType = headers.get(HTTP.CONTENT_TYPE);
|
||||
if (contentType != null) {
|
||||
String[] params = contentType.split(";");
|
||||
for (int i = 1; i < params.length; i++) {
|
||||
String[] pair = params[i].trim().split("=");
|
||||
if (pair.length == 2) {
|
||||
if (pair[0].equals("charset")) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HTTP.UTF_8;
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
import com.android.volley.Request;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An HTTP stack abstraction.
|
||||
*/
|
||||
public interface HttpStack {
|
||||
/**
|
||||
* Performs an HTTP request with the given parameters.
|
||||
*
|
||||
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
|
||||
* and the Content-Type header is set to request.getPostBodyContentType().</p>
|
||||
*
|
||||
* @param request the request to perform
|
||||
* @param additionalHeaders additional headers to be sent together with
|
||||
* {@link Request#getHeaders()}
|
||||
* @return the HTTP response
|
||||
*/
|
||||
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
|
||||
throws IOException, AuthFailureError;
|
||||
|
||||
}
|
||||
@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.AuthFailureError;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Request.Method;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.entity.BasicHttpEntity;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicHttpResponse;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* An {@link HttpStack} based on {@link HttpURLConnection}.
|
||||
*/
|
||||
public class HurlStack implements HttpStack {
|
||||
|
||||
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||
|
||||
/**
|
||||
* An interface for transforming URLs before use.
|
||||
*/
|
||||
public interface UrlRewriter {
|
||||
/**
|
||||
* Returns a URL to use instead of the provided one, or null to indicate
|
||||
* this URL should not be used at all.
|
||||
*/
|
||||
public String rewriteUrl(String originalUrl);
|
||||
}
|
||||
|
||||
private final UrlRewriter mUrlRewriter;
|
||||
private final SSLSocketFactory mSslSocketFactory;
|
||||
|
||||
public HurlStack() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urlRewriter
|
||||
* Rewriter to use for request URLs
|
||||
*/
|
||||
public HurlStack(UrlRewriter urlRewriter) {
|
||||
this(urlRewriter, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urlRewriter
|
||||
* Rewriter to use for request URLs
|
||||
* @param sslSocketFactory
|
||||
* SSL factory to use for HTTPS connections
|
||||
*/
|
||||
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
|
||||
mUrlRewriter = urlRewriter;
|
||||
mSslSocketFactory = sslSocketFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse performRequest(Request<?> request,
|
||||
Map<String, String> additionalHeaders) throws IOException,
|
||||
AuthFailureError {
|
||||
String url = request.getUrl();
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
map.putAll(request.getHeaders());
|
||||
map.putAll(additionalHeaders);
|
||||
if (mUrlRewriter != null) {
|
||||
String rewritten = mUrlRewriter.rewriteUrl(url);
|
||||
if (rewritten == null) {
|
||||
throw new IOException("URL blocked by rewriter: " + url);
|
||||
}
|
||||
url = rewritten;
|
||||
}
|
||||
URL parsedUrl = new URL(url);
|
||||
HttpURLConnection connection = openConnection(parsedUrl, request);
|
||||
|
||||
// if have etag, don't use lastmodified
|
||||
// because the aliyun cnd will use lastmodified do something we don't know
|
||||
if (map.containsKey("If-None-Match")) {
|
||||
map.remove("If-Modified-Since");
|
||||
}
|
||||
|
||||
for (String headerName : map.keySet()) {
|
||||
connection.addRequestProperty(headerName, map.get(headerName));
|
||||
}
|
||||
|
||||
setConnectionParametersForRequest(connection, request);
|
||||
// Initialize HttpResponse with data from the HttpURLConnection.
|
||||
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
if (responseCode == -1) {
|
||||
// -1 is returned by getResponseCode() if the response code could
|
||||
// not be retrieved.
|
||||
// Signal to the caller that something was wrong with the
|
||||
// connection.
|
||||
throw new IOException(
|
||||
"Could not retrieve response code from HttpUrlConnection.");
|
||||
}
|
||||
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
|
||||
connection.getResponseCode(), connection.getResponseMessage());
|
||||
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
|
||||
response.setEntity(entityFromConnection(connection));
|
||||
for (Entry<String, List<String>> header : connection.getHeaderFields()
|
||||
.entrySet()) {
|
||||
if (header.getKey() != null) {
|
||||
Header h = new BasicHeader(header.getKey(), header.getValue()
|
||||
.get(0));
|
||||
response.addHeader(h);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an {@link HttpEntity} from the given
|
||||
* {@link HttpURLConnection}.
|
||||
*
|
||||
* @param connection
|
||||
* @return an HttpEntity populated with data from <code>connection</code>.
|
||||
*/
|
||||
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
|
||||
BasicHttpEntity entity = new BasicHttpEntity();
|
||||
InputStream inputStream;
|
||||
try {
|
||||
inputStream = connection.getInputStream();
|
||||
} catch (IOException ioe) {
|
||||
inputStream = connection.getErrorStream();
|
||||
}
|
||||
entity.setContent(inputStream);
|
||||
entity.setContentLength(connection.getContentLength());
|
||||
entity.setContentEncoding(connection.getContentEncoding());
|
||||
entity.setContentType(connection.getContentType());
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link HttpURLConnection} for the specified {@code url}.
|
||||
*/
|
||||
protected HttpURLConnection createConnection(URL url) throws IOException {
|
||||
return (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an {@link HttpURLConnection} with parameters.
|
||||
*
|
||||
* @param url
|
||||
* @return an open connection
|
||||
* @throws IOException
|
||||
*/
|
||||
private HttpURLConnection openConnection(URL url, Request<?> request)
|
||||
throws IOException {
|
||||
HttpURLConnection connection = createConnection(url);
|
||||
|
||||
int timeoutMs = request.getTimeoutMs();
|
||||
connection.setConnectTimeout(timeoutMs);
|
||||
connection.setReadTimeout(timeoutMs);
|
||||
connection.setUseCaches(false);
|
||||
connection.setDoInput(true);
|
||||
|
||||
// use caller-provided custom SslSocketFactory, if any, for HTTPS
|
||||
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
|
||||
((HttpsURLConnection) connection)
|
||||
.setSSLSocketFactory(mSslSocketFactory);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
/* package */static void setConnectionParametersForRequest(
|
||||
HttpURLConnection connection, Request<?> request)
|
||||
throws IOException, AuthFailureError {
|
||||
switch (request.getMethod()) {
|
||||
case Method.DEPRECATED_GET_OR_POST:
|
||||
// This is the deprecated way that needs to be handled for backwards
|
||||
// compatibility.
|
||||
// If the request's post body is null, then the assumption is that
|
||||
// the request is
|
||||
// GET. Otherwise, it is assumed that the request is a POST.
|
||||
byte[] postBody = request.getPostBody();
|
||||
if (postBody != null) {
|
||||
// Prepare output. There is no need to set Content-Length
|
||||
// explicitly,
|
||||
// since this is handled by HttpURLConnection using the size of
|
||||
// the prepared
|
||||
// output stream.
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty(HEADER_CONTENT_TYPE,
|
||||
request.getPostBodyContentType());
|
||||
DataOutputStream out = new DataOutputStream(
|
||||
connection.getOutputStream());
|
||||
out.write(postBody);
|
||||
out.close();
|
||||
}
|
||||
break;
|
||||
case Method.GET:
|
||||
// Not necessary to set the request method because connection
|
||||
// defaults to GET but
|
||||
// being explicit here.
|
||||
connection.setRequestMethod("GET");
|
||||
break;
|
||||
case Method.DELETE:
|
||||
connection.setRequestMethod("DELETE");
|
||||
break;
|
||||
case Method.POST:
|
||||
connection.setRequestMethod("POST");
|
||||
addBodyIfExists(connection, request);
|
||||
break;
|
||||
case Method.PUT:
|
||||
connection.setRequestMethod("PUT");
|
||||
addBodyIfExists(connection, request);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown method type.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void addBodyIfExists(HttpURLConnection connection,
|
||||
Request<?> request) throws IOException, AuthFailureError {
|
||||
byte[] body = request.getBody();
|
||||
if (body != null) {
|
||||
connection.setDoOutput(true);
|
||||
connection.addRequestProperty(HEADER_CONTENT_TYPE,
|
||||
request.getBodyContentType());
|
||||
DataOutputStream out = new DataOutputStream(
|
||||
connection.getOutputStream());
|
||||
out.write(body);
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,479 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2013 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 com.android.volley.toolbox;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.Response.ErrorListener;
|
||||
import com.android.volley.Response.Listener;
|
||||
import com.android.volley.VolleyError;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Helper that handles loading and caching images from remote URLs.
|
||||
*
|
||||
* The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)}
|
||||
* and to pass in the default image listener provided by
|
||||
* {@link ImageLoader#getImageListener(ImageView, int, int)}. Note that all function calls to
|
||||
* this class must be made from the main thead, and all responses will be delivered to the main
|
||||
* thread as well.
|
||||
*/
|
||||
public class ImageLoader {
|
||||
/** RequestQueue for dispatching ImageRequests onto. */
|
||||
private final RequestQueue mRequestQueue;
|
||||
|
||||
/** Amount of time to wait after first response arrives before delivering all responses. */
|
||||
private int mBatchResponseDelayMs = 100;
|
||||
|
||||
/** The cache implementation to be used as an L1 cache before calling into volley. */
|
||||
private final ImageCache mCache;
|
||||
|
||||
/**
|
||||
* HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so
|
||||
* that we can coalesce multiple requests to the same URL into a single network request.
|
||||
*/
|
||||
private final HashMap<String, BatchedImageRequest> mInFlightRequests =
|
||||
new HashMap<String, BatchedImageRequest>();
|
||||
|
||||
/** HashMap of the currently pending responses (waiting to be delivered). */
|
||||
private final HashMap<String, BatchedImageRequest> mBatchedResponses =
|
||||
new HashMap<String, BatchedImageRequest>();
|
||||
|
||||
/** Handler to the main thread. */
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/** Runnable for in-flight response delivery. */
|
||||
private Runnable mRunnable;
|
||||
|
||||
/**
|
||||
* Simple cache adapter interface. If provided to the ImageLoader, it
|
||||
* will be used as an L1 cache before dispatch to Volley. Implementations
|
||||
* must not block. Implementation with an LruCache is recommended.
|
||||
*/
|
||||
public interface ImageCache {
|
||||
public Bitmap getBitmap(String url);
|
||||
public void putBitmap(String url, Bitmap bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ImageLoader.
|
||||
* @param queue The RequestQueue to use for making image requests.
|
||||
* @param imageCache The cache to use as an L1 cache.
|
||||
*/
|
||||
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
|
||||
mRequestQueue = queue;
|
||||
mCache = imageCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of ImageListener which handles basic functionality
|
||||
* of showing a default image until the network response is received, at which point
|
||||
* it will switch to either the actual image or the error image.
|
||||
* @param imageView The imageView that the listener is associated with.
|
||||
* @param defaultImageResId Default image resource ID to use, or 0 if it doesn't exist.
|
||||
* @param errorImageResId Error image resource ID to use, or 0 if it doesn't exist.
|
||||
*/
|
||||
public static ImageListener getImageListener(final ImageView view,
|
||||
final int defaultImageResId, final int errorImageResId) {
|
||||
return new ImageListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (errorImageResId != 0) {
|
||||
view.setImageResource(errorImageResId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(ImageContainer response, boolean isImmediate) {
|
||||
if (response.getBitmap() != null) {
|
||||
view.setImageBitmap(response.getBitmap());
|
||||
} else if (defaultImageResId != 0) {
|
||||
view.setImageResource(defaultImageResId);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for the response handlers on image requests.
|
||||
*
|
||||
* The call flow is this:
|
||||
* 1. Upon being attached to a request, onResponse(response, true) will
|
||||
* be invoked to reflect any cached data that was already available. If the
|
||||
* data was available, response.getBitmap() will be non-null.
|
||||
*
|
||||
* 2. After a network response returns, only one of the following cases will happen:
|
||||
* - onResponse(response, false) will be called if the image was loaded.
|
||||
* or
|
||||
* - onErrorResponse will be called if there was an error loading the image.
|
||||
*/
|
||||
public interface ImageListener extends ErrorListener {
|
||||
/**
|
||||
* Listens for non-error changes to the loading of the image request.
|
||||
*
|
||||
* @param response Holds all information pertaining to the request, as well
|
||||
* as the bitmap (if it is loaded).
|
||||
* @param isImmediate True if this was called during ImageLoader.get() variants.
|
||||
* This can be used to differentiate between a cached image loading and a network
|
||||
* image loading in order to, for example, run an animation to fade in network loaded
|
||||
* images.
|
||||
*/
|
||||
public void onResponse(ImageContainer response, boolean isImmediate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is available in the cache.
|
||||
* @param requestUrl The url of the remote image
|
||||
* @param maxWidth The maximum width of the returned image.
|
||||
* @param maxHeight The maximum height of the returned image.
|
||||
* @return True if the item exists in cache, false otherwise.
|
||||
*/
|
||||
public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {
|
||||
throwIfNotOnMainThread();
|
||||
|
||||
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
|
||||
return mCache.getBitmap(cacheKey) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ImageContainer for the requested URL.
|
||||
*
|
||||
* The ImageContainer will contain either the specified default bitmap or the loaded bitmap.
|
||||
* If the default was returned, the {@link ImageLoader} will be invoked when the
|
||||
* request is fulfilled.
|
||||
*
|
||||
* @param requestUrl The URL of the image to be loaded.
|
||||
* @param defaultImage Optional default image to return until the actual image is loaded.
|
||||
*/
|
||||
public ImageContainer get(String requestUrl, final ImageListener listener) {
|
||||
return get(requestUrl, listener, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues a bitmap request with the given URL if that image is not available
|
||||
* in the cache, and returns a bitmap container that contains all of the data
|
||||
* relating to the request (as well as the default image if the requested
|
||||
* image is not available).
|
||||
* @param requestUrl The url of the remote image
|
||||
* @param imageListener The listener to call when the remote image is loaded
|
||||
* @param maxWidth The maximum width of the returned image.
|
||||
* @param maxHeight The maximum height of the returned image.
|
||||
* @return A container object that contains all of the properties of the request, as well as
|
||||
* the currently available image (default if remote is not loaded).
|
||||
*/
|
||||
public ImageContainer get(String requestUrl, ImageListener imageListener,
|
||||
int maxWidth, int maxHeight) {
|
||||
// only fulfill requests that were initiated from the main thread.
|
||||
throwIfNotOnMainThread();
|
||||
|
||||
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
|
||||
|
||||
// Try to look up the request in the cache of remote images.
|
||||
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
|
||||
if (cachedBitmap != null) {
|
||||
// Return the cached bitmap.
|
||||
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
|
||||
imageListener.onResponse(container, true);
|
||||
return container;
|
||||
}
|
||||
|
||||
// The bitmap did not exist in the cache, fetch it!
|
||||
ImageContainer imageContainer =
|
||||
new ImageContainer(null, requestUrl, cacheKey, imageListener);
|
||||
|
||||
// Update the caller to let them know that they should use the default bitmap.
|
||||
imageListener.onResponse(imageContainer, true);
|
||||
|
||||
// Check to see if a request is already in-flight.
|
||||
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
|
||||
if (request != null) {
|
||||
// If it is, add this request to the list of listeners.
|
||||
request.addContainer(imageContainer);
|
||||
return imageContainer;
|
||||
}
|
||||
|
||||
// The request is not already in flight. Send the new request to the network and
|
||||
// track it.
|
||||
Request<?> newRequest =
|
||||
new ImageRequest(requestUrl, new Listener<Bitmap>() {
|
||||
@Override
|
||||
public void onResponse(Bitmap response) {
|
||||
onGetImageSuccess(cacheKey, response);
|
||||
}
|
||||
}, maxWidth, maxHeight,
|
||||
Config.RGB_565, new ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
onGetImageError(cacheKey, error);
|
||||
}
|
||||
});
|
||||
|
||||
mRequestQueue.add(newRequest);
|
||||
mInFlightRequests.put(cacheKey,
|
||||
new BatchedImageRequest(newRequest, imageContainer));
|
||||
return imageContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the amount of time to wait after the first response arrives before delivering all
|
||||
* responses. Batching can be disabled entirely by passing in 0.
|
||||
* @param newBatchedResponseDelayMs The time in milliseconds to wait.
|
||||
*/
|
||||
public void setBatchedResponseDelay(int newBatchedResponseDelayMs) {
|
||||
mBatchResponseDelayMs = newBatchedResponseDelayMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when an image was successfully loaded.
|
||||
* @param cacheKey The cache key that is associated with the image request.
|
||||
* @param response The bitmap that was returned from the network.
|
||||
*/
|
||||
private void onGetImageSuccess(String cacheKey, Bitmap response) {
|
||||
// cache the image that was fetched.
|
||||
mCache.putBitmap(cacheKey, response);
|
||||
|
||||
// remove the request from the list of in-flight requests.
|
||||
BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
|
||||
|
||||
if (request != null) {
|
||||
// Update the response bitmap.
|
||||
request.mResponseBitmap = response;
|
||||
|
||||
// Send the batched response
|
||||
batchResponse(cacheKey, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when an image failed to load.
|
||||
* @param cacheKey The cache key that is associated with the image request.
|
||||
*/
|
||||
private void onGetImageError(String cacheKey, VolleyError error) {
|
||||
// Notify the requesters that something failed via a null result.
|
||||
// Remove this request from the list of in-flight requests.
|
||||
BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
|
||||
|
||||
// Set the error for this request
|
||||
request.setError(error);
|
||||
|
||||
if (request != null) {
|
||||
// Send the batched response
|
||||
batchResponse(cacheKey, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Container object for all of the data surrounding an image request.
|
||||
*/
|
||||
public class ImageContainer {
|
||||
/**
|
||||
* The most relevant bitmap for the container. If the image was in cache, the
|
||||
* Holder to use for the final bitmap (the one that pairs to the requested URL).
|
||||
*/
|
||||
private Bitmap mBitmap;
|
||||
|
||||
private final ImageListener mListener;
|
||||
|
||||
/** The cache key that was associated with the request */
|
||||
private final String mCacheKey;
|
||||
|
||||
/** The request URL that was specified */
|
||||
private final String mRequestUrl;
|
||||
|
||||
/**
|
||||
* Constructs a BitmapContainer object.
|
||||
* @param bitmap The final bitmap (if it exists).
|
||||
* @param requestUrl The requested URL for this container.
|
||||
* @param cacheKey The cache key that identifies the requested URL for this container.
|
||||
*/
|
||||
public ImageContainer(Bitmap bitmap, String requestUrl,
|
||||
String cacheKey, ImageListener listener) {
|
||||
mBitmap = bitmap;
|
||||
mRequestUrl = requestUrl;
|
||||
mCacheKey = cacheKey;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases interest in the in-flight request (and cancels it if no one else is listening).
|
||||
*/
|
||||
public void cancelRequest() {
|
||||
if (mListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BatchedImageRequest request = mInFlightRequests.get(mCacheKey);
|
||||
if (request != null) {
|
||||
boolean canceled = request.removeContainerAndCancelIfNecessary(this);
|
||||
if (canceled) {
|
||||
mInFlightRequests.remove(mCacheKey);
|
||||
}
|
||||
} else {
|
||||
// check to see if it is already batched for delivery.
|
||||
request = mBatchedResponses.get(mCacheKey);
|
||||
if (request != null) {
|
||||
request.removeContainerAndCancelIfNecessary(this);
|
||||
if (request.mContainers.size() == 0) {
|
||||
mBatchedResponses.remove(mCacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitmap associated with the request URL if it has been loaded, null otherwise.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested URL for this container.
|
||||
*/
|
||||
public String getRequestUrl() {
|
||||
return mRequestUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class used to map a Request to the set of active ImageContainer objects that are
|
||||
* interested in its results.
|
||||
*/
|
||||
private class BatchedImageRequest {
|
||||
/** The request being tracked */
|
||||
private final Request<?> mRequest;
|
||||
|
||||
/** The result of the request being tracked by this item */
|
||||
private Bitmap mResponseBitmap;
|
||||
|
||||
/** Error if one occurred for this response */
|
||||
private VolleyError mError;
|
||||
|
||||
/** List of all of the active ImageContainers that are interested in the request */
|
||||
private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();
|
||||
|
||||
/**
|
||||
* Constructs a new BatchedImageRequest object
|
||||
* @param request The request being tracked
|
||||
* @param container The ImageContainer of the person who initiated the request.
|
||||
*/
|
||||
public BatchedImageRequest(Request<?> request, ImageContainer container) {
|
||||
mRequest = request;
|
||||
mContainers.add(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error for this response
|
||||
*/
|
||||
public void setError(VolleyError error) {
|
||||
mError = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error for this response
|
||||
*/
|
||||
public VolleyError getError() {
|
||||
return mError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds another ImageContainer to the list of those interested in the results of
|
||||
* the request.
|
||||
*/
|
||||
public void addContainer(ImageContainer container) {
|
||||
mContainers.add(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detatches the bitmap container from the request and cancels the request if no one is
|
||||
* left listening.
|
||||
* @param container The container to remove from the list
|
||||
* @return True if the request was canceled, false otherwise.
|
||||
*/
|
||||
public boolean removeContainerAndCancelIfNecessary(ImageContainer container) {
|
||||
mContainers.remove(container);
|
||||
if (mContainers.size() == 0) {
|
||||
mRequest.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the runnable for batched delivery of responses if it is not already started.
|
||||
* @param cacheKey The cacheKey of the response being delivered.
|
||||
* @param request The BatchedImageRequest to be delivered.
|
||||
* @param error The volley error associated with the request (if applicable).
|
||||
*/
|
||||
private void batchResponse(String cacheKey, BatchedImageRequest request) {
|
||||
mBatchedResponses.put(cacheKey, request);
|
||||
// If we don't already have a batch delivery runnable in flight, make a new one.
|
||||
// Note that this will be used to deliver responses to all callers in mBatchedResponses.
|
||||
if (mRunnable == null) {
|
||||
mRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (BatchedImageRequest bir : mBatchedResponses.values()) {
|
||||
for (ImageContainer container : bir.mContainers) {
|
||||
// If one of the callers in the batched request canceled the request
|
||||
// after the response was received but before it was delivered,
|
||||
// skip them.
|
||||
if (container.mListener == null) {
|
||||
continue;
|
||||
}
|
||||
if (bir.getError() == null) {
|
||||
container.mBitmap = bir.mResponseBitmap;
|
||||
container.mListener.onResponse(container, false);
|
||||
} else {
|
||||
container.mListener.onErrorResponse(bir.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
mBatchedResponses.clear();
|
||||
mRunnable = null;
|
||||
}
|
||||
|
||||
};
|
||||
// Post the runnable.
|
||||
mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfNotOnMainThread() {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
throw new IllegalStateException("ImageLoader must be invoked from the main thread.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a cache key for use with the L1 cache.
|
||||
* @param url The URL of the request.
|
||||
* @param maxWidth The max-width of the output.
|
||||
* @param maxHeight The max-height of the output.
|
||||
*/
|
||||
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
|
||||
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
|
||||
.append("#H").append(maxHeight).append(url).toString();
|
||||
}
|
||||
}
|
||||
@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import com.android.volley.DefaultRetryPolicy;
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.ParseError;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyLog;
|
||||
|
||||
/**
|
||||
* A canned request for getting an image at a given URL and calling
|
||||
* back with a decoded Bitmap.
|
||||
*/
|
||||
public class ImageRequest extends Request<Bitmap> {
|
||||
/** Socket timeout in milliseconds for image requests */
|
||||
private static final int IMAGE_TIMEOUT_MS = 1000;
|
||||
|
||||
/** Default number of retries for image requests */
|
||||
private static final int IMAGE_MAX_RETRIES = 2;
|
||||
|
||||
/** Default backoff multiplier for image requests */
|
||||
private static final float IMAGE_BACKOFF_MULT = 2f;
|
||||
|
||||
private final Response.Listener<Bitmap> mListener;
|
||||
private final Config mDecodeConfig;
|
||||
private final int mMaxWidth;
|
||||
private final int mMaxHeight;
|
||||
|
||||
/** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
|
||||
private static final Object sDecodeLock = new Object();
|
||||
|
||||
/**
|
||||
* Creates a new image request, decoding to a maximum specified width and
|
||||
* height. If both width and height are zero, the image will be decoded to
|
||||
* its natural size. If one of the two is nonzero, that dimension will be
|
||||
* clamped and the other one will be set to preserve the image's aspect
|
||||
* ratio. If both width and height are nonzero, the image will be decoded to
|
||||
* be fit in the rectangle of dimensions width x height while keeping its
|
||||
* aspect ratio.
|
||||
*
|
||||
* @param url URL of the image
|
||||
* @param listener Listener to receive the decoded bitmap
|
||||
* @param maxWidth Maximum width to decode this bitmap to, or zero for none
|
||||
* @param maxHeight Maximum height to decode this bitmap to, or zero for
|
||||
* none
|
||||
* @param decodeConfig Format to decode the bitmap to
|
||||
* @param errorListener Error listener, or null to ignore errors
|
||||
*/
|
||||
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
|
||||
Config decodeConfig, Response.ErrorListener errorListener) {
|
||||
super(Method.GET, url, errorListener);
|
||||
setRetryPolicy(
|
||||
new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
|
||||
mListener = listener;
|
||||
mDecodeConfig = decodeConfig;
|
||||
mMaxWidth = maxWidth;
|
||||
mMaxHeight = maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
return Priority.LOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales one side of a rectangle to fit aspect ratio.
|
||||
*
|
||||
* @param maxPrimary Maximum size of the primary dimension (i.e. width for
|
||||
* max width), or zero to maintain aspect ratio with secondary
|
||||
* dimension
|
||||
* @param maxSecondary Maximum size of the secondary dimension, or zero to
|
||||
* maintain aspect ratio with primary dimension
|
||||
* @param actualPrimary Actual size of the primary dimension
|
||||
* @param actualSecondary Actual size of the secondary dimension
|
||||
*/
|
||||
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
|
||||
int actualSecondary) {
|
||||
// If no dominant value at all, just return the actual.
|
||||
if (maxPrimary == 0 && maxSecondary == 0) {
|
||||
return actualPrimary;
|
||||
}
|
||||
|
||||
// If primary is unspecified, scale primary to match secondary's scaling ratio.
|
||||
if (maxPrimary == 0) {
|
||||
double ratio = (double) maxSecondary / (double) actualSecondary;
|
||||
return (int) (actualPrimary * ratio);
|
||||
}
|
||||
|
||||
if (maxSecondary == 0) {
|
||||
return maxPrimary;
|
||||
}
|
||||
|
||||
double ratio = (double) actualSecondary / (double) actualPrimary;
|
||||
int resized = maxPrimary;
|
||||
if (resized * ratio > maxSecondary) {
|
||||
resized = (int) (maxSecondary / ratio);
|
||||
}
|
||||
return resized;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
|
||||
// Serialize all decode on a global lock to reduce concurrent heap usage.
|
||||
synchronized (sDecodeLock) {
|
||||
try {
|
||||
return doParse(response);
|
||||
} catch (OutOfMemoryError e) {
|
||||
VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
|
||||
return Response.error(new ParseError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The real guts of parseNetworkResponse. Broken out for readability.
|
||||
*/
|
||||
private Response<Bitmap> doParse(NetworkResponse response) {
|
||||
byte[] data = response.data;
|
||||
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
|
||||
Bitmap bitmap = null;
|
||||
if (mMaxWidth == 0 && mMaxHeight == 0) {
|
||||
decodeOptions.inPreferredConfig = mDecodeConfig;
|
||||
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
|
||||
} else {
|
||||
// If we have to resize this image, first get the natural bounds.
|
||||
decodeOptions.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
|
||||
int actualWidth = decodeOptions.outWidth;
|
||||
int actualHeight = decodeOptions.outHeight;
|
||||
|
||||
// Then compute the dimensions we would ideally like to decode to.
|
||||
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
|
||||
actualWidth, actualHeight);
|
||||
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
|
||||
actualHeight, actualWidth);
|
||||
|
||||
// Decode to the nearest power of two scaling factor.
|
||||
decodeOptions.inJustDecodeBounds = false;
|
||||
// TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
|
||||
// decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
|
||||
decodeOptions.inSampleSize =
|
||||
findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
|
||||
Bitmap tempBitmap =
|
||||
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
|
||||
|
||||
// If necessary, scale down to the maximal acceptable size.
|
||||
if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
|
||||
tempBitmap.getHeight() > desiredHeight)) {
|
||||
bitmap = Bitmap.createScaledBitmap(tempBitmap,
|
||||
desiredWidth, desiredHeight, true);
|
||||
tempBitmap.recycle();
|
||||
} else {
|
||||
bitmap = tempBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap == null) {
|
||||
return Response.error(new ParseError(response));
|
||||
} else {
|
||||
return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deliverResponse(Bitmap response) {
|
||||
mListener.onResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest power-of-two divisor for use in downscaling a bitmap
|
||||
* that will not result in the scaling past the desired dimensions.
|
||||
*
|
||||
* @param actualWidth Actual width of the bitmap
|
||||
* @param actualHeight Actual height of the bitmap
|
||||
* @param desiredWidth Desired width of the bitmap
|
||||
* @param desiredHeight Desired height of the bitmap
|
||||
*/
|
||||
// Visible for testing.
|
||||
static int findBestSampleSize(
|
||||
int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
|
||||
double wr = (double) actualWidth / desiredWidth;
|
||||
double hr = (double) actualHeight / desiredHeight;
|
||||
double ratio = Math.min(wr, hr);
|
||||
float n = 1.0f;
|
||||
while ((n * 2) <= ratio) {
|
||||
n *= 2;
|
||||
}
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.ParseError;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.Response.ErrorListener;
|
||||
import com.android.volley.Response.Listener;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* A request for retrieving a {@link JSONArray} response body at a given URL.
|
||||
*/
|
||||
public class JsonArrayRequest extends JsonRequest<JSONArray> {
|
||||
|
||||
/**
|
||||
* Creates a new request.
|
||||
* @param url URL to fetch the JSON from
|
||||
* @param listener Listener to receive the JSON response
|
||||
* @param errorListener Error listener, or null to ignore errors.
|
||||
*/
|
||||
public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {
|
||||
super(Method.GET, url, null, listener, errorListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
|
||||
try {
|
||||
String jsonString =
|
||||
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
|
||||
return Response.success(new JSONArray(jsonString),
|
||||
HttpHeaderParser.parseCacheHeaders(response));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return Response.error(new ParseError(e));
|
||||
} catch (JSONException je) {
|
||||
return Response.error(new ParseError(je));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.ParseError;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.Response.ErrorListener;
|
||||
import com.android.volley.Response.Listener;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* A request for retrieving a {@link JSONObject} response body at a given URL, allowing for an
|
||||
* optional {@link JSONObject} to be passed in as part of the request body.
|
||||
*/
|
||||
public class JsonObjectRequest extends JsonRequest<JSONObject> {
|
||||
|
||||
/**
|
||||
* Creates a new request.
|
||||
* @param method the HTTP method to use
|
||||
* @param url URL to fetch the JSON from
|
||||
* @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and
|
||||
* indicates no parameters will be posted along with request.
|
||||
* @param listener Listener to receive the JSON response
|
||||
* @param errorListener Error listener, or null to ignore errors.
|
||||
*/
|
||||
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
|
||||
Listener<JSONObject> listener, ErrorListener errorListener) {
|
||||
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
|
||||
errorListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
|
||||
* <code>null</code>, <code>POST</code> otherwise.
|
||||
*
|
||||
* @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
|
||||
*/
|
||||
public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
|
||||
ErrorListener errorListener) {
|
||||
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
|
||||
listener, errorListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
|
||||
try {
|
||||
String jsonString =
|
||||
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
|
||||
return Response.success(new JSONObject(jsonString),
|
||||
HttpHeaderParser.parseCacheHeaders(response));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return Response.error(new ParseError(e));
|
||||
} catch (JSONException je) {
|
||||
return Response.error(new ParseError(je));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.Response.ErrorListener;
|
||||
import com.android.volley.Response.Listener;
|
||||
import com.android.volley.VolleyLog;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* A request for retrieving a T type response body at a given URL that also
|
||||
* optionally sends along a JSON body in the request specified.
|
||||
*
|
||||
* @param <T> JSON type of response expected
|
||||
*/
|
||||
public abstract class JsonRequest<T> extends Request<T> {
|
||||
/** Charset for request. */
|
||||
private static final String PROTOCOL_CHARSET = "utf-8";
|
||||
|
||||
/** Content type for request. */
|
||||
private static final String PROTOCOL_CONTENT_TYPE =
|
||||
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
|
||||
|
||||
private final Listener<T> mListener;
|
||||
private final String mRequestBody;
|
||||
|
||||
/**
|
||||
* Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}
|
||||
* or {@link #getPostParams()} is overridden (which defaults to POST).
|
||||
*
|
||||
* @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}.
|
||||
*/
|
||||
public JsonRequest(String url, String requestBody, Listener<T> listener,
|
||||
ErrorListener errorListener) {
|
||||
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
|
||||
}
|
||||
|
||||
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
|
||||
ErrorListener errorListener) {
|
||||
super(method, url, errorListener);
|
||||
mListener = listener;
|
||||
mRequestBody = requestBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deliverResponse(T response) {
|
||||
mListener.onResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBodyContentType()}.
|
||||
*/
|
||||
@Override
|
||||
public String getPostBodyContentType() {
|
||||
return getBodyContentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBody()}.
|
||||
*/
|
||||
@Override
|
||||
public byte[] getPostBody() {
|
||||
return getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBodyContentType() {
|
||||
return PROTOCOL_CONTENT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
try {
|
||||
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
|
||||
mRequestBody, PROTOCOL_CHARSET);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,202 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2013 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 com.android.volley.toolbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.ImageLoader.ImageContainer;
|
||||
import com.android.volley.toolbox.ImageLoader.ImageListener;
|
||||
|
||||
/**
|
||||
* Handles fetching an image from a URL as well as the life-cycle of the
|
||||
* associated request.
|
||||
*/
|
||||
public class NetworkImageView extends ImageView {
|
||||
/** The URL of the network image to load */
|
||||
private String mUrl;
|
||||
|
||||
/**
|
||||
* Resource ID of the image to be used as a placeholder until the network image is loaded.
|
||||
*/
|
||||
private int mDefaultImageId;
|
||||
|
||||
/**
|
||||
* Resource ID of the image to be used if the network response fails.
|
||||
*/
|
||||
private int mErrorImageId;
|
||||
|
||||
/** Local copy of the ImageLoader. */
|
||||
private ImageLoader mImageLoader;
|
||||
|
||||
/** Current ImageContainer. (either in-flight or finished) */
|
||||
private ImageContainer mImageContainer;
|
||||
|
||||
public NetworkImageView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NetworkImageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URL of the image that should be loaded into this view. Note that calling this will
|
||||
* immediately either set the cached image (if available) or the default image specified by
|
||||
* {@link NetworkImageView#setDefaultImageResId(int)} on the view.
|
||||
*
|
||||
* NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and
|
||||
* {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling
|
||||
* this function.
|
||||
*
|
||||
* @param url The URL that should be loaded into this ImageView.
|
||||
* @param imageLoader ImageLoader that will be used to make the request.
|
||||
*/
|
||||
public void setImageUrl(String url, ImageLoader imageLoader) {
|
||||
mUrl = url;
|
||||
mImageLoader = imageLoader;
|
||||
// The URL has potentially changed. See if we need to load it.
|
||||
loadImageIfNecessary(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default image resource ID to be used for this view until the attempt to load it
|
||||
* completes.
|
||||
*/
|
||||
public void setDefaultImageResId(int defaultImage) {
|
||||
mDefaultImageId = defaultImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error image resource ID to be used for this view in the event that the image
|
||||
* requested fails to load.
|
||||
*/
|
||||
public void setErrorImageResId(int errorImage) {
|
||||
mErrorImageId = errorImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the image for the view if it isn't already loaded.
|
||||
* @param isInLayoutPass True if this was invoked from a layout pass, false otherwise.
|
||||
*/
|
||||
private void loadImageIfNecessary(final boolean isInLayoutPass) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
boolean isFullyWrapContent = getLayoutParams() != null
|
||||
&& getLayoutParams().height == LayoutParams.WRAP_CONTENT
|
||||
&& getLayoutParams().width == LayoutParams.WRAP_CONTENT;
|
||||
// if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content
|
||||
// view, hold off on loading the image.
|
||||
if (width == 0 && height == 0 && !isFullyWrapContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the URL to be loaded in this view is empty, cancel any old requests and clear the
|
||||
// currently loaded image.
|
||||
if (TextUtils.isEmpty(mUrl)) {
|
||||
if (mImageContainer != null) {
|
||||
mImageContainer.cancelRequest();
|
||||
mImageContainer = null;
|
||||
}
|
||||
setImageBitmap(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// if there was an old request in this view, check if it needs to be canceled.
|
||||
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
|
||||
if (mImageContainer.getRequestUrl().equals(mUrl)) {
|
||||
// if the request is from the same URL, return.
|
||||
return;
|
||||
} else {
|
||||
// if there is a pre-existing request, cancel it if it's fetching a different URL.
|
||||
mImageContainer.cancelRequest();
|
||||
setImageBitmap(null);
|
||||
}
|
||||
}
|
||||
|
||||
// The pre-existing content of this view didn't match the current URL. Load the new image
|
||||
// from the network.
|
||||
ImageContainer newContainer = mImageLoader.get(mUrl,
|
||||
new ImageListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (mErrorImageId != 0) {
|
||||
setImageResource(mErrorImageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(final ImageContainer response, boolean isImmediate) {
|
||||
// If this was an immediate response that was delivered inside of a layout
|
||||
// pass do not set the image immediately as it will trigger a requestLayout
|
||||
// inside of a layout. Instead, defer setting the image by posting back to
|
||||
// the main thread.
|
||||
if (isImmediate && isInLayoutPass) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onResponse(response, false);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.getBitmap() != null) {
|
||||
setImageBitmap(response.getBitmap());
|
||||
} else if (mDefaultImageId != 0) {
|
||||
setImageResource(mDefaultImageId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update the ImageContainer to be the new bitmap container.
|
||||
mImageContainer = newContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
loadImageIfNecessary(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
if (mImageContainer != null) {
|
||||
// If the view was bound to an image request, cancel it and clear
|
||||
// out the image from the view.
|
||||
mImageContainer.cancelRequest();
|
||||
setImageBitmap(null);
|
||||
// also clear out the container so we can reload the image if necessary.
|
||||
mImageContainer = null;
|
||||
}
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.Cache;
|
||||
|
||||
/**
|
||||
* A cache that doesn't.
|
||||
*/
|
||||
public class NoCache implements Cache {
|
||||
@Override
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry get(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Entry entry) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(String key, boolean fullExpire) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 com.android.volley.toolbox;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A variation of {@link ByteArrayOutputStream} that uses a pool of byte[] buffers instead
|
||||
* of always allocating them fresh, saving on heap churn.
|
||||
*/
|
||||
public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
/**
|
||||
* If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is
|
||||
* the default size to which the underlying byte array is initialized.
|
||||
*/
|
||||
private static final int DEFAULT_SIZE = 256;
|
||||
|
||||
private final ByteArrayPool mPool;
|
||||
|
||||
/**
|
||||
* Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written
|
||||
* to this instance, the underlying byte array will expand.
|
||||
*/
|
||||
public PoolingByteArrayOutputStream(ByteArrayPool pool) {
|
||||
this(pool, DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If
|
||||
* more than {@code size} bytes are written to this instance, the underlying byte array will
|
||||
* expand.
|
||||
*
|
||||
* @param size initial size for the underlying byte array. The value will be pinned to a default
|
||||
* minimum size.
|
||||
*/
|
||||
public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {
|
||||
mPool = pool;
|
||||
buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mPool.returnBuf(buf);
|
||||
buf = null;
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize() {
|
||||
mPool.returnBuf(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures there is enough space in the buffer for the given number of additional bytes.
|
||||
*/
|
||||
private void expand(int i) {
|
||||
/* Can the buffer handle @i more bytes, if not expand it */
|
||||
if (count + i <= buf.length) {
|
||||
return;
|
||||
}
|
||||
byte[] newbuf = mPool.getBuf((count + i) * 2);
|
||||
System.arraycopy(buf, 0, newbuf, 0, count);
|
||||
mPool.returnBuf(buf);
|
||||
buf = newbuf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] buffer, int offset, int len) {
|
||||
expand(len);
|
||||
super.write(buffer, offset, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int oneByte) {
|
||||
expand(1);
|
||||
super.write(oneByte);
|
||||
}
|
||||
}
|
||||
@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* A Future that represents a Volley request.
|
||||
*
|
||||
* Used by providing as your response and error listeners. For example:
|
||||
* <pre>
|
||||
* RequestFuture<JSONObject> future = RequestFuture.newFuture();
|
||||
* MyRequest request = new MyRequest(URL, future, future);
|
||||
*
|
||||
* // If you want to be able to cancel the request:
|
||||
* future.setRequest(requestQueue.add(request));
|
||||
*
|
||||
* // Otherwise:
|
||||
* requestQueue.add(request);
|
||||
*
|
||||
* try {
|
||||
* JSONObject response = future.get();
|
||||
* // do something with response
|
||||
* } catch (InterruptedException e) {
|
||||
* // handle the error
|
||||
* } catch (ExecutionException e) {
|
||||
* // handle the error
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param <T> The type of parsed response this future expects.
|
||||
*/
|
||||
public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
|
||||
Response.ErrorListener {
|
||||
private Request<?> mRequest;
|
||||
private boolean mResultReceived = false;
|
||||
private T mResult;
|
||||
private VolleyError mException;
|
||||
|
||||
public static <E> RequestFuture<E> newFuture() {
|
||||
return new RequestFuture<E>();
|
||||
}
|
||||
|
||||
private RequestFuture() {}
|
||||
|
||||
public void setRequest(Request<?> request) {
|
||||
mRequest = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (mRequest == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDone()) {
|
||||
mRequest.cancel();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
try {
|
||||
return doGet(null);
|
||||
} catch (TimeoutException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));
|
||||
}
|
||||
|
||||
private synchronized T doGet(Long timeoutMs)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
if (mException != null) {
|
||||
throw new ExecutionException(mException);
|
||||
}
|
||||
|
||||
if (mResultReceived) {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
if (timeoutMs == null) {
|
||||
wait(0);
|
||||
} else if (timeoutMs > 0) {
|
||||
wait(timeoutMs);
|
||||
}
|
||||
|
||||
if (mException != null) {
|
||||
throw new ExecutionException(mException);
|
||||
}
|
||||
|
||||
if (!mResultReceived) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
return mResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
if (mRequest == null) {
|
||||
return false;
|
||||
}
|
||||
return mRequest.isCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isDone() {
|
||||
return mResultReceived || mException != null || isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onResponse(T response) {
|
||||
mResultReceived = true;
|
||||
mResult = response;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onErrorResponse(VolleyError error) {
|
||||
mException = error;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.volley.toolbox;
|
||||
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.Response.ErrorListener;
|
||||
import com.android.volley.Response.Listener;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* A canned request for retrieving the response body at a given URL as a String.
|
||||
*/
|
||||
public class StringRequest extends Request<String> {
|
||||
private final Listener<String> mListener;
|
||||
|
||||
/**
|
||||
* Creates a new request with the given method.
|
||||
*
|
||||
* @param method the request {@link Method} to use
|
||||
* @param url URL to fetch the string at
|
||||
* @param listener Listener to receive the String response
|
||||
* @param errorListener Error listener, or null to ignore errors
|
||||
*/
|
||||
public StringRequest(int method, String url, Listener<String> listener,
|
||||
ErrorListener errorListener) {
|
||||
super(method, url, errorListener);
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GET request.
|
||||
*
|
||||
* @param url URL to fetch the string at
|
||||
* @param listener Listener to receive the String response
|
||||
* @param errorListener Error listener, or null to ignore errors
|
||||
*/
|
||||
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
|
||||
this(Method.GET, url, listener, errorListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deliverResponse(String response) {
|
||||
mListener.onResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<String> parseNetworkResponse(NetworkResponse response) {
|
||||
String parsed;
|
||||
try {
|
||||
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
parsed = new String(response.data);
|
||||
}
|
||||
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
|
||||
}
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 com.android.volley.toolbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.http.AndroidHttpClient;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.volley.Network;
|
||||
import com.android.volley.RequestQueue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Volley {
|
||||
|
||||
/** Default on-disk cache directory. */
|
||||
private static final String DEFAULT_CACHE_DIR = "volley";
|
||||
|
||||
/**
|
||||
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
|
||||
*
|
||||
* @param context A {@link Context} to use for creating the cache dir.
|
||||
* @param stack An {@link HttpStack} to use for the network, or null for default.
|
||||
* @return A started {@link RequestQueue} instance.
|
||||
*/
|
||||
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
|
||||
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
|
||||
|
||||
String userAgent = "volley/0";
|
||||
try {
|
||||
String packageName = context.getPackageName();
|
||||
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
|
||||
userAgent = packageName + "/" + info.versionCode;
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
if (Build.VERSION.SDK_INT >= 9) {
|
||||
stack = new HurlStack();
|
||||
} else {
|
||||
// Prior to Gingerbread, HttpUrlConnection was unreliable.
|
||||
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
|
||||
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
|
||||
}
|
||||
}
|
||||
|
||||
Network network = new BasicNetwork(stack);
|
||||
|
||||
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
|
||||
queue.start();
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
|
||||
*
|
||||
* @param context A {@link Context} to use for creating the cache dir.
|
||||
* @return A started {@link RequestQueue} instance.
|
||||
*/
|
||||
public static RequestQueue newRequestQueue(Context context) {
|
||||
return newRequestQueue(context, null);
|
||||
}
|
||||
}
|
||||
@ -7,40 +7,52 @@ import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
import android.support.v4.util.ArrayMap;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.HttpsUtils;
|
||||
import com.gh.common.util.TokenUtils;
|
||||
import com.gh.common.util.Utils;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AppController extends Application {
|
||||
//public class AppController extends TinkerApplication {
|
||||
|
||||
public static final String TAG = AppController.class.getSimpleName();
|
||||
// xiaomi push appid
|
||||
public static final String APP_ID = "2882303761517352993";
|
||||
// xiaomi push appkey
|
||||
public static final String APP_KEY = "5451735292993";
|
||||
// TODO xiaomi push appid 2882303761517352993
|
||||
public static final String APP_ID = "2882303761517564447";
|
||||
// TODO xiaomi push appkey 5451735292993
|
||||
public static final String APP_KEY = "5761756481447";
|
||||
|
||||
private static AppController mInstance;
|
||||
private static ArrayMap<String, Object> objectMap = new ArrayMap<>();
|
||||
|
||||
private RequestQueue mRequestQueue;
|
||||
|
||||
private ArrayList<Activity> list = new ArrayList<>();
|
||||
|
||||
private boolean isFinish = false;
|
||||
|
||||
//快传文件发送单线程
|
||||
public static Executor FILE_SENDER_EXECUTOR = Executors.newSingleThreadExecutor();
|
||||
|
||||
//快传文件发送主要的线程池
|
||||
public static Executor MAIN_EXECUTOR = Executors.newFixedThreadPool(5);
|
||||
|
||||
// public AppController() {
|
||||
// super(ShareConstants.TINKER_ENABLE_ALL, "com.gh.base.AppControllerLike",
|
||||
// "com.tencent.tinker.loader.TinkerLoader", false);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@ -80,6 +92,55 @@ public class AppController extends Application {
|
||||
};
|
||||
Logger.setLogger(this, newLogger);
|
||||
|
||||
//友盟推送
|
||||
final PushAgent mPushAgent = PushAgent.getInstance(this);
|
||||
|
||||
|
||||
//注册推送服务,每次调用register方法都会回调该接口
|
||||
mPushAgent.register(new IUmengRegisterCallback() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(String deviceToken) {
|
||||
//注册成功会返回device token
|
||||
Utils.log("deviceToken::" + deviceToken);
|
||||
|
||||
//设置别名
|
||||
mPushAgent.addExclusiveAlias(TokenUtils.getDeviceId(getApplicationContext())
|
||||
, "GHDID", new UTrack.ICallBack() {
|
||||
@Override
|
||||
public void onMessage(boolean b, String s) {
|
||||
Utils.log("ExclusiveAlias::" + b + "==" + s);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String s, String s1) {
|
||||
Utils.log("deviceToken::" + "注册失败");
|
||||
}
|
||||
});
|
||||
|
||||
// 友盟推送数据处理
|
||||
mPushAgent.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());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static void put(String key, Object object) {
|
||||
@ -160,31 +221,6 @@ public class AppController extends Application {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public static <T> void addToRequestQueue(Request<T> request) {
|
||||
request.setTag(TAG);
|
||||
getInstance().addRequest(request);
|
||||
}
|
||||
|
||||
public static void canclePendingRequests(String tag) {
|
||||
if (TextUtils.isEmpty(tag)) {
|
||||
tag = TAG;
|
||||
}
|
||||
getInstance().cancleRequest(tag);
|
||||
}
|
||||
|
||||
public <T> void addRequest(Request<T> request) {
|
||||
if (mRequestQueue == null) {
|
||||
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
|
||||
}
|
||||
mRequestQueue.add(request);
|
||||
}
|
||||
|
||||
public void cancleRequest(Object tag){
|
||||
if (mRequestQueue != null) {
|
||||
mRequestQueue.cancelAll(tag);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldInit() {
|
||||
ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
|
||||
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
|
||||
|
||||
51
app/src/main/java/com/gh/base/AppControllerLike.java
Normal file
51
app/src/main/java/com/gh/base/AppControllerLike.java
Normal 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);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
29
app/src/main/java/com/gh/base/AppTinkerResultService.java
Normal file
29
app/src/main/java/com/gh/base/AppTinkerResultService.java
Normal 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));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@ -10,10 +10,9 @@ import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.gh.common.constant.Config;
|
||||
import com.gh.common.util.DataCollectionUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.NetworkUtils;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.gh.gamecenter.manager.DataCollectionManager;
|
||||
import com.tencent.stat.StatService;
|
||||
|
||||
import java.io.File;
|
||||
@ -22,9 +21,7 @@ import java.io.IOException;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class AppUncaHandler implements UncaughtExceptionHandler {
|
||||
|
||||
@ -88,13 +85,8 @@ public class AppUncaHandler implements UncaughtExceptionHandler {
|
||||
// MTA主动上传错误
|
||||
StatService.reportError(appController.getApplicationContext(), errorMsg);
|
||||
|
||||
// WIFI实时上传错误数据
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("content", errorMsg);
|
||||
map.put("type", android.os.Build.MODEL);
|
||||
map.put("system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
|
||||
DataCollectionManager.onEvent(appController.getApplicationContext(), "error", map,
|
||||
NetworkUtils.isWifiConnected(appController.getApplicationContext()));
|
||||
// 上传错误数据
|
||||
DataCollectionUtils.uploadError(appController.getApplicationContext(), errorMsg);
|
||||
|
||||
// 保存到本地
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.base;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
@ -31,15 +32,17 @@ import com.gh.gamecenter.listener.OnCallBackListener;
|
||||
import com.gh.gamecenter.manager.SystemBarTintManager;
|
||||
import com.gh.gamecenter.manager.SystemBarTintManager.SystemBarConfig;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
|
||||
private SystemBarTintManager tintManager;
|
||||
|
||||
protected String entrance;
|
||||
|
||||
private boolean isPause;
|
||||
|
||||
@Override
|
||||
@ -48,6 +51,10 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
Utils.log(this.getClass().getSimpleName());
|
||||
AppController.getInstance().addActivity(this);
|
||||
EventBus.getDefault().register(this);
|
||||
entrance = getIntent().getStringExtra("entrance");
|
||||
if (getIntent().getBundleExtra("data") != null) {
|
||||
entrance = getIntent().getBundleExtra("data").getString("entrance");
|
||||
}
|
||||
}
|
||||
|
||||
protected void init(View contentView, String title) {
|
||||
@ -62,7 +69,7 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
setTranslucentStatus(true);
|
||||
tintManager = new SystemBarTintManager(this);
|
||||
tintManager.setStatusBarTintEnabled(true);
|
||||
tintManager.setStatusBarTintResource(R.color.theme);
|
||||
tintManager.setStatusBarTintColor(Color.BLACK);
|
||||
SystemBarConfig config = tintManager.getConfig();
|
||||
contentView.setPadding(0, config.getPixelInsetTop(false), 0,
|
||||
config.getPixelInsetBottom());
|
||||
@ -70,9 +77,11 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
|
||||
setContentView(contentView);
|
||||
|
||||
ButterKnife.bind(this);
|
||||
|
||||
int actionbar_height = getSharedPreferences(Config.PREFERENCE,
|
||||
Context.MODE_PRIVATE).getInt("actionbar_height",
|
||||
DisplayUtils.dip2px(getApplicationContext(), 48));
|
||||
DisplayUtils.dip2px(getApplicationContext(), 55));
|
||||
|
||||
RelativeLayout reuse_actionbar = (RelativeLayout) findViewById(R.id.reuse_actionbar);
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
@ -86,25 +95,6 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
//简化findViewById
|
||||
try {
|
||||
Class<?> clazz = this.getClass();
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
int id = Utils.getId(field.getName());
|
||||
if (id != -1) {
|
||||
Utils.log("reflect name = " + field.getName());
|
||||
field.setAccessible(true);
|
||||
Class<?> fieldType = field.getType();
|
||||
Object injectedValue = fieldType.cast(findViewById(id));
|
||||
field.set(this, injectedValue);
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected SystemBarTintManager getTintManager() {
|
||||
@ -139,7 +129,7 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
}
|
||||
|
||||
//如果是游戏分享,newsTitle默认为空
|
||||
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag, String entrance, String type) {
|
||||
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
|
||||
|
||||
//判断是否是官方版
|
||||
boolean isPlugin = false;
|
||||
@ -151,11 +141,11 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
}
|
||||
}
|
||||
|
||||
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin);
|
||||
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
|
||||
|
||||
if (newsTitle == null){
|
||||
if (newsTitle == null) {
|
||||
DataUtils.onEvent(this, "内容分享", gameName);
|
||||
}else {
|
||||
} else {
|
||||
DataUtils.onEvent(this, "内容分享", newsTitle);
|
||||
}
|
||||
}
|
||||
@ -220,4 +210,5 @@ public class BaseActivity extends Activity implements OnCallBackListener {
|
||||
public void loadEmpty() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -17,28 +17,23 @@ 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.PlatformUtils;
|
||||
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.download.DownloadStatus;
|
||||
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.DataCollectionManager;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
import com.tencent.tauth.Tencent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2016/9/19.
|
||||
* 游戏详情、新闻详情基类(控制底部下载栏)
|
||||
@ -53,6 +48,8 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
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;
|
||||
@ -87,12 +84,15 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
entrance = getIntent().getStringExtra("entrance");
|
||||
if (getIntent().getBundleExtra("data") != null) {
|
||||
entrance = getIntent().getBundleExtra("data").getString("entrance");
|
||||
}
|
||||
|
||||
View contentView = View.inflate(this, R.layout.activity_detail, null);
|
||||
|
||||
// 添加分享图标
|
||||
iv_share = new ImageView(this);
|
||||
iv_share.setImageResource(R.drawable.share_icon);
|
||||
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)
|
||||
@ -115,6 +115,8 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
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);
|
||||
@ -157,7 +159,7 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
if (Config.isShow(this)) {
|
||||
detail_ll_bottom.setVisibility(View.VISIBLE);
|
||||
detail_rv_show.setPadding(0, 0, 0,
|
||||
DisplayUtils.dip2px(getApplicationContext(), 44));
|
||||
DisplayUtils.dip2px(getApplicationContext(), 60));
|
||||
} else {
|
||||
detail_ll_bottom.setVisibility(View.GONE);
|
||||
detail_rv_show.setPadding(0, 0, 0, 0);
|
||||
@ -229,49 +231,12 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int doneCount = 0; // 下载完成数量
|
||||
int pluginCount = 0; // 可插件化数量
|
||||
int updateCount = 0; // 可更新数量
|
||||
int installCount = 0; // 已安装数量
|
||||
DownloadEntity downloadEntity;
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
downloadEntity = DownloadManager.getInstance(this).get(apkEntity.getUrl());
|
||||
if (downloadEntity != null) {
|
||||
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
|
||||
doneCount++;
|
||||
} else if (downloadEntity.isPluggable()) {
|
||||
pluginCount++;
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
if (PackageManager.isCanUpdate(gameEntity.getId(), apkEntity.getPackageName())) {
|
||||
updateCount++;
|
||||
}
|
||||
if (PackageManager.isInstalled(apkEntity.getPackageName())) {
|
||||
if (!PackageUtils.isSignature(this, apkEntity.getPackageName())) {
|
||||
pluginCount++;
|
||||
} else {
|
||||
installCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String status;
|
||||
if (doneCount != 0) {
|
||||
status = "安装";
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
} else if (pluginCount != 0) {
|
||||
status = "插件化";
|
||||
String status = GameUtils.getDownloadBtnText(this, gameEntity);
|
||||
if ("插件化".equals(status)) {
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else if (updateCount != 0) {
|
||||
status = "更新";
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
} else if (installCount != 0) {
|
||||
status = "打开";
|
||||
} else if ("打开".equals(status)) {
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
} else {
|
||||
status = "下载";
|
||||
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
|
||||
@ -370,8 +335,7 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
});
|
||||
}
|
||||
} else {
|
||||
DownloadDialog.getInstance(this)
|
||||
.showPopupWindow(v, gameEntity, entrance, name + ":" + title);
|
||||
DownloadDialog.getInstance(this).showPopupWindow(v, gameEntity, entrance, name + ":" + title);
|
||||
}
|
||||
} else {
|
||||
toast("稍等片刻~!游戏正在上传中...");
|
||||
@ -380,7 +344,9 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
String str = detail_tv_per.getText().toString();
|
||||
if ("下载中".equals(str)) {
|
||||
Intent intent = new Intent(this, DownloadManagerActivity.class);
|
||||
intent.putExtra("currentItem", 1);
|
||||
intent.putExtra("url", gameEntity.getApk().get(0).getUrl());
|
||||
intent.putExtra("entrance", entrance + "+(" + name + "[" + title + "])");
|
||||
startActivity(intent);
|
||||
} else if ("安装".equals(str)) {
|
||||
PackageUtils.launchSetup(this, mDownloadEntity.getPath());
|
||||
@ -391,10 +357,7 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
private void download() {
|
||||
String str = detail_tv_download.getText().toString();
|
||||
if (str.contains("启动")) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
kv.put("页面", name);
|
||||
DataUtils.onEvent(BaseDetailActivity.this, "游戏启动", gameEntity.getName(), kv);
|
||||
DataUtils.onGameLaunchEvent(this, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), name);
|
||||
|
||||
PackageUtils.launchApplicationByPackageName(this, gameEntity.getApk().get(0).getPackageName());
|
||||
} else {
|
||||
@ -409,35 +372,7 @@ public abstract class BaseDetailActivity extends BaseActivity implements View.On
|
||||
ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
String msg = FileUtils.isCanDownload(this, apkEntity.getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", apkEntity.getPlatform());
|
||||
kv.put("状态", "下载开始");
|
||||
DataUtils.onEvent(BaseDetailActivity.this, "游戏下载", gameEntity.getName(), kv);
|
||||
|
||||
Map<String, Object> kv2 = new HashMap<>();
|
||||
kv2.put("版本", apkEntity.getPlatform());
|
||||
kv2.put("状态", "下载开始");
|
||||
kv2.put("页面", name);
|
||||
kv2.put("位置", entrance);
|
||||
DataUtils.onEvent(BaseDetailActivity.this, "游戏下载位置", gameEntity.getName(), kv2);
|
||||
|
||||
Map<String, Object> kv3 = new HashMap<>();
|
||||
kv3.put(entrance, "下载数");
|
||||
kv3.put(entrance, "下载开始");
|
||||
DataUtils.onEvent(BaseDetailActivity.this, "应用数据", gameEntity.getName(), kv3);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
map.put("method", method.equals("下载") ? "正常" : method);
|
||||
map.put("platform", PlatformUtils.getInstance(getApplicationContext())
|
||||
.getPlatformName(gameEntity.getApk().get(0).getPlatform()));
|
||||
map.put("status", "开始");
|
||||
map.put("location", name + ":" + title);
|
||||
map.put("entrance", entrance);
|
||||
map.put("btn_status", method);
|
||||
map.put("network", NetworkUtils.getConnectedType(this));
|
||||
DataCollectionManager.onEvent(this, "download", map);
|
||||
DataUtils.onGameDownloadEvent(this, gameEntity.getName(), apkEntity.getPlatform(), entrance, "下载开始");
|
||||
|
||||
DownloadManager.createDownload(this, apkEntity, gameEntity, method, entrance, name + ":" + title);
|
||||
|
||||
|
||||
@ -8,11 +8,8 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.gh.common.util.Utils;
|
||||
import com.gh.gamecenter.listener.OnCallBackListener;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
@ -22,46 +19,16 @@ import de.greenrobot.event.EventBus;
|
||||
*/
|
||||
public class BaseFragment extends Fragment implements OnCallBackListener {
|
||||
|
||||
// private Unbinder unbinder;
|
||||
|
||||
protected View view;
|
||||
|
||||
protected Handler handler = new Handler();
|
||||
|
||||
protected boolean isEverpause;
|
||||
|
||||
protected void init(int layout, boolean flag) {
|
||||
protected void init(int layout) {
|
||||
view = View.inflate(getActivity(), layout, null);
|
||||
|
||||
// unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
if (flag) {
|
||||
//简化findViewById
|
||||
try {
|
||||
Class<?> clazz = this.getClass();
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
int id = Utils.getId(field.getName());
|
||||
if (id != -1) {
|
||||
Utils.log("reflect name = " + field.getName());
|
||||
field.setAccessible(true);
|
||||
Class<?> fieldType = field.getType();
|
||||
Object injectedValue = fieldType.cast(view.findViewById(id));
|
||||
field.set(this, injectedValue);
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void init(int layout) {
|
||||
init(layout, true);
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,7 +68,6 @@ public class BaseFragment extends Fragment implements OnCallBackListener {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
// unbinder.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -2,6 +2,7 @@ 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;
|
||||
@ -30,13 +31,15 @@ import com.gh.gamecenter.eventbus.EBShowDialog;
|
||||
import com.gh.gamecenter.manager.SystemBarTintManager;
|
||||
import com.gh.gamecenter.manager.SystemBarTintManager.SystemBarConfig;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class BaseFragmentActivity extends FragmentActivity {
|
||||
|
||||
protected String entrance;
|
||||
|
||||
private boolean isPause;
|
||||
|
||||
@Override
|
||||
@ -45,6 +48,10 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
Utils.log(this.getClass().getSimpleName());
|
||||
AppController.getInstance().addActivity(this);
|
||||
EventBus.getDefault().register(this);
|
||||
entrance = getIntent().getStringExtra("entrance");
|
||||
if (getIntent().getBundleExtra("data") != null) {
|
||||
entrance = getIntent().getBundleExtra("data").getString("entrance");
|
||||
}
|
||||
}
|
||||
|
||||
public void init(View contentView, String title) {
|
||||
@ -59,7 +66,7 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
setTranslucentStatus(true);
|
||||
SystemBarTintManager tintManager = new SystemBarTintManager(this);
|
||||
tintManager.setStatusBarTintEnabled(true);
|
||||
tintManager.setStatusBarTintResource(R.color.theme);
|
||||
tintManager.setStatusBarTintColor(Color.BLACK);
|
||||
SystemBarConfig config = tintManager.getConfig();
|
||||
contentView.setPadding(0, config.getPixelInsetTop(false), 0,
|
||||
config.getPixelInsetBottom());
|
||||
@ -67,6 +74,8 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
|
||||
setContentView(contentView);
|
||||
|
||||
ButterKnife.bind(this);
|
||||
|
||||
int actionbar_height = getSharedPreferences(Config.PREFERENCE,
|
||||
Context.MODE_PRIVATE).getInt("actionbar_height",
|
||||
DisplayUtils.dip2px(getApplicationContext(), 48));
|
||||
@ -83,24 +92,6 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Class<?> clazz = this.getClass();
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
int id = Utils.getId(field.getName());
|
||||
if (id != -1) {
|
||||
Utils.log("reflect name = " + field.getName());
|
||||
field.setAccessible(true);
|
||||
Class<?> fieldType = field.getType();
|
||||
Object injectedValue = fieldType.cast(findViewById(id));
|
||||
field.set(this, injectedValue);
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -131,7 +122,7 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
}
|
||||
|
||||
//如果是游戏分享,newsTitle默认为空
|
||||
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag, String entrance, String type) {
|
||||
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
|
||||
|
||||
//判断是否是官方版
|
||||
boolean isPlugin = false;
|
||||
@ -143,7 +134,7 @@ public class BaseFragmentActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
|
||||
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin);
|
||||
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
|
||||
|
||||
if (newsTitle == null){
|
||||
DataUtils.onEvent(this, "内容分享", gameName);
|
||||
|
||||
@ -9,15 +9,23 @@ 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.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.gh.common.util.ClassUtils;
|
||||
import com.gh.common.util.FileUtils;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.common.util.RunningUtils;
|
||||
import com.gh.common.util.TokenUtils;
|
||||
import com.gh.common.util.Utils;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
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;
|
||||
@ -27,7 +35,6 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -56,6 +63,7 @@ import java.util.Locale;
|
||||
* @author mayixiang
|
||||
*/
|
||||
public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
private String mAlias;
|
||||
|
||||
@Override
|
||||
public void onReceivePassThroughMessage(Context context,
|
||||
@ -172,18 +180,11 @@ public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
}
|
||||
|
||||
String url = jsonObject.getString("icon");
|
||||
String path = context.getCacheDir() + File.separator
|
||||
+ url.substring(url.lastIndexOf("/") + 1);
|
||||
try {
|
||||
int result = FileUtils.downloadFile(url, path);
|
||||
if (result == -1) {
|
||||
// 下载出错,使用光环logo
|
||||
path = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String path = context.getCacheDir() + File.separator + url.substring(url.lastIndexOf("/") + 1);
|
||||
int result = FileUtils.downloadFile(url, path);
|
||||
if (result != 200) {
|
||||
// 下载出错,使用光环logo
|
||||
path = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (remoteViews != null) {
|
||||
@ -228,8 +229,7 @@ public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
List<PackageInfo> packageInfos = context.getPackageManager()
|
||||
.getInstalledPackages(0);
|
||||
for (int i = 0, size = packageInfos.size(); i < size; i++) {
|
||||
PackageInfo packageInfo = packageInfos.get(i);
|
||||
for (PackageInfo packageInfo : packageInfos) {
|
||||
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
list.add(packageInfo.packageName);
|
||||
}
|
||||
@ -238,10 +238,52 @@ public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationMessageClicked(Context context,
|
||||
MiPushMessage message) {
|
||||
Log.v(AppController.TAG, "onNotificationMessageClicked is called. "
|
||||
+ message.toString());
|
||||
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
|
||||
Log.v(AppController.TAG, "onNotificationMessageClicked is called. " + message.toString());
|
||||
|
||||
try {
|
||||
String content = message.getContent();
|
||||
JSONObject response = new JSONObject(content);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("entrance", "(小米推送)");
|
||||
String type = response.getString("type");
|
||||
if ("article".equals(type)) {
|
||||
bundle.putString("to", "NewsDetailActivity");
|
||||
bundle.putString("newsId", response.getString("target"));
|
||||
} else if ("game".equals(type)) {
|
||||
bundle.putString("to", "GameDetailActivity");
|
||||
bundle.putString("gameId", response.getString("target"));
|
||||
} else if ("column".equals(type)) {
|
||||
bundle.putString("to", "SubjectActivity");
|
||||
bundle.putString("id", response.getString("target"));
|
||||
} else if ("web".equals(type)) {
|
||||
bundle.putString("to", "WebActivity");
|
||||
bundle.putString("url", response.getString("target"));
|
||||
}
|
||||
if (RunningUtils.isRunning(context)) {
|
||||
// 应用正在运行,前台或后台
|
||||
String to = bundle.getString("to");
|
||||
if (!TextUtils.isEmpty(to)) {
|
||||
Class<?> clazz = ClassUtils.forName(to);
|
||||
if (clazz != null) {
|
||||
Intent intent1 = new Intent(context, clazz);
|
||||
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent1.putExtra("data", bundle);
|
||||
context.startActivity(intent1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 应用未在运行
|
||||
Intent intent1 = new Intent(context, SplashScreenActivity.class);
|
||||
intent1.setAction(Intent.ACTION_MAIN);
|
||||
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent1.putExtra("data", bundle);
|
||||
context.startActivity(intent1);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -255,6 +297,20 @@ public class GHPushMessageReceiver extends PushMessageReceiver {
|
||||
public void onCommandResult(Context context, MiPushCommandMessage message) {
|
||||
Log.v(AppController.TAG, "onCommandResult is called. "
|
||||
+ message.toString());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
package com.gh.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.gh.common.util.ClassUtils;
|
||||
import com.gh.common.util.RunningUtils;
|
||||
import com.gh.gamecenter.MainActivity;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
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("data");
|
||||
JSONObject response = new JSONObject(content);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("entrance", "(友盟推送)");
|
||||
String type = response.getString("type");
|
||||
if ("article".equals(type)) {
|
||||
bundle.putString("to", "NewsDetailActivity");
|
||||
bundle.putString("newsId", response.getString("target"));
|
||||
} else if ("game".equals(type)) {
|
||||
bundle.putString("to", "GameDetailActivity");
|
||||
bundle.putString("gameId", response.getString("target"));
|
||||
} else if ("column".equals(type)) {
|
||||
bundle.putString("to", "SubjectActivity");
|
||||
bundle.putString("id", response.getString("target"));
|
||||
} else if ("web".equals(type)) {
|
||||
bundle.putString("to", "WebActivity");
|
||||
bundle.putString("url", response.getString("target"));
|
||||
}
|
||||
if (RunningUtils.isRunning(context)
|
||||
&& MainActivity.class.getName().equals(RunningUtils.getBaseActivity(context))) {
|
||||
// 应用正在运行,前台或后台
|
||||
String to = bundle.getString("to");
|
||||
if (!TextUtils.isEmpty(to)) {
|
||||
Class<?> clazz = ClassUtils.forName(to);
|
||||
if (clazz != null) {
|
||||
Intent intent1 = new Intent(context, clazz);
|
||||
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent1.putExtra("data", bundle);
|
||||
context.startActivity(intent1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 应用未在运行
|
||||
Intent intent1 = new Intent(context, SplashScreenActivity.class);
|
||||
intent1.setAction(Intent.ACTION_MAIN);
|
||||
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent1.putExtra("data", bundle);
|
||||
context.startActivity(intent1);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -8,16 +8,17 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.text.TextUtils;
|
||||
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.download.DownloadManager;
|
||||
@ -27,12 +28,9 @@ 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.DataCollectionManager;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
@ -43,24 +41,22 @@ import de.greenrobot.event.EventBus;
|
||||
public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
|
||||
protected View view;
|
||||
protected LinearLayout home_ll_top;
|
||||
protected View home_slide_line;
|
||||
protected ViewPager home_vp_content;
|
||||
|
||||
protected LinearLayout.LayoutParams lparams;
|
||||
|
||||
protected Handler handler = new Handler();
|
||||
|
||||
private TextView downloadHint;
|
||||
private TextView searchHint;
|
||||
|
||||
private String hint;
|
||||
private AlphaAnimation mAlphaAnimation;
|
||||
|
||||
private ArrayList<String> hintList;
|
||||
private int hintIndex;
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (!TextUtils.isEmpty(hint)) {
|
||||
outState.putString("hint", hint);
|
||||
if (hintList != null && !hintList.isEmpty()) {
|
||||
outState.putStringArrayList("hint", hintList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,20 +65,21 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
hint = savedInstanceState.getString("hint");
|
||||
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, sp.getInt("actionbar_height",
|
||||
DisplayUtils.dip2px(getActivity(), 48)));
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,DisplayUtils.dip2px(getActivity(), 55));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
int top = DisplayUtils.getInternalDimensionSize(getResources(), "status_bar_height");
|
||||
int top = DisplayUtils.getStatusBarHeight(getResources());
|
||||
home_actionbar.setPadding(0, top, 0, 0);
|
||||
lparams.height += top;
|
||||
}
|
||||
@ -90,9 +87,69 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
|
||||
initActionBar();
|
||||
|
||||
home_ll_top = (LinearLayout) view.findViewById(R.id.home_ll_top);
|
||||
home_slide_line = view.findViewById(R.id.home_slide_line);
|
||||
home_vp_content = (ViewPager) view.findViewById(R.id.home_vp_content);
|
||||
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);
|
||||
}
|
||||
@ -111,6 +168,7 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
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);
|
||||
@ -133,56 +191,44 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
|
||||
searchHint = (TextView) view.findViewById(R.id.actionbar_search_input);
|
||||
searchHint.setOnClickListener(this);
|
||||
if (!TextUtils.isEmpty(hint)) {
|
||||
searchHint.setHint(hint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final int id = v.getId();
|
||||
if (id == R.id.actionbar_notification) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "关注图标");
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", "关注图标");
|
||||
map.put("page", "主页");
|
||||
DataCollectionManager.onEvent(getActivity(), "click-item", map);
|
||||
|
||||
startActivity(new Intent(getActivity(), ConcernActivity.class));
|
||||
} else if (id == R.id.actionbar_rl_download) {
|
||||
if (id == R.id.actionbar_rl_download) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "下载图标");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "下载图标", "主页");
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", "下载图标");
|
||||
map.put("page", "主页");
|
||||
DataCollectionManager.onEvent(getActivity(), "click-item", map);
|
||||
|
||||
startActivity(new Intent(getActivity(), DownloadManagerActivity.class));
|
||||
} else if (id == R.id.actionbar_search_input) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索框");
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", "搜索框");
|
||||
map.put("page", "主页");
|
||||
DataCollectionManager.onEvent(getActivity(), "click-item", map);
|
||||
|
||||
Intent goSearch = new Intent(getActivity(), SearchActivity.class);
|
||||
goSearch.putExtra("clicked", false);
|
||||
goSearch.putExtra("hint", hint);
|
||||
startActivity(goSearch);
|
||||
Intent intent = new Intent(getActivity(), DownloadManagerActivity.class);
|
||||
intent.putExtra("entrance", "(工具栏)");
|
||||
intent.putExtra("currentItem", 0);
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.actionbar_iv_search) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索图标");
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索图标");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "搜索图标", "主页");
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", "搜索图标");
|
||||
map.put("page", "主页");
|
||||
DataCollectionManager.onEvent(getActivity(), "click-item", map);
|
||||
Intent intent = new Intent(getActivity(), SearchActivity.class);
|
||||
intent.putExtra("clicked", true);
|
||||
intent.putExtra("hint", searchHint.getHint().toString());
|
||||
intent.putExtra("entrance", "(工具栏)");
|
||||
startActivity(intent);
|
||||
} else if (id == R.id.actionbar_search_input || id == R.id.actionbar_search_rl) {
|
||||
DataUtils.onEvent(getActivity(), "主页", "搜索框");
|
||||
DataCollectionUtils.uploadClick(getActivity(), "搜索框", "主页");
|
||||
|
||||
Intent searchIntent = new Intent(getActivity(), SearchActivity.class);
|
||||
searchIntent.putExtra("clicked", true);
|
||||
searchIntent.putExtra("hint", hint);
|
||||
startActivity(searchIntent);
|
||||
Intent intent = new Intent(getActivity(), SearchActivity.class);
|
||||
intent.putExtra("clicked", false);
|
||||
intent.putExtra("hint", searchHint.getHint().toString());
|
||||
intent.putExtra("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("entrance", "(工具栏)");
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,11 +243,13 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void setHint(String hint) {
|
||||
if (!TextUtils.isEmpty(hint)) {
|
||||
this.hint = hint;
|
||||
if (searchHint != null) {
|
||||
searchHint.setHint(hint);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,28 +268,10 @@ public class HomeFragment extends Fragment implements View.OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenChanged(boolean hidden) {
|
||||
super.onHiddenChanged(hidden);
|
||||
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||
List<Fragment> list = getChildFragmentManager().getFragments();
|
||||
if (list != null) {
|
||||
if (hidden) {
|
||||
for (Fragment fragment : getChildFragmentManager().getFragments()) {
|
||||
transaction.hide(fragment);
|
||||
}
|
||||
} else {
|
||||
for (Fragment fragment : getChildFragmentManager().getFragments()) {
|
||||
transaction.show(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,10 +6,11 @@ import android.content.SharedPreferences;
|
||||
|
||||
public class Config {
|
||||
|
||||
public static final String HOST = "http://api.ghzhushou.com/v2d2/";
|
||||
public static final String USER_HOST = "http://user.ghzhushou.com/v1d0/";
|
||||
public static final String COMMENT_HOST = "http://comment.ghzhushou.com/v1d0/";
|
||||
public static final String HOST = "http://dev.api2.ghzhushou.com/v2d5/";
|
||||
public static final String USER_HOST = "http://dev.user2.ghzhushou.com/v1d2/";
|
||||
public static final String COMMENT_HOST = "http://dev.comment2.ghzhushou.com/v1d2/";
|
||||
public static final String DATA_HOST = "http://data.ghzhushou.com/";
|
||||
public static final String LIBAO_HOST = "http://dev.libao2.ghzhushou.com/v1d2/";
|
||||
public static final String PREFERENCE = "ghzhushou";
|
||||
|
||||
public static boolean isShow(Context context) {
|
||||
|
||||
@ -20,5 +20,9 @@ public class ItemViewType {
|
||||
public static final int SEARCH_NORMAL = 12; // 搜索正常布局
|
||||
public static final int SEARCH_DELETE = 13; // 清空历史记录布局
|
||||
public static final int LOADING = 14; // 加载布局
|
||||
public static final int LIBAO_NORMAL = 15; // 礼包正常布局
|
||||
public static final int LIBAO_SKIP_CONCERN = 16; // 跳转关注管理页面布局
|
||||
public static final int KC_HINT = 16;
|
||||
public static final int ZIXUNTOP_TOP = 7; // 有料顶部布局
|
||||
|
||||
}
|
||||
|
||||
@ -2,7 +2,10 @@ package com.gh.common.util;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.ExifInterface;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -84,4 +87,30 @@ public class BitmapUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drawable转Bitmap
|
||||
*
|
||||
*/
|
||||
public static Bitmap drawableToBitmap(Drawable drawable){
|
||||
if(drawable == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
// 取 drawable 的长宽
|
||||
int w = drawable.getIntrinsicWidth();
|
||||
int h = drawable.getIntrinsicHeight();
|
||||
// 取 drawable 的颜色格式
|
||||
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
|
||||
: Bitmap.Config.RGB_565;
|
||||
//建立对应的Bitmap
|
||||
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
|
||||
// 建立对应 bitmap 的画布
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, w, h);
|
||||
// 把 drawable 内容画到画布中
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
20
app/src/main/java/com/gh/common/util/ClassUtils.java
Normal file
20
app/src/main/java/com/gh/common/util/ClassUtils.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
|
||||
public class ClassUtils {
|
||||
|
||||
public static Class<?> forName(String name) {
|
||||
if ("NewsActivity".equals(name)) {
|
||||
name = "NewsDetailActivity";
|
||||
} else if ("GameDetailsActivity".equals(name)) {
|
||||
name = "GameDetailActivity";
|
||||
}
|
||||
try {
|
||||
return Class.forName("com.gh.gamecenter." + name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
206
app/src/main/java/com/gh/common/util/CommentUtils.java
Normal file
206
app/src/main/java/com/gh/common/util/CommentUtils.java
Normal file
@ -0,0 +1,206 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.AppController;
|
||||
import com.gh.gamecenter.CommentDetailActivity;
|
||||
import com.gh.gamecenter.MessageDetailActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.MessageDetailAdapter;
|
||||
import com.gh.gamecenter.db.CommentDao;
|
||||
import com.gh.gamecenter.entity.CommentEntity;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by khy on 2017/3/22.
|
||||
*/
|
||||
public class CommentUtils {
|
||||
|
||||
public static void setCommentTime(TextView textView, long time) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
long day = time * 1000;
|
||||
if (day >= today && day < today + 86400 * 1000) {
|
||||
long min = new Date().getTime()/1000 - day/1000;
|
||||
int hour = (int) (min/ (60 * 60));
|
||||
if (hour == 0) {
|
||||
if (min < 60) {
|
||||
textView.setText("刚刚");
|
||||
} else {
|
||||
textView.setText(String.format(Locale.getDefault(), "%d分钟前", (int) (min / 60)));
|
||||
}
|
||||
} else {
|
||||
textView.setText(String.format(Locale.getDefault(), "%d小时前", hour));
|
||||
}
|
||||
} else if (day >= today - 86400 * 1000 && day < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText("昨天 ");
|
||||
} else {
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
textView.setText(format.format(day));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
format.applyPattern("yyyy-MM-dd");
|
||||
textView.setText(format.format(time * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
public static void showReportDialog(final CommentEntity commentEntity, final Context mContext
|
||||
, final MessageDetailAdapter.OnCommentCallBackListener mCallBackListener, final String newsId) {
|
||||
|
||||
CommentDao commentDao = new CommentDao(mContext);
|
||||
|
||||
final Dialog dialog = new Dialog(mContext);
|
||||
|
||||
LinearLayout container = new LinearLayout(mContext);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
container.setBackgroundColor(Color.WHITE);
|
||||
container.setPadding(0, DisplayUtils.dip2px(mContext, 12), 0, DisplayUtils.dip2px(mContext, 12));
|
||||
|
||||
List<String> dialogType = new ArrayList<>();
|
||||
|
||||
if (!commentDao.isMyComment(commentEntity.getId())) {
|
||||
dialogType.add("回复");
|
||||
}
|
||||
|
||||
dialogType.add("复制");
|
||||
dialogType.add("举报");
|
||||
|
||||
if (commentEntity.getParent() != null) {
|
||||
dialogType.add("查看对话");
|
||||
}
|
||||
|
||||
for (String s : dialogType) {
|
||||
final TextView reportTv = new TextView(mContext);
|
||||
reportTv.setPadding(DisplayUtils.dip2px(mContext, 20), DisplayUtils.dip2px(mContext, 12),
|
||||
0, DisplayUtils.dip2px(mContext, 12));
|
||||
reportTv.setText(s);
|
||||
reportTv.setTextSize(17);
|
||||
reportTv.setTextColor(mContext.getResources().getColor(R.color.title));
|
||||
reportTv.setBackgroundResource(R.drawable.textview_white_style);
|
||||
int widthPixels = mContext.getResources().getDisplayMetrics().widthPixels;
|
||||
reportTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9)/10,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
container.addView(reportTv);
|
||||
|
||||
reportTv.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.cancel();
|
||||
switch (reportTv.getText().toString()) {
|
||||
case "回复":
|
||||
if (mCallBackListener != null) {
|
||||
mCallBackListener.showSoftInput(commentEntity);
|
||||
} else if (!TextUtils.isEmpty(newsId)){
|
||||
Intent intent = new Intent(mContext, MessageDetailActivity.class);
|
||||
AppController.put("CommentEntity", commentEntity);
|
||||
intent.putExtra("commentNum", -1);
|
||||
intent.putExtra("newsId", newsId);
|
||||
intent.putExtra("openSoftInput", true);
|
||||
mContext.startActivity(intent);
|
||||
} else {
|
||||
Utils.toast(mContext, "缺少关键属性");
|
||||
}
|
||||
|
||||
break;
|
||||
case "复制":
|
||||
LibaoUtils.copyLink(commentEntity.getContent(), mContext);
|
||||
break;
|
||||
case "举报":
|
||||
showReportTypeDialog(commentEntity, mContext);
|
||||
break;
|
||||
case "查看对话":
|
||||
Intent intent = new Intent(mContext, CommentDetailActivity.class);
|
||||
intent.putExtra("commentId", commentEntity.getId());
|
||||
mContext.startActivity(intent);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(container);
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
private static void showReportTypeDialog(final CommentEntity commentEntity, final Context mContext) {
|
||||
final String[] arrReportType = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息",
|
||||
"违法有害信息", "其它"};
|
||||
int widthPixels = mContext.getResources().getDisplayMetrics().widthPixels;
|
||||
|
||||
final Dialog reportTypeDialog = new Dialog(mContext);
|
||||
LinearLayout container = new LinearLayout(mContext);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
container.setPadding(0, DisplayUtils.dip2px(mContext, 12), 0, DisplayUtils.dip2px(mContext, 12));
|
||||
container.setBackgroundColor(Color.WHITE);
|
||||
|
||||
for (final String s : arrReportType) {
|
||||
TextView reportTypeTv = new TextView(mContext);
|
||||
reportTypeTv.setText(s);
|
||||
reportTypeTv.setTextSize(17);
|
||||
reportTypeTv.setTextColor(mContext.getResources().getColor(R.color.title));
|
||||
reportTypeTv.setBackgroundResource(R.drawable.textview_white_style);
|
||||
reportTypeTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9)/10,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
reportTypeTv.setPadding(DisplayUtils.dip2px(mContext, 20), DisplayUtils.dip2px(mContext, 12),
|
||||
0, DisplayUtils.dip2px(mContext, 12));
|
||||
container.addView(reportTypeTv);
|
||||
|
||||
reportTypeTv.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
try {
|
||||
jsonObject.put("comment_id", commentEntity.getId());
|
||||
jsonObject.put("reason", s);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
PostCommentUtils.addReportData(mContext, jsonObject.toString(), true,
|
||||
new PostCommentUtils.PostCommentListener() {
|
||||
@Override
|
||||
public void postSucced(JSONObject response) {
|
||||
Utils.toast(mContext, "感谢您的举报");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFailed(Throwable error) {
|
||||
Utils.toast(mContext, "举报失败,请检查网络设置");
|
||||
}
|
||||
});
|
||||
reportTypeDialog.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reportTypeDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
reportTypeDialog.setContentView(container);
|
||||
reportTypeDialog.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -20,42 +20,40 @@ import java.util.List;
|
||||
**/
|
||||
public class ConcernContentUtils {
|
||||
|
||||
public static void addContentPic(int width, LinearLayout linearLayout, List<String> list, Context context) {
|
||||
public static void addContentPic(Context context, LinearLayout linearLayout, List<String> list,
|
||||
String entrance, int width) {
|
||||
int count = list.size();
|
||||
LinearLayout ll;
|
||||
int index = 0;
|
||||
for (int i = 0, size = (int) Math.ceil(list.size() / 3.0f); i < size; i++) {
|
||||
switch (count % 3) {
|
||||
case 0:
|
||||
ll = new LinearLayout(context);
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
ll.addView(getImageView(list, index, width, 0, context));
|
||||
index += 1;
|
||||
}
|
||||
linearLayout.addView(ll);
|
||||
count -= 3;
|
||||
break;
|
||||
case 1:
|
||||
linearLayout.addView(getImageView(list, index, width, 1, context));
|
||||
count -= 1;
|
||||
int type = count % 3;
|
||||
if (type == 0) {
|
||||
LinearLayout ll = new LinearLayout(context);
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
ll.addView(getImageView(context, list, entrance, index, width, 0));
|
||||
index += 1;
|
||||
break;
|
||||
case 2:
|
||||
ll = new LinearLayout(context);
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
ll.addView(getImageView(list, index, width, 2, context));
|
||||
index += 1;
|
||||
}
|
||||
linearLayout.addView(ll);
|
||||
count -= 2;
|
||||
break;
|
||||
}
|
||||
linearLayout.addView(ll);
|
||||
count -= 3;
|
||||
} else if (type == 1) {
|
||||
linearLayout.addView(getImageView(context, list, entrance, index, width, 1));
|
||||
count -= 1;
|
||||
index += 1;
|
||||
} else if (type == 2) {
|
||||
LinearLayout ll = new LinearLayout(context);
|
||||
ll.setOrientation(LinearLayout.HORIZONTAL);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
ll.addView(getImageView(context, list, entrance, index, width, 2));
|
||||
index += 1;
|
||||
}
|
||||
linearLayout.addView(ll);
|
||||
count -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SimpleDraweeView getImageView(final List<String> list, final int position, int width, int type, final Context context) {
|
||||
private static SimpleDraweeView getImageView(final Context context, final List<String> list, final String entrance,
|
||||
final int position, int width, int type) {
|
||||
SimpleDraweeView imageView;
|
||||
if (type == 0) {
|
||||
imageView = new SimpleDraweeView(context);
|
||||
@ -65,14 +63,16 @@ public class ConcernContentUtils {
|
||||
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
|
||||
lparams.weight = 1;
|
||||
imageView.setLayoutParams(lparams);
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView, ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView,
|
||||
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
} else if (type == 1) {
|
||||
imageView = new SimpleDraweeView(context);
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(width, width / 2);
|
||||
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
|
||||
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
|
||||
imageView.setLayoutParams(lparams);
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView, ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView,
|
||||
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
} else {
|
||||
imageView = new SimpleDraweeView(context);
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
@ -81,7 +81,8 @@ public class ConcernContentUtils {
|
||||
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
|
||||
lparams.weight = 1;
|
||||
imageView.setLayoutParams(lparams);
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView, ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
ImageUtils.getInstance().display(context.getResources(), imageView,
|
||||
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
|
||||
}
|
||||
imageView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -90,6 +91,7 @@ public class ConcernContentUtils {
|
||||
checkIntent.putExtra("urls", (ArrayList<String>) list);
|
||||
checkIntent.putExtra("current", position);
|
||||
checkIntent.putExtra("ScaleType", "FIT_CENTER");
|
||||
checkIntent.putExtra("entrance", entrance);
|
||||
context.startActivity(checkIntent);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,153 +1,80 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.gh.base.AppController;
|
||||
import com.gh.gamecenter.volley.extended.JsonArrayExtendedRequest;
|
||||
import com.gh.gamecenter.volley.extended.StringExtendedRequest;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.util.UUID;
|
||||
import de.greenrobot.event.EventBus;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.adapter.rxjava.HttpException;
|
||||
import rx.Observable;
|
||||
import rx.functions.Func1;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by khy on 2016/8/24.
|
||||
* croncern 工具类
|
||||
*/
|
||||
public class ConcernUtils {
|
||||
|
||||
public static void loadConcernData(final String url, final DownJsonListener listener) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
JsonArrayExtendedRequest request = new JsonArrayExtendedRequest(url,
|
||||
new Response.Listener<JSONArray>() {
|
||||
@Override
|
||||
public void onResponse(JSONArray response) {
|
||||
if (listener != null){
|
||||
listener.downSucced(response.toString());
|
||||
}
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (listener != null){
|
||||
listener.downFailed();
|
||||
}
|
||||
}
|
||||
});
|
||||
request.setShouldCache(false);
|
||||
AppController.addToRequestQueue(request);
|
||||
}
|
||||
}).start();
|
||||
|
||||
public static void postConcernGameId(final Context context, final String gameId) {
|
||||
TokenUtils.getToken(context, true)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
JSONArray params = new JSONArray();
|
||||
params.put(gameId);
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), params.toString());
|
||||
return RetrofitManager.getUser().postConcern(token, body);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(new Response<ResponseBody>());
|
||||
}
|
||||
|
||||
public static void postConcernGameId(final String gameId, final String postUrl, final DownJsonListener listener){
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
JSONArray data = new JSONArray();
|
||||
data.put(gameId);
|
||||
|
||||
StringExtendedRequest request = new StringExtendedRequest(
|
||||
Request.Method.POST, postUrl, data.toString(),
|
||||
new Response.Listener<String>() {
|
||||
@Override
|
||||
public void onResponse(String response) {
|
||||
if (listener != null) {
|
||||
listener.downSucced("关注成功");
|
||||
}
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (listener != null) {
|
||||
listener.downFailed();
|
||||
}
|
||||
}
|
||||
});
|
||||
request.setShouldCache(false);
|
||||
AppController.addToRequestQueue(request);
|
||||
}
|
||||
}).start();
|
||||
public static void deleteConcernData(final Context context, final String gameId) {
|
||||
TokenUtils.getToken(context, true)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
return RetrofitManager.getUser().deleteConcern(token, gameId);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(new Response<ResponseBody>());
|
||||
}
|
||||
|
||||
public static void deleteConcernData(final String url, final DownJsonListener listener){
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
StringExtendedRequest request = new StringExtendedRequest(
|
||||
Request.Method.DELETE, url,
|
||||
new Response.Listener<String>() {
|
||||
@Override
|
||||
public void onResponse(String response) {
|
||||
if (listener != null) {
|
||||
listener.downSucced("删除成功");
|
||||
}
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (listener != null) {
|
||||
listener.downFailed();
|
||||
}
|
||||
}
|
||||
});
|
||||
request.setShouldCache(false);
|
||||
AppController.addToRequestQueue(request);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
public static void updateConcernData(final Context context, final JSONArray data) {
|
||||
TokenUtils.getToken(context, true)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
|
||||
data.toString());
|
||||
return RetrofitManager.getUser().putConcern(token, body);
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(new Response<ResponseBody>(){
|
||||
@Override
|
||||
public void onResponse(ResponseBody response) {
|
||||
super.onResponse(response);
|
||||
EventBus.getDefault().post(new EBReuse("UpdateConcernSuccess"));
|
||||
}
|
||||
|
||||
public static void updateConcernData(final String url, final JSONArray data, final DownJsonListener listener){
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
StringExtendedRequest request = new StringExtendedRequest(
|
||||
Request.Method.PUT, url, data.toString(),
|
||||
new Response.Listener<String>() {
|
||||
@Override
|
||||
public void onResponse(String response) {
|
||||
if (listener != null) {
|
||||
listener.downSucced("更新设备关注成功");
|
||||
}
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
if (listener != null) {
|
||||
listener.downFailed();
|
||||
}
|
||||
}
|
||||
});
|
||||
request.setShouldCache(false);
|
||||
AppController.addToRequestQueue(request);
|
||||
}
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
public interface DownJsonListener {
|
||||
void downSucced(String str);
|
||||
void downFailed();
|
||||
}
|
||||
|
||||
// 获取设备号ID
|
||||
public static String uuid(Context context){
|
||||
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
final String tmDevice, tmSerial, androidId;
|
||||
tmDevice = "" + tm.getDeviceId();
|
||||
tmSerial = "" + tm.getSimSerialNumber();
|
||||
androidId = "" + android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
|
||||
UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
|
||||
return deviceUuid.toString();
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
super.onFailure(e);
|
||||
EventBus.getDefault().post(new EBReuse("UpdateConcernFailure"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
194
app/src/main/java/com/gh/common/util/DataCollectionUtils.java
Normal file
194
app/src/main/java/com/gh/common/util/DataCollectionUtils.java
Normal file
@ -0,0 +1,194 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import com.gh.download.DownloadEntity;
|
||||
import com.gh.gamecenter.db.info.ConcernInfo;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.NewsDetailEntity;
|
||||
import com.gh.gamecenter.manager.ConcernManager;
|
||||
import com.gh.gamecenter.manager.DataCollectionManager;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/12/9.
|
||||
* 数据收集 工具类(data.ghzhushou.com)
|
||||
*/
|
||||
public class DataCollectionUtils {
|
||||
|
||||
// 上传助手奔溃数据
|
||||
public static void uploadError(Context context, String msg) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("content", msg);
|
||||
map.put("type", android.os.Build.MODEL);
|
||||
map.put("system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
|
||||
// WIFI实时
|
||||
DataCollectionManager.onEvent(context, "error", map, NetworkUtils.isWifiConnected(context));
|
||||
}
|
||||
|
||||
// 上传下载数据(开始、完成)
|
||||
public static void uploadDownload(Context context, DownloadEntity downloadEntity, String status) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", downloadEntity.getName());
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
if (downloadEntity.isPluggable()) {
|
||||
map.put("method", "插件化");
|
||||
map.put("btn_status", "插件化");
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
map.put("method", "更新");
|
||||
map.put("btn_status", "更新");
|
||||
} else {
|
||||
map.put("method", "正常");
|
||||
map.put("btn_status", "下载");
|
||||
}
|
||||
map.put("platform", PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform()));
|
||||
map.put("status", status);
|
||||
map.put("location", downloadEntity.getLocation());
|
||||
map.put("entrance", downloadEntity.getEntrance());
|
||||
map.put("installed", downloadEntity.getInstalled());
|
||||
map.put("network", NetworkUtils.getConnectedType(context));
|
||||
DataCollectionManager.onEvent(context, "download", map);
|
||||
}
|
||||
|
||||
// 上传点击数据
|
||||
public static void uploadClick(Context context, String... args) {
|
||||
if (args.length < 2) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", args[0]);
|
||||
map.put("page", args[1]);
|
||||
if (args.length == 3) {
|
||||
map.put("title", args[2]);
|
||||
}
|
||||
DataCollectionManager.onEvent(context, "click-item", map);
|
||||
}
|
||||
|
||||
// 上传游戏数据
|
||||
public static void uploadGame(Context context, GameEntity gameEntity, int seconds, String from) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
map.put("time", seconds);
|
||||
map.put("from", from);
|
||||
DataCollectionManager.onEvent(context, "game", map);
|
||||
}
|
||||
|
||||
// 上传新闻数据
|
||||
public static void uploadNews(Context context, NewsDetailEntity newsDetailEntity,
|
||||
GameEntity gameEntity, int seconds, String from) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("title", newsDetailEntity.getTitle());
|
||||
map.put("type", newsDetailEntity.getType());
|
||||
map.put("author", newsDetailEntity.getAuthor());
|
||||
if (gameEntity != null) {
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
}
|
||||
map.put("time", seconds);
|
||||
map.put("from", from);
|
||||
DataCollectionManager.onEvent(context, "news", map);
|
||||
}
|
||||
|
||||
// 上传安装或卸载数据
|
||||
public static void uploadInorunstall(Context context, String type, String packageName) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("type", type);
|
||||
map.put("packageName", packageName);
|
||||
DataCollectionManager.onEvent(context, "inorunstall", map);
|
||||
}
|
||||
|
||||
// 上传劫持数据
|
||||
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("url", downloadEntity.getUrl());
|
||||
map.put("game", downloadEntity.getName());
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
map.put("platform", downloadEntity.getPlatform());
|
||||
map.put("hijack_url", downloadEntity.getError());
|
||||
DataCollectionManager.onEvent(context, "hijack", map);
|
||||
}
|
||||
|
||||
// 上传用户数据
|
||||
public static void uploadUser(Context context) {
|
||||
ConcernManager concernManager = new ConcernManager(context);
|
||||
ArrayList<String> concernList = new ArrayList<>();
|
||||
for (ConcernInfo entity : concernManager.getAllConcern()) {
|
||||
if (entity.isConcern()) {
|
||||
concernList.add(entity.getGameName());
|
||||
}
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("type", Build.MODEL);
|
||||
map.put("system", Build.VERSION.SDK_INT + "=" + Build.VERSION.RELEASE);
|
||||
map.put("install", PackageManager.getInstalledList());
|
||||
map.put("concern", concernList);
|
||||
DataCollectionManager.upsert(context, "user", map);
|
||||
}
|
||||
|
||||
// 上传搜索数据
|
||||
public static void uploadSearch(Context context, String... args) {
|
||||
if (args.length != 5) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("from", args[2]);
|
||||
map.put("search_type", args[1]);
|
||||
map.put("key", args[0]);
|
||||
map.put("game_id", args[3]);
|
||||
map.put("game_name", args[4]);
|
||||
map.put("netword", NetworkUtils.getConnectedType(context));
|
||||
map.put("type", "search");
|
||||
map.put("device_type", android.os.Build.MODEL);
|
||||
map.put("device_system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
|
||||
DataCollectionManager.onEvent(context, "search", map);
|
||||
}
|
||||
|
||||
// 上传搜索点击数据
|
||||
public static void uploadSearchClick(Context context, String... args) {
|
||||
if (args.length != 5) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("from", args[2]);
|
||||
map.put("search_type", args[1]);
|
||||
map.put("key", args[0]);
|
||||
map.put("game_id", args[3]);
|
||||
map.put("game_name", args[4]);
|
||||
map.put("netword", NetworkUtils.getConnectedType(context));
|
||||
map.put("type", "click");
|
||||
map.put("device_type", android.os.Build.MODEL);
|
||||
map.put("device_system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
|
||||
DataCollectionManager.onEvent(context, "search", map);
|
||||
}
|
||||
|
||||
// 上传关注数据
|
||||
public static void uploadConcern(Context context, String... args) {
|
||||
if (args.length < 3) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", args[0]);
|
||||
map.put("game_id", args[1]);
|
||||
map.put("type", args[2]);
|
||||
DataCollectionManager.onEvent(context, "concern", map);
|
||||
}
|
||||
|
||||
//上传推荐位数据
|
||||
public static void uploadPosition(Context context, String... args) {
|
||||
if (args.length < 3) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("page", args[0]);
|
||||
map.put("location", args[1]);
|
||||
map.put("name", args[2]);
|
||||
DataCollectionManager.onEvent(context, "position", map);
|
||||
}
|
||||
|
||||
}
|
||||
89
app/src/main/java/com/gh/common/util/DataLogUtils.java
Normal file
89
app/src/main/java/com/gh/common/util/DataLogUtils.java
Normal file
@ -0,0 +1,89 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.gh.download.DownloadEntity;
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/12/8.
|
||||
* 日志上传工具类
|
||||
*/
|
||||
public class DataLogUtils {
|
||||
|
||||
// 上传日志
|
||||
public static void uploadLog(Context context, String topic, Map<String, Object> map) {
|
||||
String version = PackageUtils.getVersionName(context);
|
||||
String user = Installation.getUUID(context);
|
||||
String channel = (String) PackageUtils.getMetaData(context, context.getPackageName(), "TD_CHANNEL_ID");
|
||||
map.put("version", version);
|
||||
map.put("user", user);
|
||||
map.put("device_id", TokenUtils.getDeviceId(context));
|
||||
map.put("channel", channel);
|
||||
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("topic", topic);
|
||||
params.put("source", "GH-ASSIST-Client");
|
||||
params.put("time", String.valueOf(Utils.getTime(context)));
|
||||
params.put("content", new JSONObject(map).toString());
|
||||
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
|
||||
new JSONObject(params).toString());
|
||||
RetrofitManager.getData().postLog(body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<ResponseBody>());
|
||||
}
|
||||
|
||||
// 轮播图
|
||||
public static void uploadLunbotuLog(Context context, String type, String title, String location) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("location", location);
|
||||
map.put("type", type);
|
||||
map.put("title", title);
|
||||
map.put("form", "click");
|
||||
uploadLog(context, "lunbotu", map);
|
||||
}
|
||||
|
||||
// 网络错误
|
||||
public static void uploadNeterrorLog(Context context, DownloadEntity downloadEntity) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("url", downloadEntity.getUrl());
|
||||
map.put("game", downloadEntity.getName());
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
map.put("platform", downloadEntity.getPlatform());
|
||||
map.put("error", downloadEntity.getError());
|
||||
uploadLog(context, "neterror", map);
|
||||
}
|
||||
|
||||
// 助手更新
|
||||
public static void uploadUpgradeLog(Context context, String step) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("step", step);
|
||||
uploadLog(context, "upgrade", map);
|
||||
}
|
||||
|
||||
// 链接劫持
|
||||
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("url", downloadEntity.getUrl());
|
||||
map.put("game", downloadEntity.getName());
|
||||
map.put("game_id", downloadEntity.getGameId());
|
||||
map.put("platform", downloadEntity.getPlatform());
|
||||
map.put("hijack_url", downloadEntity.getError());
|
||||
uploadLog(context, "hijack", map);
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,11 +10,13 @@ import com.tencent.stat.StatReportStrategy;
|
||||
import com.tencent.stat.StatService;
|
||||
import com.tendcloud.tenddata.TCAgent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/6/15.
|
||||
* 数据收集 工具类 (TalkingData、MTA)
|
||||
*/
|
||||
public class DataUtils {
|
||||
|
||||
@ -68,4 +70,39 @@ public class DataUtils {
|
||||
StatService.onResume(var0);
|
||||
}
|
||||
|
||||
// 游戏启动
|
||||
public static void onGameLaunchEvent(Context context, String gameName, String platform, String page) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", platform);
|
||||
kv.put("页面", page);
|
||||
onEvent(context, "游戏启动", gameName, kv);
|
||||
}
|
||||
|
||||
// 游戏下载
|
||||
public static void onGameDownloadEvent(Context context, String gameName, String platform, String entrance, String status) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", platform);
|
||||
kv.put("状态", status);
|
||||
DataUtils.onEvent(context, "游戏下载", gameName, kv);
|
||||
|
||||
Map<String, Object> kv2 = new HashMap<>();
|
||||
kv2.put("版本", platform);
|
||||
kv2.put("状态", status);
|
||||
kv2.put("位置", entrance);
|
||||
DataUtils.onEvent(context, "游戏下载位置", gameName, kv2);
|
||||
|
||||
Map<String, Object> kv3 = new HashMap<>();
|
||||
kv3.put(entrance, "下载数");
|
||||
kv3.put(entrance, status);
|
||||
DataUtils.onEvent(context, "应用数据", gameName, kv3);
|
||||
}
|
||||
|
||||
// 游戏更新
|
||||
public static void onGameUpdateEvent(Context context, String gameName, String paltform, String status) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", paltform);
|
||||
kv.put("状态", status);
|
||||
DataUtils.onEvent(context, "游戏更新", gameName, kv);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DeviceUtils {
|
||||
|
||||
public synchronized static String getDeviceHeader(Context context) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String imei = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
|
||||
if (!TextUtils.isEmpty(imei)) {
|
||||
buffer.append("imei=");
|
||||
buffer.append(imei);
|
||||
}
|
||||
String android_id = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append("android_id=");
|
||||
buffer.append(android_id);
|
||||
}
|
||||
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
String mac = wm.getConnectionInfo().getMacAddress();
|
||||
if (!TextUtils.isEmpty(mac)) {
|
||||
if (buffer.length() != 0) {
|
||||
buffer.append(",");
|
||||
}
|
||||
buffer.append("mac=");
|
||||
buffer.append(mac);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public synchronized static Map<String, String> getDeviceParams(Context context) {
|
||||
HashMap<String, String> params = new HashMap<String, String>();
|
||||
params.put("imei", ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId());
|
||||
params.put("android_id", Secure.getString(context.getContentResolver(), Secure.ANDROID_ID));
|
||||
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
params.put("mac", wm.getConnectionInfo().getMacAddress());
|
||||
return params;
|
||||
}
|
||||
|
||||
public synchronized static String getDeviceID(Context context) {
|
||||
String imei = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
|
||||
if (!TextUtils.isEmpty(imei)) {
|
||||
return imei;
|
||||
}
|
||||
String android_id = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
return android_id;
|
||||
}
|
||||
return Installation.getUUID(context);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,38 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.AppController;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.kuaichuan.WifiMgr;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class DialogUtils {
|
||||
|
||||
@ -30,6 +50,168 @@ public class DialogUtils {
|
||||
|
||||
private static boolean isShow = false;
|
||||
|
||||
// 快传成绩单
|
||||
public static void showKuaiChuanResult(final Activity activity, Handler handler, int requestCode, final String picName) {
|
||||
|
||||
AppController.remove("FileInfo");
|
||||
|
||||
List<Map<String, String>> mapList = (List<Map<String, String>>) AppController.get("sendData", true);
|
||||
if (mapList == null || mapList.size() == 0) return;
|
||||
|
||||
WifiMgr.getInstance(activity).disconnectCurrentNetwork(); // 断开当前WiFi
|
||||
|
||||
// int heightPixels = getContext().getResources().getDisplayMetrics().heightPixels;
|
||||
// int widthPixels = getContext().getResources().getDisplayMetrics().widthPixels;
|
||||
|
||||
int filesCount = mapList.size();
|
||||
int filesSize = 0;
|
||||
int sendTime = 0;
|
||||
|
||||
View view = View.inflate(activity
|
||||
, R.layout.dialog_kuaichuan, null);
|
||||
final LinearLayout mShareLl = (LinearLayout) view.findViewById(R.id.kuaichuan_dialog_ll);
|
||||
final LinearLayout mShareBottomLl = (LinearLayout) view.findViewById(R.id.kuaichuan_dialog_share_rl);
|
||||
RelativeLayout content = (RelativeLayout) view.findViewById(R.id.kuaichuan_dialog);
|
||||
LinearLayout shareIconLl = (LinearLayout) view.findViewById(R.id.kuaichuan_icon_ll);
|
||||
ImageView qrCode = (ImageView) view.findViewById(R.id.kuaichuan_qrcode);
|
||||
TextView dateTv = (TextView) view.findViewById(R.id.kuaichuan_dialog_date);
|
||||
TextView countTv = (TextView) view.findViewById(R.id.kuaichuan_send_count);
|
||||
TextView sizeTv = (TextView) view.findViewById(R.id.kuaichuan_send_size);
|
||||
TextView speedTv = (TextView) view.findViewById(R.id.kuaichuan_send_speed);
|
||||
TextView timeCount = (TextView) view.findViewById(R.id.kuaichuan_time_count);
|
||||
TextView timeTv = (TextView) view.findViewById(R.id.kuaichuan_time_tv);
|
||||
TextView sendCountTv = (TextView) view.findViewById(R.id.dialog_send_tv);
|
||||
ImageView closeIv = (ImageView) view.findViewById(R.id.kuaichuan_dialog_colse);
|
||||
// content.setLayoutParams(new LinearLayout.LayoutParams((int)(((float)heightPixels)*0.85), (int)((float)widthPixels*0.81)));
|
||||
|
||||
final Dialog dialog = new Dialog(activity);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setContentView(view);
|
||||
dialog.show();
|
||||
|
||||
Window dialogWindow = dialog.getWindow();
|
||||
WindowManager m = activity.getWindowManager();
|
||||
Display d = m.getDefaultDisplay();
|
||||
WindowManager.LayoutParams p = dialogWindow.getAttributes();
|
||||
p.height = (int) (d.getHeight() * 0.82);
|
||||
p.width = (int) (d.getWidth() * 0.80);
|
||||
dialogWindow.setAttributes(p);
|
||||
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日", Locale.getDefault());
|
||||
dateTv.setText(format.format(new Date().getTime()));
|
||||
|
||||
for (Map<String, String> map : mapList) {
|
||||
int size = Integer.parseInt(map.get("apkSize"));
|
||||
int time = 10;
|
||||
try {
|
||||
time = Integer.parseInt(map.get("sendTime"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
String apkPath = map.get("apkPath");
|
||||
filesSize = filesSize + size;
|
||||
sendTime = sendTime + time;
|
||||
|
||||
if (shareIconLl.getChildCount() >= 5) continue;
|
||||
|
||||
android.content.pm.PackageManager pm = activity.getPackageManager();
|
||||
PackageInfo info = pm.getPackageArchiveInfo(apkPath,
|
||||
android.content.pm.PackageManager.GET_ACTIVITIES);
|
||||
if (info != null) {
|
||||
ApplicationInfo appInfo = info.applicationInfo;
|
||||
appInfo.sourceDir = apkPath;
|
||||
appInfo.publicSourceDir = apkPath;
|
||||
Bitmap bitmap = BitmapUtils.drawableToBitmap(appInfo.loadIcon(pm));
|
||||
|
||||
ImageView imageView = new ImageView(activity);
|
||||
imageView.setLayoutParams(new LinearLayout.LayoutParams(DisplayUtils.dip2px(activity, 25)
|
||||
, DisplayUtils.dip2px(activity, 24)));
|
||||
imageView.setImageBitmap(bitmap);
|
||||
shareIconLl.addView(imageView);
|
||||
}
|
||||
}
|
||||
|
||||
if (requestCode == 0x170) { // 发送
|
||||
qrCode.setImageResource(R.drawable.kc_qrcode_120);
|
||||
sendCountTv.setText("成功传送游戏");
|
||||
} else {
|
||||
qrCode.setImageResource(R.drawable.kc_qrcode_110);
|
||||
sendCountTv.setText("成功接收游戏");
|
||||
}
|
||||
|
||||
double size = (((float)filesSize/1024)/1024);
|
||||
String sizeName;
|
||||
if (size > 1024) {
|
||||
DecimalFormat df = new DecimalFormat("#.0");
|
||||
sizeName = df.format(size/1024) + "GB";
|
||||
} else {
|
||||
DecimalFormat df = new DecimalFormat("#.0");
|
||||
sizeName = df.format(size) + "MB";
|
||||
}
|
||||
|
||||
|
||||
int i = (filesSize / 1024) / (sendTime / 1000);
|
||||
String speed ;
|
||||
if (i >= 1000) {
|
||||
float mSpeed = i / 1024f;
|
||||
DecimalFormat df = new DecimalFormat("#.0");
|
||||
String str = df.format(mSpeed);
|
||||
if (str.length() > 4) {
|
||||
str = str.substring(0, 4);
|
||||
}
|
||||
speed = str + "MB/s";
|
||||
} else {
|
||||
speed = i + "KB/s";
|
||||
}
|
||||
|
||||
if (sendTime > 60000) {
|
||||
timeCount.setText(String.valueOf(sendTime/1000/60));
|
||||
timeTv.setText("分钟传送完成");
|
||||
} else {
|
||||
timeCount.setText(String.valueOf(sendTime/1000));
|
||||
timeTv.setText("秒传送完成");
|
||||
}
|
||||
|
||||
sizeTv.setText(sizeName);
|
||||
speedTv.setText(speed);
|
||||
countTv.setText(filesCount + "个");
|
||||
|
||||
// 延迟操作,等待截图部分绘制完成
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mShareLl.setDrawingCacheEnabled(true);
|
||||
mShareLl.buildDrawingCache();
|
||||
Bitmap drawingCache = mShareLl.getDrawingCache();
|
||||
saveBitmap(drawingCache, activity, picName);
|
||||
MessageShareUtils.getInstance(activity).showShareWindows(mShareBottomLl, drawingCache, picName, 2);
|
||||
mShareBottomLl.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
closeIv.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void saveBitmap(Bitmap bm, Activity activity, String picName) {
|
||||
File file = new File(activity.getExternalCacheDir().getPath() + "/ShareImg");
|
||||
if (!file.isDirectory()) {
|
||||
file.delete();
|
||||
file.mkdirs();
|
||||
}
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
MessageShareUtils.getInstance(activity).writeBitmap(file.getPath(), picName, bm, false);
|
||||
|
||||
}
|
||||
|
||||
public static void showWarningDialog(Context context, String title, CharSequence msg, String cancel, String confirm,
|
||||
final ConfiremListener cmListener, final CancelListener clListener) {
|
||||
|
||||
@ -88,14 +270,106 @@ public class DialogUtils {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showInstallHintDialog(Context context ,final ConfiremListener cmListener) {
|
||||
|
||||
|
||||
final Dialog dialog = new Dialog(context);
|
||||
|
||||
View view = View.inflate(context, R.layout.dialog_install_hint, null);
|
||||
|
||||
// 标题
|
||||
TextView alertdialog_title = (TextView) view.findViewById(R.id.installhint_title);
|
||||
alertdialog_title.setText("重要提示");
|
||||
Spanned content = Html.fromHtml("如果您使用的是"+ "<font color=\"#ff0000\">华为</font>" +"或"+
|
||||
"<font color=\"#ff0000\">OPPO</font>" +"手机,安装游戏时请选择“" +
|
||||
"<font color=\"#ff0000\">继续安装</font>" +
|
||||
"”(记住不要选择“官方推荐”或“软件商店安装”)");
|
||||
// 内容
|
||||
TextView alertdialog_content = (TextView) view.findViewById(R.id.installhint_content);
|
||||
alertdialog_content.setText(content);
|
||||
|
||||
// 确定按钮
|
||||
TextView installhint_confirm = (TextView) view.findViewById(R.id.installhint_confirm);
|
||||
installhint_confirm.setText("知道了");
|
||||
|
||||
final ImageView installhint_unselect = (ImageView) view.findViewById(R.id.installhint_unselect);
|
||||
final ImageView installhint_select = (ImageView) view.findViewById(R.id.installhint_select);
|
||||
|
||||
TextView installhint_cancel = (TextView) view.findViewById(R.id.installhint_cancel);
|
||||
LinearLayout installhint_unselect_ll = (LinearLayout) view.findViewById(R.id.installhint_unselect_ll);
|
||||
|
||||
installhint_unselect_ll.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (installhint_unselect.getVisibility() == View.GONE) {
|
||||
installhint_unselect.setVisibility(View.VISIBLE);
|
||||
installhint_select.setVisibility(View.GONE);
|
||||
} else {
|
||||
installhint_unselect.setVisibility(View.GONE);
|
||||
installhint_select.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
installhint_confirm.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.dismiss();
|
||||
if (installhint_select.getVisibility() == View.VISIBLE) {
|
||||
if (cmListener != null) {
|
||||
cmListener.onConfirem();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.setOnDismissListener(new Dialog.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
isShow = false;
|
||||
}
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(view);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showHintDialog(Context context, String title, CharSequence msg, String confirm) {
|
||||
final Dialog dialog = new Dialog(context);
|
||||
|
||||
View view = View.inflate(context, R.layout.common_hintdialog, null);
|
||||
|
||||
TextView hintdialog_title = (TextView) view.findViewById(R.id.hintdialog_title);
|
||||
hintdialog_title.setText(title);
|
||||
|
||||
// 内容
|
||||
TextView hintdialog_content = (TextView) view.findViewById(R.id.hintdialog_content);
|
||||
hintdialog_content.setText(msg);
|
||||
|
||||
TextView hintdialog_confirm = (TextView) view.findViewById(R.id.hintdialog_confirm);
|
||||
|
||||
hintdialog_confirm.setText(confirm);
|
||||
|
||||
hintdialog_confirm.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(view);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void showHijackDialog(final Context context) {
|
||||
showWarningDialog(context, "警告", "您当前网络环境异常,下载地址已被替换(网络劫持),请更换网络环境进行下载。",
|
||||
new ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
// 跳转wifi管理界面
|
||||
Intent intent = new Intent("android.settings.WIFI_SETTINGS");
|
||||
context.startActivity(intent);
|
||||
context.startActivity(new Intent("android.settings.WIFI_SETTINGS"));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -114,14 +388,12 @@ public class DialogUtils {
|
||||
if (ShareUtils.isQQClientAvailable(context)) {
|
||||
//安装了QQ会直接调用QQ,打开手机QQ进行会话 QQ号:2586716223
|
||||
String str = "mqqwpa://im/chat?chat_type=wpa&uin=" + finalQq + "&version=1&src_type=web&web_src=oicqzone.com";
|
||||
Uri uri = Uri.parse(str);
|
||||
Intent it = new Intent(Intent.ACTION_VIEW, uri);
|
||||
context.startActivity(it);
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(str)));
|
||||
} else {
|
||||
//没有安装QQ 复制账号
|
||||
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setText(finalQq);
|
||||
Utils.toast(context,"已复制" + finalQq);
|
||||
Utils.toast(context, "已复制" + finalQq);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
@ -138,8 +410,8 @@ public class DialogUtils {
|
||||
|
||||
public static void showCancelDialog(Context context, final ConfiremListener listener) {
|
||||
Spanned content = Html.fromHtml("取消关注游戏后,您将无法及时收到游戏" +
|
||||
"<font color='#ff0000'>攻略</font>、" +
|
||||
"<font color='#ff0000'>资讯</font>等最新动态提醒。");
|
||||
"<font color=\"#ff0000\">攻略</font>、" +
|
||||
"<font color=\"#ff0000\">资讯</font>等最新动态提醒。");
|
||||
showWarningDialog(context, "取消关注", content, "暂不取消", "确定取消", listener, null);
|
||||
}
|
||||
|
||||
|
||||
@ -49,10 +49,13 @@ public class DisplayUtils {
|
||||
|
||||
/**
|
||||
* 获取状态栏的高度
|
||||
* @param res
|
||||
* @param key
|
||||
* @return
|
||||
* @param resources 资源
|
||||
* @return height
|
||||
*/
|
||||
public static int getStatusBarHeight(Resources resources) {
|
||||
return getInternalDimensionSize(resources, "status_bar_height");
|
||||
}
|
||||
|
||||
public static int getInternalDimensionSize(Resources res, String key) {
|
||||
int result = 0;
|
||||
int resourceId = res.getIdentifier(key, "dimen", "android");
|
||||
|
||||
@ -8,8 +8,7 @@ import android.support.v4.util.ArrayMap;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -21,13 +20,10 @@ import com.gh.download.DownloadManager;
|
||||
import com.gh.download.DownloadStatus;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.manager.DataCollectionManager;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class DownloadItemUtils {
|
||||
@ -90,14 +86,7 @@ public class DownloadItemUtils {
|
||||
}
|
||||
|
||||
// 更新正常的条目,只有一个apk包
|
||||
public static void updateNormalItem(Context context,
|
||||
TextView textView,
|
||||
ProgressBar game_progressbar,
|
||||
LinearLayout game_ll_info,
|
||||
TextView download_speed,
|
||||
TextView download_percentage,
|
||||
TextView downloadBtn,
|
||||
GameEntity gameEntity,
|
||||
public static void updateNormalItem(Context context, GameViewHolder holder, GameEntity gameEntity,
|
||||
boolean isShowPlatform) {
|
||||
|
||||
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
|
||||
@ -105,122 +94,65 @@ public class DownloadItemUtils {
|
||||
DownloadEntity downloadEntity = entryMap.get(gameEntity.getApk().get(0).getPlatform());
|
||||
if (downloadEntity != null) {
|
||||
// 更改进度条和提示文本的状态
|
||||
changeStatus(context, textView, game_progressbar, game_ll_info, download_speed, download_percentage, downloadBtn,
|
||||
downloadEntity, isShowPlatform, true);
|
||||
changeStatus(context, holder, downloadEntity, isShowPlatform, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
game_progressbar.setVisibility(View.GONE);
|
||||
game_ll_info.setVisibility(View.GONE);
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
|
||||
downloadBtn.setTextColor(Color.WHITE);
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
if (gameEntity.isPluggable()) {
|
||||
downloadBtn.setText("插件化");
|
||||
holder.gameDownloadBtn.setText("插件化");
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getByPackage(
|
||||
gameEntity.getApk().get(0).getPackageName());
|
||||
if (downloadEntity == null
|
||||
|| downloadEntity.getUrl().equals(gameEntity.getApk().get(0).getUrl())) {
|
||||
downloadBtn.setClickable(true);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
holder.gameDownloadBtn.setClickable(true);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else {
|
||||
downloadBtn.setClickable(false);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
|
||||
holder.gameDownloadBtn.setClickable(false);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
|
||||
}
|
||||
} else if (PackageManager.isInstalled(gameEntity.getApk().get(0).getPackageName())) {
|
||||
if (PackageManager.isCanUpdate(gameEntity.getId(), gameEntity.getApk().get(0).getPackageName())) {
|
||||
downloadBtn.setText("更新");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
holder.gameDownloadBtn.setText("更新");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
} else {
|
||||
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
|
||||
&& !TextUtils.isEmpty(gameEntity.getApk().get(0).getGhVersion())
|
||||
&& !PackageUtils.isSignature(context, gameEntity.getApk().get(0).getPackageName())) {
|
||||
downloadBtn.setText("插件化");
|
||||
holder.gameDownloadBtn.setText("插件化");
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getByPackage(
|
||||
gameEntity.getApk().get(0).getPackageName());
|
||||
if (downloadEntity == null
|
||||
|| downloadEntity.getUrl().equals(gameEntity.getApk().get(0).getUrl())) {
|
||||
downloadBtn.setClickable(true);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
holder.gameDownloadBtn.setClickable(true);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else {
|
||||
downloadBtn.setClickable(false);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
|
||||
holder.gameDownloadBtn.setClickable(false);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
|
||||
}
|
||||
} else {
|
||||
downloadBtn.setText("启动");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
holder.gameDownloadBtn.setText("启动");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
downloadBtn.setText("下载");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
holder.gameDownloadBtn.setText("下载");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新插件的条目,有多个apk包
|
||||
public static void updatePluginItem(Context context,
|
||||
TextView textView,
|
||||
ProgressBar game_progressbar,
|
||||
LinearLayout game_ll_info,
|
||||
TextView download_speed,
|
||||
TextView download_percentage,
|
||||
TextView downloadBtn,
|
||||
GameEntity gameEntity,
|
||||
public static void updatePluginItem(Context context, GameViewHolder holder, GameEntity gameEntity,
|
||||
boolean isShowPlatform) {
|
||||
|
||||
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn);
|
||||
|
||||
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
|
||||
|
||||
// 更新下载按钮状态
|
||||
int doneCount = 0; // 下载完成数量
|
||||
int pluginCount = 0; // 可插件化数量
|
||||
int updateCount = 0; // 可更新数量
|
||||
int installCount = 0; // 已安装数量
|
||||
if (entryMap != null && !entryMap.isEmpty()) {
|
||||
DownloadEntity downloadEntity;
|
||||
for (String key : entryMap.keySet()) {
|
||||
downloadEntity = entryMap.get(key);
|
||||
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
|
||||
doneCount++;
|
||||
} else if (downloadEntity.isPluggable()) {
|
||||
pluginCount++;
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
if (PackageManager.isCanUpdate(gameEntity.getId(), apkEntity.getPackageName())) {
|
||||
updateCount++;
|
||||
}
|
||||
if (PackageManager.isInstalled(apkEntity.getPackageName())) {
|
||||
if (!PackageUtils.isSignature(context, apkEntity.getPackageName())) {
|
||||
pluginCount++;
|
||||
} else {
|
||||
installCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadBtn.setTextColor(Color.WHITE);
|
||||
if (doneCount != 0) {
|
||||
downloadBtn.setText("安装");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
} else if (pluginCount != 0) {
|
||||
downloadBtn.setText("插件化");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else if (updateCount != 0) {
|
||||
downloadBtn.setText("更新");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
} else if (installCount != 0) {
|
||||
downloadBtn.setText("打开");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
} else {
|
||||
downloadBtn.setText("下载");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
|
||||
if (entryMap != null && !entryMap.isEmpty()) {
|
||||
|
||||
DownloadEntity downloadEntity;
|
||||
@ -234,31 +166,22 @@ public class DownloadItemUtils {
|
||||
|
||||
if (downloadEntity != null) {
|
||||
// 更改进度条和提示文本的状态
|
||||
changeStatus(context, textView, game_progressbar, game_ll_info, download_speed,
|
||||
download_percentage, downloadBtn, downloadEntity, isShowPlatform, false);
|
||||
changeStatus(context, holder, downloadEntity, isShowPlatform, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
game_progressbar.setVisibility(View.GONE);
|
||||
game_ll_info.setVisibility(View.GONE);
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 更改进度条和提示文本的状态
|
||||
public static void changeStatus(Context context,
|
||||
TextView textView,
|
||||
ProgressBar game_progressbar,
|
||||
LinearLayout game_ll_info,
|
||||
TextView download_speed,
|
||||
TextView download_percentage,
|
||||
TextView downloadBtn,
|
||||
DownloadEntity downloadEntity,
|
||||
boolean isShowPlatform,
|
||||
boolean isNormal) {
|
||||
textView.setVisibility(View.GONE);
|
||||
game_progressbar.setVisibility(View.VISIBLE);
|
||||
game_ll_info.setVisibility(View.VISIBLE);
|
||||
public static void changeStatus(Context context, GameViewHolder holder, DownloadEntity downloadEntity,
|
||||
boolean isShowPlatform, boolean isNormal) {
|
||||
holder.gameDes.setVisibility(View.GONE);
|
||||
holder.gameProgressbar.setVisibility(View.VISIBLE);
|
||||
holder.gameInfo.setVisibility(View.VISIBLE);
|
||||
|
||||
String platform = PlatformUtils.getInstance(context)
|
||||
.getPlatformName(downloadEntity.getPlatform());
|
||||
@ -266,207 +189,191 @@ public class DownloadItemUtils {
|
||||
DownloadStatus status = downloadEntity.getStatus();
|
||||
if (status.equals(DownloadStatus.downloading)) {
|
||||
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
|
||||
game_progressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
if (isShowPlatform && platform != null) {
|
||||
download_speed.setText(String.format("%s - %s(剩%s)", platform,
|
||||
holder.gameDownloadSpeed.setText(String.format("%s - %s(剩%s)", platform,
|
||||
SpeedUtils.getSpeed(downloadEntity.getSpeed()),
|
||||
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
|
||||
} else {
|
||||
download_speed.setText(String.format("%s(剩%s)", SpeedUtils.getSpeed(downloadEntity.getSpeed()),
|
||||
holder.gameDownloadSpeed.setText(String.format("%s(剩%s)", SpeedUtils.getSpeed(downloadEntity.getSpeed()),
|
||||
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
|
||||
}
|
||||
download_percentage.setText(downloadEntity.getPercent() + "%");
|
||||
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
|
||||
}
|
||||
|
||||
if (isNormal) {
|
||||
downloadBtn.setText("下载中");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
downloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
holder.gameDownloadBtn.setText("下载中");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
}
|
||||
} else if (status.equals(DownloadStatus.waiting)) {
|
||||
game_progressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
if (isShowPlatform && platform != null) {
|
||||
download_speed.setText(String.format("%s - 等待", platform));
|
||||
holder.gameDownloadSpeed.setText(String.format("%s - 等待", platform));
|
||||
} else {
|
||||
download_speed.setText("等待");
|
||||
holder.gameDownloadSpeed.setText("等待");
|
||||
}
|
||||
download_percentage.setText(downloadEntity.getPercent() + "%");
|
||||
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
|
||||
|
||||
if (isNormal) {
|
||||
downloadBtn.setText("下载中");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
downloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
holder.gameDownloadBtn.setText("下载中");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
}
|
||||
} else if (status.equals(DownloadStatus.pause)
|
||||
|| status.equals(DownloadStatus.timeout)
|
||||
|| status.equals(DownloadStatus.neterror)) {
|
||||
game_progressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
|
||||
if (isShowPlatform && platform != null) {
|
||||
download_speed.setText(String.format("%s - 暂停", platform));
|
||||
holder.gameDownloadSpeed.setText(String.format("%s - 暂停", platform));
|
||||
} else {
|
||||
download_speed.setText("暂停");
|
||||
holder.gameDownloadSpeed.setText("暂停");
|
||||
}
|
||||
download_percentage.setText(downloadEntity.getPercent() + "%");
|
||||
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
|
||||
|
||||
if (isNormal) {
|
||||
downloadBtn.setText("下载中");
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
downloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
holder.gameDownloadBtn.setText("下载中");
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
|
||||
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
|
||||
}
|
||||
} else if (status.equals(DownloadStatus.done)) {
|
||||
game_progressbar.setProgress(1000);
|
||||
holder.gameProgressbar.setProgress(1000);
|
||||
if (isShowPlatform && platform != null) {
|
||||
download_speed.setText(String.format("%s - 下载完成", platform));
|
||||
holder.gameDownloadSpeed.setText(String.format("%s - 下载完成", platform));
|
||||
} else {
|
||||
download_speed.setText("下载完成");
|
||||
holder.gameDownloadSpeed.setText("下载完成");
|
||||
}
|
||||
download_percentage.setText(R.string.hundred_percent);
|
||||
holder.gameDownloadPercentage.setText(R.string.hundred_percent);
|
||||
|
||||
if (isNormal) {
|
||||
downloadBtn.setText("安装");
|
||||
downloadBtn.setTextColor(Color.WHITE);
|
||||
holder.gameDownloadBtn.setText("安装");
|
||||
holder.gameDownloadBtn.setTextColor(Color.WHITE);
|
||||
if (downloadEntity.isPluggable()
|
||||
&& PackageManager.isInstalled(downloadEntity.getPackageName())) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateItem(Context context,
|
||||
TextView textView,
|
||||
ProgressBar game_progressbar,
|
||||
LinearLayout game_ll_info,
|
||||
TextView download_speed,
|
||||
TextView download_percentage,
|
||||
TextView downloadBtn,
|
||||
GameEntity entity,
|
||||
boolean isShowPlatform) {
|
||||
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder, boolean isShowPlatform) {
|
||||
|
||||
// 控制是否显示下载按钮
|
||||
if (!Config.isShow(context) || "光环助手".equals(entity.getName())) {
|
||||
downloadBtn.setVisibility(View.GONE);
|
||||
if (!Config.isShow(context) || "光环助手".equals(gameEntity.getName())) {
|
||||
holder.gameDownloadBtn.setVisibility(View.GONE);
|
||||
} else {
|
||||
downloadBtn.setVisibility(View.VISIBLE);
|
||||
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (entity.getApk() == null || entity.getApk().isEmpty()) {
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
game_progressbar.setVisibility(View.GONE);
|
||||
game_ll_info.setVisibility(View.GONE);
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_style);
|
||||
downloadBtn.setText("暂无");
|
||||
downloadBtn.setClickable(false);
|
||||
} else if (entity.getApk().size() == 1) {
|
||||
updateNormalItem(context, textView, game_progressbar, game_ll_info, download_speed,
|
||||
download_percentage, downloadBtn, entity, isShowPlatform);
|
||||
if (gameEntity.isLibaoExists()) {
|
||||
holder.gameLibaoIcon.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
updatePluginItem(context, textView, game_progressbar, game_ll_info, download_speed,
|
||||
download_percentage, downloadBtn, entity, isShowPlatform);
|
||||
holder.gameLibaoIcon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// LibaoDao libaoDao = new LibaoDao(context);
|
||||
// if (libaoDao.isExist(gameEntity.getId())) {
|
||||
// holder.gameLibaoIcon.setVisibility(View.VISIBLE);
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
|
||||
if (gameEntity.getApk() == null || gameEntity.getApk().isEmpty()) {
|
||||
holder.gameDes.setVisibility(View.VISIBLE);
|
||||
holder.gameProgressbar.setVisibility(View.GONE);
|
||||
holder.gameInfo.setVisibility(View.GONE);
|
||||
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_style);
|
||||
holder.gameDownloadBtn.setText("暂无");
|
||||
holder.gameDownloadBtn.setClickable(false);
|
||||
} else if (gameEntity.getApk().size() == 1) {
|
||||
updateNormalItem(context, holder, gameEntity, isShowPlatform);
|
||||
} else {
|
||||
updatePluginItem(context, holder, gameEntity, isShowPlatform);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void updateItem(Context context,
|
||||
TextView textView,
|
||||
ProgressBar game_progressbar,
|
||||
LinearLayout game_ll_info,
|
||||
TextView download_speed,
|
||||
TextView download_percentage,
|
||||
TextView downloadBtn,
|
||||
GameEntity entity) {
|
||||
updateItem(context, textView, game_progressbar, game_ll_info, download_speed,
|
||||
download_percentage, downloadBtn, entity, true);
|
||||
}
|
||||
public static void onNormalClick(final Context context,
|
||||
final TextView downloadBtn,
|
||||
final GameEntity gameEntity,
|
||||
final int position,
|
||||
final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
|
||||
final String entrance,
|
||||
final String location) {
|
||||
|
||||
public static void setNormalOnClickListener(final Context context,
|
||||
final TextView downloadBtn,
|
||||
final GameEntity gameEntity,
|
||||
final int position,
|
||||
final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
|
||||
final String entrance,
|
||||
final String location) {
|
||||
downloadBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String str = downloadBtn.getText().toString();
|
||||
if ("下载".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
String str = downloadBtn.getText().toString();
|
||||
if ("下载".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
download(context, gameEntity, downloadBtn, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
download(context, gameEntity, downloadBtn, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
download(context, gameEntity, downloadBtn, entrance, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if ("插件化".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
});
|
||||
}
|
||||
} else if ("插件化".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
plugin(context, gameEntity, downloadBtn, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
plugin(context, gameEntity, downloadBtn, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
plugin(context, gameEntity, downloadBtn, entrance, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if ("安装".equals(str)) {
|
||||
install(context, gameEntity, position, adapter);
|
||||
} else if ("启动".equals(str)) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
DataUtils.onEvent(context, "游戏启动", gameEntity.getName(), kv);
|
||||
});
|
||||
}
|
||||
} else if ("安装".equals(str)) {
|
||||
install(context, gameEntity, position, adapter);
|
||||
} else if ("启动".equals(str)) {
|
||||
DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
|
||||
|
||||
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
|
||||
} else if ("下载中".equals(str)) {
|
||||
Intent intent = new Intent(context, DownloadManagerActivity.class);
|
||||
intent.putExtra("url", gameEntity.getApk().get(0).getUrl());
|
||||
context.startActivity(intent);
|
||||
} else if ("更新".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
|
||||
} else if ("下载中".equals(str)) {
|
||||
Intent intent = new Intent(context, DownloadManagerActivity.class);
|
||||
intent.putExtra("currentItem", 1);
|
||||
intent.putExtra("url", gameEntity.getApk().get(0).getUrl());
|
||||
intent.putExtra("entrance", entrance + "+(" + location.split(":")[0] + ")");
|
||||
context.startActivity(intent);
|
||||
} else if ("更新".equals(str)) {
|
||||
if (NetworkUtils.isWifiConnected(context)) {
|
||||
update(context, gameEntity, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
update(context, gameEntity, entrance, location);
|
||||
} else {
|
||||
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
update(context, gameEntity, entrance, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPluginOnClickListener(final Context context,
|
||||
final TextView downloadBtn,
|
||||
final GameEntity entity,
|
||||
final String entrance,
|
||||
final String location) {
|
||||
downloadBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
DownloadDialog.getInstance(context).showPopupWindow(v, entity, entrance, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setOnClickListener(Context context,
|
||||
TextView downloadBtn,
|
||||
GameEntity gameEntity,
|
||||
int position,
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
|
||||
String entrance,
|
||||
String location) {
|
||||
public static void setOnClickListener(final Context context,
|
||||
final TextView downloadBtn,
|
||||
final GameEntity gameEntity,
|
||||
final int position,
|
||||
final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
|
||||
final String entrance,
|
||||
final String location) {
|
||||
|
||||
if (gameEntity.getApk().size() == 1) {
|
||||
setNormalOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, location);
|
||||
downloadBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setPluginOnClickListener(context, downloadBtn, gameEntity, entrance, location);
|
||||
downloadBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
DownloadDialog.getInstance(context).showPopupWindow(v, gameEntity, entrance, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -476,26 +383,7 @@ public class DownloadItemUtils {
|
||||
GameEntity gameEntity,
|
||||
String entrance,
|
||||
String location) {
|
||||
ApkEntity apkEntity = gameEntity.getApk().get(0);
|
||||
//下载可更新游戏
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", apkEntity.getPlatform());
|
||||
kv.put("状态", "下载开始");
|
||||
DataUtils.onEvent(context, "游戏更新", gameEntity.getName(), kv);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
map.put("method", "更新");
|
||||
map.put("platform", PlatformUtils.getInstance(context)
|
||||
.getPlatformName(gameEntity.getApk().get(0).getPlatform()));
|
||||
map.put("status", "开始");
|
||||
map.put("location", location);
|
||||
map.put("entrance", entrance);
|
||||
map.put("btn_status", "更新");
|
||||
map.put("network", NetworkUtils.getConnectedType(context));
|
||||
DataCollectionManager.onEvent(context, "download", map);
|
||||
|
||||
DataUtils.onGameUpdateEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), "下载开始");
|
||||
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location);
|
||||
}
|
||||
|
||||
@ -507,34 +395,7 @@ public class DownloadItemUtils {
|
||||
String location) {
|
||||
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
kv.put("状态", "下载开始");
|
||||
DataUtils.onEvent(context, "游戏下载", gameEntity.getName(), kv);
|
||||
|
||||
Map<String, Object> kv2 = new HashMap<>();
|
||||
kv2.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
kv2.put("状态", "下载开始");
|
||||
kv2.put("位置", entrance + "-开始");
|
||||
DataUtils.onEvent(context, "游戏下载位置", gameEntity.getName(), kv2);
|
||||
|
||||
Map<String, Object> kv3 = new HashMap<>();
|
||||
kv3.put(entrance, "下载数");
|
||||
kv3.put(entrance, "下载开始");
|
||||
DataUtils.onEvent(context, "应用数据", gameEntity.getName(), kv3);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
map.put("method", "正常");
|
||||
map.put("platform", PlatformUtils.getInstance(context)
|
||||
.getPlatformName(gameEntity.getApk().get(0).getPlatform()));
|
||||
map.put("status", "开始");
|
||||
map.put("location", location);
|
||||
map.put("entrance", entrance);
|
||||
map.put("btn_status", "下载");
|
||||
map.put("network", NetworkUtils.getConnectedType(context));
|
||||
DataCollectionManager.onEvent(context, "download", map);
|
||||
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
|
||||
|
||||
DownloadManager.createDownload(context, gameEntity, "下载", entrance, location);
|
||||
Toast.makeText(context, gameEntity.getName() + "已加入下载队列", Toast.LENGTH_SHORT).show();
|
||||
@ -557,34 +418,7 @@ public class DownloadItemUtils {
|
||||
String location) {
|
||||
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
Map<String, Object> kv = new HashMap<>();
|
||||
kv.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
kv.put("状态", "下载开始");
|
||||
DataUtils.onEvent(context, "游戏下载", gameEntity.getName(), kv);
|
||||
|
||||
Map<String, Object> kv2 = new HashMap<>();
|
||||
kv2.put("版本", gameEntity.getApk().get(0).getPlatform());
|
||||
kv2.put("状态", "下载开始");
|
||||
kv2.put("位置", entrance + "-开始");
|
||||
DataUtils.onEvent(context, "游戏下载位置", gameEntity.getName(), kv2);
|
||||
|
||||
Map<String, Object> kv3 = new HashMap<>();
|
||||
kv3.put(entrance, "下载数");
|
||||
kv3.put(entrance, "下载开始");
|
||||
DataUtils.onEvent(context, "应用数据", gameEntity.getName(), kv3);
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("game", gameEntity.getName());
|
||||
map.put("game_id", gameEntity.getId());
|
||||
map.put("method", "插件化");
|
||||
map.put("platform", PlatformUtils.getInstance(context)
|
||||
.getPlatformName(gameEntity.getApk().get(0).getPlatform()));
|
||||
map.put("status", "开始");
|
||||
map.put("location", location);
|
||||
map.put("entrance", entrance);
|
||||
map.put("btn_status", "插件化");
|
||||
map.put("network", NetworkUtils.getConnectedType(context));
|
||||
DataCollectionManager.onEvent(context, "download", map);
|
||||
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
|
||||
|
||||
DownloadManager.createDownload(context, gameEntity, "插件化", entrance, location);
|
||||
Toast.makeText(context, gameEntity.getName() + "已加入下载队列", Toast.LENGTH_SHORT).show();
|
||||
|
||||
@ -75,14 +75,6 @@ public class FileUtils {
|
||||
public static String getPlatformPicDir(Context context) {
|
||||
return checkDir(getDir(context, "PlatformPic"));
|
||||
}
|
||||
|
||||
public static String getAdPicDir(Context context) {
|
||||
return checkDir(getDir(context, "AdPic"));
|
||||
}
|
||||
|
||||
public static String getPicPath(Context context, String name) {
|
||||
return checkDir(getDir(context, "Pic")) + File.separator + name;
|
||||
}
|
||||
|
||||
private static String checkDir(String dir) {
|
||||
File directory = new File(dir);
|
||||
@ -102,12 +94,24 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteFolder(File folder) {
|
||||
if (folder != null) {
|
||||
if (folder.isDirectory()) {
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
deleteFolder(file);
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
folder.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEmptyFile(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists() && file.length() != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !(file.exists() && file.length() != 0);
|
||||
}
|
||||
|
||||
public static boolean isMounted() {
|
||||
@ -126,9 +130,7 @@ public class FileUtils {
|
||||
return context.getFilesDir().getAbsolutePath() + File.separator + dir;
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回剩余空间 单位MB
|
||||
*/
|
||||
// 返回剩余空间 单位MB
|
||||
@SuppressWarnings("deprecation")
|
||||
public static float getFreeSpaceByPath(String path) {
|
||||
StatFs statfs = new StatFs(path);
|
||||
@ -158,8 +160,7 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
public static int downloadFile(String url, String savePath)
|
||||
throws IOException {
|
||||
public static int downloadFile(String url, String savePath) {
|
||||
DataInputStream dis = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
@ -183,32 +184,36 @@ public class FileUtils {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
fos.flush();
|
||||
fos.close();
|
||||
dis.close();
|
||||
}
|
||||
return code;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (dis != null) {
|
||||
dis.close();
|
||||
try {
|
||||
dis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
public static JSONObject uploadFile(String url, String filePath, String token) {
|
||||
String end = "\r\n";
|
||||
String twoHyphens = "--";
|
||||
String boundary = UUID.randomUUID().toString().replaceAll("-", "")
|
||||
.substring(0, 16);
|
||||
String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url)
|
||||
.openConnection();
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
/*
|
||||
* Output to the connection. Default is false, set to true because
|
||||
* post method must write something to the connection
|
||||
@ -225,8 +230,7 @@ public class FileUtils {
|
||||
// 设置请求属性
|
||||
connection.setRequestProperty("Connection", "Keep-Alive");
|
||||
connection.setRequestProperty("Charset", "UTF-8");
|
||||
connection.setRequestProperty("Content-Type",
|
||||
"multipart/form-data;boundary=" + boundary);
|
||||
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
|
||||
if (token != null) {
|
||||
connection.setRequestProperty("TOKEN", token);
|
||||
}
|
||||
@ -240,13 +244,11 @@ public class FileUtils {
|
||||
Utils.log("length = " + file.length());
|
||||
}
|
||||
// 设置DataOutputStream,getOutputStream中默认调用connect()
|
||||
DataOutputStream dos = new DataOutputStream(
|
||||
connection.getOutputStream()); // output
|
||||
DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); // output
|
||||
// to the connection
|
||||
dos.writeBytes(twoHyphens + boundary + end);
|
||||
dos.writeBytes("Content-Disposition: form-data; "
|
||||
+ "name=\"Filedata\";filename=\"" + file.getName() + "\""
|
||||
+ end);
|
||||
+ "name=\"Filedata\";filename=\"" + file.getName() + "\"" + end);
|
||||
dos.writeBytes(end);
|
||||
// 取得文件的FileInputStream
|
||||
FileInputStream fStream = new FileInputStream(file);
|
||||
|
||||
@ -2,24 +2,55 @@ package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.AppController;
|
||||
import com.gh.download.DownloadEntity;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.download.DownloadStatus;
|
||||
import com.gh.gamecenter.GameDetailActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.entity.ApkEntity;
|
||||
import com.gh.gamecenter.entity.GameEntity;
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity;
|
||||
import com.gh.gamecenter.manager.PackageManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Created by LGT on 2016/9/29.
|
||||
*/
|
||||
public class GameUtils {
|
||||
|
||||
/**
|
||||
* 去除与重复sourceList相同的数据
|
||||
*/
|
||||
public static List<GameEntity> removeDuplicateData(List<GameEntity> sourceList, List<GameEntity> rawList) {
|
||||
if (sourceList == null || sourceList.isEmpty()
|
||||
|| rawList == null || rawList.isEmpty()) {
|
||||
return rawList;
|
||||
}
|
||||
String id;
|
||||
for (int i = 0; i < rawList.size(); i++) {
|
||||
id = rawList.get(i).getId();
|
||||
for (GameEntity gameEntity : sourceList) {
|
||||
if (id.equals(gameEntity.getId())) {
|
||||
rawList.remove(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动游戏详情页面
|
||||
*/
|
||||
public static void startGameDetailActivity(Context context, GameEntity gameEntity, String entrance) {
|
||||
AppController.put("GameEntity", gameEntity);
|
||||
Intent intent = new Intent(context, GameDetailActivity.class);
|
||||
intent.putExtra("entrance", entrance);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra("entrance", entrance);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@ -28,10 +59,93 @@ public class GameUtils {
|
||||
*/
|
||||
public static void startGameDetailActivity(Context context, String gameId, String entrance) {
|
||||
Intent intent = new Intent(context, GameDetailActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra("gameId", gameId);
|
||||
intent.putExtra("entrance", entrance);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置下载按钮状态
|
||||
*/
|
||||
public static void setDownloadBtnStatus(Context context, GameEntity gameEntity, TextView downloadBtn) {
|
||||
String status = getDownloadBtnText(context, gameEntity);
|
||||
downloadBtn.setTextColor(Color.WHITE);
|
||||
downloadBtn.setText(status);
|
||||
if ("插件化".equals(status)) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
|
||||
} else if ("打开".equals(status)) {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
|
||||
} else {
|
||||
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载按钮文案
|
||||
*/
|
||||
public static String getDownloadBtnText(Context context, GameEntity gameEntity) {
|
||||
int doneCount = 0; // 下载完成数量
|
||||
int pluginCount = 0; // 可插件化数量
|
||||
int updateCount = 0; // 可更新数量
|
||||
int installCount = 0; // 已安装数量
|
||||
DownloadEntity downloadEntity;
|
||||
Object gh_id;
|
||||
for (ApkEntity apkEntity : gameEntity.getApk()) {
|
||||
downloadEntity = DownloadManager.getInstance(context).get(apkEntity.getUrl());
|
||||
if (downloadEntity != null) {
|
||||
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
|
||||
doneCount++;
|
||||
} else if (downloadEntity.isPluggable()) {
|
||||
pluginCount++;
|
||||
} else if (downloadEntity.isUpdate()) {
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
if (PackageManager.isCanUpdate(gameEntity.getId(), apkEntity.getPackageName())) {
|
||||
updateCount++;
|
||||
}
|
||||
if (PackageManager.isInstalled(apkEntity.getPackageName())) {
|
||||
gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
|
||||
if (!PackageUtils.isSignature(context, apkEntity.getPackageName())) {
|
||||
pluginCount++;
|
||||
} else if (gh_id == null || gh_id.equals(gameEntity.getId())) {
|
||||
installCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doneCount != 0) {
|
||||
return "安装";
|
||||
} else if (pluginCount != 0) {
|
||||
return "插件化";
|
||||
} else if (updateCount != 0) {
|
||||
return "更新";
|
||||
} else if (installCount != 0) {
|
||||
return "打开";
|
||||
} else {
|
||||
return "下载";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GameUpdateEntity
|
||||
*/
|
||||
public static GameUpdateEntity getGameUpdateEntity(GameEntity gameEntity, ApkEntity apkEntity) {
|
||||
GameUpdateEntity gameUpdateEntity = new GameUpdateEntity();
|
||||
gameUpdateEntity.setId(gameEntity.getId());
|
||||
gameUpdateEntity.setIcon(gameEntity.getIcon());
|
||||
gameUpdateEntity.setName(gameEntity.getName());
|
||||
gameUpdateEntity.setPackageName(apkEntity.getPackageName());
|
||||
gameUpdateEntity.setSize(apkEntity.getSize());
|
||||
gameUpdateEntity.setVersion(apkEntity.getVersion());
|
||||
gameUpdateEntity.setGhVersion(apkEntity.getGhVersion());
|
||||
gameUpdateEntity.setUrl(apkEntity.getUrl());
|
||||
gameUpdateEntity.setPlatform(apkEntity.getPlatform());
|
||||
gameUpdateEntity.setEtag(apkEntity.getEtag());
|
||||
gameUpdateEntity.setPluggable(true);
|
||||
gameUpdateEntity.setTag(gameEntity.getTag());
|
||||
gameUpdateEntity.setBrief(gameEntity.getBrief());
|
||||
return gameUpdateEntity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.TextView;
|
||||
@ -31,30 +34,46 @@ public class GameViewUtils {
|
||||
if (tag == null || tag.isEmpty()) {
|
||||
labelLayout.addView(getGameTagView(context, "官方版", 0));
|
||||
} else {
|
||||
for (int i = 0, size = tag.size() > 3 ? 3 : tag.size(); i < size; i++) {
|
||||
for (int i = 0, size = tag.size(); i < size; i++) {
|
||||
View view;
|
||||
if (i == size - 1) {
|
||||
labelLayout.addView(getGameTagView(context, tag.get(i), 0));
|
||||
view = getGameTagView(context, tag.get(i), 0);
|
||||
} else {
|
||||
labelLayout.addView(getGameTagView(context, tag.get(i), DisplayUtils.dip2px(context, 5)));
|
||||
view = getGameTagView(context, tag.get(i), DisplayUtils.dip2px(context, 6));
|
||||
}
|
||||
if (view != null) {
|
||||
labelLayout.addView(view);
|
||||
}
|
||||
if (labelLayout.getChildCount() == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TextView getGameTagView(Context context, String tagStr, int rightMargin) {
|
||||
private static TextView getGameTagView(Context context, String tagStr, int rightMargin) {
|
||||
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
lparams.rightMargin = rightMargin;
|
||||
TextView tag = new TextView(context);
|
||||
tag.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
|
||||
tag.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
tag.setSingleLine(true);
|
||||
tag.setText(tagStr);
|
||||
if ("官方版".equals(tagStr) || "已关注".equals(tagStr)) {
|
||||
tag.setBackgroundResource(R.drawable.border_green_bg);
|
||||
tag.setTextColor(context.getResources().getColor(R.color.tag_green));
|
||||
} else {
|
||||
tag.setBackgroundResource(R.drawable.border_blue_bg);
|
||||
tag.setTextColor(context.getResources().getColor(R.color.theme));
|
||||
String colorStr = TagUtils.getInstance(context).getColor(tagStr);
|
||||
if (colorStr == null) {
|
||||
return null;
|
||||
}
|
||||
int color = Color.parseColor(colorStr);
|
||||
GradientDrawable gradientDrawable = new GradientDrawable();
|
||||
gradientDrawable.setColor(Color.TRANSPARENT);
|
||||
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 0.6f), color);
|
||||
tag.setBackgroundDrawable(gradientDrawable);
|
||||
// tag.setBackgroundResource(R.drawable.border_blue_bg);
|
||||
tag.setTextColor(color);
|
||||
}
|
||||
tag.setLayoutParams(lparams);
|
||||
tag.setPadding(DisplayUtils.dip2px(context, 3),
|
||||
|
||||
@ -77,9 +77,6 @@ public class HttpsUtils {
|
||||
mHostnameVerifier = HOSTNAME_VERIFIER;
|
||||
}
|
||||
mSSLSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(mSSLSocketFactory);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -88,7 +85,7 @@ public class HttpsUtils {
|
||||
private static KeyStore getHttpsKeyStore(Context context) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = context.getResources().openRawResource(R.raw.download);
|
||||
is = context.getResources().openRawResource(R.raw.cacert);
|
||||
//读取证书
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
certificateFactory.generateCertificate(is);
|
||||
@ -113,24 +110,17 @@ public class HttpsUtils {
|
||||
}
|
||||
|
||||
public static HttpsURLConnection getHttpsURLConnection(URL url) throws Exception {
|
||||
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
|
||||
if (mSSLSocketFactory == null || mHostnameVerifier == null) {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, TRUST_MANAGERS, null);
|
||||
mSSLSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
mHostnameVerifier = HOSTNAME_VERIFIER;
|
||||
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(mSSLSocketFactory);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
|
||||
} else {
|
||||
if (HttpsURLConnection.getDefaultSSLSocketFactory() != mSSLSocketFactory) {
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(mSSLSocketFactory);
|
||||
}
|
||||
if (HttpsURLConnection.getDefaultHostnameVerifier() != mHostnameVerifier) {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
|
||||
}
|
||||
}
|
||||
return (HttpsURLConnection) url.openConnection();
|
||||
httpsURLConnection.setSSLSocketFactory(mSSLSocketFactory);
|
||||
httpsURLConnection.setHostnameVerifier(mHostnameVerifier);
|
||||
return httpsURLConnection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -36,6 +36,13 @@ public class ImageUtils {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
public static void display(SimpleDraweeView simpleDraweeView, String url) {
|
||||
// if (url.startsWith("http://image.ghzhushou.com") && url.endsWith(".jpg")) {
|
||||
// url = url + "?x-oss-process=image/format,webp";
|
||||
// }
|
||||
simpleDraweeView.setImageURI(url);
|
||||
}
|
||||
|
||||
// 自适应图片宽高
|
||||
public void display(final SimpleDraweeView simpleDraweeView, String url, final int width) {
|
||||
ControllerListener<ImageInfo> listener = new BaseControllerListener<ImageInfo>(){
|
||||
@ -66,7 +73,8 @@ public class ImageUtils {
|
||||
.setBackground(new ColorDrawable(resources.getColor(R.color.placeholder_bg)))
|
||||
.setActualImageScaleType(scaleType)
|
||||
.build());
|
||||
simpleDraweeView.setImageURI(url);
|
||||
// simpleDraweeView.setImageURI(url);
|
||||
display(simpleDraweeView, url);
|
||||
}
|
||||
|
||||
// 设置占位符
|
||||
@ -77,7 +85,8 @@ public class ImageUtils {
|
||||
.setBackground(new ColorDrawable(resources.getColor(R.color.placeholder_bg)))
|
||||
.setPlaceholderImage(placeholderImage)
|
||||
.build());
|
||||
simpleDraweeView.setImageURI(url);
|
||||
// simpleDraweeView.setImageURI(url);
|
||||
display(simpleDraweeView, url);
|
||||
}
|
||||
|
||||
// 图片下载监听和设置低高分辨率图片
|
||||
|
||||
148
app/src/main/java/com/gh/common/util/InstallUtils.java
Normal file
148
app/src/main/java/com/gh/common/util/InstallUtils.java
Normal file
@ -0,0 +1,148 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
import com.gh.gamecenter.eventbus.EBPackage;
|
||||
import com.gh.gamecenter.manager.CommentManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
*
|
||||
* 下载完成跳安装,
|
||||
*/
|
||||
public class InstallUtils {
|
||||
|
||||
private static final int MAX_TIME = 5 * 60 * 1000;
|
||||
|
||||
private static Map<String, Long> installMap;
|
||||
private static Map<String, Long> uninstallMap;
|
||||
|
||||
private static InstallUtils mInstance;
|
||||
|
||||
public static InstallUtils getInstance(Context context) {
|
||||
if (mInstance == null) {
|
||||
synchronized (CommentManager.class) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new InstallUtils(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
private PackageManager packageManager;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
private boolean isRunning;
|
||||
|
||||
private InstallUtils(Context context) {
|
||||
isRunning = false;
|
||||
|
||||
packageManager = context.getPackageManager();
|
||||
|
||||
handler = new Handler(context.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == 0x123 && packageManager != null) {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
|
||||
for (PackageInfo packageInfo : packageInfos) {
|
||||
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
list.add(packageInfo.packageName);
|
||||
}
|
||||
}
|
||||
if (installMap != null && installMap.size() != 0) {
|
||||
ArrayList<String> keys = new ArrayList<>();
|
||||
for (String packageName : installMap.keySet()) {
|
||||
long time = installMap.get(packageName);
|
||||
if (System.currentTimeMillis() - time >= MAX_TIME) {
|
||||
keys.add(packageName);
|
||||
} else if (list.contains(packageName)) {
|
||||
keys.add(packageName);
|
||||
EventBus.getDefault().post(new EBPackage("安装", packageName));
|
||||
}
|
||||
}
|
||||
for (String key : keys) {
|
||||
installMap.remove(key);
|
||||
}
|
||||
}
|
||||
if (uninstallMap != null && uninstallMap.size() != 0) {
|
||||
ArrayList<String> keys = new ArrayList<>();
|
||||
for (String packageName : uninstallMap.keySet()) {
|
||||
long time = uninstallMap.get(packageName);
|
||||
if (System.currentTimeMillis() - time >= MAX_TIME) {
|
||||
keys.add(packageName);
|
||||
} else if (list.contains(packageName)) {
|
||||
keys.add(packageName);
|
||||
EventBus.getDefault().post(new EBPackage("卸载", packageName));
|
||||
}
|
||||
}
|
||||
for (String key : keys) {
|
||||
uninstallMap.remove(key);
|
||||
}
|
||||
}
|
||||
if ((installMap != null && installMap.size() != 0)
|
||||
|| (uninstallMap != null && uninstallMap.size() != 0)) {
|
||||
sendEmptyMessageDelayed(0x123, 3000);
|
||||
} else {
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void addInstall(String packageName) {
|
||||
if (installMap == null) {
|
||||
installMap = Collections.synchronizedMap(new HashMap<String, Long>());
|
||||
}
|
||||
installMap.put(packageName, System.currentTimeMillis());
|
||||
run();
|
||||
}
|
||||
|
||||
public void removeInstall(String packageName) {
|
||||
if (installMap != null) {
|
||||
installMap.remove(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
public void addUninstall(String packageName) {
|
||||
if (uninstallMap == null) {
|
||||
uninstallMap = Collections.synchronizedMap(new HashMap<String, Long>());
|
||||
}
|
||||
uninstallMap.put(packageName, System.currentTimeMillis());
|
||||
run();
|
||||
}
|
||||
|
||||
public void removeUninstall(String packageName) {
|
||||
if (uninstallMap != null) {
|
||||
uninstallMap.remove(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if ((installMap == null || installMap.isEmpty()) && (uninstallMap == null || uninstallMap.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
if (isRunning) {
|
||||
return;
|
||||
}
|
||||
isRunning = true;
|
||||
handler.sendEmptyMessageDelayed(0x123, 10000);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,9 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@ -15,6 +18,14 @@ public class Installation {
|
||||
private static String sID = null;
|
||||
|
||||
public synchronized static String getUUID(Context context) {
|
||||
String imei = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
|
||||
if (!TextUtils.isEmpty(imei)) {
|
||||
return imei;
|
||||
}
|
||||
String android_id = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
if (!TextUtils.isEmpty(android_id)) {
|
||||
return android_id;
|
||||
}
|
||||
if (sID == null) {
|
||||
File installation = new File(context.getFilesDir(), INSTALLATION);
|
||||
try {
|
||||
|
||||
78
app/src/main/java/com/gh/common/util/KaiFuUtils.java
Normal file
78
app/src/main/java/com/gh/common/util/KaiFuUtils.java
Normal file
@ -0,0 +1,78 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.gamecenter.R;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by khy on 2017/3/19.
|
||||
*/
|
||||
public class KaiFuUtils {
|
||||
|
||||
public static void setKaiFuTime(TextView textView, long time) {
|
||||
time = time * 1000;
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
|
||||
try {
|
||||
long today = format.parse(format.format(new Date())).getTime();
|
||||
if (time >= today && time < today + 86400 * 1000) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("今天 %s", format.format(time)));
|
||||
textView.setBackgroundResource(R.drawable.border_white_bg);
|
||||
textView.setTextColor(Color.parseColor("#ffb13c"));
|
||||
} else if (time >= today - 86400 * 1000 && time < today) {
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("昨天 %s", format.format(time)));
|
||||
textView.setBackgroundResource(R.drawable.kaifu_time_tag_gray);
|
||||
textView.setTextColor(Color.parseColor("#c7c7c7"));
|
||||
} else if (time > today && time < today + 86400 * 1000 * 2){
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("明天 %s", format.format(time)));
|
||||
textView.setBackgroundResource(R.drawable.border_white_bg);
|
||||
textView.setTextColor(Color.parseColor("#ffb13c"));
|
||||
} else if (time >= today - 86400 * 1000 * 2 && time < today){
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("前天 %s", format.format(time)));
|
||||
textView.setBackgroundResource(R.drawable.kaifu_time_tag_gray);
|
||||
textView.setTextColor(Color.parseColor("#c7c7c7"));
|
||||
} else if (time > today && time < today + 86400 * 1000 * 3){
|
||||
format.applyPattern("HH:mm");
|
||||
textView.setText(String.format("后天 %s", format.format(time)));
|
||||
textView.setBackgroundResource(R.drawable.kaifu_time_tag_gray);
|
||||
textView.setTextColor(Color.parseColor("#c7c7c7"));
|
||||
} else {
|
||||
format.applyPattern("MM-dd HH:mm");
|
||||
textView.setText(format.format(time));
|
||||
textView.setBackgroundResource(R.drawable.kaifu_time_tag_gray);
|
||||
textView.setTextColor(Color.parseColor("#c7c7c7"));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
format.applyPattern("yyyy年MM月dd日 HH:mm");
|
||||
textView.setText(format.format(time));
|
||||
}
|
||||
}
|
||||
|
||||
public static void setKaiFuType(TextView textView, String type) {
|
||||
textView.setText(type);
|
||||
switch (type) {
|
||||
case "不删档内测":
|
||||
textView.setBackgroundColor(Color.parseColor("#c7c7c7"));
|
||||
break;
|
||||
case "删档内测":
|
||||
textView.setBackgroundColor(Color.parseColor("#c7c7c7"));
|
||||
break;
|
||||
case "公测":
|
||||
textView.setBackgroundColor(Color.parseColor("#06d0a8"));
|
||||
break;
|
||||
default:
|
||||
textView.setBackgroundColor(Color.parseColor("#06d0a8"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
585
app/src/main/java/com/gh/common/util/LibaoUtils.java
Normal file
585
app/src/main/java/com/gh/common/util/LibaoUtils.java
Normal file
@ -0,0 +1,585 @@
|
||||
package com.gh.common.util;
|
||||
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.AppController;
|
||||
import com.gh.gamecenter.LibaoDetailActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.LibaoDetailAdapter;
|
||||
import com.gh.gamecenter.db.LibaoDao;
|
||||
import com.gh.gamecenter.db.info.LibaoInfo;
|
||||
import com.gh.gamecenter.entity.LibaoEntity;
|
||||
import com.gh.gamecenter.entity.LibaoStatusEntity;
|
||||
import com.gh.gamecenter.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.geetest.GeetestUtils;
|
||||
import com.gh.gamecenter.retrofit.JSONObjectResponse;
|
||||
import com.gh.gamecenter.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.adapter.rxjava.HttpException;
|
||||
import rx.Observable;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.functions.Func1;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by khy on 2016/12/16.
|
||||
* 礼包工具类, 包括联网操作和领取按钮状态
|
||||
*/
|
||||
public class LibaoUtils {
|
||||
|
||||
// 礼包去重
|
||||
public static List<LibaoEntity> removeDuplicateData(List<LibaoEntity> sourceList, List<LibaoEntity> rawList) {
|
||||
if (sourceList == null || sourceList.isEmpty()
|
||||
|| rawList == null || rawList.isEmpty()) {
|
||||
return rawList;
|
||||
}
|
||||
String id;
|
||||
for (int i = 0; i < rawList.size(); i++) {
|
||||
id = rawList.get(i).getId();
|
||||
for (LibaoEntity libaoEntity : sourceList) {
|
||||
if (id.equals(libaoEntity.getId())) {
|
||||
rawList.remove(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawList;
|
||||
}
|
||||
|
||||
//初始化存号箱 获取存号箱所有礼包
|
||||
public static void getCunHaoXiang(final Context context, final boolean isCheck) {
|
||||
TokenUtils.getToken(context, isCheck)
|
||||
.flatMap(new Func1<String, Observable<List<LibaoEntity>>>() {
|
||||
@Override
|
||||
public Observable<List<LibaoEntity>> call(String token) {
|
||||
return RetrofitManager.getLibao().getCunHaoXiang(token);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<List<LibaoEntity>>() {
|
||||
@Override
|
||||
public void onResponse(List<LibaoEntity> response) {
|
||||
LibaoDao libaoDao = new LibaoDao(context);
|
||||
libaoDao.deleteAll(); // 清空之前所有数据
|
||||
for (LibaoEntity libaoEntity : response) {
|
||||
|
||||
if ("ling".equals(libaoEntity.getStatus())) {
|
||||
libaoEntity.setStatus("linged");
|
||||
} else {
|
||||
libaoEntity.setStatus("taoed");
|
||||
}
|
||||
|
||||
LibaoInfo libaoInfo = LibaoInfo.createLibaoInfo(libaoEntity);
|
||||
libaoInfo.setActive(libaoEntity.isActive());
|
||||
libaoDao.add(libaoInfo);
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(new EBReuse("libaoChanged"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
if (e != null && e.code() == 401) {
|
||||
getCunHaoXiang(context, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void postLibaoLing(final Context context, final String libaoId, final boolean isCheck,
|
||||
final PostLibaoListener listener, final String captchaCode) {
|
||||
TokenUtils.getToken(context, isCheck)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
if (!TextUtils.isEmpty(captchaCode)) {
|
||||
return RetrofitManager.getLibao().postLibaoLing(token, captchaCode, libaoId);
|
||||
} else {
|
||||
return RetrofitManager.getLibao().postLibaoLing(token, libaoId);
|
||||
}
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new JSONObjectResponse() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
listener.postSucced(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
if (e != null && e.code() == 401) {
|
||||
postLibaoLing(context, libaoId, false, listener, captchaCode);
|
||||
return;
|
||||
} else if (e.code() == 410) { // 该接口已废弃
|
||||
Utils.toast(context, "领取失败,请安装最新版本的光环助手");
|
||||
}
|
||||
listener.postFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void postLibaoTao(final Context context, final String libaoId, final boolean isCheck,
|
||||
final PostLibaoListener listener) {
|
||||
TokenUtils.getToken(context, isCheck)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
return RetrofitManager.getLibao().postLibaoTao(token, libaoId);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new JSONObjectResponse() {
|
||||
@Override
|
||||
public void onResponse(JSONObject response) {
|
||||
listener.postSucced(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
if (e != null && e.code() == 401) {
|
||||
postLibaoTao(context, libaoId, false, listener);
|
||||
return;
|
||||
}
|
||||
listener.postFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void deleteLibaoCode(final Context context, final String code, final boolean isCheck,
|
||||
final PostLibaoListener listener) {
|
||||
TokenUtils.getToken(context, isCheck)
|
||||
.flatMap(new Func1<String, Observable<ResponseBody>>() {
|
||||
@Override
|
||||
public Observable<ResponseBody> call(String token) {
|
||||
return RetrofitManager.getLibao().deleteLibaoCode(token, code);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(ResponseBody response) {
|
||||
listener.postSucced(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
if (e != null && e.code() == 401) {
|
||||
deleteLibaoCode(context, code, false, listener);
|
||||
return;
|
||||
}
|
||||
listener.postFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void getLibaoStatus(String ids, final PostLibaoListener listener) {
|
||||
RetrofitManager.getLibao().getLibaoStatus(ids)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<List<LibaoStatusEntity>>() {
|
||||
@Override
|
||||
public void onResponse(List<LibaoStatusEntity> response) {
|
||||
listener.postSucced(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
listener.postFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public interface PostLibaoListener {
|
||||
void postSucced(Object response);
|
||||
void postFailed(Throwable error);
|
||||
}
|
||||
|
||||
public static void initLibaoBtn(final TextView libaoBtn, final LibaoEntity libaoEntity, final LibaoDao libaoDao,
|
||||
final boolean isInstallRequired, final LibaoDetailAdapter adapter, final String entrance) {
|
||||
|
||||
libaoBtn.setTextColor(Color.WHITE);
|
||||
String status = libaoEntity.getStatus();
|
||||
if (TextUtils.isEmpty(status)) return;
|
||||
switch (status) {
|
||||
case "coming":
|
||||
libaoBtn.setText("未开抢");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
break;
|
||||
case "ling":
|
||||
libaoBtn.setText("领取");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_green_style);
|
||||
break;
|
||||
case "tao":
|
||||
libaoBtn.setText("淘号");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_orange_style);
|
||||
break;
|
||||
case "used_up":
|
||||
libaoBtn.setText("已领光");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "finish":
|
||||
libaoBtn.setText("已结束");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_cancel_up);
|
||||
break;
|
||||
case "linged":
|
||||
int[][] states = new int[2][];
|
||||
states[0] = new int[] { android.R.attr.state_pressed };
|
||||
states[1] = new int[] {};
|
||||
int[] colors = new int[] { Color.WHITE,
|
||||
Color.parseColor("#06D0A8") };
|
||||
ColorStateList sl = new ColorStateList(states, colors);
|
||||
libaoBtn.setText("已领取");
|
||||
libaoBtn.setBackgroundResource(R.drawable.libao_linged_style);
|
||||
libaoBtn.setTextColor(sl);
|
||||
break;
|
||||
case "taoed":
|
||||
int[][] states2 = new int[2][];
|
||||
states2[0] = new int[] { android.R.attr.state_pressed };
|
||||
states2[1] = new int[] {};
|
||||
int[] colors2 = new int[] { Color.WHITE,
|
||||
Color.parseColor("#ffb13c") };
|
||||
ColorStateList sl2 = new ColorStateList(states2, colors2);
|
||||
libaoBtn.setText("已淘号");
|
||||
libaoBtn.setBackgroundResource(R.drawable.libao_taoed_style);
|
||||
libaoBtn.setTextColor(sl2);
|
||||
break;
|
||||
case "copy":
|
||||
libaoBtn.setText("复制");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
break;
|
||||
default:
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_cancel_style);
|
||||
libaoBtn.setText("异常");
|
||||
}
|
||||
|
||||
libaoBtn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 领取限制
|
||||
if ("领取".equals(libaoBtn.getText().toString()) || "淘号".equals(libaoBtn.getText().toString())) {
|
||||
if (isInstallRequired && !isAppInstalled(libaoBtn.getContext(), libaoEntity.getPackageName())) {
|
||||
String platform;
|
||||
if (TextUtils.isEmpty(libaoEntity.getPlatform())) {
|
||||
platform = "";
|
||||
} else {
|
||||
platform = PlatformUtils.getInstance(libaoBtn.getContext())
|
||||
.getPlatformName(libaoEntity.getPlatform()) + "版";
|
||||
}
|
||||
|
||||
DialogUtils.showWarningDialog(libaoBtn.getContext(), "条件不符",
|
||||
Html.fromHtml("请先"+ "<font color=\"#06D0A8\">"
|
||||
+ "安装《" + libaoEntity.getGame().getName() + "》 "
|
||||
+ platform + "</font>"), "关闭", "立即安装"
|
||||
, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
adapter.openDownload();
|
||||
}
|
||||
}, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (libaoBtn.getText().toString()) {
|
||||
case "未开始":
|
||||
Utils.toast(libaoBtn.getContext(), "还没到开始领取时间");
|
||||
break;
|
||||
case "查看":
|
||||
AppController.put("libaoEntity", libaoEntity);
|
||||
Intent intent = new Intent(libaoBtn.getContext(), LibaoDetailActivity.class);
|
||||
intent.putExtra("entrance", entrance);
|
||||
libaoBtn.getContext().startActivity(intent);
|
||||
break;
|
||||
case "领取":
|
||||
libaoLing(libaoBtn, libaoEntity, adapter, isInstallRequired, libaoDao, null, entrance);
|
||||
break;
|
||||
case "淘号":
|
||||
postLibaoTao(libaoBtn.getContext(), libaoEntity.getId(), true, new PostLibaoListener() {
|
||||
@Override
|
||||
public void postSucced(Object response) {
|
||||
|
||||
JSONObject responseBody = (JSONObject) response;
|
||||
Utils.log("postLibaoTao=====" + responseBody);
|
||||
String libaoCode = null;
|
||||
try {
|
||||
libaoCode = responseBody.getString("code");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(libaoCode)) {
|
||||
try {
|
||||
String detail = responseBody.getString("detail");
|
||||
if ("maintaining".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "网络状态异常,请稍后再试");
|
||||
} else if ("fail to compete".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "淘号失败,稍后重试");
|
||||
} else {
|
||||
Utils.toast(libaoBtn.getContext(), "淘号异常");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.toast(libaoBtn.getContext(), "淘号成功");
|
||||
|
||||
libaoEntity.setStatus("taoed");
|
||||
|
||||
LibaoInfo libaoInfo = LibaoInfo.createLibaoInfo(libaoEntity);
|
||||
libaoInfo.setCode(libaoCode);
|
||||
libaoInfo.setTime(Utils.getTime(libaoBtn.getContext()));
|
||||
libaoDao.add(libaoInfo);
|
||||
|
||||
EventBus.getDefault().post(new EBReuse("libaoChanged"));
|
||||
|
||||
adapter.initLibaoDao();
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
final String finalLibaoCode = libaoCode;
|
||||
DialogUtils.showWarningDialog(libaoBtn.getContext(), "淘号成功",Html.fromHtml("礼包码:"
|
||||
+ "<font color=\"#ffb13c\">" + libaoCode + "</font>" +
|
||||
"<br/>淘号礼包不保证可用,请尽快进入游戏尝试兑换")
|
||||
, "关闭", " 复制礼包码"
|
||||
, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
copyLink(finalLibaoCode, libaoBtn.getContext());
|
||||
|
||||
if (isInstallRequired) {
|
||||
libaoBtn.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
lunningAppDialog(libaoBtn.getContext()
|
||||
, Html.fromHtml("礼包码:"
|
||||
+ "<font color=\"#ffb13c\">" + finalLibaoCode + "</font>"
|
||||
+ " 复制成功"
|
||||
+"<br/>淘号礼包不保证可用,请尽快进入游戏尝试兑换"), libaoEntity);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFailed(Throwable error) {
|
||||
Utils.log("---" + error.toString());
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
HttpException exception = (HttpException) error;
|
||||
if (exception.code() == 403) {
|
||||
try {
|
||||
JSONObject errorJson = new JSONObject(exception.response().errorBody().string());
|
||||
String detail = errorJson.getString("detail");
|
||||
Utils.toast(libaoBtn.getContext(), "返回::" + detail);
|
||||
if ("coming".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "礼包领取时间未开始");
|
||||
} else if ("finish".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "礼包领取时间已结束");
|
||||
} else if ("fetched".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "你已领过这个礼包了");
|
||||
getCunHaoXiang(libaoBtn.getContext(), true);
|
||||
libaoBtn.setText("复制");
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
} else if ("try tao".equals(detail) || "used up".equals(detail)) {
|
||||
DialogUtils.showHintDialog(libaoBtn.getContext(), "礼包已领光"
|
||||
, "手速不够快,礼包已经被抢光了,十分抱歉", "知道了");
|
||||
} else if ("maintaining".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "网络状态异常,请稍后再试");
|
||||
} else if ("fail to compete".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "淘号失败,稍后重试");
|
||||
} else {
|
||||
Utils.toast(libaoBtn.getContext(), "操作失败");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
Utils.toast(libaoBtn.getContext(),"礼包处理异常"+ ex.toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Utils.toast(libaoBtn.getContext(), "发生异常");
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void libaoLing(final TextView libaoBtn, final LibaoEntity libaoEntity, final LibaoDetailAdapter adapter,
|
||||
final boolean isInstallRequired, final LibaoDao libaoDao, String captchaCode, final String entrance) {
|
||||
|
||||
postLibaoLing(libaoBtn.getContext(), libaoEntity.getId(), true, new PostLibaoListener() {
|
||||
@Override
|
||||
public void postSucced(Object response) {
|
||||
|
||||
JSONObject responseBody = (JSONObject) response;
|
||||
Utils.log("postLibaoLing=====" + responseBody);
|
||||
String libaoCode = null;
|
||||
try {
|
||||
libaoCode = responseBody.getString("code");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(libaoCode)) {
|
||||
Utils.toast(libaoBtn.getContext(), "领取异常");
|
||||
return;
|
||||
}
|
||||
libaoEntity.setAvailable(libaoEntity.getAvailable() - 1);
|
||||
|
||||
libaoEntity.setStatus("linged");
|
||||
|
||||
LibaoInfo libaoInfo = LibaoInfo.createLibaoInfo(libaoEntity);
|
||||
libaoInfo.setTime(Utils.getTime(libaoBtn.getContext()));
|
||||
libaoInfo.setCode(libaoCode);
|
||||
libaoDao.add(libaoInfo);
|
||||
|
||||
EventBus.getDefault().post(new EBReuse("libaoChanged"));
|
||||
|
||||
adapter.initLibaoDao();
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
final String finalLibaoCode = libaoCode;
|
||||
DialogUtils.showWarningDialog(libaoBtn.getContext(), "领取成功", Html.fromHtml("礼包码:"
|
||||
+ "<font color=\"#00B7FA\">" + libaoCode + "</font>" +
|
||||
"<br/>请尽快使用,礼包码将于60分钟后进入淘号池")
|
||||
, "关闭", " 复制礼包码"
|
||||
, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
copyLink(finalLibaoCode, libaoBtn.getContext());
|
||||
if (isInstallRequired) {
|
||||
libaoBtn.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
lunningAppDialog(libaoBtn.getContext()
|
||||
, Html.fromHtml("礼包码:"
|
||||
+ "<font color=\"#00B7FA\">" + finalLibaoCode + "</font>"
|
||||
+ " 复制成功" +"<br/>请尽快进入游戏兑换"), libaoEntity);
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postFailed(Throwable error) {
|
||||
Utils.log("-----" + error.toString());
|
||||
|
||||
if (error instanceof HttpException) {
|
||||
HttpException exception = (HttpException) error;
|
||||
if (exception.code() == 403) {
|
||||
try {
|
||||
String string = exception.response().errorBody().string();
|
||||
JSONObject errorJson = new JSONObject(string);
|
||||
String detail = errorJson.getString("detail");
|
||||
|
||||
if ("coming".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "礼包领取时间未开始");
|
||||
} else if ("finish".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "礼包领取时间已结束");
|
||||
} else if ("fetched".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "你已领过这个礼包了");
|
||||
getCunHaoXiang(libaoBtn.getContext(), true);
|
||||
libaoBtn.setText("复制");// TODO
|
||||
libaoBtn.setBackgroundResource(R.drawable.textview_blue_style);
|
||||
|
||||
} else if ("try tao".equals(detail) || "used up".equals(detail)) {
|
||||
DialogUtils.showHintDialog(libaoBtn.getContext(), "礼包已领光"
|
||||
, "手速不够快,礼包已经被抢光了,十分抱歉", "知道了");
|
||||
libaoEntity.setStatus("used_up");
|
||||
initLibaoBtn(libaoBtn, libaoEntity, libaoDao, isInstallRequired, adapter, entrance);
|
||||
|
||||
} else if ("maintaining".equals(detail)) {
|
||||
Utils.toast(libaoBtn.getContext(), "网络状态异常,请稍后再试");
|
||||
} else {
|
||||
Utils.toast(libaoBtn.getContext(), "操作失败");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
Utils.toast(libaoBtn.getContext(),"礼包处理异常");
|
||||
}
|
||||
return;
|
||||
} else if (exception.code() == 412) {
|
||||
// 需要验证
|
||||
GeetestUtils.getInstance(libaoBtn.getContext())
|
||||
.showDialog(new GeetestUtils.GeetestListener() {
|
||||
@Override
|
||||
public void succed(String response) {
|
||||
libaoLing(libaoBtn, libaoEntity, adapter, isInstallRequired, libaoDao, response, entrance);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
Utils.toast(libaoBtn.getContext(), "发生异常");
|
||||
}
|
||||
}, captchaCode);
|
||||
}
|
||||
|
||||
public static boolean isAppInstalled(Context context, String packageName) {
|
||||
final android.content.pm.PackageManager packageManager = context.getPackageManager();
|
||||
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
|
||||
if (pinfo != null) {
|
||||
for (int i = 0; i < pinfo.size(); i++) {
|
||||
String pn = pinfo.get(i).packageName;
|
||||
if (pn.equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void lunningAppDialog (final Context context, Spanned msg, final LibaoEntity libaoEntity) {
|
||||
DialogUtils.showWarningDialog(context, "复制成功", msg
|
||||
, "关闭", "启动游戏"
|
||||
, new DialogUtils.ConfiremListener() {
|
||||
@Override
|
||||
public void onConfirem() {
|
||||
if (LibaoUtils.isAppInstalled(context, libaoEntity.getPackageName())) {
|
||||
PackageUtils.launchApplicationByPackageName(context, libaoEntity.getPackageName());
|
||||
} else {
|
||||
Utils.toast(context, "请安装游戏:" + libaoEntity.getGame().getName()
|
||||
+ PlatformUtils.getInstance(context).getPlatformName(libaoEntity.getPlatform()) + "版");
|
||||
}
|
||||
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
|
||||
//复制文字
|
||||
public static void copyLink(String copyContent, Context context) {
|
||||
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cmb.setText(copyContent);
|
||||
|
||||
Utils.toast(context,copyContent + " 复制成功");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user