commit
d3015cc2f7
@ -0,0 +1,127 @@
|
||||
tng-customsuite.xml
|
||||
**pom.xml.releaseBackup
|
||||
release.properties
|
||||
gen
|
||||
*/seed.txt
|
||||
notes
|
||||
logs
|
||||
gen-external-apklibs
|
||||
.idea
|
||||
*.iml
|
||||
.DS_Store
|
||||
*.swp
|
||||
out
|
||||
.gradle
|
||||
/local.properties
|
||||
/build
|
||||
|
||||
###OSX###
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must ends with two \r.
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
|
||||
###Linux###
|
||||
|
||||
*~
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
|
||||
###Android###
|
||||
|
||||
# Built application files
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# Files for ART and Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
.gradletasknamecache
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Lint
|
||||
lint-report.html
|
||||
lint-report_files/
|
||||
lint_result.txt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
###IntelliJ###
|
||||
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
|
||||
###Eclipse###
|
||||
|
||||
*.pydevproject
|
||||
.metadata
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipseXml version="1.0" encoding="UTF-8"?>
|
||||
/gradle.properties
|
||||
sign.properties
|
||||
|
||||
captures/*
|
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,131 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
applicationId rootProject.ext.applicationId
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
}
|
||||
|
||||
// rename the apk with the version name
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.outputFile = new File(
|
||||
output.outputFile.parent + "/${variant.buildType.name}",
|
||||
"osc-android-${variant.versionName}-${variant.productFlavors[0].name}-${variant.buildType.name}.apk".toLowerCase())
|
||||
}
|
||||
}
|
||||
|
||||
//signing files settings
|
||||
signingConfigs {
|
||||
|
||||
debug {
|
||||
keyAlias 'AndroidDebugKey'
|
||||
keyPassword 'android'
|
||||
storeFile file('C:/Users/46708/.android/debug.keystore')
|
||||
storePassword 'android'
|
||||
}
|
||||
|
||||
// release {
|
||||
// storeFile file(RELEASE_STORE_FILE)
|
||||
// storePassword RELEASE_STORE_PASSWORD
|
||||
// keyAlias RELEASE_KEY_ALIAS
|
||||
// keyPassword RELEASE_KEY_PASSWORD
|
||||
// }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
|
||||
// 移除lint检查的error
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
//build type setting
|
||||
buildTypes {
|
||||
|
||||
debug {
|
||||
zipAlignEnabled false
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
zipAlignEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
//product flavors
|
||||
productFlavors {
|
||||
|
||||
oschina {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "oschina"]
|
||||
}
|
||||
|
||||
google {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "google"]
|
||||
}
|
||||
|
||||
tencent {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "tentcent"]
|
||||
}
|
||||
|
||||
wandoujia {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "wandoujia"]
|
||||
}
|
||||
|
||||
xiaomi {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "xiaomi"]
|
||||
}
|
||||
|
||||
huawei {
|
||||
manifestPlaceholders = [UMENG_CHANNEL: "huawei"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
compile(name: 'social_sdk_library_project', ext: 'aar')
|
||||
|
||||
compile 'com.android.support:support-v4:24.0.0'
|
||||
compile 'com.android.support:appcompat-v7:24.0.0'
|
||||
compile 'com.android.support:design:24.0.0'
|
||||
compile 'com.android.support:recyclerview-v7:24.0.0'
|
||||
compile 'com.github.chrisbanes.photoview:library:1.2.4'
|
||||
compile 'com.loopj.android:android-async-http:1.4.9'
|
||||
compile 'com.jakewharton:butterknife:7.0.1'
|
||||
compile 'org.kymjs.kjframe:kjframe:2.6'
|
||||
compile 'com.google.zxing:core:3.2.0'
|
||||
compile 'com.joanzapata.android:android-iconify:1.0.9'
|
||||
compile 'com.makeramen:roundedimageview:2.1.1'
|
||||
compile 'pub.devrel:easypermissions:0.1.7'
|
||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||
compile 'com.google.code.gson:gson:2.6.2'
|
||||
compile 'net.qiujuer.genius:blur:2.0.0'
|
||||
compile 'net.qiujuer.genius:res:2.0.0-beta6'
|
||||
compile 'net.qiujuer.genius:ui:2.0.0-beta6'
|
||||
compile 'com.umeng.analytics:analytics:latest.integration'
|
||||
|
||||
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
|
||||
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,106 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/yanghonghe/Downloads/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
##---------------Begin: proguard configuration for Gson ----------
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
-keepattributes Signature
|
||||
|
||||
# For using GSON @Expose annotation
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Gson specific classes
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
#-keep class com.google.gson.stream.** { *; }
|
||||
|
||||
# Application classes that will be serialized/deserialized over Gson
|
||||
-keep class net.oschina.app.improve.bean.** { *; }
|
||||
|
||||
-keepattributes EnclosingMethod
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
|
||||
-keep class net.oschina.app.** { *; }
|
||||
|
||||
|
||||
-keep class butterknife.** { *; }
|
||||
-dontwarn butterknife.internal.**
|
||||
-keep class **$$ViewBinder { *; }
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
@butterknife.* <fields>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
@butterknife.* <methods>;
|
||||
}
|
||||
|
||||
-keep public class net.oschina.app.R$* {
|
||||
public static final int *;
|
||||
}
|
||||
|
||||
-dontwarn com.thoughtworks.xstream.**
|
||||
-keep class com.thoughtworks.xstream.** { *; }
|
||||
|
||||
-dontwarn com.makeramen.roundedimageview.**
|
||||
-keep class com.makeramen.roundedimageview.RoundedTransformationBuilder
|
||||
|
||||
-dontwarn com.tencent.weibo.sdk.android.**
|
||||
-keep class com.tencent.weibo.sdk.android.** { *; }
|
||||
|
||||
-dontwarn com.squareup.leakcanary.DisplayLeakService
|
||||
-keep class com.squareup.leakcanary.DisplayLeakService
|
||||
|
||||
-dontwarn android.widget.**
|
||||
-keep class android.widget.** {*;}
|
||||
|
||||
-dontwarn android.support.v7.widget.**
|
||||
-keep class android.support.v7.widget.**{*;}
|
||||
|
||||
-dontshrink
|
||||
-dontoptimize
|
||||
-dontwarn com.google.android.maps.**
|
||||
-dontwarn android.webkit.WebView
|
||||
-dontwarn com.umeng.**
|
||||
-dontwarn com.tencent.weibo.sdk.**
|
||||
|
||||
-keepattributes Exceptions,InnerClasses,Signature
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-keep public interface com.tencent.**
|
||||
-keep public interface com.umeng.socialize.**
|
||||
-keep public interface com.umeng.socialize.sensor.**
|
||||
-keep public interface com.umeng.scrshot.**
|
||||
|
||||
-keep public class com.umeng.socialize.* {*;}
|
||||
-keep public class javax.**
|
||||
-keep public class android.webkit.**
|
||||
|
||||
-keep class com.umeng.scrshot.**
|
||||
-keep public class com.tencent.** {*;}
|
||||
-keep class com.umeng.socialize.sensor.**
|
||||
|
||||
-keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;}
|
||||
-keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;}
|
||||
|
||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.oschina.app;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package net.oschina.app.service;
|
||||
|
||||
interface INoticeService
|
||||
{
|
||||
void scheduleNotice();
|
||||
void requestNotice();
|
||||
void clearNotice(int uid,int type);
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,20 @@
|
||||
function showImagePreview(url) {
|
||||
var idx = 0;
|
||||
for(var i=0;i<allImgUrls.length;i++){
|
||||
if(allImgUrls[i]==url){
|
||||
idx= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var jsonData = "{\"url\":\""+ url +"\",\"index\":"+idx+",\"urls\":\""+allImgUrls+"\"}";
|
||||
window.location = "ima-api:action=showImage&data="+jsonData;
|
||||
}
|
||||
|
||||
function getAllImgSrc(htmlstr) {
|
||||
var reg=/<img.+?src=('|")?([^'"]+)('|")?(?:\s+|>)/gim;
|
||||
var arr = [];
|
||||
while(tem=reg.exec(htmlstr)){
|
||||
arr.push(tem[2]);
|
||||
}
|
||||
return arr;
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
word-wrap: break-word;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.about {
|
||||
float: left;
|
||||
padding-left:0;
|
||||
padding-top: 0;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.about li {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
img.alignleft {
|
||||
float:left;
|
||||
max-width:120px;
|
||||
margin:0 10px 5px 0;
|
||||
border:1px solid #ccc;
|
||||
background:#fff;padding:2px;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size:9pt;
|
||||
line-height:12pt;
|
||||
font-family:Courier New,Arial;border:1px solid #ddd;
|
||||
border-left:5px solid #6CE26C;background:#f6f6f6;
|
||||
padding:5px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a.tag {
|
||||
font-size:15px;
|
||||
text-decoration:none;
|
||||
background-color:#cfc;
|
||||
color:#060;
|
||||
border-bottom:1px solid #B1D3EB;
|
||||
border-right:1px solid #B1D3EB;
|
||||
color:#3E6D8E;margin:2px 2px 2px 0;
|
||||
padding:2px 4px;
|
||||
white-space:nowrap;position:relative
|
||||
}
|
||||
|
||||
a{
|
||||
text-decoration:none;
|
||||
color: #3E62A6
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #333333;
|
||||
font-size: 22px;
|
||||
line-height: 24px;
|
||||
font-weight: bold;
|
||||
margin-top: 15px
|
||||
}
|
||||
|
||||
.title img {
|
||||
width: 28px;
|
||||
vertical-align:middle;
|
||||
margin-right: 8
|
||||
}
|
||||
|
||||
.title .recommend {
|
||||
width:20px
|
||||
}
|
||||
|
||||
.authortime {
|
||||
font-size: 13px;
|
||||
color: #aaaaaa;
|
||||
margin: 4px 0 2px 0
|
||||
}
|
||||
|
||||
.authortime a {
|
||||
color: #0b9a27
|
||||
}
|
||||
|
||||
.author {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.contentstyle {
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
// 相关软件
|
||||
.oschina_software {
|
||||
color: #999999
|
||||
}
|
||||
|
||||
// 软件详情
|
||||
.software_attr {
|
||||
background: #f6f6f6;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.software_attr {
|
||||
list-style-type: none;
|
||||
border: 1px solid #eee;
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
.software_attr li {
|
||||
padding: 0 0 5 0
|
||||
}
|
||||
|
||||
.software_urls {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.software_urls {
|
||||
margin: 10px 10px 10px 0px;
|
||||
margin: 10 0 50 0
|
||||
}
|
||||
|
||||
.software_urls li {
|
||||
float: left;
|
||||
margin-right: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.software_urls li a {
|
||||
border-radius: 5px;
|
||||
background: #557DBA;
|
||||
color: #fff;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
font-size: 12pt;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
padding: 5 10 5 10
|
||||
|
||||
}
|
||||
|
||||
body.night .contentstyle {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.contentstyle p {
|
||||
line-height: 120%;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
body.night {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
body.night {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
body.night .about {
|
||||
color: #999999
|
||||
}
|
||||
|
||||
body.night .oschina_software {
|
||||
color: #999999
|
||||
}
|
||||
|
||||
body.night p {
|
||||
color: #999999
|
||||
}
|
||||
|
||||
body.night a {
|
||||
color: #4877b1
|
||||
}
|
||||
|
||||
body.night .title {
|
||||
color: #a8a8a8;
|
||||
}
|
||||
|
||||
body.night .authortime {
|
||||
color: #696969;
|
||||
}
|
||||
|
||||
body.night .authortime a {
|
||||
color: #0b9a27
|
||||
}
|
||||
|
||||
body.night .software_urls li a {
|
||||
background: #696969;
|
||||
color: #fff;r
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
font-size: 12pt;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
padding: 5 10 5 10
|
||||
}
|
||||
|
||||
body.night pre {
|
||||
background: #222222
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
html, body {
|
||||
font-family: -apple-system, "Helvetica Neue", Helvetica, "Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB", "Source Han Sans CN", "Source Han Sans SC", "Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti", SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
word-wrap: break-word;
|
||||
font-size: 16px;
|
||||
line-height: 165%;
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
.body-content{
|
||||
box-pack: justify;
|
||||
text-justify: inter-ideograph;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.2em;
|
||||
margin: 0;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
p{
|
||||
margin: 0;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
table {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
border: 1px solid #CFCDD3;
|
||||
border-collapse: none;
|
||||
background: #f8f8f8;
|
||||
font-size: 0.8em;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin: 4px 0;
|
||||
background: #fefefe;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
font-size: 0.8em;
|
||||
font-family: monaco, Consolas, 'Liberation Mono', Courier, monospace;
|
||||
border: none;
|
||||
border-left: 2px solid #CFCDD3;
|
||||
background: #f8f8f8;
|
||||
padding: 8px 6px 6px 6px;
|
||||
line-height: 140%;
|
||||
overflow: auto;
|
||||
word-wrap: break-word;
|
||||
word-break:break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #24cf5f
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
html, body {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
word-wrap: break-word;
|
||||
font-size: 15px;
|
||||
letter-spacing: 1px;
|
||||
line-height: 165%;
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
.body-content{
|
||||
box-pack: justify;
|
||||
text-justify: inter-ideograph;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #010101;
|
||||
}
|
||||
|
||||
table {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
border: 1px solid #CFCDD3;
|
||||
border-collapse: none;
|
||||
background: #f8f8f8;
|
||||
font-size: 0.8em;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
margin: 4px 0;
|
||||
background: #fefefe;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 1px;
|
||||
margin-bottom: 1px;
|
||||
font-size: 0.8em;
|
||||
font-family: monaco, Consolas, 'Liberation Mono', Courier, monospace;
|
||||
border: none;
|
||||
border-left: 2px solid #CFCDD3;
|
||||
background: #f8f8f8;
|
||||
padding: 8px 6px 6px 6px;
|
||||
line-height: 140%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #24cf5f
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/vnd.wap.xhtml+xml;charset=utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link href="file:///android_asset/css/common.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="file:///android_asset/shCore.js"></script>
|
||||
<script type="text/javascript" src="file:///android_asset/detail_page.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="article_body" class="contentstyle"></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,55 @@
|
||||
var title;
|
||||
var author;
|
||||
var time;
|
||||
var body;
|
||||
var images;
|
||||
var imageCount = 0;
|
||||
|
||||
function load_day() {
|
||||
document.bgColor="#FF0000";
|
||||
var font = document.getElementById("font");
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
font.style.color="white";
|
||||
}
|
||||
|
||||
function load_night() {
|
||||
document.bgColor="#1f1f1f";
|
||||
var font = document.getElementById("font");
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
font.style.color="black";
|
||||
}
|
||||
|
||||
function fill(detailBody) {
|
||||
var myBody = document.getElementById('body');
|
||||
myBody.innerHTML = detailBody
|
||||
}
|
||||
|
||||
function changeFontSize(body) {
|
||||
var myBody = document.getElementById('article_body');
|
||||
myBody.style.fontSize="22px"
|
||||
myBody.innerHTML = body
|
||||
}
|
||||
|
||||
function showSuperBigSize() {
|
||||
var myBody = document.getElementById('article_body');
|
||||
myBody.style.fontSize="26px";
|
||||
}
|
||||
|
||||
function showBigSize() {
|
||||
var myBody = document.getElementById('article_body');
|
||||
myBody.style.fontSize="22px";
|
||||
}
|
||||
|
||||
function showMidSize() {
|
||||
var myBody = document.getElementById('article_body');
|
||||
myBody.style.fontSize="18px";
|
||||
}
|
||||
|
||||
function showSmallSize() {
|
||||
var myBody = document.getElementById('article_body');
|
||||
myBody.style.fontSize="16px";
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,303 @@
|
||||
.syntaxhighlighter,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody
|
||||
{
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
outline: 0 !important;
|
||||
background: none !important;
|
||||
text-align: left !important;
|
||||
float: none !important;
|
||||
vertical-align: baseline !important;
|
||||
position: static !important;
|
||||
left: auto !important;
|
||||
top: auto !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
height: auto !important;
|
||||
width: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 10pt !important;
|
||||
min-height: inherit !important; /* For IE8, FF & WebKit */
|
||||
min-height: auto !important; /* For IE7 */
|
||||
}
|
||||
|
||||
.syntaxhighlighter
|
||||
{
|
||||
width: 99% !important; /* 99% fixes IE8 horizontal scrollbar */
|
||||
margin: 1em 0 1em 0 !important;
|
||||
padding: 1px !important; /* adds a little border on top and bottom */
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .bold
|
||||
{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .italic
|
||||
{
|
||||
font-style: italic !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line
|
||||
{
|
||||
}
|
||||
|
||||
.syntaxhighlighter .no-wrap .line .content
|
||||
{
|
||||
white-space: pre !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line table
|
||||
{
|
||||
border-collapse: collapse !important;
|
||||
style=table-layout:fixed;
|
||||
style=word-wrap:break-word;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line td
|
||||
{
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line .number
|
||||
{
|
||||
width: 1.8em !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line .number code
|
||||
{
|
||||
width: 1.8em !important;
|
||||
padding-right: .3em !important;
|
||||
text-align: right !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line .content
|
||||
{
|
||||
padding-left: .5em !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .line .spaces
|
||||
{
|
||||
}
|
||||
|
||||
/* Disable border and margin on the lines when no gutter option is set */
|
||||
.syntaxhighlighter.nogutter .line .content
|
||||
{
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .bar
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .bar.show
|
||||
{
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.collapsed .bar
|
||||
{
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Adjust some properties when collapsed */
|
||||
|
||||
.syntaxhighlighter.collapsed .lines
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .lines.no-wrap
|
||||
{
|
||||
overflow: auto !important;
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
|
||||
/* Styles for the toolbar */
|
||||
|
||||
.syntaxhighlighter .toolbar
|
||||
{
|
||||
position: absolute !important;
|
||||
right: 0px !important;
|
||||
top: 0px !important;
|
||||
font-size: 1px !important;
|
||||
padding: 8px 8px 8px 0 !important; /* in px because images don't scale with ems */
|
||||
}
|
||||
|
||||
.syntaxhighlighter.collapsed .toolbar
|
||||
{
|
||||
font-size: 80% !important;
|
||||
padding: .2em 0 .5em .5em !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar a.item,
|
||||
.syntaxhighlighter .toolbar .item
|
||||
{
|
||||
display: block !important;
|
||||
float: left !important;
|
||||
margin-left: 8px !important;
|
||||
background-repeat: no-repeat !important;
|
||||
overflow: hidden !important;
|
||||
text-indent: -5000px !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.collapsed .toolbar .item
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.collapsed .toolbar .item.expandSource
|
||||
{
|
||||
background-image: url(magnifier.png) !important;
|
||||
display: inline !important;
|
||||
text-indent: 0 !important;
|
||||
width: auto !important;
|
||||
float: none !important;
|
||||
height: 16px !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar .item.viewSource
|
||||
{
|
||||
background-image: url(page_white_code.png) !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar .item.printSource
|
||||
{
|
||||
background-image: url(printer.png) !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar .item.copyToClipboard
|
||||
{
|
||||
text-indent: 0 !important;
|
||||
background: none !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar .item.about
|
||||
{
|
||||
background-image: url(help.png) !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print view.
|
||||
* Colors are based on the default theme without background.
|
||||
*/
|
||||
|
||||
.syntaxhighlighter.printing,
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content,
|
||||
{
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Gutter line numbers */
|
||||
.syntaxhighlighter.printing .line .number
|
||||
{
|
||||
color: #bbb !important;
|
||||
}
|
||||
|
||||
/* Add border to the lines */
|
||||
.syntaxhighlighter.printing .line .content
|
||||
{
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Toolbar when visible */
|
||||
.syntaxhighlighter.printing .toolbar
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing a
|
||||
{
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .plain,
|
||||
.syntaxhighlighter.printing .plain a
|
||||
{
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .comments,
|
||||
.syntaxhighlighter.printing .comments a
|
||||
{
|
||||
color: #008200 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .string,
|
||||
.syntaxhighlighter.printing .string a
|
||||
{
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .keyword
|
||||
{
|
||||
color: #069 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .preprocessor
|
||||
{
|
||||
color: gray !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .variable
|
||||
{
|
||||
color: #a70 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .value
|
||||
{
|
||||
color: #090 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .functions
|
||||
{
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .constants
|
||||
{
|
||||
color: #0066CC !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .script
|
||||
{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .color1,
|
||||
.syntaxhighlighter.printing .color1 a
|
||||
{
|
||||
color: #808080 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .color2,
|
||||
.syntaxhighlighter.printing .color2 a
|
||||
{
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .color3,
|
||||
.syntaxhighlighter.printing .color3 a
|
||||
{
|
||||
color: red !important;
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,142 @@
|
||||
.syntaxhighlighter
|
||||
{
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
/* Highlighed line number */
|
||||
.syntaxhighlighter .line.highlighted .number
|
||||
{
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
/* Highlighed line */
|
||||
.syntaxhighlighter .line.highlighted.alt1,
|
||||
.syntaxhighlighter .line.highlighted.alt2
|
||||
{
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
/* Gutter line numbers */
|
||||
.syntaxhighlighter .line .number
|
||||
{
|
||||
color: #afafaf !important;
|
||||
}
|
||||
|
||||
/* Add border to the lines */
|
||||
.syntaxhighlighter .line .content
|
||||
{
|
||||
border-left: 3px solid #6CE26C !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter.printing .line .content
|
||||
{
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* First line */
|
||||
.syntaxhighlighter .line.alt1
|
||||
{
|
||||
background-color: #fff !important;
|
||||
word-wrap:break-word
|
||||
}
|
||||
|
||||
/* Second line */
|
||||
.syntaxhighlighter .line.alt2
|
||||
{
|
||||
background-color: #F8F8F8 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar
|
||||
{
|
||||
background-color: #F8F8F8 !important;
|
||||
border: #E7E5DC solid 1px !important;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar a
|
||||
{
|
||||
color: #a0a0a0 !important;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .toolbar a:hover
|
||||
{
|
||||
color: red !important;
|
||||
display:none;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Actual syntax highlighter colors.
|
||||
************************************/
|
||||
.syntaxhighlighter .plain,
|
||||
.syntaxhighlighter .plain a
|
||||
{
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .comments,
|
||||
.syntaxhighlighter .comments a
|
||||
{
|
||||
color: #008200 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .string,
|
||||
.syntaxhighlighter .string a
|
||||
{
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword
|
||||
{
|
||||
color: #069 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .preprocessor
|
||||
{
|
||||
color: gray !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .variable
|
||||
{
|
||||
color: #a70 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .value
|
||||
{
|
||||
color: #090 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .functions
|
||||
{
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .constants
|
||||
{
|
||||
color: #0066CC !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .script
|
||||
{
|
||||
background-color: yellow !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .color1,
|
||||
.syntaxhighlighter .color1 a
|
||||
{
|
||||
color: #808080 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .color2,
|
||||
.syntaxhighlighter .color2 a
|
||||
{
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .color3,
|
||||
.syntaxhighlighter .color3 a
|
||||
{
|
||||
color: red !important;
|
||||
}
|
@ -0,0 +1,597 @@
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dtr.zxing.camera.CameraManager;
|
||||
import com.dtr.zxing.decode.DecodeThread;
|
||||
import com.dtr.zxing.utils.BeepManager;
|
||||
import com.dtr.zxing.utils.CaptureActivityHandler;
|
||||
import com.dtr.zxing.utils.InactivityTimer;
|
||||
import com.google.zxing.Result;
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.AppException;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.api.remote.OSChinaApi;
|
||||
import net.oschina.app.base.BaseActivity;
|
||||
import net.oschina.app.bean.BarCode;
|
||||
import net.oschina.app.bean.ResultBean;
|
||||
import net.oschina.app.bean.SingInResult;
|
||||
import net.oschina.app.util.DialogHelp;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.UIHelper;
|
||||
import net.oschina.app.util.XmlUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import cz.msebera.android.httpclient.Header;
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
/**
|
||||
* This activity opens the camera and does the actual scanning on a background
|
||||
* thread. It draws a viewfinder to help the user place the barcode correctly,
|
||||
* shows feedback as the image processing is happening, and then overlays the
|
||||
* results when a scan is successful.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class CaptureActivity extends BaseActivity implements
|
||||
SurfaceHolder.Callback, EasyPermissions.PermissionCallbacks {
|
||||
|
||||
private static final String TAG = CaptureActivity.class.getSimpleName();
|
||||
|
||||
private CameraManager cameraManager;
|
||||
private CaptureActivityHandler handler;
|
||||
private InactivityTimer inactivityTimer;
|
||||
private BeepManager beepManager;
|
||||
|
||||
private SurfaceView scanPreview = null;
|
||||
private RelativeLayout scanContainer;
|
||||
private RelativeLayout scanCropView;
|
||||
private ImageView scanLine;
|
||||
private ImageView mFlash;
|
||||
|
||||
private Rect mCropRect = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
Window window = getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
setContentView(R.layout.activity_qr_scan);
|
||||
|
||||
scanPreview = (SurfaceView) findViewById(R.id.capture_preview);
|
||||
// Install the callback and wait for surfaceCreated() to init the
|
||||
// camera.
|
||||
scanPreview.getHolder().addCallback(this);
|
||||
|
||||
cameraTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
if (scanPreview != null) {
|
||||
handler = null;
|
||||
if (isHasSurface) {
|
||||
// The activity was paused but not stopped, so the surface still
|
||||
// exists. Therefore
|
||||
// surfaceCreated() won't be called, so init the camera here.
|
||||
initCamera(scanPreview.getHolder());
|
||||
}
|
||||
}
|
||||
if (inactivityTimer != null) {
|
||||
inactivityTimer.onResume();
|
||||
}
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (handler != null) {
|
||||
handler.quitSynchronously();
|
||||
handler = null;
|
||||
}
|
||||
if (inactivityTimer != null) {
|
||||
inactivityTimer.onPause();
|
||||
}
|
||||
if (beepManager != null) {
|
||||
beepManager.close();
|
||||
}
|
||||
if (cameraManager != null) {
|
||||
cameraManager.closeDriver();
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (inactivityTimer != null) {
|
||||
inactivityTimer.shutdown();
|
||||
}
|
||||
if (scanPreview != null) {
|
||||
scanPreview.getHolder().removeCallback(this);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public CameraManager getCameraManager() {
|
||||
return cameraManager;
|
||||
}
|
||||
|
||||
private boolean isHasSurface = false;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
protected boolean hasActionBar() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
getSupportActionBar().hide();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
if (holder == null) {
|
||||
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
|
||||
}
|
||||
isHasSurface = true;
|
||||
initCamera(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
isHasSurface = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width,
|
||||
int height) {
|
||||
// Doing
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid barcode has been found, so give an indication of success and show
|
||||
* the results.
|
||||
*
|
||||
* @param rawResult The contents of the barcode.
|
||||
* @param bundle The extras
|
||||
*/
|
||||
public void handleDecode(final Result rawResult, Bundle bundle) {
|
||||
inactivityTimer.onActivity();
|
||||
beepManager.playBeepSoundAndVibrate();
|
||||
|
||||
// 通过这种方式可以获取到扫描的图片
|
||||
// bundle.putInt("width", mCropRect.width());
|
||||
// bundle.putInt("height", mCropRect.height());
|
||||
// bundle.putString("result", rawResult.getText());
|
||||
//
|
||||
// startActivity(new Intent(CaptureActivity.this, ResultActivity.class)
|
||||
// .putExtras(bundle));
|
||||
|
||||
handler.postDelayed(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handleText(rawResult.getText());
|
||||
}
|
||||
}, 800);
|
||||
}
|
||||
|
||||
private void handleText(String text) {
|
||||
|
||||
if (StringUtils.isUrl(text)) {
|
||||
showUrlOption(text);
|
||||
} else {
|
||||
handleOtherText(text);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUrlOption(final String url) {
|
||||
if (url.contains("scan_login")) {
|
||||
showConfirmLogin(url);
|
||||
return;
|
||||
}
|
||||
if (url.contains("oschina.net")) {
|
||||
UIHelper.showUrlRedirect(CaptureActivity.this, url);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
DialogHelp.getConfirmDialog(this, "可能存在风险,是否打开链接?</br>" + url, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
UIHelper.showUrlRedirect(CaptureActivity.this, url);
|
||||
finish();
|
||||
}
|
||||
}, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
finish();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void showConfirmLogin(final String url) {
|
||||
if (!AppContext.getInstance().isLogin()) {
|
||||
showLogin();
|
||||
return;
|
||||
}
|
||||
DialogHelp.getConfirmDialog(this, "扫描成功,是否进行网页登陆", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
handleScanLogin(url);
|
||||
finish();
|
||||
}
|
||||
}, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
finish();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleScanLogin(final String url) {
|
||||
OSChinaApi.scanQrCodeLogin(url, new AsyncHttpResponseHandler() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
|
||||
ResultBean result = XmlUtils.toBean(ResultBean.class, arg2);
|
||||
if (result != null && result.getResult() != null
|
||||
&& result.getResult().OK()) {
|
||||
AppContext.showToast(result.getResult().getErrorMessage());
|
||||
finish();
|
||||
} else {
|
||||
handler.sendEmptyMessage(R.id.restart_preview);
|
||||
AppContext.showToast(result != null
|
||||
&& result.getResult() != null ? result.getResult()
|
||||
.getErrorMessage() : "登陆失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
|
||||
Throwable arg3) {
|
||||
handler.sendEmptyMessage(R.id.restart_preview);
|
||||
if (arg2 != null) {
|
||||
AppContext.showToast(new String(arg2));
|
||||
} else {
|
||||
AppContext.showToast("网页登陆失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
showWaitDialog("已扫描,正在登陆...");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
super.onFinish();
|
||||
hideWaitDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleOtherText(final String text) {
|
||||
// 判断是否符合基本的json格式
|
||||
if (!text.matches("^\\{.*")) {
|
||||
showCopyTextOption(text);
|
||||
} else {
|
||||
try {
|
||||
BarCode barcode = BarCode.parse(text);
|
||||
int type = barcode.getType();
|
||||
switch (type) {
|
||||
case BarCode.SIGN_IN:// 签到
|
||||
handleSignIn(barcode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (AppException e) {
|
||||
showCopyTextOption(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSignIn(BarCode barCode) {
|
||||
if (barCode.isRequireLogin() && !AppContext.getInstance().isLogin()) {
|
||||
showLogin();
|
||||
return;
|
||||
}
|
||||
showWaitDialog("正在签到...");
|
||||
AsyncHttpResponseHandler handler = new AsyncHttpResponseHandler() {
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
|
||||
try {
|
||||
SingInResult res = SingInResult.parse(new String(arg2));
|
||||
if (res.isOk()) {
|
||||
DialogHelp.getMessageDialog(CaptureActivity.this, res.getMessage()).show();
|
||||
} else {
|
||||
DialogHelp.getMessageDialog(CaptureActivity.this, res.getErrorMes()).show();
|
||||
}
|
||||
} catch (AppException e) {
|
||||
e.printStackTrace();
|
||||
onFailure(arg0, arg1, arg2, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
|
||||
Throwable arg3) {
|
||||
hideWaitDialog();
|
||||
DialogHelp.getMessageDialog(CaptureActivity.this, arg3.getMessage()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
super.onFinish();
|
||||
hideWaitDialog();
|
||||
}
|
||||
};
|
||||
OSChinaApi.singnIn(barCode.getUrl(), handler);
|
||||
}
|
||||
|
||||
private void showLogin() {
|
||||
DialogHelp.getConfirmDialog(this, "请先登录,再进行", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
UIHelper.showLoginActivity(CaptureActivity.this);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void showCopyTextOption(final String text) {
|
||||
DialogHelp.getConfirmDialog(this, text, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
ClipboardManager cbm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
cbm.setText(text);
|
||||
AppContext.showToast("复制成功");
|
||||
finish();
|
||||
}
|
||||
}, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
finish();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void initCamera(SurfaceHolder surfaceHolder) {
|
||||
if (cameraManager == null)
|
||||
return;
|
||||
|
||||
if (surfaceHolder == null) {
|
||||
throw new IllegalStateException("No SurfaceHolder provided");
|
||||
}
|
||||
if (cameraManager.isOpen()) {
|
||||
Log.w(TAG,
|
||||
"initCamera() while already open -- late SurfaceView callback?");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
cameraManager.openDriver(surfaceHolder);
|
||||
// Creating the handler starts the preview, which can also throw a
|
||||
// RuntimeException.
|
||||
if (handler == null) {
|
||||
handler = new CaptureActivityHandler(this, cameraManager,
|
||||
DecodeThread.ALL_MODE);
|
||||
}
|
||||
|
||||
initCrop();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
Log.w(TAG, "Unexpected error initializing camera", e);
|
||||
displayFrameworkBugMessageAndExit();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayFrameworkBugMessageAndExit() {
|
||||
Toast.makeText(this, R.string.permissions_camera_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void restartPreviewAfterDelay(long delayMS) {
|
||||
if (handler != null) {
|
||||
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect getCropRect() {
|
||||
return mCropRect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化截取的矩形区域
|
||||
*/
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private void initCrop() {
|
||||
int cameraWidth = cameraManager.getCameraResolution().y;
|
||||
int cameraHeight = cameraManager.getCameraResolution().x;
|
||||
|
||||
/** 获取布局中扫描框的位置信息 */
|
||||
int[] location = new int[2];
|
||||
scanCropView.getLocationInWindow(location);
|
||||
|
||||
int cropLeft = location[0];
|
||||
int cropTop = location[1] - getStatusBarHeight();
|
||||
|
||||
int cropWidth = scanCropView.getWidth();
|
||||
int cropHeight = scanCropView.getHeight();
|
||||
|
||||
/** 获取布局容器的宽高 */
|
||||
int containerWidth = scanContainer.getWidth();
|
||||
int containerHeight = scanContainer.getHeight();
|
||||
|
||||
/** 计算最终截取的矩形的左上角顶点x坐标 */
|
||||
int x = cropLeft * cameraWidth / containerWidth;
|
||||
/** 计算最终截取的矩形的左上角顶点y坐标 */
|
||||
int y = cropTop * cameraHeight / containerHeight;
|
||||
|
||||
/** 计算最终截取的矩形的宽度 */
|
||||
int width = cropWidth * cameraWidth / containerWidth;
|
||||
/** 计算最终截取的矩形的高度 */
|
||||
int height = cropHeight * cameraHeight / containerHeight;
|
||||
|
||||
/** 生成最终的截取的矩形 */
|
||||
mCropRect = new Rect(x, y, width + x, height + y);
|
||||
}
|
||||
|
||||
private int getStatusBarHeight() {
|
||||
try {
|
||||
Class<?> c = Class.forName("com.android.internal.R$dimen");
|
||||
Object obj = c.newInstance();
|
||||
Field field = c.getField("status_bar_height");
|
||||
int x = Integer.parseInt(field.get(obj).toString());
|
||||
return getResources().getDimensionPixelSize(x);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.capture_flash:
|
||||
light();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean flag;
|
||||
|
||||
private void light() {
|
||||
try {
|
||||
if (flag) {
|
||||
flag = false;
|
||||
// 开闪光灯
|
||||
cameraManager.openLight();
|
||||
mFlash.setBackgroundResource(R.mipmap.flash_open);
|
||||
} else {
|
||||
flag = true;
|
||||
// 关闪光灯
|
||||
cameraManager.offLight();
|
||||
mFlash.setBackgroundResource(R.mipmap.flash_default);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initView() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
}
|
||||
|
||||
private void initCamera() {
|
||||
scanContainer = (RelativeLayout) findViewById(R.id.capture_container);
|
||||
scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view);
|
||||
scanLine = (ImageView) findViewById(R.id.capture_scan_line);
|
||||
mFlash = (ImageView) findViewById(R.id.capture_flash);
|
||||
mFlash.setOnClickListener(this);
|
||||
|
||||
inactivityTimer = new InactivityTimer(this);
|
||||
beepManager = new BeepManager(this);
|
||||
|
||||
TranslateAnimation animation = new TranslateAnimation(
|
||||
Animation.RELATIVE_TO_PARENT, 0.0f,
|
||||
Animation.RELATIVE_TO_PARENT, 0.0f,
|
||||
Animation.RELATIVE_TO_PARENT, 0.0f,
|
||||
Animation.RELATIVE_TO_PARENT, 0.9f);
|
||||
animation.setDuration(4500);
|
||||
animation.setRepeatCount(-1);
|
||||
animation.setRepeatMode(Animation.RESTART);
|
||||
scanLine.startAnimation(animation);
|
||||
|
||||
cameraManager = new CameraManager(getApplication());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, List<String> perms) {
|
||||
if (perms != null && perms.size() == 2) {
|
||||
initCamera();
|
||||
}else{
|
||||
displayFrameworkBugMessageAndExit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, List<String> perms) {
|
||||
displayFrameworkBugMessageAndExit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
// EasyPermissions handles the request result.
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
||||
}
|
||||
|
||||
|
||||
private static final int CAMERA_PERM = 1;
|
||||
|
||||
@AfterPermissionGranted(CAMERA_PERM)
|
||||
private void cameraTask() {
|
||||
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.VIBRATE};
|
||||
if (EasyPermissions.hasPermissions(this, perms)) {
|
||||
initCamera();
|
||||
} else {
|
||||
// Request one permission
|
||||
EasyPermissions.requestPermissions(this,
|
||||
getResources().getString(R.string.str_request_camera_message),
|
||||
CAMERA_PERM, perms);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2012 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.camera;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.hardware.Camera;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
public class AutoFocusManager implements Camera.AutoFocusCallback {
|
||||
|
||||
private static final String TAG = AutoFocusManager.class.getSimpleName();
|
||||
|
||||
private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;
|
||||
private static final Collection<String> FOCUS_MODES_CALLING_AF;
|
||||
|
||||
static {
|
||||
FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
|
||||
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
|
||||
}
|
||||
|
||||
private boolean stopped;
|
||||
private boolean focusing;
|
||||
private final boolean useAutoFocus;
|
||||
private final Camera camera;
|
||||
private AsyncTask<?, ?, ?> outstandingTask;
|
||||
|
||||
public AutoFocusManager(Context context, Camera camera) {
|
||||
this.camera = camera;
|
||||
String currentFocusMode = camera.getParameters().getFocusMode();
|
||||
useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
|
||||
Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onAutoFocus(boolean success, Camera theCamera) {
|
||||
focusing = false;
|
||||
autoFocusAgainLater();
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private synchronized void autoFocusAgainLater() {
|
||||
if (!stopped && outstandingTask == null) {
|
||||
AutoFocusTask newTask = new AutoFocusTask();
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} else {
|
||||
newTask.execute();
|
||||
}
|
||||
outstandingTask = newTask;
|
||||
} catch (RejectedExecutionException ree) {
|
||||
Log.w(TAG, "Could not request auto focus", ree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (useAutoFocus) {
|
||||
outstandingTask = null;
|
||||
if (!stopped && !focusing) {
|
||||
try {
|
||||
camera.autoFocus(this);
|
||||
focusing = true;
|
||||
} catch (RuntimeException re) {
|
||||
// Have heard RuntimeException reported in Android 4.0.x+;
|
||||
// continue?
|
||||
Log.w(TAG, "Unexpected exception while focusing", re);
|
||||
// Try again later to keep cycle going
|
||||
autoFocusAgainLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void cancelOutstandingTask() {
|
||||
if (outstandingTask != null) {
|
||||
if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
|
||||
outstandingTask.cancel(true);
|
||||
}
|
||||
outstandingTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
stopped = true;
|
||||
if (useAutoFocus) {
|
||||
cancelOutstandingTask();
|
||||
// Doesn't hurt to call this even if not focusing
|
||||
try {
|
||||
camera.cancelAutoFocus();
|
||||
} catch (RuntimeException re) {
|
||||
// Have heard RuntimeException reported in Android 4.0.x+;
|
||||
// continue?
|
||||
Log.w(TAG, "Unexpected exception while cancelling focusing", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AutoFocusTask extends AsyncTask<Object, Object, Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... voids) {
|
||||
try {
|
||||
Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
|
||||
} catch (InterruptedException e) {
|
||||
// continue
|
||||
}
|
||||
start();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.Parameters;
|
||||
import android.hardware.Camera.Size;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.dtr.zxing.camera.open.OpenCameraInterface;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This object wraps the Camera service object and expects to be the only one
|
||||
* talking to it. The implementation encapsulates the steps needed to take
|
||||
* preview-sized images, which are used for both preview and decoding.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public class CameraManager {
|
||||
|
||||
private static final String TAG = CameraManager.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final CameraConfigurationManager configManager;
|
||||
private Camera camera;
|
||||
private AutoFocusManager autoFocusManager;
|
||||
private Parameters parameter;
|
||||
|
||||
private boolean initialized;
|
||||
private boolean previewing;
|
||||
private int requestedCameraId = -1;
|
||||
/**
|
||||
* Preview frames are delivered here, which we pass on to the registered
|
||||
* handler. Make sure to clear the handler so it will only receive one
|
||||
* message.
|
||||
*/
|
||||
private final PreviewCallback previewCallback;
|
||||
|
||||
public CameraManager(Context context) {
|
||||
this.context = context;
|
||||
this.configManager = new CameraConfigurationManager(context);
|
||||
previewCallback = new PreviewCallback(configManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the camera driver and initializes the hardware parameters.
|
||||
*
|
||||
* @param holder
|
||||
* The surface object which the camera will draw preview frames
|
||||
* into.
|
||||
* @throws java.io.IOException
|
||||
* Indicates the camera driver failed to open.
|
||||
*/
|
||||
public synchronized void openDriver(SurfaceHolder holder)
|
||||
throws IOException {
|
||||
Camera theCamera = camera;
|
||||
if (theCamera == null) {
|
||||
|
||||
if (requestedCameraId >= 0) {
|
||||
theCamera = OpenCameraInterface.open(requestedCameraId);
|
||||
} else {
|
||||
theCamera = OpenCameraInterface.open();
|
||||
}
|
||||
|
||||
if (theCamera == null) {
|
||||
throw new IOException();
|
||||
}
|
||||
camera = theCamera;
|
||||
}
|
||||
theCamera.setPreviewDisplay(holder);
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
configManager.initFromCameraParameters(theCamera);
|
||||
}
|
||||
|
||||
Camera.Parameters parameters = theCamera.getParameters();
|
||||
String parametersFlattened = parameters == null ? null : parameters
|
||||
.flatten(); // Save
|
||||
// these,
|
||||
// temporarily
|
||||
try {
|
||||
configManager.setDesiredCameraParameters(theCamera, false);
|
||||
} catch (RuntimeException re) {
|
||||
// Driver failed
|
||||
Log.w(TAG,
|
||||
"Camera rejected parameters. Setting only minimal safe-mode parameters");
|
||||
Log.i(TAG, "Resetting to saved camera params: "
|
||||
+ parametersFlattened);
|
||||
// Reset:
|
||||
if (parametersFlattened != null) {
|
||||
parameters = theCamera.getParameters();
|
||||
parameters.unflatten(parametersFlattened);
|
||||
try {
|
||||
theCamera.setParameters(parameters);
|
||||
configManager.setDesiredCameraParameters(theCamera, true);
|
||||
} catch (RuntimeException re2) {
|
||||
// Well, darn. Give up
|
||||
Log.w(TAG,
|
||||
"Camera rejected even safe-mode parameters! No configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public synchronized boolean isOpen() {
|
||||
return camera != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the camera driver if still in use.
|
||||
*/
|
||||
public synchronized void closeDriver() {
|
||||
if (camera != null) {
|
||||
camera.release();
|
||||
camera = null;
|
||||
// Make sure to clear these each time we close the camera, so that
|
||||
// any scanning rect
|
||||
// requested by intent is forgotten.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the camera hardware to begin drawing preview frames to the screen.
|
||||
*/
|
||||
public synchronized void startPreview() {
|
||||
Camera theCamera = camera;
|
||||
if (theCamera != null && !previewing) {
|
||||
theCamera.startPreview();
|
||||
previewing = true;
|
||||
autoFocusManager = new AutoFocusManager(context, camera);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the camera to stop drawing preview frames.
|
||||
*/
|
||||
public synchronized void stopPreview() {
|
||||
if (autoFocusManager != null) {
|
||||
autoFocusManager.stop();
|
||||
autoFocusManager = null;
|
||||
}
|
||||
if (camera != null && previewing) {
|
||||
camera.stopPreview();
|
||||
previewCallback.setHandler(null, 0);
|
||||
previewing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single preview frame will be returned to the handler supplied. The data
|
||||
* will arrive as byte[] in the message.obj field, with width and height
|
||||
* encoded as message.arg1 and message.arg2, respectively.
|
||||
*
|
||||
* @param handler
|
||||
* The handler to send the message to.
|
||||
* @param message
|
||||
* The what field of the message to be sent.
|
||||
*/
|
||||
public synchronized void requestPreviewFrame(Handler handler, int message) {
|
||||
Camera theCamera = camera;
|
||||
if (theCamera != null && previewing) {
|
||||
previewCallback.setHandler(handler, message);
|
||||
theCamera.setOneShotPreviewCallback(previewCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows third party apps to specify the camera ID, rather than determine
|
||||
* it automatically based on available cameras and their orientation.
|
||||
*
|
||||
* @param cameraId
|
||||
* camera ID of the camera to use. A negative value means
|
||||
* "no preference".
|
||||
*/
|
||||
public synchronized void setManualCameraId(int cameraId) {
|
||||
requestedCameraId = cameraId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相机分辨率
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Point getCameraResolution() {
|
||||
return configManager.getCameraResolution();
|
||||
}
|
||||
|
||||
public Size getPreviewSize() {
|
||||
if (null != camera) {
|
||||
return camera.getParameters().getPreviewSize();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void openLight() {
|
||||
if (camera != null) {
|
||||
parameter = camera.getParameters();
|
||||
parameter.setFlashMode(Parameters.FLASH_MODE_TORCH);
|
||||
camera.setParameters(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public void offLight() {
|
||||
if (camera != null) {
|
||||
parameter = camera.getParameters();
|
||||
parameter.setFlashMode(Parameters.FLASH_MODE_OFF);
|
||||
camera.setParameters(parameter);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.camera;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
public class PreviewCallback implements Camera.PreviewCallback {
|
||||
|
||||
private static final String TAG = PreviewCallback.class.getSimpleName();
|
||||
|
||||
private final CameraConfigurationManager configManager;
|
||||
private Handler previewHandler;
|
||||
private int previewMessage;
|
||||
|
||||
public PreviewCallback(CameraConfigurationManager configManager) {
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
public void setHandler(Handler previewHandler, int previewMessage) {
|
||||
this.previewHandler = previewHandler;
|
||||
this.previewMessage = previewMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
Point cameraResolution = configManager.getCameraResolution();
|
||||
Handler thePreviewHandler = previewHandler;
|
||||
if (cameraResolution != null && thePreviewHandler != null) {
|
||||
Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x, cameraResolution.y, data);
|
||||
message.sendToTarget();
|
||||
previewHandler = null;
|
||||
} else {
|
||||
Log.d(TAG, "Got preview callback, but no handler or resolution available");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2012 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.camera.open;
|
||||
|
||||
import android.hardware.Camera;
|
||||
import android.util.Log;
|
||||
|
||||
public class OpenCameraInterface {
|
||||
|
||||
private static final String TAG = OpenCameraInterface.class.getName();
|
||||
|
||||
/**
|
||||
* Opens the requested camera with {@link android.hardware.Camera#open(int)}, if one exists.
|
||||
*
|
||||
* @param cameraId
|
||||
* camera ID of the camera to use. A negative value means
|
||||
* "no preference"
|
||||
* @return handle to {@link android.hardware.Camera} that was opened
|
||||
*/
|
||||
public static Camera open(int cameraId) {
|
||||
|
||||
int numCameras = Camera.getNumberOfCameras();
|
||||
if (numCameras == 0) {
|
||||
Log.w(TAG, "No cameras!");
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean explicitRequest = cameraId >= 0;
|
||||
|
||||
if (!explicitRequest) {
|
||||
// Select a camera if no explicit camera requested
|
||||
int index = 0;
|
||||
while (index < numCameras) {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(index, cameraInfo);
|
||||
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
cameraId = index;
|
||||
}
|
||||
|
||||
Camera camera;
|
||||
if (cameraId < numCameras) {
|
||||
Log.i(TAG, "Opening camera #" + cameraId);
|
||||
camera = Camera.open(cameraId);
|
||||
} else {
|
||||
if (explicitRequest) {
|
||||
Log.w(TAG, "Requested camera does not exist: " + cameraId);
|
||||
camera = null;
|
||||
} else {
|
||||
Log.i(TAG, "No camera facing back; returning camera #0");
|
||||
camera = Camera.open(0);
|
||||
}
|
||||
}
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a rear-facing camera with {@link android.hardware.Camera#open(int)}, if one exists,
|
||||
* or opens camera 0.
|
||||
*
|
||||
* @return handle to {@link android.hardware.Camera} that was opened
|
||||
*/
|
||||
public static Camera open() {
|
||||
return open(-1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.decode;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class DecodeFormatManager {
|
||||
|
||||
// 1D解码
|
||||
private static final Set<BarcodeFormat> PRODUCT_FORMATS;
|
||||
private static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
|
||||
private static final Set<BarcodeFormat> ONE_D_FORMATS;
|
||||
|
||||
// 二维码解码
|
||||
private static final Set<BarcodeFormat> QR_CODE_FORMATS;
|
||||
|
||||
static {
|
||||
PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A, BarcodeFormat.UPC_E, BarcodeFormat.EAN_13, BarcodeFormat.EAN_8, BarcodeFormat.RSS_14, BarcodeFormat.RSS_EXPANDED);
|
||||
INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39, BarcodeFormat.CODE_93, BarcodeFormat.CODE_128, BarcodeFormat.ITF, BarcodeFormat.CODABAR);
|
||||
ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);
|
||||
ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);
|
||||
|
||||
QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
|
||||
}
|
||||
|
||||
public static Collection<BarcodeFormat> getQrCodeFormats() {
|
||||
return QR_CODE_FORMATS;
|
||||
}
|
||||
|
||||
public static Collection<BarcodeFormat> getBarCodeFormats() {
|
||||
return ONE_D_FORMATS;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.decode;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.dtr.zxing.activity.CaptureActivity;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* This thread does all the heavy lifting of decoding the images.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public class DecodeThread extends Thread {
|
||||
|
||||
public static final String BARCODE_BITMAP = "barcode_bitmap";
|
||||
|
||||
public static final int BARCODE_MODE = 0X100;
|
||||
public static final int QRCODE_MODE = 0X200;
|
||||
public static final int ALL_MODE = 0X300;
|
||||
|
||||
private final CaptureActivity activity;
|
||||
private final Map<DecodeHintType, Object> hints;
|
||||
private Handler handler;
|
||||
private final CountDownLatch handlerInitLatch;
|
||||
|
||||
public DecodeThread(CaptureActivity activity, int decodeMode) {
|
||||
|
||||
this.activity = activity;
|
||||
handlerInitLatch = new CountDownLatch(1);
|
||||
|
||||
hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
|
||||
|
||||
Collection<BarcodeFormat> decodeFormats = new ArrayList<BarcodeFormat>();
|
||||
decodeFormats.addAll(EnumSet.of(BarcodeFormat.AZTEC));
|
||||
decodeFormats.addAll(EnumSet.of(BarcodeFormat.PDF_417));
|
||||
|
||||
switch (decodeMode) {
|
||||
case BARCODE_MODE:
|
||||
decodeFormats.addAll(DecodeFormatManager.getBarCodeFormats());
|
||||
break;
|
||||
|
||||
case QRCODE_MODE:
|
||||
decodeFormats.addAll(DecodeFormatManager.getQrCodeFormats());
|
||||
break;
|
||||
|
||||
case ALL_MODE:
|
||||
decodeFormats.addAll(DecodeFormatManager.getBarCodeFormats());
|
||||
decodeFormats.addAll(DecodeFormatManager.getQrCodeFormats());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
try {
|
||||
handlerInitLatch.await();
|
||||
} catch (InterruptedException ie) {
|
||||
// continue?
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
handler = new DecodeHandler(activity, hints);
|
||||
handlerInitLatch.countDown();
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import com.dtr.zxing.activity.CaptureActivity;
|
||||
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.oschina.app.R;
|
||||
|
||||
/**
|
||||
* Manages beeps and vibrations for {@link CaptureActivity}.
|
||||
*/
|
||||
public class BeepManager implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, Closeable {
|
||||
|
||||
private static final String TAG = BeepManager.class.getSimpleName();
|
||||
|
||||
private static final float BEEP_VOLUME = 0.10f;
|
||||
private static final long VIBRATE_DURATION = 200L;
|
||||
|
||||
private final Activity activity;
|
||||
private MediaPlayer mediaPlayer;
|
||||
private boolean playBeep;
|
||||
private boolean vibrate;
|
||||
|
||||
public BeepManager(Activity activity) {
|
||||
this.activity = activity;
|
||||
this.mediaPlayer = null;
|
||||
updatePrefs();
|
||||
}
|
||||
|
||||
private synchronized void updatePrefs() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
playBeep = shouldBeep(prefs, activity);
|
||||
vibrate = true;
|
||||
if (playBeep && mediaPlayer == null) {
|
||||
// The volume on STREAM_SYSTEM is not adjustable, and users found it
|
||||
// too loud,
|
||||
// so we now play on the music stream.
|
||||
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
mediaPlayer = buildMediaPlayer(activity);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void playBeepSoundAndVibrate() {
|
||||
if (playBeep && mediaPlayer != null) {
|
||||
mediaPlayer.start();
|
||||
}
|
||||
if (vibrate) {
|
||||
Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(VIBRATE_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
|
||||
boolean shouldPlayBeep = true;
|
||||
if (shouldPlayBeep) {
|
||||
// See if sound settings overrides this
|
||||
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
|
||||
shouldPlayBeep = false;
|
||||
}
|
||||
}
|
||||
return shouldPlayBeep;
|
||||
}
|
||||
|
||||
private MediaPlayer buildMediaPlayer(Context activity) {
|
||||
MediaPlayer mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mediaPlayer.setOnCompletionListener(this);
|
||||
mediaPlayer.setOnErrorListener(this);
|
||||
try {
|
||||
AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.qr_sacn);
|
||||
try {
|
||||
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
|
||||
} finally {
|
||||
file.close();
|
||||
}
|
||||
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
|
||||
mediaPlayer.prepare();
|
||||
return mediaPlayer;
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, ioe);
|
||||
mediaPlayer.release();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
// When the beep has finished playing, rewind to queue up another one.
|
||||
mp.seekTo(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
|
||||
// we are finished, so put up an appropriate error toast if required
|
||||
// and finish
|
||||
activity.finish();
|
||||
} else {
|
||||
// possibly media player error, so release and recreate
|
||||
mp.release();
|
||||
mediaPlayer = null;
|
||||
updatePrefs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.utils;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
import com.dtr.zxing.activity.CaptureActivity;
|
||||
import com.dtr.zxing.camera.CameraManager;
|
||||
import com.dtr.zxing.decode.DecodeThread;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
|
||||
/**
|
||||
* This class handles all the messaging which comprises the state machine for
|
||||
* capture.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public class CaptureActivityHandler extends Handler {
|
||||
|
||||
private final CaptureActivity activity;
|
||||
private final DecodeThread decodeThread;
|
||||
private final CameraManager cameraManager;
|
||||
private State state;
|
||||
|
||||
private enum State {
|
||||
PREVIEW, SUCCESS, DONE
|
||||
}
|
||||
|
||||
public CaptureActivityHandler(CaptureActivity activity, CameraManager cameraManager, int decodeMode) {
|
||||
this.activity = activity;
|
||||
decodeThread = new DecodeThread(activity, decodeMode);
|
||||
decodeThread.start();
|
||||
state = State.SUCCESS;
|
||||
|
||||
// Start ourselves capturing previews and decoding.
|
||||
this.cameraManager = cameraManager;
|
||||
cameraManager.startPreview();
|
||||
restartPreviewAndDecode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case R.id.restart_preview:
|
||||
restartPreviewAndDecode();
|
||||
break;
|
||||
case R.id.decode_succeeded:
|
||||
state = State.SUCCESS;
|
||||
Bundle bundle = message.getData();
|
||||
|
||||
activity.handleDecode((Result) message.obj, bundle);
|
||||
break;
|
||||
case R.id.decode_failed:
|
||||
// We're decoding as fast as possible, so when one decode fails,
|
||||
// start another.
|
||||
state = State.PREVIEW;
|
||||
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
||||
break;
|
||||
case R.id.return_scan_result:
|
||||
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
|
||||
activity.finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void quitSynchronously() {
|
||||
state = State.DONE;
|
||||
cameraManager.stopPreview();
|
||||
Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
|
||||
quit.sendToTarget();
|
||||
try {
|
||||
// Wait at most half a second; should be enough time, and onPause()
|
||||
// will timeout quickly
|
||||
decodeThread.join(500L);
|
||||
} catch (InterruptedException e) {
|
||||
// continue
|
||||
}
|
||||
|
||||
// Be absolutely sure we don't send any queued up messages
|
||||
removeMessages(R.id.decode_succeeded);
|
||||
removeMessages(R.id.decode_failed);
|
||||
}
|
||||
|
||||
private void restartPreviewAndDecode() {
|
||||
if (state == State.SUCCESS) {
|
||||
state = State.PREVIEW;
|
||||
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.dtr.zxing.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Finishes an activity after a period of inactivity if the device is on battery
|
||||
* power.
|
||||
*/
|
||||
public class InactivityTimer {
|
||||
|
||||
private static final String TAG = InactivityTimer.class.getSimpleName();
|
||||
|
||||
private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
|
||||
|
||||
private Activity activity;
|
||||
private BroadcastReceiver powerStatusReceiver;
|
||||
private boolean registered;
|
||||
private AsyncTask<Object, Object, Object> inactivityTask;
|
||||
|
||||
public InactivityTimer(Activity activity) {
|
||||
this.activity = activity;
|
||||
powerStatusReceiver = new PowerStatusReceiver();
|
||||
registered = false;
|
||||
onActivity();
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public synchronized void onActivity() {
|
||||
cancel();
|
||||
inactivityTask = new InactivityAsyncTask();
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} else {
|
||||
inactivityTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void onPause() {
|
||||
cancel();
|
||||
if (registered) {
|
||||
activity.unregisterReceiver(powerStatusReceiver);
|
||||
registered = false;
|
||||
} else {
|
||||
Log.w(TAG, "PowerStatusReceiver was never registered?");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void onResume() {
|
||||
if (registered) {
|
||||
Log.w(TAG, "PowerStatusReceiver was already registered?");
|
||||
} else {
|
||||
activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
registered = true;
|
||||
}
|
||||
onActivity();
|
||||
}
|
||||
|
||||
private synchronized void cancel() {
|
||||
AsyncTask<?, ?, ?> task = inactivityTask;
|
||||
if (task != null) {
|
||||
task.cancel(true);
|
||||
inactivityTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
cancel();
|
||||
}
|
||||
|
||||
private class PowerStatusReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
|
||||
// 0 indicates that we're on battery
|
||||
boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
|
||||
if (onBatteryNow) {
|
||||
InactivityTimer.this.onActivity();
|
||||
} else {
|
||||
InactivityTimer.this.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InactivityAsyncTask extends AsyncTask<Object, Object, Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... objects) {
|
||||
try {
|
||||
Thread.sleep(INACTIVITY_DELAY_MS);
|
||||
Log.i(TAG, "Finishing activity due to inactivity");
|
||||
activity.finish();
|
||||
} catch (InterruptedException e) {
|
||||
// continue without killing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.ViewAnimator;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class AccessibleDateAnimator extends ViewAnimator {
|
||||
private long mDateMillis;
|
||||
|
||||
public AccessibleDateAnimator(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setDateMillis(long dateMillis) {
|
||||
mDateMillis = dateMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce the currently-selected date when launched.
|
||||
*/
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
// Clear the event's current text so that only the current date will
|
||||
// be spoken.
|
||||
event.getText().clear();
|
||||
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
|
||||
| DateUtils.FORMAT_SHOW_WEEKDAY;
|
||||
|
||||
String dateString = DateUtils.formatDateTime(getContext(),
|
||||
mDateMillis, flags);
|
||||
event.getText().add(dateString);
|
||||
return true;
|
||||
}
|
||||
return super.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
abstract interface DatePickerController {
|
||||
public abstract int getFirstDayOfWeek();
|
||||
|
||||
public abstract int getMaxYear();
|
||||
|
||||
public abstract int getMinYear();
|
||||
|
||||
public abstract SimpleMonthAdapter.CalendarDay getSelectedDay();
|
||||
|
||||
public abstract void onDayOfMonthSelected(int year, int month, int day);
|
||||
|
||||
public abstract void onYearSelected(int year);
|
||||
|
||||
public abstract void registerOnDateChangedListener(
|
||||
DatePickerDialog.OnDateChangedListener onDateChangedListener);
|
||||
|
||||
public abstract void tryVibrate();
|
||||
}
|
@ -0,0 +1,477 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Vibrator;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class DatePickerDialog extends DialogFragment implements
|
||||
View.OnClickListener, DatePickerController {
|
||||
|
||||
private static final String KEY_SELECTED_YEAR = "year";
|
||||
private static final String KEY_SELECTED_MONTH = "month";
|
||||
private static final String KEY_SELECTED_DAY = "day";
|
||||
private static final String KEY_VIBRATE = "vibrate";
|
||||
|
||||
// https://code.google.com/p/android/issues/detail?id=13050
|
||||
private static final int MAX_YEAR = 2037;
|
||||
private static final int MIN_YEAR = 1902;
|
||||
|
||||
private static final int UNINITIALIZED = -1;
|
||||
private static final int MONTH_AND_DAY_VIEW = 0;
|
||||
private static final int YEAR_VIEW = 1;
|
||||
|
||||
public static final int ANIMATION_DELAY = 500;
|
||||
public static final String KEY_WEEK_START = "week_start";
|
||||
public static final String KEY_YEAR_START = "year_start";
|
||||
public static final String KEY_YEAR_END = "year_end";
|
||||
public static final String KEY_CURRENT_VIEW = "current_view";
|
||||
public static final String KEY_LIST_POSITION = "list_position";
|
||||
public static final String KEY_LIST_POSITION_OFFSET = "list_position_offset";
|
||||
|
||||
private static SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("dd",
|
||||
Locale.getDefault());
|
||||
private static SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy",
|
||||
Locale.getDefault());
|
||||
private final DateFormatSymbols mDateFormatSymbols = new DateFormatSymbols();
|
||||
|
||||
private final Calendar mCalendar = Calendar.getInstance();
|
||||
private final HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
|
||||
private OnDateSetListener mCallBack;
|
||||
|
||||
private AccessibleDateAnimator mAnimator;
|
||||
private boolean mDelayAnimation = true;
|
||||
private long mLastVibrate;
|
||||
private int mCurrentView = UNINITIALIZED;
|
||||
|
||||
private int mWeekStart = mCalendar.getFirstDayOfWeek();
|
||||
private int mMaxYear = MAX_YEAR;
|
||||
private int mMinYear = MIN_YEAR;
|
||||
|
||||
private String mDayPickerDescription;
|
||||
private String mYearPickerDescription;
|
||||
private String mSelectDay;
|
||||
private String mSelectYear;
|
||||
|
||||
private TextView mDayOfWeekView;
|
||||
private DayPickerView mDayPickerView;
|
||||
private Button mDoneButton;
|
||||
private LinearLayout mMonthAndDayView;
|
||||
private TextView mSelectedDayTextView;
|
||||
private TextView mSelectedMonthTextView;
|
||||
private Vibrator mVibrator;
|
||||
private YearPickerView mYearPickerView;
|
||||
private TextView mYearView;
|
||||
|
||||
private boolean mVibrate = true;
|
||||
private boolean mCloseOnSingleTapDay;
|
||||
|
||||
private void adjustDayInMonthIfNeeded(int month, int year) {
|
||||
int day = mCalendar.get(Calendar.DAY_OF_MONTH);
|
||||
int daysInMonth = Utils.getDaysInMonth(month, year);
|
||||
if (day > daysInMonth) {
|
||||
mCalendar.set(Calendar.DAY_OF_MONTH, daysInMonth);
|
||||
}
|
||||
}
|
||||
|
||||
public DatePickerDialog() {
|
||||
// Empty constructor required for dialog fragment. DO NOT REMOVE
|
||||
}
|
||||
|
||||
public static DatePickerDialog newInstance(
|
||||
OnDateSetListener onDateSetListener, int year, int month, int day) {
|
||||
return newInstance(onDateSetListener, year, month, day, true);
|
||||
}
|
||||
|
||||
public static DatePickerDialog newInstance(
|
||||
OnDateSetListener onDateSetListener, int year, int month, int day,
|
||||
boolean vibrate) {
|
||||
DatePickerDialog datePickerDialog = new DatePickerDialog();
|
||||
datePickerDialog.initialize(onDateSetListener, year, month, day,
|
||||
vibrate);
|
||||
return datePickerDialog;
|
||||
}
|
||||
|
||||
public void setVibrate(boolean vibrate) {
|
||||
mVibrate = vibrate;
|
||||
}
|
||||
|
||||
private void setCurrentView(int currentView) {
|
||||
setCurrentView(currentView, false);
|
||||
}
|
||||
|
||||
private void setCurrentView(int currentView, boolean forceRefresh) {
|
||||
long timeInMillis = mCalendar.getTimeInMillis();
|
||||
switch (currentView) {
|
||||
case MONTH_AND_DAY_VIEW:
|
||||
ObjectAnimator monthDayAnim = Utils.getPulseAnimator(
|
||||
mMonthAndDayView, 0.9F, 1.05F);
|
||||
if (mDelayAnimation) {
|
||||
monthDayAnim.setStartDelay(ANIMATION_DELAY);
|
||||
mDelayAnimation = false;
|
||||
}
|
||||
mDayPickerView.onDateChanged();
|
||||
if (mCurrentView != currentView || forceRefresh) {
|
||||
mMonthAndDayView.setSelected(true);
|
||||
mYearView.setSelected(false);
|
||||
mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
|
||||
mCurrentView = currentView;
|
||||
}
|
||||
monthDayAnim.start();
|
||||
String monthDayDesc = DateUtils.formatDateTime(getActivity(),
|
||||
timeInMillis, DateUtils.FORMAT_SHOW_DATE);
|
||||
mAnimator.setContentDescription(mDayPickerDescription + ": "
|
||||
+ monthDayDesc);
|
||||
Utils.tryAccessibilityAnnounce(mAnimator, mSelectDay);
|
||||
break;
|
||||
case YEAR_VIEW:
|
||||
ObjectAnimator yearAnim = Utils.getPulseAnimator(mYearView, 0.85F,
|
||||
1.1F);
|
||||
if (mDelayAnimation) {
|
||||
yearAnim.setStartDelay(ANIMATION_DELAY);
|
||||
mDelayAnimation = false;
|
||||
}
|
||||
mYearPickerView.onDateChanged();
|
||||
if (mCurrentView != currentView || forceRefresh) {
|
||||
mMonthAndDayView.setSelected(false);
|
||||
mYearView.setSelected(true);
|
||||
mAnimator.setDisplayedChild(YEAR_VIEW);
|
||||
mCurrentView = currentView;
|
||||
}
|
||||
yearAnim.start();
|
||||
String dayDesc = YEAR_FORMAT.format(timeInMillis);
|
||||
mAnimator.setContentDescription(mYearPickerDescription + ": "
|
||||
+ dayDesc);
|
||||
Utils.tryAccessibilityAnnounce(mAnimator, mSelectYear);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay(boolean announce) {
|
||||
/*
|
||||
* if (mDayOfWeekView != null) {
|
||||
* mDayOfWeekView.setText(mCalendar.getDisplayName(Calendar.DAY_OF_WEEK,
|
||||
* Calendar.LONG,
|
||||
* Locale.getDefault()).toUpperCase(Locale.getDefault())); }
|
||||
*
|
||||
* mSelectedMonthTextView.setText(mCalendar.getDisplayName(Calendar.MONTH
|
||||
* , Calendar.SHORT,
|
||||
* Locale.getDefault()).toUpperCase(Locale.getDefault()));
|
||||
*/
|
||||
|
||||
if (this.mDayOfWeekView != null) {
|
||||
this.mCalendar.setFirstDayOfWeek(mWeekStart);
|
||||
this.mDayOfWeekView
|
||||
.setText(mDateFormatSymbols.getWeekdays()[this.mCalendar
|
||||
.get(Calendar.DAY_OF_WEEK)].toUpperCase(Locale
|
||||
.getDefault()));
|
||||
}
|
||||
|
||||
this.mSelectedMonthTextView
|
||||
.setText(mDateFormatSymbols.getMonths()[this.mCalendar
|
||||
.get(Calendar.MONTH)].toUpperCase(Locale.getDefault()));
|
||||
|
||||
mSelectedDayTextView.setText(DAY_FORMAT.format(mCalendar.getTime()));
|
||||
mYearView.setText(YEAR_FORMAT.format(mCalendar.getTime()));
|
||||
|
||||
// Accessibility.
|
||||
long millis = mCalendar.getTimeInMillis();
|
||||
mAnimator.setDateMillis(millis);
|
||||
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
|
||||
String monthAndDayText = DateUtils.formatDateTime(getActivity(),
|
||||
millis, flags);
|
||||
mMonthAndDayView.setContentDescription(monthAndDayText);
|
||||
|
||||
if (announce) {
|
||||
flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
|
||||
String fullDateText = DateUtils.formatDateTime(getActivity(),
|
||||
millis, flags);
|
||||
Utils.tryAccessibilityAnnounce(mAnimator, fullDateText);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePickers() {
|
||||
Iterator<OnDateChangedListener> iterator = mListeners.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next().onDateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstDayOfWeek() {
|
||||
return mWeekStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxYear() {
|
||||
return mMaxYear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinYear() {
|
||||
return mMinYear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleMonthAdapter.CalendarDay getSelectedDay() {
|
||||
return new SimpleMonthAdapter.CalendarDay(mCalendar);
|
||||
}
|
||||
|
||||
public void initialize(OnDateSetListener onDateSetListener, int year,
|
||||
int month, int day, boolean vibrate) {
|
||||
if (year > MAX_YEAR)
|
||||
throw new IllegalArgumentException("year end must < " + MAX_YEAR);
|
||||
if (year < MIN_YEAR)
|
||||
throw new IllegalArgumentException("year end must > " + MIN_YEAR);
|
||||
mCallBack = onDateSetListener;
|
||||
mCalendar.set(Calendar.YEAR, year);
|
||||
mCalendar.set(Calendar.MONTH, month);
|
||||
mCalendar.set(Calendar.DAY_OF_MONTH, day);
|
||||
mVibrate = vibrate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
tryVibrate();
|
||||
if (view.getId() == R.id.date_picker_year)
|
||||
setCurrentView(YEAR_VIEW);
|
||||
else if (view.getId() == R.id.date_picker_month_and_day)
|
||||
setCurrentView(MONTH_AND_DAY_VIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
Activity activity = getActivity();
|
||||
activity.getWindow().setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
mVibrator = ((Vibrator) activity.getSystemService("vibrator"));
|
||||
if (bundle != null) {
|
||||
mCalendar.set(Calendar.YEAR, bundle.getInt(KEY_SELECTED_YEAR));
|
||||
mCalendar.set(Calendar.MONTH, bundle.getInt(KEY_SELECTED_MONTH));
|
||||
mCalendar.set(Calendar.DAY_OF_MONTH,
|
||||
bundle.getInt(KEY_SELECTED_DAY));
|
||||
mVibrate = bundle.getBoolean(KEY_VIBRATE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater layoutInflater, ViewGroup parent,
|
||||
Bundle bundle) {
|
||||
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
View view = layoutInflater.inflate(R.layout.date_picker_dialog, null);
|
||||
|
||||
mDayOfWeekView = ((TextView) view.findViewById(R.id.date_picker_header));
|
||||
mMonthAndDayView = ((LinearLayout) view
|
||||
.findViewById(R.id.date_picker_month_and_day));
|
||||
mMonthAndDayView.setOnClickListener(this);
|
||||
mSelectedMonthTextView = ((TextView) view
|
||||
.findViewById(R.id.date_picker_month));
|
||||
mSelectedDayTextView = ((TextView) view
|
||||
.findViewById(R.id.date_picker_day));
|
||||
mYearView = ((TextView) view.findViewById(R.id.date_picker_year));
|
||||
mYearView.setOnClickListener(this);
|
||||
|
||||
int listPosition = -1;
|
||||
int currentView = MONTH_AND_DAY_VIEW;
|
||||
int listPositionOffset = 0;
|
||||
if (bundle != null) {
|
||||
mWeekStart = bundle.getInt(KEY_WEEK_START);
|
||||
mMinYear = bundle.getInt(KEY_YEAR_START);
|
||||
mMaxYear = bundle.getInt(KEY_YEAR_END);
|
||||
currentView = bundle.getInt(KEY_CURRENT_VIEW);
|
||||
listPosition = bundle.getInt(KEY_LIST_POSITION);
|
||||
listPositionOffset = bundle.getInt(KEY_LIST_POSITION_OFFSET);
|
||||
}
|
||||
|
||||
Activity activity = getActivity();
|
||||
mDayPickerView = new DayPickerView(activity, this);
|
||||
mYearPickerView = new YearPickerView(activity, this);
|
||||
|
||||
mDayPickerDescription = "按月份划分的日期网格";
|
||||
mSelectDay = "选择月份和日期";
|
||||
mYearPickerDescription = "年份列表";
|
||||
mSelectYear = "选择年份";
|
||||
|
||||
mAnimator = ((AccessibleDateAnimator) view.findViewById(R.id.animator));
|
||||
mAnimator.addView(mDayPickerView);
|
||||
mAnimator.addView(mYearPickerView);
|
||||
mAnimator.setDateMillis(mCalendar.getTimeInMillis());
|
||||
|
||||
AlphaAnimation inAlphaAnimation = new AlphaAnimation(0.0F, 1.0F);
|
||||
inAlphaAnimation.setDuration(300L);
|
||||
mAnimator.setInAnimation(inAlphaAnimation);
|
||||
|
||||
AlphaAnimation outAlphaAnimation = new AlphaAnimation(1.0F, 0.0F);
|
||||
outAlphaAnimation.setDuration(300L);
|
||||
mAnimator.setOutAnimation(outAlphaAnimation);
|
||||
|
||||
mDoneButton = ((Button) view.findViewById(R.id.done));
|
||||
mDoneButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onDoneButtonClick();
|
||||
}
|
||||
});
|
||||
|
||||
updateDisplay(false);
|
||||
setCurrentView(currentView, true);
|
||||
|
||||
if (listPosition != -1) {
|
||||
if (currentView == MONTH_AND_DAY_VIEW) {
|
||||
mDayPickerView.postSetSelection(listPosition);
|
||||
}
|
||||
if (currentView == YEAR_VIEW) {
|
||||
mYearPickerView.postSetSelectionFromTop(listPosition,
|
||||
listPositionOffset);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
private void onDoneButtonClick() {
|
||||
tryVibrate();
|
||||
if (mCallBack != null) {
|
||||
mCallBack.onDateSet(this, mCalendar.get(Calendar.YEAR),
|
||||
mCalendar.get(Calendar.MONTH),
|
||||
mCalendar.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDayOfMonthSelected(int year, int month, int day) {
|
||||
mCalendar.set(Calendar.YEAR, year);
|
||||
mCalendar.set(Calendar.MONTH, month);
|
||||
mCalendar.set(Calendar.DAY_OF_MONTH, day);
|
||||
updatePickers();
|
||||
updateDisplay(true);
|
||||
|
||||
if (mCloseOnSingleTapDay) {
|
||||
onDoneButtonClick();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
super.onSaveInstanceState(bundle);
|
||||
bundle.putInt(KEY_SELECTED_YEAR, mCalendar.get(Calendar.YEAR));
|
||||
bundle.putInt(KEY_SELECTED_MONTH, mCalendar.get(Calendar.MONTH));
|
||||
bundle.putInt(KEY_SELECTED_DAY, mCalendar.get(Calendar.DAY_OF_MONTH));
|
||||
bundle.putInt(KEY_WEEK_START, mWeekStart);
|
||||
bundle.putInt(KEY_YEAR_START, mMinYear);
|
||||
bundle.putInt(KEY_YEAR_END, mMaxYear);
|
||||
bundle.putInt(KEY_CURRENT_VIEW, mCurrentView);
|
||||
|
||||
int listPosition = -1;
|
||||
if (mCurrentView == 0) {
|
||||
listPosition = mDayPickerView.getMostVisiblePosition();
|
||||
}
|
||||
if (mCurrentView == 1) {
|
||||
listPosition = mYearPickerView.getFirstVisiblePosition();
|
||||
bundle.putInt(KEY_LIST_POSITION_OFFSET,
|
||||
mYearPickerView.getFirstPositionOffset());
|
||||
}
|
||||
bundle.putInt(KEY_LIST_POSITION, listPosition);
|
||||
bundle.putBoolean(KEY_VIBRATE, mVibrate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onYearSelected(int year) {
|
||||
adjustDayInMonthIfNeeded(mCalendar.get(Calendar.MONTH), year);
|
||||
mCalendar.set(Calendar.YEAR, year);
|
||||
updatePickers();
|
||||
setCurrentView(MONTH_AND_DAY_VIEW);
|
||||
updateDisplay(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOnDateChangedListener(
|
||||
OnDateChangedListener onDateChangedListener) {
|
||||
mListeners.add(onDateChangedListener);
|
||||
}
|
||||
|
||||
public void setFirstDayOfWeek(int startOfWeek) {
|
||||
if (startOfWeek < Calendar.SUNDAY || startOfWeek > Calendar.SATURDAY) {
|
||||
throw new IllegalArgumentException(
|
||||
"Value must be between Calendar.SUNDAY and "
|
||||
+ "Calendar.SATURDAY");
|
||||
}
|
||||
mWeekStart = startOfWeek;
|
||||
if (mDayPickerView != null) {
|
||||
mDayPickerView.onChange();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnDateSetListener(OnDateSetListener onDateSetListener) {
|
||||
mCallBack = onDateSetListener;
|
||||
}
|
||||
|
||||
public void setYearRange(int minYear, int maxYear) {
|
||||
if (maxYear < minYear)
|
||||
throw new IllegalArgumentException(
|
||||
"Year end must be larger than year start");
|
||||
if (maxYear > MAX_YEAR)
|
||||
throw new IllegalArgumentException("max year end must < "
|
||||
+ MAX_YEAR);
|
||||
if (minYear < MIN_YEAR)
|
||||
throw new IllegalArgumentException("min year end must > "
|
||||
+ MIN_YEAR);
|
||||
mMinYear = minYear;
|
||||
mMaxYear = maxYear;
|
||||
if (mDayPickerView != null)
|
||||
mDayPickerView.onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tryVibrate() {
|
||||
if (mVibrator != null && mVibrate) {
|
||||
long timeInMillis = SystemClock.uptimeMillis();
|
||||
if (timeInMillis - mLastVibrate >= 125L) {
|
||||
mVibrator.vibrate(5L);
|
||||
mLastVibrate = timeInMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setCloseOnSingleTapDay(boolean closeOnSingleTapDay) {
|
||||
mCloseOnSingleTapDay = closeOnSingleTapDay;
|
||||
}
|
||||
|
||||
static abstract interface OnDateChangedListener {
|
||||
public abstract void onDateChanged();
|
||||
}
|
||||
|
||||
public static abstract interface OnDateSetListener {
|
||||
public abstract void onDateSet(DatePickerDialog datePickerDialog,
|
||||
int year, int month, int day);
|
||||
}
|
||||
}
|
@ -0,0 +1,281 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ListView;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class DayPickerView extends ListView implements
|
||||
AbsListView.OnScrollListener, DatePickerDialog.OnDateChangedListener {
|
||||
|
||||
protected static final int GOTO_SCROLL_DURATION = 250;
|
||||
protected static final int SCROLL_CHANGE_DELAY = 40;
|
||||
|
||||
public static int LIST_TOP_OFFSET = -1;
|
||||
|
||||
protected Context mContext;
|
||||
protected Handler mHandler = new Handler();
|
||||
|
||||
protected SimpleMonthAdapter mAdapter;
|
||||
private final DatePickerController mController;
|
||||
|
||||
protected int mCurrentMonthDisplayed;
|
||||
protected int mCurrentScrollState = 0;
|
||||
private boolean mPerformingScroll;
|
||||
protected long mPreviousScrollPosition;
|
||||
protected int mPreviousScrollState = 0;
|
||||
|
||||
protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
|
||||
protected SimpleMonthAdapter.CalendarDay mSelectedDay = new SimpleMonthAdapter.CalendarDay();
|
||||
protected SimpleMonthAdapter.CalendarDay mTempDay = new SimpleMonthAdapter.CalendarDay();
|
||||
|
||||
protected int mNumWeeks = 6;
|
||||
protected boolean mShowWeekNumber = false;
|
||||
protected int mDaysPerWeek = 7;
|
||||
|
||||
protected float mFriction = 1.0F;
|
||||
|
||||
public DayPickerView(Context context,
|
||||
DatePickerController datePickerController) {
|
||||
super(context);
|
||||
mController = datePickerController;
|
||||
mController.registerOnDateChangedListener(this);
|
||||
setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT));
|
||||
setDrawSelectorOnTop(false);
|
||||
init(context);
|
||||
onDateChanged();
|
||||
}
|
||||
|
||||
public int getMostVisiblePosition() {
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int height = getHeight();
|
||||
|
||||
int maxDisplayedHeight = 0;
|
||||
int mostVisibleIndex = 0;
|
||||
int i = 0;
|
||||
int bottom = 0;
|
||||
while (bottom < height) {
|
||||
View child = getChildAt(i);
|
||||
if (child == null) {
|
||||
break;
|
||||
}
|
||||
bottom = child.getBottom();
|
||||
int displayedHeight = Math.min(bottom, height)
|
||||
- Math.max(0, child.getTop());
|
||||
if (displayedHeight > maxDisplayedHeight) {
|
||||
mostVisibleIndex = i;
|
||||
maxDisplayedHeight = displayedHeight;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return firstPosition + mostVisibleIndex;
|
||||
}
|
||||
|
||||
public boolean goTo(SimpleMonthAdapter.CalendarDay day, boolean animate,
|
||||
boolean setSelected, boolean forceScroll) {
|
||||
// Set the selected day
|
||||
if (setSelected) {
|
||||
mSelectedDay.set(day);
|
||||
}
|
||||
|
||||
mTempDay.set(day);
|
||||
final int position = (day.year - mController.getMinYear())
|
||||
* SimpleMonthAdapter.MONTHS_IN_YEAR + day.month;
|
||||
|
||||
View child;
|
||||
int i = 0;
|
||||
int top = 0;
|
||||
// Find a child that's completely in the view
|
||||
do {
|
||||
child = getChildAt(i++);
|
||||
if (child == null) {
|
||||
break;
|
||||
}
|
||||
top = child.getTop();
|
||||
} while (top < 0);
|
||||
|
||||
// Compute the first and last position visible
|
||||
int selectedPosition;
|
||||
if (child != null) {
|
||||
selectedPosition = getPositionForView(child);
|
||||
} else {
|
||||
selectedPosition = 0;
|
||||
}
|
||||
|
||||
if (setSelected) {
|
||||
mAdapter.setSelectedDay(mSelectedDay);
|
||||
}
|
||||
|
||||
// Check if the selected day is now outside of our visible range
|
||||
// and if so scroll to the month that contains it
|
||||
if (position != selectedPosition || forceScroll) {
|
||||
setMonthDisplayed(mTempDay);
|
||||
mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
|
||||
if (animate && Build.VERSION.SDK_INT >= 11) {
|
||||
smoothScrollToPositionFromTop(position, LIST_TOP_OFFSET,
|
||||
GOTO_SCROLL_DURATION);
|
||||
return true;
|
||||
} else {
|
||||
postSetSelection(position);
|
||||
}
|
||||
} else if (setSelected) {
|
||||
setMonthDisplayed(mSelectedDay);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void init(Context paramContext) {
|
||||
mContext = paramContext;
|
||||
setUpListView();
|
||||
setUpAdapter();
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren() {
|
||||
super.layoutChildren();
|
||||
if (mPerformingScroll) {
|
||||
mPerformingScroll = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onChange() {
|
||||
setUpAdapter();
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDateChanged() {
|
||||
goTo(mController.getSelectedDay(), false, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
|
||||
if (child == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out where we are
|
||||
long currScroll = view.getFirstVisiblePosition() * child.getHeight()
|
||||
- child.getBottom();
|
||||
mPreviousScrollPosition = currScroll;
|
||||
mPreviousScrollState = mCurrentScrollState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView absListView, int scroll) {
|
||||
mScrollStateChangedRunnable.doScrollStateChange(absListView, scroll);
|
||||
}
|
||||
|
||||
public void postSetSelection(final int position) {
|
||||
clearFocus();
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DayPickerView.this.setSelection(position);
|
||||
}
|
||||
});
|
||||
onScrollStateChanged(this, 0);
|
||||
}
|
||||
|
||||
protected void setMonthDisplayed(SimpleMonthAdapter.CalendarDay calendarDay) {
|
||||
this.mCurrentMonthDisplayed = calendarDay.month;
|
||||
invalidateViews();
|
||||
}
|
||||
|
||||
protected void setUpAdapter() {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = new SimpleMonthAdapter(getContext(), mController);
|
||||
}
|
||||
mAdapter.setSelectedDay(this.mSelectedDay);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
protected void setUpListView() {
|
||||
setCacheColorHint(0);
|
||||
setDivider(null);
|
||||
setItemsCanFocus(true);
|
||||
setFastScrollEnabled(false);
|
||||
setVerticalScrollBarEnabled(false);
|
||||
setOnScrollListener(this);
|
||||
setFadingEdgeLength(0);
|
||||
setFrictionIfSupported(ViewConfiguration.getScrollFriction()
|
||||
* mFriction);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
void setFrictionIfSupported(float friction) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
setFriction(friction);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ScrollStateRunnable implements Runnable {
|
||||
private int mNewState;
|
||||
|
||||
/**
|
||||
* Sets up the runnable with a short delay in case the scroll state
|
||||
* immediately changes again.
|
||||
*
|
||||
* @param view
|
||||
* The list view that changed state
|
||||
* @param scrollState
|
||||
* The new state it changed to
|
||||
*/
|
||||
public void doScrollStateChange(AbsListView view, int scrollState) {
|
||||
mHandler.removeCallbacks(this);
|
||||
mNewState = scrollState;
|
||||
mHandler.postDelayed(this, SCROLL_CHANGE_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentScrollState = mNewState;
|
||||
// Fix the position after a scroll or a fling ends
|
||||
if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
|
||||
&& mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
|
||||
&& mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
|
||||
mPreviousScrollState = mNewState;
|
||||
int i = 0;
|
||||
View child = getChildAt(i);
|
||||
while (child != null && child.getBottom() <= 0) {
|
||||
child = getChildAt(++i);
|
||||
}
|
||||
if (child == null) {
|
||||
// The view is no longer visible, just return
|
||||
return;
|
||||
}
|
||||
int firstPosition = getFirstVisiblePosition();
|
||||
int lastPosition = getLastVisiblePosition();
|
||||
boolean scroll = firstPosition != 0
|
||||
&& lastPosition != getCount() - 1;
|
||||
final int top = child.getTop();
|
||||
final int bottom = child.getBottom();
|
||||
final int midpoint = getHeight() / 2;
|
||||
if (scroll && top < LIST_TOP_OFFSET) {
|
||||
if (bottom > midpoint) {
|
||||
smoothScrollBy(top, GOTO_SCROLL_DURATION);
|
||||
} else {
|
||||
smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mPreviousScrollState = mNewState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class SimpleMonthAdapter extends BaseAdapter implements
|
||||
SimpleMonthView.OnDayClickListener {
|
||||
|
||||
protected static int WEEK_7_OVERHANG_HEIGHT = 7;
|
||||
protected static final int MONTHS_IN_YEAR = 12;
|
||||
|
||||
private final Context mContext;
|
||||
private final DatePickerController mController;
|
||||
|
||||
private CalendarDay mSelectedDay;
|
||||
|
||||
public SimpleMonthAdapter(Context context,
|
||||
DatePickerController datePickerController) {
|
||||
mContext = context;
|
||||
mController = datePickerController;
|
||||
init();
|
||||
setSelectedDay(mController.getSelectedDay());
|
||||
}
|
||||
|
||||
private boolean isSelectedDayInMonth(int year, int month) {
|
||||
return (mSelectedDay.year == year) && (mSelectedDay.month == month);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return ((mController.getMaxYear() - mController.getMinYear()) + 1)
|
||||
* MONTHS_IN_YEAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
SimpleMonthView v;
|
||||
HashMap<String, Integer> drawingParams = null;
|
||||
if (convertView != null) {
|
||||
v = (SimpleMonthView) convertView;
|
||||
drawingParams = (HashMap<String, Integer>) v.getTag();
|
||||
} else {
|
||||
v = new SimpleMonthView(mContext);
|
||||
v.setLayoutParams(new AbsListView.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
v.setClickable(true);
|
||||
v.setOnDayClickListener(this);
|
||||
}
|
||||
if (drawingParams == null) {
|
||||
drawingParams = new HashMap<String, Integer>();
|
||||
}
|
||||
drawingParams.clear();
|
||||
|
||||
final int month = position % MONTHS_IN_YEAR;
|
||||
final int year = position / MONTHS_IN_YEAR + mController.getMinYear();
|
||||
|
||||
int selectedDay = -1;
|
||||
if (isSelectedDayInMonth(year, month)) {
|
||||
selectedDay = mSelectedDay.day;
|
||||
}
|
||||
|
||||
v.reuse();
|
||||
|
||||
drawingParams
|
||||
.put(SimpleMonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
|
||||
drawingParams.put(SimpleMonthView.VIEW_PARAMS_YEAR, year);
|
||||
drawingParams.put(SimpleMonthView.VIEW_PARAMS_MONTH, month);
|
||||
drawingParams.put(SimpleMonthView.VIEW_PARAMS_WEEK_START,
|
||||
mController.getFirstDayOfWeek());
|
||||
v.setMonthParams(drawingParams);
|
||||
v.invalidate();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
mSelectedDay = new CalendarDay(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDayClick(SimpleMonthView simpleMonthView,
|
||||
CalendarDay calendarDay) {
|
||||
if (calendarDay != null) {
|
||||
onDayTapped(calendarDay);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDayTapped(CalendarDay calendarDay) {
|
||||
mController.tryVibrate();
|
||||
mController.onDayOfMonthSelected(calendarDay.year, calendarDay.month,
|
||||
calendarDay.day);
|
||||
setSelectedDay(calendarDay);
|
||||
}
|
||||
|
||||
public void setSelectedDay(CalendarDay calendarDay) {
|
||||
mSelectedDay = calendarDay;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public static class CalendarDay {
|
||||
private Calendar calendar;
|
||||
|
||||
int day;
|
||||
int month;
|
||||
int year;
|
||||
|
||||
public CalendarDay() {
|
||||
setTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public CalendarDay(int year, int month, int day) {
|
||||
setDay(year, month, day);
|
||||
}
|
||||
|
||||
public CalendarDay(long timeInMillis) {
|
||||
setTime(timeInMillis);
|
||||
}
|
||||
|
||||
public CalendarDay(Calendar calendar) {
|
||||
year = calendar.get(Calendar.YEAR);
|
||||
month = calendar.get(Calendar.MONTH);
|
||||
day = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
private void setTime(long timeInMillis) {
|
||||
if (calendar == null) {
|
||||
calendar = Calendar.getInstance();
|
||||
}
|
||||
calendar.setTimeInMillis(timeInMillis);
|
||||
month = this.calendar.get(Calendar.MONTH);
|
||||
year = this.calendar.get(Calendar.YEAR);
|
||||
day = this.calendar.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
public void set(CalendarDay calendarDay) {
|
||||
year = calendarDay.year;
|
||||
month = calendarDay.month;
|
||||
day = calendarDay.day;
|
||||
}
|
||||
|
||||
public void setDay(int year, int month, int day) {
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,359 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class SimpleMonthView extends View {
|
||||
|
||||
public static final String VIEW_PARAMS_HEIGHT = "height";
|
||||
public static final String VIEW_PARAMS_MONTH = "month";
|
||||
public static final String VIEW_PARAMS_YEAR = "year";
|
||||
public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day";
|
||||
public static final String VIEW_PARAMS_WEEK_START = "week_start";
|
||||
public static final String VIEW_PARAMS_NUM_DAYS = "num_days";
|
||||
public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month";
|
||||
public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num";
|
||||
|
||||
private static final int SELECTED_CIRCLE_ALPHA = 60;
|
||||
protected static int DEFAULT_HEIGHT = 32;
|
||||
protected static final int DEFAULT_NUM_ROWS = 6;
|
||||
protected static int DAY_SELECTED_CIRCLE_SIZE;
|
||||
protected static int DAY_SEPARATOR_WIDTH = 1;
|
||||
protected static int MINI_DAY_NUMBER_TEXT_SIZE;
|
||||
protected static int MIN_HEIGHT = 10;
|
||||
protected static int MONTH_DAY_LABEL_TEXT_SIZE;
|
||||
protected static int MONTH_HEADER_SIZE;
|
||||
protected static int MONTH_LABEL_TEXT_SIZE;
|
||||
|
||||
protected static float mScale = 0.0F;
|
||||
protected int mPadding = 0;
|
||||
|
||||
private final String mDayOfWeekTypeface;
|
||||
private final String mMonthTitleTypeface;
|
||||
|
||||
protected Paint mMonthDayLabelPaint;
|
||||
protected Paint mMonthNumPaint;
|
||||
protected Paint mMonthTitleBGPaint;
|
||||
protected Paint mMonthTitlePaint;
|
||||
protected Paint mSelectedCirclePaint;
|
||||
protected int mDayTextColor;
|
||||
protected int mMonthTitleBGColor;
|
||||
protected int mMonthTitleColor;
|
||||
protected int mTodayNumberColor;
|
||||
|
||||
private final StringBuilder mStringBuilder;
|
||||
|
||||
protected int mFirstJulianDay = -1;
|
||||
protected int mFirstMonth = -1;
|
||||
protected int mLastMonth = -1;
|
||||
protected boolean mHasToday = false;
|
||||
protected int mSelectedDay = -1;
|
||||
protected int mToday = -1;
|
||||
protected int mWeekStart = 1;
|
||||
protected int mNumDays = 7;
|
||||
protected int mNumCells = mNumDays;
|
||||
protected int mSelectedLeft = -1;
|
||||
protected int mSelectedRight = -1;
|
||||
private int mDayOfWeekStart = 0;
|
||||
protected int mMonth;
|
||||
protected int mRowHeight = DEFAULT_HEIGHT;
|
||||
protected int mWidth;
|
||||
protected int mYear;
|
||||
|
||||
private final Calendar mCalendar;
|
||||
private final Calendar mDayLabelCalendar;
|
||||
|
||||
private int mNumRows = DEFAULT_NUM_ROWS;
|
||||
|
||||
private final DateFormatSymbols mDateFormatSymbols = new DateFormatSymbols();
|
||||
|
||||
private OnDayClickListener mOnDayClickListener;
|
||||
|
||||
public SimpleMonthView(Context context) {
|
||||
super(context);
|
||||
Resources resources = context.getResources();
|
||||
mDayLabelCalendar = Calendar.getInstance();
|
||||
mCalendar = Calendar.getInstance();
|
||||
|
||||
mDayOfWeekTypeface = "sans-serif";
|
||||
mMonthTitleTypeface = "sans-serif";
|
||||
mDayTextColor = resources.getColor(R.color.date_picker_text_normal);
|
||||
mTodayNumberColor = resources.getColor(R.color.blue);
|
||||
mMonthTitleColor = resources.getColor(R.color.white);
|
||||
mMonthTitleBGColor = resources.getColor(R.color.circle_background);
|
||||
|
||||
mStringBuilder = new StringBuilder(50);
|
||||
|
||||
MINI_DAY_NUMBER_TEXT_SIZE = resources
|
||||
.getDimensionPixelSize(R.dimen.day_number_size);
|
||||
MONTH_LABEL_TEXT_SIZE = resources
|
||||
.getDimensionPixelSize(R.dimen.month_label_size);
|
||||
MONTH_DAY_LABEL_TEXT_SIZE = resources
|
||||
.getDimensionPixelSize(R.dimen.month_day_label_text_size);
|
||||
MONTH_HEADER_SIZE = resources
|
||||
.getDimensionPixelOffset(R.dimen.month_list_item_header_height);
|
||||
DAY_SELECTED_CIRCLE_SIZE = resources
|
||||
.getDimensionPixelSize(R.dimen.day_number_select_circle_radius);
|
||||
|
||||
mRowHeight = ((resources
|
||||
.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height) - MONTH_HEADER_SIZE) / 6);
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
private int calculateNumRows() {
|
||||
int offset = findDayOffset();
|
||||
int dividend = (offset + mNumCells) / mNumDays;
|
||||
int remainder = (offset + mNumCells) % mNumDays;
|
||||
return (dividend + (remainder > 0 ? 1 : 0));
|
||||
}
|
||||
|
||||
private void drawMonthDayLabels(Canvas canvas) {
|
||||
int y = MONTH_HEADER_SIZE - (MONTH_DAY_LABEL_TEXT_SIZE / 2);
|
||||
int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
|
||||
|
||||
for (int i = 0; i < mNumDays; i++) {
|
||||
int calendarDay = (i + mWeekStart) % mNumDays;
|
||||
int x = (2 * i + 1) * dayWidthHalf + mPadding;
|
||||
mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
|
||||
canvas.drawText(
|
||||
mDateFormatSymbols.getShortWeekdays()[mDayLabelCalendar
|
||||
.get(Calendar.DAY_OF_WEEK)].toUpperCase(Locale
|
||||
.getDefault()), x, y, mMonthDayLabelPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMonthTitle(Canvas canvas) {
|
||||
int x = (mWidth + 2 * mPadding) / 2;
|
||||
int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2
|
||||
+ (MONTH_LABEL_TEXT_SIZE / 3);
|
||||
canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint);
|
||||
}
|
||||
|
||||
private int findDayOffset() {
|
||||
return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays)
|
||||
: mDayOfWeekStart) - mWeekStart;
|
||||
}
|
||||
|
||||
private String getMonthAndYearString() {
|
||||
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
|
||||
| DateUtils.FORMAT_NO_MONTH_DAY;
|
||||
mStringBuilder.setLength(0);
|
||||
long millis = mCalendar.getTimeInMillis();
|
||||
return DateUtils.formatDateRange(getContext(), millis, millis, flags);
|
||||
}
|
||||
|
||||
private void onDayClick(SimpleMonthAdapter.CalendarDay calendarDay) {
|
||||
if (mOnDayClickListener != null) {
|
||||
mOnDayClickListener.onDayClick(this, calendarDay);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sameDay(int monthDay, Time time) {
|
||||
return (mYear == time.year) && (mMonth == time.month)
|
||||
&& (monthDay == time.monthDay);
|
||||
}
|
||||
|
||||
protected void drawMonthNums(Canvas canvas) {
|
||||
int y = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2
|
||||
- DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE;
|
||||
int paddingDay = (mWidth - 2 * mPadding) / (2 * mNumDays);
|
||||
int dayOffset = findDayOffset();
|
||||
int day = 1;
|
||||
|
||||
while (day <= mNumCells) {
|
||||
int x = paddingDay * (1 + dayOffset * 2) + mPadding;
|
||||
if (mSelectedDay == day) {
|
||||
canvas.drawCircle(x, y - MINI_DAY_NUMBER_TEXT_SIZE / 3,
|
||||
DAY_SELECTED_CIRCLE_SIZE, mSelectedCirclePaint);
|
||||
}
|
||||
if (mHasToday && (mToday == day)) {
|
||||
mMonthNumPaint.setColor(mTodayNumberColor);
|
||||
} else {
|
||||
mMonthNumPaint.setColor(mDayTextColor);
|
||||
}
|
||||
|
||||
canvas.drawText(String.format("%d", day), x, y, mMonthNumPaint);
|
||||
|
||||
dayOffset++;
|
||||
if (dayOffset == mNumDays) {
|
||||
dayOffset = 0;
|
||||
y += mRowHeight;
|
||||
}
|
||||
day++;
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleMonthAdapter.CalendarDay getDayFromLocation(float x, float y) {
|
||||
int padding = mPadding;
|
||||
if ((x < padding) || (x > mWidth - mPadding)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int yDay = (int) (y - MONTH_HEADER_SIZE) / mRowHeight;
|
||||
int day = 1
|
||||
+ ((int) ((x - padding) * mNumDays / (mWidth - padding - mPadding)) - findDayOffset())
|
||||
+ yDay * mNumDays;
|
||||
|
||||
return new SimpleMonthAdapter.CalendarDay(mYear, mMonth, day);
|
||||
}
|
||||
|
||||
protected void initView() {
|
||||
mMonthTitlePaint = new Paint();
|
||||
mMonthTitlePaint.setFakeBoldText(true);
|
||||
mMonthTitlePaint.setAntiAlias(true);
|
||||
mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE);
|
||||
mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface,
|
||||
Typeface.BOLD));
|
||||
mMonthTitlePaint.setColor(mDayTextColor);
|
||||
mMonthTitlePaint.setTextAlign(Align.CENTER);
|
||||
mMonthTitlePaint.setStyle(Style.FILL);
|
||||
|
||||
mMonthTitleBGPaint = new Paint();
|
||||
mMonthTitleBGPaint.setFakeBoldText(true);
|
||||
mMonthTitleBGPaint.setAntiAlias(true);
|
||||
mMonthTitleBGPaint.setColor(mMonthTitleBGColor);
|
||||
mMonthTitleBGPaint.setTextAlign(Align.CENTER);
|
||||
mMonthTitleBGPaint.setStyle(Style.FILL);
|
||||
|
||||
mSelectedCirclePaint = new Paint();
|
||||
mSelectedCirclePaint.setFakeBoldText(true);
|
||||
mSelectedCirclePaint.setAntiAlias(true);
|
||||
mSelectedCirclePaint.setColor(mTodayNumberColor);
|
||||
mSelectedCirclePaint.setTextAlign(Align.CENTER);
|
||||
mSelectedCirclePaint.setStyle(Style.FILL);
|
||||
mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA);
|
||||
|
||||
mMonthDayLabelPaint = new Paint();
|
||||
mMonthDayLabelPaint.setAntiAlias(true);
|
||||
mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE);
|
||||
mMonthDayLabelPaint.setColor(mDayTextColor);
|
||||
mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface,
|
||||
Typeface.NORMAL));
|
||||
mMonthDayLabelPaint.setStyle(Style.FILL);
|
||||
mMonthDayLabelPaint.setTextAlign(Align.CENTER);
|
||||
mMonthDayLabelPaint.setFakeBoldText(true);
|
||||
|
||||
mMonthNumPaint = new Paint();
|
||||
mMonthNumPaint.setAntiAlias(true);
|
||||
mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE);
|
||||
mMonthNumPaint.setStyle(Style.FILL);
|
||||
mMonthNumPaint.setTextAlign(Align.CENTER);
|
||||
mMonthNumPaint.setFakeBoldText(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
drawMonthTitle(canvas);
|
||||
drawMonthDayLabels(canvas);
|
||||
drawMonthNums(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(View.MeasureSpec.getSize(widthMeasureSpec),
|
||||
mRowHeight * mNumRows + MONTH_HEADER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
mWidth = w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(
|
||||
event.getX(), event.getY());
|
||||
if (calendarDay != null) {
|
||||
onDayClick(calendarDay);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void reuse() {
|
||||
mNumRows = DEFAULT_NUM_ROWS;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setMonthParams(HashMap<String, Integer> params) {
|
||||
if (!params.containsKey(VIEW_PARAMS_MONTH)
|
||||
&& !params.containsKey(VIEW_PARAMS_YEAR)) {
|
||||
throw new InvalidParameterException(
|
||||
"You must specify month and year for this view");
|
||||
}
|
||||
setTag(params);
|
||||
|
||||
if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
|
||||
mRowHeight = params.get(VIEW_PARAMS_HEIGHT);
|
||||
if (mRowHeight < MIN_HEIGHT) {
|
||||
mRowHeight = MIN_HEIGHT;
|
||||
}
|
||||
}
|
||||
if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
|
||||
mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY);
|
||||
}
|
||||
|
||||
mMonth = params.get(VIEW_PARAMS_MONTH);
|
||||
mYear = params.get(VIEW_PARAMS_YEAR);
|
||||
|
||||
final Time today = new Time(Time.getCurrentTimezone());
|
||||
today.setToNow();
|
||||
mHasToday = false;
|
||||
mToday = -1;
|
||||
|
||||
mCalendar.set(Calendar.MONTH, mMonth);
|
||||
mCalendar.set(Calendar.YEAR, mYear);
|
||||
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
|
||||
mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
|
||||
|
||||
if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
|
||||
mWeekStart = params.get(VIEW_PARAMS_WEEK_START);
|
||||
} else {
|
||||
mWeekStart = mCalendar.getFirstDayOfWeek();
|
||||
}
|
||||
|
||||
mNumCells = Utils.getDaysInMonth(mMonth, mYear);
|
||||
for (int i = 0; i < mNumCells; i++) {
|
||||
final int day = i + 1;
|
||||
if (sameDay(day, today)) {
|
||||
mHasToday = true;
|
||||
mToday = day;
|
||||
}
|
||||
}
|
||||
|
||||
mNumRows = calculateNumRows();
|
||||
}
|
||||
|
||||
public void setOnDayClickListener(OnDayClickListener onDayClickListener) {
|
||||
mOnDayClickListener = onDayClickListener;
|
||||
}
|
||||
|
||||
public static abstract interface OnDayClickListener {
|
||||
public abstract void onDayClick(SimpleMonthView simpleMonthView,
|
||||
SimpleMonthAdapter.CalendarDay calendarDay);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class TextViewWithCircularIndicator extends TextView {
|
||||
|
||||
private final int mCircleColor;
|
||||
private final Paint mCirclePaint = new Paint();
|
||||
private boolean mDrawCircle;
|
||||
private final String mItemIsSelectedText;
|
||||
|
||||
public TextViewWithCircularIndicator(Context context,
|
||||
AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
|
||||
Resources res = context.getResources();
|
||||
mCircleColor = res.getColor(R.color.blue);
|
||||
mItemIsSelectedText = "已选择";
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mCirclePaint.setFakeBoldText(true);
|
||||
mCirclePaint.setAntiAlias(true);
|
||||
mCirclePaint.setColor(mCircleColor);
|
||||
mCirclePaint.setTextAlign(Paint.Align.CENTER);
|
||||
mCirclePaint.setStyle(Paint.Style.FILL);
|
||||
mCirclePaint.setAlpha(60);
|
||||
}
|
||||
|
||||
public void drawIndicator(boolean drawIndicator) {
|
||||
mDrawCircle = drawIndicator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getContentDescription() {
|
||||
CharSequence text = getText();
|
||||
if (mDrawCircle) {
|
||||
text = String.format(mItemIsSelectedText, text);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (mDrawCircle) {
|
||||
int width = getWidth();
|
||||
int heigth = getHeight();
|
||||
int radius = Math.min(width, heigth) / 2;
|
||||
canvas.drawCircle(width / 2, heigth / 2, radius, mCirclePaint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.nineoldandroids.animation.Keyframe;
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.nineoldandroids.animation.PropertyValuesHolder;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
public static final int PULSE_ANIMATOR_DURATION = 544;
|
||||
|
||||
public static int getDaysInMonth(int month, int year) {
|
||||
switch (month) {
|
||||
case Calendar.JANUARY:
|
||||
case Calendar.MARCH:
|
||||
case Calendar.MAY:
|
||||
case Calendar.JULY:
|
||||
case Calendar.AUGUST:
|
||||
case Calendar.OCTOBER:
|
||||
case Calendar.DECEMBER:
|
||||
return 31;
|
||||
case Calendar.APRIL:
|
||||
case Calendar.JUNE:
|
||||
case Calendar.SEPTEMBER:
|
||||
case Calendar.NOVEMBER:
|
||||
return 30;
|
||||
case Calendar.FEBRUARY:
|
||||
return (year % 4 == 0) ? 29 : 28;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Month");
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectAnimator getPulseAnimator(View labelToAnimate,
|
||||
float decreaseRatio, float increaseRatio) {
|
||||
Keyframe k0 = Keyframe.ofFloat(0f, 1f);
|
||||
Keyframe k1 = Keyframe.ofFloat(0.275f, decreaseRatio);
|
||||
Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
|
||||
Keyframe k3 = Keyframe.ofFloat(1f, 1f);
|
||||
|
||||
PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX",
|
||||
k0, k1, k2, k3);
|
||||
PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY",
|
||||
k0, k1, k2, k3);
|
||||
ObjectAnimator pulseAnimator = ObjectAnimator.ofPropertyValuesHolder(
|
||||
labelToAnimate, scaleX, scaleY);
|
||||
pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
|
||||
|
||||
return pulseAnimator;
|
||||
}
|
||||
|
||||
public static boolean isJellybeanOrLater() {
|
||||
return Build.VERSION.SDK_INT >= 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to speak the specified text, for accessibility. Only available on JB
|
||||
* or later.
|
||||
*
|
||||
* @param text
|
||||
* Text to announce.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public static void tryAccessibilityAnnounce(View view, CharSequence text) {
|
||||
if (isJellybeanOrLater() && view != null && text != null) {
|
||||
view.announceForAccessibility(text);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTouchExplorationEnabled(
|
||||
AccessibilityManager accessibilityManager) {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
return accessibilityManager.isTouchExplorationEnabled();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package com.fourmob.datetimepicker.date;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* 摘取自https://github.com/flavienlaurent/datetimepicker
|
||||
*
|
||||
* @author kymjs
|
||||
*
|
||||
*/
|
||||
public class YearPickerView extends ListView implements
|
||||
AdapterView.OnItemClickListener, DatePickerDialog.OnDateChangedListener {
|
||||
|
||||
private YearAdapter mAdapter;
|
||||
private final int mChildSize;
|
||||
private final DatePickerController mController;
|
||||
private TextViewWithCircularIndicator mSelectedView;
|
||||
private final int mViewSize;
|
||||
|
||||
public YearPickerView(Context context,
|
||||
DatePickerController datePickerController) {
|
||||
super(context);
|
||||
mController = datePickerController;
|
||||
mController.registerOnDateChangedListener(this);
|
||||
|
||||
setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT));
|
||||
|
||||
Resources resources = context.getResources();
|
||||
mViewSize = resources
|
||||
.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height);
|
||||
mChildSize = resources
|
||||
.getDimensionPixelOffset(R.dimen.year_label_height);
|
||||
|
||||
setVerticalFadingEdgeEnabled(true);
|
||||
setFadingEdgeLength(mChildSize / 3);
|
||||
init(context);
|
||||
setOnItemClickListener(this);
|
||||
setSelector(new StateListDrawable());
|
||||
setDividerHeight(0);
|
||||
onDateChanged();
|
||||
}
|
||||
|
||||
private static int getYearFromTextView(TextView view) {
|
||||
return Integer.valueOf(view.getText().toString());
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
ArrayList<String> years = new ArrayList<String>();
|
||||
for (int year = mController.getMinYear(); year <= mController
|
||||
.getMaxYear(); year++) {
|
||||
years.add(String.format("%d", year));
|
||||
}
|
||||
mAdapter = new YearAdapter(context, R.layout.year_label_text_view,
|
||||
years);
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
public int getFirstPositionOffset() {
|
||||
final View firstChild = getChildAt(0);
|
||||
if (firstChild == null) {
|
||||
return 0;
|
||||
}
|
||||
return firstChild.getTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDateChanged() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
postSetSelectionCentered(mController.getSelectedDay().year
|
||||
- mController.getMinYear());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
mController.tryVibrate();
|
||||
TextViewWithCircularIndicator clickedView = (TextViewWithCircularIndicator) view;
|
||||
if (clickedView != null) {
|
||||
if (clickedView != mSelectedView) {
|
||||
if (mSelectedView != null) {
|
||||
mSelectedView.drawIndicator(false);
|
||||
mSelectedView.requestLayout();
|
||||
}
|
||||
clickedView.drawIndicator(true);
|
||||
clickedView.requestLayout();
|
||||
mSelectedView = clickedView;
|
||||
}
|
||||
mController.onYearSelected(getYearFromTextView(clickedView));
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void postSetSelectionCentered(int position) {
|
||||
postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
|
||||
}
|
||||
|
||||
public void postSetSelectionFromTop(final int position, final int y) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setSelectionFromTop(position, y);
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class YearAdapter extends ArrayAdapter<String> {
|
||||
|
||||
public YearAdapter(Context context, int resource, List<String> years) {
|
||||
super(context, resource, years);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextViewWithCircularIndicator v = (TextViewWithCircularIndicator) super
|
||||
.getView(position, convertView, parent);
|
||||
v.requestLayout();
|
||||
int year = getYearFromTextView(v);
|
||||
boolean selected = mController.getSelectedDay().year == year;
|
||||
v.drawIndicator(selected);
|
||||
if (selected) {
|
||||
mSelectedView = v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package net.oschina.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import net.oschina.app.ui.MainActivity;
|
||||
import net.oschina.app.util.TDevice;
|
||||
|
||||
import org.kymjs.kjframe.http.KJAsyncTask;
|
||||
import org.kymjs.kjframe.utils.FileUtils;
|
||||
import org.kymjs.kjframe.utils.PreferenceHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 应用启动界面
|
||||
*/
|
||||
public class AppStart extends Activity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// 防止第三方跳转时出现双实例
|
||||
Activity aty = AppManager.getActivity(MainActivity.class);
|
||||
if (aty != null && !aty.isFinishing()) {
|
||||
finish();
|
||||
}
|
||||
|
||||
setContentView(R.layout.app_start);
|
||||
findViewById(R.id.app_start_view).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
redirectTo();
|
||||
}
|
||||
}, 800);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
int cacheVersion = PreferenceHelper.readInt(this, "first_install",
|
||||
"first_install", -1);
|
||||
int currentVersion = TDevice.getVersionCode();
|
||||
if (cacheVersion < currentVersion) {
|
||||
PreferenceHelper.write(this, "first_install", "first_install",
|
||||
currentVersion);
|
||||
cleanKJImageCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanKJImageCache() {
|
||||
final File folder = FileUtils.getSaveFolder("OSChina/imagecache");
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
KJAsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (folder.isDirectory()) {
|
||||
|
||||
for (File file : folder.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到...
|
||||
*/
|
||||
private void redirectTo() {
|
||||
Intent uploadLog = new Intent(this, LogUploadService.class);
|
||||
startService(uploadLog);
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package net.oschina.app;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import net.oschina.app.api.remote.OSChinaApi;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
|
||||
import cz.msebera.android.httpclient.Header;
|
||||
import org.kymjs.kjframe.utils.FileUtils;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
|
||||
public class LogUploadService extends Service {
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final File log = FileUtils.getSaveFile("OSChina", "OSCLog.log");
|
||||
String data = null;
|
||||
try {
|
||||
FileInputStream inputStream = new FileInputStream(log);
|
||||
data = StringUtils.toConvertString(inputStream);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!StringUtils.isEmpty(data)) {
|
||||
OSChinaApi.uploadLog(data, new AsyncHttpResponseHandler() {
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
|
||||
log.delete();
|
||||
LogUploadService.this.stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
|
||||
Throwable arg3) {
|
||||
LogUploadService.this.stopSelf();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LogUploadService.this.stopSelf();
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.Active;
|
||||
import net.oschina.app.bean.Active.ObjectReply;
|
||||
import net.oschina.app.emoji.InputHelper;
|
||||
import net.oschina.app.ui.OSCPhotosActivity;
|
||||
import net.oschina.app.util.BitmapHelper;
|
||||
import net.oschina.app.util.ImageUtils;
|
||||
import net.oschina.app.util.PlatfromUtil;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.UIHelper;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
import net.oschina.app.widget.MyLinkMovementMethod;
|
||||
import net.oschina.app.widget.MyURLSpan;
|
||||
import net.oschina.app.widget.TweetTextView;
|
||||
|
||||
import org.kymjs.kjframe.Core;
|
||||
import org.kymjs.kjframe.bitmap.BitmapCallBack;
|
||||
import org.kymjs.kjframe.utils.DensityUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
public class ActiveAdapter extends ListBaseAdapter {
|
||||
private final static String AT_HOST_PRE = "http://my.oschina.net";
|
||||
private final static String MAIN_HOST = "http://www.oschina.net";
|
||||
|
||||
public ActiveAdapter() {
|
||||
}
|
||||
|
||||
private Bitmap recordBitmap;
|
||||
private int rectSize;
|
||||
|
||||
private void initRecordImg(Context cxt) {
|
||||
recordBitmap = BitmapFactory.decodeResource(cxt.getResources(),
|
||||
R.mipmap.audio3);
|
||||
recordBitmap = ImageUtils.zoomBitmap(recordBitmap,
|
||||
DensityUtils.dip2px(cxt, 20f), DensityUtils.dip2px(cxt, 20f));
|
||||
}
|
||||
|
||||
private void initImageSize(Context cxt) {
|
||||
if (cxt != null && rectSize == 0) {
|
||||
rectSize = (int) cxt.getResources().getDimension(R.dimen.space_100);
|
||||
} else {
|
||||
rectSize = 300;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("InflateParams")
|
||||
protected View getRealView(int position, View convertView,
|
||||
final ViewGroup parent) {
|
||||
ViewHolder vh;
|
||||
initImageSize(parent.getContext());
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_active, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
final Active item = (Active) mDatas.get(position);
|
||||
|
||||
vh.name.setText(item.getAuthor());
|
||||
|
||||
vh.action.setText(UIHelper.parseActiveAction(item.getObjectType(),
|
||||
item.getObjectCatalog(), item.getObjectTitle()));
|
||||
|
||||
if (TextUtils.isEmpty(item.getMessage())) {
|
||||
vh.body.setVisibility(View.GONE);
|
||||
} else {
|
||||
vh.body.setMovementMethod(MyLinkMovementMethod.a());
|
||||
vh.body.setFocusable(false);
|
||||
vh.body.setDispatchToParent(true);
|
||||
vh.body.setLongClickable(false);
|
||||
|
||||
Spanned span = Html.fromHtml(modifyPath(item.getMessage()));
|
||||
|
||||
if (!StringUtils.isEmpty(item.getTweetattach())) {
|
||||
if (recordBitmap == null) {
|
||||
initRecordImg(parent.getContext());
|
||||
}
|
||||
ImageSpan recordImg = new ImageSpan(parent.getContext(),
|
||||
recordBitmap);
|
||||
SpannableString str = new SpannableString("c");
|
||||
str.setSpan(recordImg, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
vh.body.setText(str);
|
||||
span = InputHelper.displayEmoji(parent.getContext()
|
||||
.getResources(), span);
|
||||
vh.body.append(span);
|
||||
} else {
|
||||
span = InputHelper.displayEmoji(parent.getContext()
|
||||
.getResources(), span);
|
||||
vh.body.setText(span);
|
||||
}
|
||||
MyURLSpan.parseLinkText(vh.body, span);
|
||||
}
|
||||
|
||||
ObjectReply reply = item.getObjectReply();
|
||||
if (reply != null) {
|
||||
vh.reply.setMovementMethod(MyLinkMovementMethod.a());
|
||||
vh.reply.setFocusable(false);
|
||||
vh.reply.setDispatchToParent(true);
|
||||
vh.reply.setLongClickable(false);
|
||||
Spanned span = UIHelper.parseActiveReply(reply.objectName,
|
||||
reply.objectBody);
|
||||
vh.reply.setText(span);//
|
||||
MyURLSpan.parseLinkText(vh.reply, span);
|
||||
vh.lyReply.setVisibility(TextView.VISIBLE);
|
||||
} else {
|
||||
vh.reply.setText("");
|
||||
vh.lyReply.setVisibility(TextView.GONE);
|
||||
}
|
||||
|
||||
vh.time.setText(StringUtils.friendly_time(item.getPubDate()));
|
||||
|
||||
PlatfromUtil.setPlatFromString(vh.from, item.getAppClient());
|
||||
|
||||
vh.commentCount.setText(item.getCommentCount() + "");
|
||||
|
||||
vh.avatar.setUserInfo(item.getAuthorId(), item.getAuthor());
|
||||
vh.avatar.setAvatarUrl(item.getPortrait());
|
||||
|
||||
if (!TextUtils.isEmpty(item.getTweetimage())) {
|
||||
setTweetImage(parent, vh, item);
|
||||
} else {
|
||||
vh.pic.setVisibility(View.GONE);
|
||||
vh.pic.setImageBitmap(null);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态设置图片显示样式
|
||||
*/
|
||||
private void setTweetImage(final ViewGroup parent, final ViewHolder vh,
|
||||
final Active item) {
|
||||
vh.pic.setVisibility(View.VISIBLE);
|
||||
|
||||
new Core.Builder().url(item.getTweetimage()).view(vh.pic).loadBitmapRes(R.drawable
|
||||
.pic_bg).size(rectSize, rectSize).bitmapCallBack(new BitmapCallBack() {
|
||||
@Override
|
||||
public void onSuccess(Bitmap bitmap) {
|
||||
super.onSuccess(bitmap);
|
||||
if (bitmap != null) {
|
||||
bitmap = BitmapHelper.scaleWithXY(bitmap, rectSize / bitmap.getHeight());
|
||||
vh.pic.setImageBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
}).doTask();
|
||||
|
||||
vh.pic.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
OSCPhotosActivity.showImagePreview(parent.getContext(), getOriginalUrl(item.getTweetimage()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String modifyPath(String message) {
|
||||
message = message.replaceAll("(<a[^>]+href=\")/([\\S]+)\"", "$1"
|
||||
+ AT_HOST_PRE + "/$2\"");
|
||||
message = message.replaceAll(
|
||||
"(<a[^>]+href=\")http://m.oschina.net([\\S]+)\"", "$1"
|
||||
+ MAIN_HOST + "$2\"");
|
||||
return message;
|
||||
}
|
||||
|
||||
private String getOriginalUrl(String url) {
|
||||
return url.replaceAll("_thumb", "");
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
@Bind(R.id.tv_from)
|
||||
TextView from;
|
||||
@Bind(R.id.tv_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tv_action)
|
||||
TextView action;
|
||||
@Bind(R.id.tv_action_name)
|
||||
TextView actionName;
|
||||
@Bind(R.id.tv_comment_count)
|
||||
TextView commentCount;
|
||||
@Bind(R.id.tv_body)
|
||||
TweetTextView body;
|
||||
@Bind(R.id.tv_reply)
|
||||
TweetTextView reply;
|
||||
@Bind(R.id.iv_pic)
|
||||
ImageView pic;
|
||||
@Bind(R.id.ly_reply)
|
||||
View lyReply;
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.Blog;
|
||||
import net.oschina.app.bean.BlogList;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.ThemeSwitchUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
/**
|
||||
* @author HuangWenwei
|
||||
*
|
||||
* @date 2014年9月29日
|
||||
*/
|
||||
public class BlogAdapter extends ListBaseAdapter<Blog> {
|
||||
|
||||
static class ViewHolder {
|
||||
|
||||
@Bind(R.id.tv_title)
|
||||
TextView title;
|
||||
@Bind(R.id.tv_description)
|
||||
TextView description;
|
||||
@Bind(R.id.tv_source)
|
||||
TextView source;
|
||||
@Bind(R.id.tv_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tv_comment_count)
|
||||
TextView comment_count;
|
||||
@Bind(R.id.iv_tip)
|
||||
ImageView tip;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_news, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
Blog blog = mDatas.get(position);
|
||||
|
||||
vh.tip.setVisibility(View.VISIBLE);
|
||||
if (blog.getDocumenttype() == Blog.DOC_TYPE_ORIGINAL) {
|
||||
vh.tip.setImageResource(R.mipmap.widget_original_icon);
|
||||
} else {
|
||||
vh.tip.setImageResource(R.mipmap.widget_repaste_icon);
|
||||
}
|
||||
|
||||
vh.title.setText(blog.getTitle());
|
||||
|
||||
if (AppContext.isOnReadedPostList(BlogList.PREF_READED_BLOG_LIST,
|
||||
blog.getId() + "")) {
|
||||
vh.title.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleReadedColor()));
|
||||
} else {
|
||||
vh.title.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleUnReadedColor()));
|
||||
}
|
||||
|
||||
vh.description.setVisibility(View.GONE);
|
||||
String description = blog.getBody();
|
||||
if (null != description && !StringUtils.isEmpty(description)) {
|
||||
vh.description.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(description.trim());
|
||||
}
|
||||
|
||||
vh.source.setText(blog.getAuthor());
|
||||
vh.time.setText(StringUtils.friendly_time(blog.getPubDate()));
|
||||
vh.comment_count.setText(blog.getCommentCount() + "");
|
||||
return convertView;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.Messages;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
import net.oschina.app.widget.MyLinkMovementMethod;
|
||||
import net.oschina.app.widget.MyURLSpan;
|
||||
import net.oschina.app.widget.TweetTextView;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class MessageAdapter extends ListBaseAdapter<Messages> {
|
||||
|
||||
@Override
|
||||
protected boolean loadMoreHasBg() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView,
|
||||
final ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_message, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
final Messages item = (Messages) mDatas.get(position);
|
||||
|
||||
if (AppContext.getInstance().getLoginUid() == item.getSenderId()) {
|
||||
vh.sender.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
vh.sender.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
vh.name.setText(item.getFriendName());
|
||||
|
||||
vh.content.setMovementMethod(MyLinkMovementMethod.a());
|
||||
vh.content.setFocusable(false);
|
||||
vh.content.setDispatchToParent(true);
|
||||
vh.content.setLongClickable(false);
|
||||
Spanned span = Html.fromHtml(item.getContent());
|
||||
vh.content.setText(span);
|
||||
MyURLSpan.parseLinkText(vh.content, span);
|
||||
|
||||
vh.time.setText(StringUtils.friendly_time(item.getPubDate()));
|
||||
vh.count.setText(parent.getResources().getString(
|
||||
R.string.message_count, item.getMessageCount()));
|
||||
|
||||
vh.avatar.setAvatarUrl(item.getPortrait());
|
||||
vh.avatar.setUserInfo(item.getSenderId(), item.getSender());
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
@Bind(R.id.tv_sender)
|
||||
TextView sender;
|
||||
@Bind(R.id.tv_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tv_count)
|
||||
TextView count;
|
||||
@Bind(R.id.tv_content)
|
||||
TweetTextView content;
|
||||
|
||||
ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.News;
|
||||
import net.oschina.app.bean.NewsList;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.ThemeSwitchUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
public class NewsAdapter extends ListBaseAdapter<News> {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_news, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
News news = mDatas.get(position);
|
||||
vh.title.setText(news.getTitle());
|
||||
|
||||
if (AppContext.isOnReadedPostList(NewsList.PREF_READED_NEWS_LIST,
|
||||
news.getId() + "")) {
|
||||
vh.title.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleReadedColor()));
|
||||
} else {
|
||||
vh.title.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleUnReadedColor()));
|
||||
}
|
||||
|
||||
String description = news.getBody();
|
||||
vh.description.setVisibility(View.GONE);
|
||||
if (description != null && !StringUtils.isEmpty(description)) {
|
||||
vh.description.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(description.trim());
|
||||
}
|
||||
|
||||
vh.source.setText(news.getAuthor());
|
||||
if (StringUtils.isToday(news.getPubDate())) {
|
||||
vh.tip.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
vh.tip.setVisibility(View.GONE);
|
||||
}
|
||||
vh.time.setText(StringUtils.friendly_time(news.getPubDate()));
|
||||
vh.comment_count.setText(news.getCommentCount() + "");
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.tv_title)
|
||||
TextView title;
|
||||
@Bind(R.id.tv_description)
|
||||
TextView description;
|
||||
@Bind(R.id.tv_source)
|
||||
TextView source;
|
||||
@Bind(R.id.tv_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tv_comment_count)
|
||||
TextView comment_count;
|
||||
@Bind(R.id.iv_tip)
|
||||
ImageView tip;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Build;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
|
||||
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
|
||||
* start of a layout. By construction, they are displaying current information. At the end of
|
||||
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
|
||||
* could potentially be used by the adapter to avoid allocating views unnecessarily.
|
||||
* <p>
|
||||
* This class was taken from Android's implementation of {@link android.widget.AbsListView} which
|
||||
* is copyrighted 2006 The Android Open Source Project.
|
||||
*/
|
||||
public class RecycleBin {
|
||||
/**
|
||||
* Views that were on screen at the start of layout. This array is populated at the start of
|
||||
* layout, and at the end of layout all view in activeViews are moved to scrapViews.
|
||||
* Views in activeViews represent a contiguous range of Views, with position of the first
|
||||
* view store in mFirstActivePosition.
|
||||
*/
|
||||
private View[] activeViews = new View[0];
|
||||
private int[] activeViewTypes = new int[0];
|
||||
|
||||
/** Unsorted views that can be used by the adapter as a convert view. */
|
||||
private SparseArray<View>[] scrapViews;
|
||||
|
||||
private int viewTypeCount;
|
||||
|
||||
private SparseArray<View> currentScrapViews;
|
||||
|
||||
public void setViewTypeCount(int viewTypeCount) {
|
||||
if (viewTypeCount < 1) {
|
||||
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
|
||||
}
|
||||
//noinspection unchecked
|
||||
SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
|
||||
for (int i = 0; i < viewTypeCount; i++) {
|
||||
scrapViews[i] = new SparseArray<View>();
|
||||
}
|
||||
this.viewTypeCount = viewTypeCount;
|
||||
currentScrapViews = scrapViews[0];
|
||||
this.scrapViews = scrapViews;
|
||||
}
|
||||
|
||||
protected boolean shouldRecycleViewType(int viewType) {
|
||||
return viewType >= 0;
|
||||
}
|
||||
|
||||
/** @return A view from the ScrapViews collection. These are unordered. */
|
||||
View getScrapView(int position, int viewType) {
|
||||
if (viewTypeCount == 1) {
|
||||
return retrieveFromScrap(currentScrapViews, position);
|
||||
} else if (viewType >= 0 && viewType < scrapViews.length) {
|
||||
return retrieveFromScrap(scrapViews[viewType], position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a view into the ScrapViews list. These views are unordered.
|
||||
*
|
||||
* @param scrap The view to add
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
void addScrapView(View scrap, int position, int viewType) {
|
||||
if (viewTypeCount == 1) {
|
||||
currentScrapViews.put(position, scrap);
|
||||
} else {
|
||||
scrapViews[viewType].put(position, scrap);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
scrap.setAccessibilityDelegate(null);
|
||||
}
|
||||
}
|
||||
|
||||
/** Move all views remaining in activeViews to scrapViews. */
|
||||
@SuppressLint("NewApi")
|
||||
void scrapActiveViews() {
|
||||
final View[] activeViews = this.activeViews;
|
||||
final int[] activeViewTypes = this.activeViewTypes;
|
||||
final boolean multipleScraps = viewTypeCount > 1;
|
||||
|
||||
SparseArray<View> scrapViews = currentScrapViews;
|
||||
final int count = activeViews.length;
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
final View victim = activeViews[i];
|
||||
if (victim != null) {
|
||||
int whichScrap = activeViewTypes[i];
|
||||
|
||||
activeViews[i] = null;
|
||||
activeViewTypes[i] = -1;
|
||||
|
||||
if (!shouldRecycleViewType(whichScrap)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multipleScraps) {
|
||||
scrapViews = this.scrapViews[whichScrap];
|
||||
}
|
||||
scrapViews.put(i, victim);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
victim.setAccessibilityDelegate(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pruneScrapViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the size of scrapViews does not exceed the size of activeViews.
|
||||
* (This can happen if an adapter does not recycle its views).
|
||||
*/
|
||||
private void pruneScrapViews() {
|
||||
final int maxViews = activeViews.length;
|
||||
final int viewTypeCount = this.viewTypeCount;
|
||||
final SparseArray<View>[] scrapViews = this.scrapViews;
|
||||
for (int i = 0; i < viewTypeCount; ++i) {
|
||||
final SparseArray<View> scrapPile = scrapViews[i];
|
||||
int size = scrapPile.size();
|
||||
final int extras = size - maxViews;
|
||||
size--;
|
||||
for (int j = 0; j < extras; j++) {
|
||||
scrapPile.remove(scrapPile.keyAt(size--));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
|
||||
int size = scrapViews.size();
|
||||
if (size > 0) {
|
||||
// See if we still have a view for this position.
|
||||
for (int i = 0; i < size; i++) {
|
||||
int fromPosition = scrapViews.keyAt(i);
|
||||
View view = scrapViews.get(fromPosition);
|
||||
if (fromPosition == position) {
|
||||
scrapViews.remove(fromPosition);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
int index = size - 1;
|
||||
View r = scrapViews.valueAt(index);
|
||||
scrapViews.remove(scrapViews.keyAt(index));
|
||||
return r;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
/**
|
||||
* A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter}
|
||||
* with view types and view recycling.
|
||||
*/
|
||||
public abstract class RecyclingPagerAdapter extends PagerAdapter {
|
||||
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
|
||||
|
||||
private final RecycleBin recycleBin;
|
||||
|
||||
public RecyclingPagerAdapter() {
|
||||
this(new RecycleBin());
|
||||
}
|
||||
|
||||
RecyclingPagerAdapter(RecycleBin recycleBin) {
|
||||
this.recycleBin = recycleBin;
|
||||
recycleBin.setViewTypeCount(getViewTypeCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
recycleBin.scrapActiveViews();
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object instantiateItem(ViewGroup container, int position) {
|
||||
int viewType = getItemViewType(position);
|
||||
View view = null;
|
||||
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
|
||||
view = recycleBin.getScrapView(position, viewType);
|
||||
}
|
||||
view = getView(position, view, container);
|
||||
container.addView(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void destroyItem(ViewGroup container, int position,
|
||||
Object object) {
|
||||
View view = (View) object;
|
||||
container.removeView(view);
|
||||
int viewType = getItemViewType(position);
|
||||
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
|
||||
recycleBin.addScrapView(view, position, viewType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isViewFromObject(View view, Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of types of Views that will be created by
|
||||
* {@link #getView}. Each type represents a set of views that can be
|
||||
* converted in {@link #getView}. If the adapter always returns the same
|
||||
* type of View for all items, this method should return 1.
|
||||
* </p>
|
||||
* <p>
|
||||
* This method will only be called when when the adapter is set on the the
|
||||
* {@link AdapterView}.
|
||||
* </p>
|
||||
*
|
||||
* @return The number of types of Views that will be created by this adapter
|
||||
*/
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of View that will be created by {@link #getView} for the
|
||||
* specified item.
|
||||
*
|
||||
* @param position
|
||||
* The position of the item within the adapter's data set whose
|
||||
* view type we want.
|
||||
* @return An integer representing the type of View. Two views should share
|
||||
* the same type if one can be converted to the other in
|
||||
* {@link #getView}. Note: Integers must be in the range 0 to
|
||||
* {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
|
||||
* also be returned.
|
||||
* @see #IGNORE_ITEM_VIEW_TYPE
|
||||
*/
|
||||
public int getItemViewType(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a View that displays the data at the specified position in the data
|
||||
* set. You can either create a View manually or inflate it from an XML
|
||||
* layout file. When the View is inflated, the parent View (GridView,
|
||||
* ListView...) will apply default layout parameters unless you use
|
||||
* {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
|
||||
* to specify a root view and to prevent attachment to the root.
|
||||
*
|
||||
* @param position
|
||||
* The position of the item within the adapter's data set of the
|
||||
* item whose view we want.
|
||||
* @param convertView
|
||||
* The old view to reuse, if possible. Note: You should check
|
||||
* that this view is non-null and of an appropriate type before
|
||||
* using. If it is not possible to convert this view to display
|
||||
* the correct data, this method can create a new view.
|
||||
* Heterogeneous lists can specify their number of view types, so
|
||||
* that this View is always of the right type (see
|
||||
* {@link #getViewTypeCount()} and {@link #getItemViewType(int)}
|
||||
* ).
|
||||
* @param parent
|
||||
* The parent that this view will eventually be attached to
|
||||
* @return A View corresponding to the data at the specified position.
|
||||
*/
|
||||
public abstract View getView(int position, View convertView,
|
||||
ViewGroup container);
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.SearchResult;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SearchAdapter extends ListBaseAdapter<SearchResult> {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_news, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
SearchResult item = (SearchResult) mDatas.get(position);
|
||||
|
||||
vh.title.setText(item.getTitle());
|
||||
|
||||
if (item.getDescription() == null || StringUtils.isEmpty(item.getDescription())) {
|
||||
vh.description.setVisibility(View.GONE);
|
||||
} else {
|
||||
vh.description.setVisibility(View.VISIBLE);
|
||||
vh.description.setText(item.getDescription());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(item.getAuthor())) {
|
||||
vh.source.setText(item.getAuthor());
|
||||
} else {
|
||||
vh.source.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(item.getPubDate())) {
|
||||
vh.time.setText(StringUtils.friendly_time(item.getPubDate()));
|
||||
} else {
|
||||
vh.time.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
vh.tip.setVisibility(View.GONE);
|
||||
vh.comment_count.setVisibility(View.GONE);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.tv_title)TextView title;
|
||||
@Bind(R.id.tv_description)TextView description;
|
||||
@Bind(R.id.tv_source)TextView source;
|
||||
@Bind(R.id.tv_time)TextView time;
|
||||
@Bind(R.id.tv_comment_count)TextView comment_count;
|
||||
@Bind(R.id.iv_tip)ImageView tip;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.ui.SelectFriendsActivity;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
/**
|
||||
* <p>Created 15/8/27 下午9:29.</p>
|
||||
* <p><a href="mailto:730395591@qq.com">Email:730395591@qq.com</a></p>
|
||||
* <p><a href="http://www.happycodeboy.com">LeonLee Blog</a></p>
|
||||
*
|
||||
* @author 李文龙(LeonLee
|
||||
*
|
||||
* 搜索好友结果适配器
|
||||
*/
|
||||
public class SearchFriendAdapter extends BaseAdapter {
|
||||
|
||||
final List<SelectFriendsActivity.SearchItem> mSearchResults;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
public SearchFriendAdapter(List<SelectFriendsActivity.SearchItem> list) {
|
||||
this.mSearchResults = list;
|
||||
}
|
||||
|
||||
protected LayoutInflater getLayoutInflater(Context context) {
|
||||
if (mInflater == null) {
|
||||
mInflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
return mInflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mSearchResults.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectFriendsActivity.SearchItem getItem(int position) {
|
||||
return mSearchResults.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final NormalViewHolder holder;
|
||||
if(convertView == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(R.layout.list_cell_select_friend, parent, false);
|
||||
holder = new NormalViewHolder(convertView);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (NormalViewHolder) convertView.getTag();
|
||||
}
|
||||
holder.bind(getItem(position));
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class NormalViewHolder {
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
@Bind(R.id.cb_check)
|
||||
CheckBox checkBox;
|
||||
|
||||
NormalViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
avatar.setClickable(false);
|
||||
}
|
||||
|
||||
public void bind(SelectFriendsActivity.SearchItem item) {
|
||||
SelectFriendsActivity.FriendItem friendItem = item.getFriendItem();
|
||||
avatar.setAvatarUrl(friendItem.getFriend().getPortrait());
|
||||
checkBox.setChecked(friendItem.isSelected());
|
||||
|
||||
avatar.setDisplayCircle(false);
|
||||
|
||||
int start = item.getStartIndex();
|
||||
if(start != -1) {
|
||||
SpannableString ss = new SpannableString(friendItem.getFriend().getName());
|
||||
ss.setSpan(new ForegroundColorSpan(item.getHightLightColor()), start,
|
||||
start + item.getKeyLength(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
|
||||
name.setText(ss);
|
||||
} else {
|
||||
name.setText(friendItem.getFriend().getName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,219 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.ui.SelectFriendsActivity;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
/**
|
||||
* <p>Created 15/8/26 下午2:09.</p>
|
||||
* <p><a href="mailto:730395591@qq.com">Email:730395591@qq.com</a></p>
|
||||
* <p><a href="http://www.happycodeboy.com">LeonLee Blog</a></p>
|
||||
*
|
||||
* @author 李文龙(LeonLee)
|
||||
*/
|
||||
public class SelectFriendAdapter extends BaseAdapter {
|
||||
|
||||
private static final char DEFAULT_OTHER_LETTER = '#';
|
||||
|
||||
private static final int VIEWTYPE_HEADER = 0;
|
||||
private static final int VIEWTYPE_NORMAL = 1;
|
||||
|
||||
private List<ItemData> mList = new ArrayList<>();
|
||||
//记录索引值对应的位置
|
||||
private SparseArray<Integer> mPositionArray = new SparseArray<>();
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
public SelectFriendAdapter() {
|
||||
|
||||
}
|
||||
|
||||
protected LayoutInflater getLayoutInflater(Context context) {
|
||||
if (mInflater == null) {
|
||||
mInflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
return mInflater;
|
||||
}
|
||||
|
||||
public void setFriendItems(List<SelectFriendsActivity.FriendItem> list) {
|
||||
mPositionArray.clear();
|
||||
mList.clear();
|
||||
|
||||
//非字母开头的列表
|
||||
List<ItemData> otherList = null;
|
||||
|
||||
char lastIndex = '0';
|
||||
for(SelectFriendsActivity.FriendItem item : list) {
|
||||
char indexLetter;
|
||||
char c = item.getFirstLetter();
|
||||
if(c >= 'A' && c <= 'Z') {
|
||||
indexLetter = c;
|
||||
} else if(c >= 'a' && c <= 'z') {
|
||||
indexLetter = (char)(c - 'a' + 'A');
|
||||
} else {
|
||||
indexLetter = DEFAULT_OTHER_LETTER;
|
||||
}
|
||||
if(indexLetter == DEFAULT_OTHER_LETTER) {
|
||||
if(otherList == null) {
|
||||
otherList = new ArrayList<>();
|
||||
}
|
||||
otherList.add(new NormalItemData(item));
|
||||
} else {
|
||||
if (indexLetter != lastIndex) {
|
||||
mPositionArray.append(indexLetter, mList.size());
|
||||
mList.add(new HeaderItemData(String.valueOf(indexLetter)));
|
||||
lastIndex = indexLetter;
|
||||
}
|
||||
mList.add(new NormalItemData(item));
|
||||
}
|
||||
}
|
||||
//将没有索引的数据列表放到最后
|
||||
if(otherList != null && !otherList.isEmpty()) {
|
||||
mPositionArray.append(DEFAULT_OTHER_LETTER, mList.size());
|
||||
mList.add(new HeaderItemData(String.valueOf(DEFAULT_OTHER_LETTER)));
|
||||
mList.addAll(otherList);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/** 根据索引获取位置*/
|
||||
public int getPositionByIndex(char indexLetter) {
|
||||
Integer value = mPositionArray.get(indexLetter);
|
||||
if(value == null) {
|
||||
return -1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public SelectFriendsActivity.FriendItem getFriendItem(int position) {
|
||||
ItemData data = getItem(position);
|
||||
if(data instanceof NormalItemData) {
|
||||
return ((NormalItemData)data).friendItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemData getItem(int position) {
|
||||
return mList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return (getItem(position) instanceof HeaderItemData) ? VIEWTYPE_HEADER : VIEWTYPE_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final ItemData itemData = getItem(position);
|
||||
if(itemData instanceof HeaderItemData) {
|
||||
final HeaderViewHolder holder;
|
||||
if(convertView == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(R.layout.list_index_header, parent, false);
|
||||
holder = new HeaderViewHolder(convertView);
|
||||
convertView.setTag(holder);
|
||||
convertView.setEnabled(false);
|
||||
} else {
|
||||
holder = (HeaderViewHolder) convertView.getTag();
|
||||
}
|
||||
holder.bind(((HeaderItemData)itemData).index);
|
||||
} else {
|
||||
final NormalViewHolder holder;
|
||||
if(convertView == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(R.layout.list_cell_select_friend, parent, false);
|
||||
holder = new NormalViewHolder(convertView);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (NormalViewHolder) convertView.getTag();
|
||||
}
|
||||
holder.bind(((NormalItemData)itemData).friendItem);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private interface ItemData {}
|
||||
|
||||
static class HeaderItemData implements ItemData {
|
||||
String index;
|
||||
public HeaderItemData(String index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
static class NormalItemData implements ItemData {
|
||||
SelectFriendsActivity.FriendItem friendItem;
|
||||
|
||||
public NormalItemData(SelectFriendsActivity.FriendItem friendItem) {
|
||||
this.friendItem = friendItem;
|
||||
}
|
||||
}
|
||||
|
||||
static class HeaderViewHolder {
|
||||
@Bind(R.id.header_text)
|
||||
TextView text;
|
||||
|
||||
HeaderViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
public void bind(String index) {
|
||||
text.setText(index);
|
||||
}
|
||||
}
|
||||
|
||||
static class NormalViewHolder {
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
@Bind(R.id.cb_check)
|
||||
CheckBox checkBox;
|
||||
|
||||
NormalViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
avatar.setClickable(false);
|
||||
}
|
||||
|
||||
public void bind(SelectFriendsActivity.FriendItem item) {
|
||||
name.setText(item.getFriend().getName());
|
||||
avatar.setAvatarUrl(item.getFriend().getPortrait());
|
||||
checkBox.setChecked(item.isSelected());
|
||||
|
||||
avatar.setDisplayCircle(false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.SoftwareDec;
|
||||
import net.oschina.app.bean.SoftwareList;
|
||||
import net.oschina.app.util.ThemeSwitchUtils;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
public class SoftwareAdapter extends ListBaseAdapter<SoftwareDec> {
|
||||
|
||||
static class ViewHold {
|
||||
@Bind(R.id.tv_title)
|
||||
TextView name;
|
||||
@Bind(R.id.tv_software_des)
|
||||
TextView des;
|
||||
|
||||
public ViewHold(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
ViewHold vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(R.layout.list_cell_software, null);
|
||||
vh = new ViewHold(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHold) convertView.getTag();
|
||||
}
|
||||
|
||||
SoftwareDec softwareDes = (SoftwareDec) mDatas.get(position);
|
||||
vh.name.setText(softwareDes.getName());
|
||||
|
||||
if (AppContext.isOnReadedPostList(SoftwareList.PREF_READED_SOFTWARE_LIST,
|
||||
softwareDes.getName())) {
|
||||
vh.name.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleReadedColor()));
|
||||
} else {
|
||||
vh.name.setTextColor(parent.getContext().getResources()
|
||||
.getColor(ThemeSwitchUtils.getTitleUnReadedColor()));
|
||||
}
|
||||
|
||||
vh.des.setText(softwareDes.getDescription());
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.SoftwareCatalogList.SoftwareType;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SoftwareCatalogListAdapter extends ListBaseAdapter<SoftwareType> {
|
||||
|
||||
static class ViewHold {
|
||||
@Bind(R.id.tv_software_catalog_name)
|
||||
TextView name;
|
||||
|
||||
public ViewHold(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
ViewHold vh = null;
|
||||
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(R.layout
|
||||
.list_cell_softwarecatalog, null);
|
||||
vh = new ViewHold(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHold) convertView.getTag();
|
||||
}
|
||||
|
||||
SoftwareType softwareType = (SoftwareType) mDatas.get(position);
|
||||
vh.name.setText(softwareType.getName());
|
||||
return convertView;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.api.remote.OSChinaApi;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.Tweet;
|
||||
import net.oschina.app.bean.User;
|
||||
import net.oschina.app.emoji.InputHelper;
|
||||
import net.oschina.app.improve.utils.AssimilateUtils;
|
||||
import net.oschina.app.ui.OSCPhotosActivity;
|
||||
import net.oschina.app.util.DialogHelp;
|
||||
import net.oschina.app.util.ImageUtils;
|
||||
import net.oschina.app.util.PlatfromUtil;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.UIHelper;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
import net.oschina.app.widget.MyLinkMovementMethod;
|
||||
import net.oschina.app.widget.TweetTextView;
|
||||
|
||||
import org.kymjs.kjframe.Core;
|
||||
import org.kymjs.kjframe.utils.DensityUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import cz.msebera.android.httpclient.Header;
|
||||
|
||||
public class TweetAdapter extends ListBaseAdapter<Tweet> {
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.tv_tweet_name)
|
||||
TextView author;
|
||||
@Bind(R.id.tv_tweet_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tweet_item)
|
||||
TweetTextView content;
|
||||
@Bind(R.id.tv_tweet_comment_count)
|
||||
TextView commentcount;
|
||||
@Bind(R.id.tv_tweet_platform)
|
||||
TextView platform;
|
||||
@Bind(R.id.iv_tweet_face)
|
||||
AvatarView face;
|
||||
@Bind(R.id.iv_tweet_image)
|
||||
ImageView image;
|
||||
@Bind(R.id.iv_like_state)
|
||||
ImageView ivLikeState;
|
||||
// @Bind(R.id.tv_del)
|
||||
// TextView del;
|
||||
@Bind(R.id.tv_likeusers)
|
||||
TextView likeUsers;
|
||||
@Bind(R.id.tv_tweet_like_count)
|
||||
TextView tv_tweet_like_count;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap recordBitmap;
|
||||
private Context context;
|
||||
|
||||
final private AsyncHttpResponseHandler handler = new AsyncHttpResponseHandler() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
|
||||
Throwable arg3) {
|
||||
}
|
||||
};
|
||||
|
||||
private void initRecordImg(Context cxt) {
|
||||
recordBitmap = BitmapFactory.decodeResource(cxt.getResources(),
|
||||
R.mipmap.audio3);
|
||||
recordBitmap = ImageUtils.zoomBitmap(recordBitmap,
|
||||
DensityUtils.dip2px(cxt, 20f), DensityUtils.dip2px(cxt, 20f));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(final int position, View convertView, ViewGroup parent) {
|
||||
context = parent.getContext();
|
||||
final ViewHolder vh;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = View.inflate(context, R.layout.item_list_tweet, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
final Tweet tweet = mDatas.get(position);
|
||||
|
||||
// if (tweet.getAuthorid() == AppContext.getInstance().getLoginUid()) {
|
||||
// vh.del.setVisibility(View.VISIBLE);
|
||||
// vh.del.setOnClickListener(new OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// optionDel(context, tweet, position);
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// vh.del.setVisibility(View.GONE);
|
||||
// }
|
||||
|
||||
vh.face.setUserInfo(tweet.getAuthorid(), tweet.getAuthor());
|
||||
vh.face.setAvatarUrl(tweet.getPortrait());
|
||||
vh.author.setText(tweet.getAuthor());
|
||||
vh.time.setText(StringUtils.friendly_time(tweet.getPubDate()));
|
||||
vh.tv_tweet_like_count.setText(String.valueOf(tweet.getLikeCount()));
|
||||
|
||||
vh.content.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
vh.content.setFocusable(false);
|
||||
vh.content.setDispatchToParent(true);
|
||||
vh.content.setLongClickable(false);
|
||||
Spannable spannable = AssimilateUtils.assimilateOnlyLink(context, tweet.getBody());
|
||||
spannable = AssimilateUtils.assimilateOnlyAtUser(context, spannable);
|
||||
spannable = AssimilateUtils.assimilateOnlyTag(context, spannable);
|
||||
spannable = InputHelper.displayEmoji(context.getResources(), spannable);
|
||||
|
||||
if (!StringUtils.isEmpty(tweet.getAttach())) {
|
||||
if (recordBitmap == null) {
|
||||
initRecordImg(context);
|
||||
}
|
||||
ImageSpan recordImg = new ImageSpan(context, recordBitmap);
|
||||
SpannableString str = new SpannableString("c");
|
||||
str.setSpan(recordImg, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
vh.content.setText(str);
|
||||
vh.content.append(spannable);
|
||||
} else {
|
||||
vh.content.setText(spannable);
|
||||
}
|
||||
|
||||
vh.commentcount.setText(tweet.getCommentCount());
|
||||
|
||||
if (TextUtils.isEmpty(tweet.getImgSmall())) {
|
||||
vh.image.setVisibility(View.GONE);
|
||||
} else {
|
||||
vh.image.setVisibility(View.VISIBLE);
|
||||
new Core.Builder().view(vh.image).size(300, 300).url(tweet.getImgSmall() + "?300X300")
|
||||
.loadBitmapRes(R.drawable.pic_bg).doTask();
|
||||
vh.image.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
OSCPhotosActivity.showImagePreview(context, tweet.getImgBig());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tweet.setLikeUsers(context, vh.likeUsers, true);
|
||||
|
||||
vh.ivLikeState.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (AppContext.getInstance().isLogin()) {
|
||||
updateLikeState(vh, tweet);
|
||||
} else {
|
||||
AppContext.showToast("先登陆再赞~");
|
||||
UIHelper.showLoginActivity(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TypefaceUtils.setTypeface(vh.tvLikeState);
|
||||
if (tweet.getIsLike() == 1) {
|
||||
vh.ivLikeState.setImageResource(R.mipmap.ic_thumbup_actived);
|
||||
} else {
|
||||
vh.ivLikeState.setImageResource(R.mipmap.ic_thumbup_normal);
|
||||
}
|
||||
PlatfromUtil.setPlatFromString(vh.platform, tweet.getAppclient());
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private void updateLikeState(ViewHolder vh, Tweet tweet) {
|
||||
if (tweet.getIsLike() == 1) {
|
||||
tweet.setIsLike(0);
|
||||
tweet.setLikeCount(tweet.getLikeCount() - 1);
|
||||
if (!tweet.getLikeUser().isEmpty()) {
|
||||
tweet.getLikeUser().remove(0);
|
||||
}
|
||||
OSChinaApi.pubUnLikeTweet(tweet.getId(), tweet.getAuthorid(),
|
||||
handler);
|
||||
// vh.ivLikeState.setTextColor(AppContext.getInstance().getResources().getColor(R.color
|
||||
// .gray));
|
||||
vh.ivLikeState.setImageResource(R.mipmap.ic_thumbup_normal);
|
||||
} else {
|
||||
//vh.tvLikeState.setAnimation(KJAnimations.getScaleAnimation(1.5f, 300));
|
||||
List<User> likeUser = tweet.getLikeUser();
|
||||
if (likeUser!=null)
|
||||
tweet.getLikeUser().add(0, AppContext.getInstance().getLoginUser());
|
||||
OSChinaApi.pubLikeTweet(tweet.getId(), tweet.getAuthorid(), handler);
|
||||
// vh.tvLikeState.setTextColor(AppContext.getInstance().getResources().getColor(R.color
|
||||
// .day_colorPrimary));
|
||||
vh.ivLikeState.setImageResource(R.mipmap.ic_thumbup_actived);
|
||||
tweet.setIsLike(1);
|
||||
tweet.setLikeCount(tweet.getLikeCount() + 1);
|
||||
}
|
||||
vh.tv_tweet_like_count.setText(String.valueOf(tweet.getLikeCount()));
|
||||
tweet.setLikeUsers(context, vh.likeUsers, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void optionDel(Context context, final Tweet tweet, final int position) {
|
||||
|
||||
DialogHelp.getConfirmDialog(context, "确定删除吗?", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
OSChinaApi.deleteTweet(tweet.getAuthorid(), tweet.getId(),
|
||||
new AsyncHttpResponseHandler() {
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1,
|
||||
byte[] arg2) {
|
||||
mDatas.remove(position);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1,
|
||||
byte[] arg2, Throwable arg3) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.text.Spanned;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.TweetLike;
|
||||
import net.oschina.app.util.PlatfromUtil;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
import net.oschina.app.util.UIHelper;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
import net.oschina.app.widget.MyLinkMovementMethod;
|
||||
import net.oschina.app.widget.MyURLSpan;
|
||||
import net.oschina.app.widget.TweetTextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
/**
|
||||
* 动弹点赞适配器 TweetLikeAdapter.java
|
||||
*
|
||||
* @author 火蚁(http://my.oschina.net/u/253900)
|
||||
* @data 2015-4-10 上午10:19:19
|
||||
*/
|
||||
public class TweetLikeAdapter extends ListBaseAdapter<TweetLike> {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView,
|
||||
final ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_tweet_like, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
final TweetLike item = (TweetLike) mDatas.get(position);
|
||||
|
||||
vh.name.setText(item.getUser().getName().trim());
|
||||
|
||||
vh.action.setText("赞了我的动弹");
|
||||
|
||||
vh.time.setText(StringUtils.friendly_time(item.getDatatime().trim()));
|
||||
|
||||
PlatfromUtil.setPlatFromString(vh.from, item.getAppClient());
|
||||
vh.avatar.setUserInfo(item.getUser().getId(), item.getUser().getName());
|
||||
vh.avatar.setAvatarUrl(item.getUser().getPortrait());
|
||||
|
||||
vh.reply.setMovementMethod(MyLinkMovementMethod.a());
|
||||
vh.reply.setFocusable(false);
|
||||
vh.reply.setDispatchToParent(true);
|
||||
vh.reply.setLongClickable(false);
|
||||
Spanned span = UIHelper.parseActiveReply(item.getTweet().getAuthor(),
|
||||
item.getTweet().getBody());
|
||||
vh.reply.setText(span);
|
||||
MyURLSpan.parseLinkText(vh.reply, span);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
@Bind(R.id.tv_from)
|
||||
TextView from;
|
||||
@Bind(R.id.tv_time)
|
||||
TextView time;
|
||||
@Bind(R.id.tv_action)
|
||||
TextView action;
|
||||
@Bind(R.id.tv_reply)
|
||||
TweetTextView reply;
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.User;
|
||||
import net.oschina.app.widget.AvatarView;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
/**
|
||||
* TweetLikeUsersAdapter.java
|
||||
*
|
||||
* @author 火蚁(http://my.oschina.net/u/253900)
|
||||
*
|
||||
* @data 2015-3-26 下午4:11:25
|
||||
*/
|
||||
public class TweetLikeUsersAdapter extends ListBaseAdapter<User> {
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
// TODO Auto-generated method stub
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_tweet_like_user, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
User item = mDatas.get(position);
|
||||
vh.avatar.setAvatarUrl(item.getPortrait());
|
||||
vh.name.setText(item.getName());
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
|
||||
@Bind(R.id.iv_avatar)
|
||||
AvatarView avatar;
|
||||
@Bind(R.id.tv_name)
|
||||
TextView name;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.base.ListBaseAdapter;
|
||||
import net.oschina.app.bean.Favorite;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class UserFavoriteAdapter extends ListBaseAdapter<Favorite> {
|
||||
|
||||
static class ViewHolder {
|
||||
|
||||
@Bind(R.id.tv_favorite_title)
|
||||
TextView title;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRealView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null || convertView.getTag() == null) {
|
||||
convertView = getLayoutInflater(parent.getContext()).inflate(
|
||||
R.layout.list_cell_favorite, null);
|
||||
vh = new ViewHolder(convertView);
|
||||
convertView.setTag(vh);
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
Favorite favorite = (Favorite) mDatas.get(position);
|
||||
|
||||
vh.title.setText(favorite.getTitle());
|
||||
return convertView;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.util.ImageLoader;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ViewHolder {
|
||||
private SparseArray<View> mViews;
|
||||
private int mLayoutId;
|
||||
private View mConvertView;
|
||||
private int mPosition;
|
||||
private Callback mCaller;
|
||||
|
||||
public interface Callback {
|
||||
RequestManager getImgLoader();
|
||||
|
||||
LayoutInflater getInflate();
|
||||
}
|
||||
|
||||
|
||||
public ViewHolder(Callback caller, ViewGroup parent, int layoutId, int position) {
|
||||
this.mViews = new SparseArray<View>();
|
||||
this.mPosition = position;
|
||||
this.mLayoutId = layoutId;
|
||||
this.mCaller = caller;
|
||||
LayoutInflater inflater = caller.getInflate();
|
||||
this.mConvertView = inflater.inflate(layoutId, parent, false);
|
||||
this.mConvertView.setTag(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个viewHolder
|
||||
*
|
||||
* @param caller Caller
|
||||
* @param convertView view
|
||||
* @param parent parent view
|
||||
* @param layoutId 布局资源id
|
||||
* @param position 索引
|
||||
* @return
|
||||
*/
|
||||
public static ViewHolder getViewHolder(Callback caller, View convertView, ViewGroup parent, int layoutId, int position) {
|
||||
boolean needCreateView = false;
|
||||
ViewHolder vh = null;
|
||||
if (convertView == null) {
|
||||
needCreateView = true;
|
||||
} else {
|
||||
vh = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
if (vh != null && (vh.mLayoutId != layoutId)) {
|
||||
needCreateView = true;
|
||||
}
|
||||
if (needCreateView) {
|
||||
return new ViewHolder(caller, parent, layoutId, position);
|
||||
}
|
||||
return (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return this.mPosition;
|
||||
}
|
||||
|
||||
public int getLayoutId() {
|
||||
return mLayoutId;
|
||||
}
|
||||
|
||||
// 通过一个viewId来获取一个view
|
||||
public <T extends View> T getView(int viewId) {
|
||||
View view = mViews.get(viewId);
|
||||
if (view == null) {
|
||||
view = mConvertView.findViewById(viewId);
|
||||
mViews.put(viewId, view);
|
||||
}
|
||||
return (T) view;
|
||||
}
|
||||
|
||||
// 返回viewHolder的容器类
|
||||
public View getConvertView() {
|
||||
return this.mConvertView;
|
||||
}
|
||||
|
||||
// 给TextView设置文字
|
||||
public void setText(int viewId, String text) {
|
||||
if (TextUtils.isEmpty(text)) return;
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(text);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 给TextView设置文字
|
||||
public void setText(int viewId, SpannableString text) {
|
||||
if (text == null) return;
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(text);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setTextColor(int viewId, int textColor) {
|
||||
TextView tv = getView(viewId);
|
||||
tv.setTextColor(textColor);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setText(int viewId, Spanned text) {
|
||||
if (text == null) return;
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(text);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 给TextView设置文字
|
||||
public void setText(int viewId, int textRes) {
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(textRes);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setText(int viewId, int textRes, int bgRes, int textColor) {
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(textRes);
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
tv.setBackgroundResource(bgRes);
|
||||
tv.setTextColor(textColor);
|
||||
}
|
||||
|
||||
public void setText(int viewId, String text, boolean gone) {
|
||||
if (TextUtils.isEmpty(text) && gone) {
|
||||
getView(viewId).setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
setText(viewId, text);
|
||||
}
|
||||
|
||||
public void setText(int viewId, String text, int emptyRes) {
|
||||
TextView tv = getView(viewId);
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
tv.setText(emptyRes);
|
||||
} else {
|
||||
tv.setText(text);
|
||||
}
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setText(int viewId, String text, String emptyText) {
|
||||
TextView tv = getView(viewId);
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
tv.setText(emptyText);
|
||||
} else {
|
||||
tv.setText(text);
|
||||
}
|
||||
tv.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setImage(int viewId, int imgRes) {
|
||||
ImageView iv = getView(viewId);
|
||||
iv.setImageResource(imgRes);
|
||||
}
|
||||
|
||||
public void setImageForNet(int viewId, String imgUrl, int emptyRes) {
|
||||
ImageView iv = getView(viewId);
|
||||
RequestManager loader = mCaller.getImgLoader();
|
||||
ImageLoader.loadImage(loader, iv, imgUrl, emptyRes);
|
||||
}
|
||||
|
||||
public void setImageForNet(ImageView iv, String imgUrl, int emptyRes) {
|
||||
RequestManager loader = mCaller.getImgLoader();
|
||||
ImageLoader.loadImage(loader, iv, imgUrl, emptyRes);
|
||||
}
|
||||
|
||||
public void setImageForNet(ImageView iv, String imgUrl, int emptyRes, int errorRes) {
|
||||
RequestManager loader = mCaller.getImgLoader();
|
||||
ImageLoader.loadImage(loader, iv, imgUrl, emptyRes, errorRes);
|
||||
}
|
||||
|
||||
public void setImageForNet(int viewId, String imgUrl) {
|
||||
setImageForNet(viewId, imgUrl, R.drawable.bg_normal);
|
||||
}
|
||||
|
||||
// 设置头像
|
||||
public void setPortrait(int viewId, String imgUrl) {
|
||||
setImageForNet(viewId, imgUrl, R.drawable.bg_normal);
|
||||
}
|
||||
|
||||
public void setButtonText(int viewId, String text) {
|
||||
Button button = getView(viewId);
|
||||
button.setText(text);
|
||||
}
|
||||
|
||||
public void setOnClick(int viewId, View.OnClickListener onClickListener) {
|
||||
View view = getView(viewId);
|
||||
view.setOnClickListener(onClickListener);
|
||||
}
|
||||
|
||||
public void setGone(int viewId) {
|
||||
getView(viewId).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void setVisibility(int viewId) {
|
||||
getView(viewId).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void setInVisibility(int viewId) {
|
||||
getView(viewId).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
public boolean isVisibility(int viewId) {
|
||||
return (getView(viewId).getVisibility()) == View.VISIBLE;
|
||||
}
|
||||
|
||||
public void setEnabled(int viewId) {
|
||||
View view = getView(viewId);
|
||||
view.setEnabled(true);
|
||||
}
|
||||
|
||||
public void setEnabled(int viewId, boolean isEnable) {
|
||||
View view = getView(viewId);
|
||||
view.setEnabled(isEnable);
|
||||
}
|
||||
|
||||
public void setDisEnabled(int viewId) {
|
||||
View view = getView(viewId);
|
||||
view.setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.oschina.app.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public final class ViewPageInfo {
|
||||
|
||||
public final String tag;
|
||||
public final Class<?> clss;
|
||||
public final Bundle args;
|
||||
public final String title;
|
||||
|
||||
public ViewPageInfo(String _title, String _tag, Class<?> _class, Bundle _args) {
|
||||
title = _title;
|
||||
tag = _tag;
|
||||
clss = _class;
|
||||
args = _args;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.oschina.app.api;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
|
||||
public class ApiClientHelper {
|
||||
|
||||
/**
|
||||
* 获得请求的服务端数据的userAgent
|
||||
* @param appContext
|
||||
* @return
|
||||
*/
|
||||
public static String getUserAgent(AppContext appContext) {
|
||||
StringBuilder ua = new StringBuilder("OSChina.NET");
|
||||
ua.append('/' + appContext.getPackageInfo().versionName + '_'
|
||||
+ appContext.getPackageInfo().versionCode);// app版本信息
|
||||
ua.append("/Android");// 手机系统平台
|
||||
ua.append("/" + android.os.Build.VERSION.RELEASE);// 手机系统版本
|
||||
ua.append("/" + android.os.Build.MODEL); // 手机型号
|
||||
ua.append("/" + appContext.getAppId());// 客户端唯一标识
|
||||
return ua.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package net.oschina.app.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.loopj.android.http.AsyncHttpClient;
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
import com.loopj.android.http.RequestParams;
|
||||
|
||||
import net.oschina.app.AppContext;
|
||||
import net.oschina.app.util.TLog;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import cz.msebera.android.httpclient.client.params.ClientPNames;
|
||||
|
||||
public class ApiHttpClient {
|
||||
|
||||
public final static String HOST = "www.oschina.net";
|
||||
private static String API_URL = "http://www.oschina.net/%s";
|
||||
// public final static String HOST = "192.168.1.58:8080";
|
||||
//private static String API_URL = "http://192.168.1.15:8000/%s";
|
||||
|
||||
public static final String DELETE = "DELETE";
|
||||
public static final String GET = "GET";
|
||||
public static final String POST = "POST";
|
||||
public static final String PUT = "PUT";
|
||||
public static AsyncHttpClient client;
|
||||
|
||||
public ApiHttpClient() {
|
||||
}
|
||||
|
||||
public static AsyncHttpClient getHttpClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public static void cancelAll(Context context) {
|
||||
client.cancelRequests(context, true);
|
||||
}
|
||||
|
||||
public static void clearUserCookies(Context context) {
|
||||
// (new HttpClientCookieStore(context)).a();
|
||||
}
|
||||
|
||||
public static void delete(String partUrl, AsyncHttpResponseHandler handler) {
|
||||
client.delete(getAbsoluteApiUrl(partUrl), handler);
|
||||
log(new StringBuilder("DELETE ").append(partUrl).toString());
|
||||
}
|
||||
|
||||
public static void get(String partUrl, AsyncHttpResponseHandler handler) {
|
||||
client.get(getAbsoluteApiUrl(partUrl), handler);
|
||||
log(new StringBuilder("GET ").append(partUrl).toString());
|
||||
}
|
||||
|
||||
public static void get(String partUrl, RequestParams params,
|
||||
AsyncHttpResponseHandler handler) {
|
||||
client.get(getAbsoluteApiUrl(partUrl), params, handler);
|
||||
log(new StringBuilder("GET ").append(partUrl).append("&")
|
||||
.append(params).toString());
|
||||
}
|
||||
|
||||
public static String getAbsoluteApiUrl(String partUrl) {
|
||||
String url = partUrl;
|
||||
if (!partUrl.startsWith("http:") && !partUrl.startsWith("https:")) {
|
||||
url = String.format(API_URL, partUrl);
|
||||
}
|
||||
Log.d("BASE_CLIENT", "request:" + url);
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String getApiUrl() {
|
||||
return API_URL;
|
||||
}
|
||||
|
||||
public static void getDirect(String url, AsyncHttpResponseHandler handler) {
|
||||
client.get(url, handler);
|
||||
log(new StringBuilder("GET ").append(url).toString());
|
||||
}
|
||||
|
||||
public static void log(String log) {
|
||||
Log.d("BaseApi", log);
|
||||
TLog.log("Test", log);
|
||||
}
|
||||
|
||||
public static void post(String partUrl, AsyncHttpResponseHandler handler) {
|
||||
client.post(getAbsoluteApiUrl(partUrl), handler);
|
||||
log(new StringBuilder("POST ").append(partUrl).toString());
|
||||
}
|
||||
|
||||
public static void post(String partUrl, RequestParams params,
|
||||
AsyncHttpResponseHandler handler) {
|
||||
client.post(getAbsoluteApiUrl(partUrl), params, handler);
|
||||
log(new StringBuilder("POST ").append(partUrl).append("&")
|
||||
.append(params).toString());
|
||||
}
|
||||
|
||||
public static void postDirect(String url, RequestParams params,
|
||||
AsyncHttpResponseHandler handler) {
|
||||
client.post(url, params, handler);
|
||||
log(new StringBuilder("POST ").append(url).append("&").append(params)
|
||||
.toString());
|
||||
}
|
||||
|
||||
public static void put(String partUrl, AsyncHttpResponseHandler handler) {
|
||||
client.put(getAbsoluteApiUrl(partUrl), handler);
|
||||
log(new StringBuilder("PUT ").append(partUrl).toString());
|
||||
}
|
||||
|
||||
public static void put(String partUrl, RequestParams params,
|
||||
AsyncHttpResponseHandler handler) {
|
||||
client.put(getAbsoluteApiUrl(partUrl), params, handler);
|
||||
log(new StringBuilder("PUT ").append(partUrl).append("&")
|
||||
.append(params).toString());
|
||||
}
|
||||
|
||||
public static void setApiUrl(String apiUrl) {
|
||||
API_URL = apiUrl;
|
||||
}
|
||||
|
||||
public static void setHttpClient(AsyncHttpClient c) {
|
||||
client = c;
|
||||
client.addHeader("Accept-Language", Locale.getDefault().toString());
|
||||
client.addHeader("Host", HOST);
|
||||
client.addHeader("Connection", "Keep-Alive");
|
||||
client.getHttpClient().getParams()
|
||||
.setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
|
||||
|
||||
setUserAgent(ApiClientHelper.getUserAgent(AppContext.getInstance()));
|
||||
}
|
||||
|
||||
public static void setUserAgent(String userAgent) {
|
||||
client.setUserAgent(userAgent);
|
||||
}
|
||||
|
||||
public static void setCookie(String cookie) {
|
||||
client.addHeader("Cookie", cookie);
|
||||
}
|
||||
|
||||
private static String appCookie;
|
||||
|
||||
public static void cleanCookie() {
|
||||
appCookie = "";
|
||||
}
|
||||
|
||||
public static String getCookie(AppContext appContext) {
|
||||
if (appCookie == null || appCookie == "") {
|
||||
appCookie = appContext.getProperty("cookie");
|
||||
}
|
||||
return appCookie;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package net.oschina.app.api;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class ApiResponse {
|
||||
protected Object _data;
|
||||
protected String _message;
|
||||
protected int _errorCode;
|
||||
protected boolean _isOk;
|
||||
private long _total;
|
||||
private String _serverTime;
|
||||
private boolean isCanceled;
|
||||
|
||||
public ApiResponse(JSONObject json) {
|
||||
if (json != null) {
|
||||
setData(json.optJSONObject("data") == null ? json
|
||||
.optJSONArray("data") : json.optJSONObject("data"));
|
||||
setMessage(json.optString("result_desc"));
|
||||
setErrorCode(json.optInt("result_code"));
|
||||
setOk(getErrorCode() == 0);
|
||||
setServerTime(json.optString("timestamp"));
|
||||
}
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public void setData(Object _data) {
|
||||
this._data = _data;
|
||||
}
|
||||
|
||||
public boolean isOk() {
|
||||
return _isOk;
|
||||
}
|
||||
|
||||
public void setOk(boolean _isOk) {
|
||||
this._isOk = _isOk;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return _message;
|
||||
}
|
||||
|
||||
public void setMessage(String _message) {
|
||||
this._message = _message;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return _errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(int _errorCode) {
|
||||
this._errorCode = _errorCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "data:" + getData() + " message:" + getMessage() + " errocode:"
|
||||
+ _errorCode;
|
||||
}
|
||||
|
||||
public void update(ApiResponse response) {
|
||||
_message = response.getMessage();
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
_total = total;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return _total;
|
||||
}
|
||||
|
||||
public String getServerTime() {
|
||||
return _serverTime;
|
||||
}
|
||||
|
||||
public void setServerTime(String _serverTime) {
|
||||
this._serverTime = _serverTime;
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return isCanceled;
|
||||
}
|
||||
|
||||
public void setCanceled(boolean isCanceled) {
|
||||
this.isCanceled = isCanceled;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package net.oschina.app.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
|
||||
import android.os.Looper;
|
||||
import cz.msebera.android.httpclient.Header;
|
||||
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
|
||||
public class OperationResponseHandler extends AsyncHttpResponseHandler {
|
||||
|
||||
private Object[] args;
|
||||
|
||||
public OperationResponseHandler(Looper looper, Object... args) {
|
||||
super(looper);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public OperationResponseHandler(Object... args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {
|
||||
onFailure(arg0, arg3.getMessage(), args);
|
||||
}
|
||||
|
||||
public void onFailure(int code, String errorMessage, Object[] args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
|
||||
try {
|
||||
onSuccess(arg0, new ByteArrayInputStream(arg2), args);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
onFailure(arg0, e.getMessage(), args);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSuccess(int code, ByteArrayInputStream is, Object[] args)
|
||||
throws Exception {
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,267 @@
|
||||
package net.oschina.app.base;
|
||||
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.oschina.app.R;
|
||||
import net.oschina.app.util.StringUtils;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public class BaseApplication extends Application {
|
||||
private static String PREF_NAME = "creativelocker.pref";
|
||||
private static String LAST_REFRESH_TIME = "last_refresh_time.pref";
|
||||
static Context _context;
|
||||
static Resources _resource;
|
||||
private static String lastToast = "";
|
||||
private static long lastToastTime;
|
||||
|
||||
private static boolean sIsAtLeastGB;
|
||||
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||
sIsAtLeastGB = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
_context = getApplicationContext();
|
||||
_resource = _context.getResources();
|
||||
//LeakCanary.install(this);
|
||||
}
|
||||
|
||||
public static synchronized BaseApplication context() {
|
||||
return (BaseApplication) _context;
|
||||
}
|
||||
|
||||
public static Resources resources() {
|
||||
return _resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 放入已读文章列表中
|
||||
*
|
||||
*/
|
||||
public static void putReadedPostList(String prefFileName, String key,
|
||||
String value) {
|
||||
SharedPreferences preferences = getPreferences(prefFileName);
|
||||
int size = preferences.getAll().size();
|
||||
Editor editor = preferences.edit();
|
||||
if (size >= 100) {
|
||||
editor.clear();
|
||||
}
|
||||
editor.putString(key, value);
|
||||
apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取是否是已读的文章列表
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isOnReadedPostList(String prefFileName, String key) {
|
||||
return getPreferences(prefFileName).contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录列表上次刷新时间
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return void
|
||||
* @author 火蚁
|
||||
* 2015-2-9 下午2:21:37
|
||||
*/
|
||||
public static void putToLastRefreshTime(String key, String value) {
|
||||
SharedPreferences preferences = getPreferences(LAST_REFRESH_TIME);
|
||||
Editor editor = preferences.edit();
|
||||
editor.putString(key, value);
|
||||
apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表的上次刷新时间
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @author 火蚁
|
||||
* 2015-2-9 下午2:22:04
|
||||
*/
|
||||
public static String getLastRefreshTime(String key) {
|
||||
return getPreferences(LAST_REFRESH_TIME).getString(key, StringUtils.getCurTimeStr());
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
|
||||
public static void apply(SharedPreferences.Editor editor) {
|
||||
if (sIsAtLeastGB) {
|
||||
editor.apply();
|
||||
} else {
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public static void set(String key, int value) {
|
||||
Editor editor = getPreferences().edit();
|
||||
editor.putInt(key, value);
|
||||
apply(editor);
|
||||
}
|
||||
|
||||
public static void set(String key, boolean value) {
|
||||
Editor editor = getPreferences().edit();
|
||||
editor.putBoolean(key, value);
|
||||
apply(editor);
|
||||
}
|
||||
|
||||
public static void set(String key, String value) {
|
||||
Editor editor = getPreferences().edit();
|
||||
editor.putString(key, value);
|
||||
apply(editor);
|
||||
}
|
||||
|
||||
public static boolean get(String key, boolean defValue) {
|
||||
return getPreferences().getBoolean(key, defValue);
|
||||
}
|
||||
|
||||
public static String get(String key, String defValue) {
|
||||
return getPreferences().getString(key, defValue);
|
||||
}
|
||||
|
||||
public static int get(String key, int defValue) {
|
||||
return getPreferences().getInt(key, defValue);
|
||||
}
|
||||
|
||||
public static long get(String key, long defValue) {
|
||||
return getPreferences().getLong(key, defValue);
|
||||
}
|
||||
|
||||
public static float get(String key, float defValue) {
|
||||
return getPreferences().getFloat(key, defValue);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static SharedPreferences getPreferences() {
|
||||
SharedPreferences pre = context().getSharedPreferences(PREF_NAME,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
return pre;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static SharedPreferences getPreferences(String prefName) {
|
||||
return context().getSharedPreferences(prefName,
|
||||
Context.MODE_MULTI_PROCESS);
|
||||
}
|
||||
|
||||
public static int[] getDisplaySize() {
|
||||
return new int[]{getPreferences().getInt("screen_width", 480),
|
||||
getPreferences().getInt("screen_height", 854)};
|
||||
}
|
||||
|
||||
public static void saveDisplaySize(Activity activity) {
|
||||
DisplayMetrics displaymetrics = new DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay()
|
||||
.getMetrics(displaymetrics);
|
||||
SharedPreferences.Editor editor = getPreferences().edit();
|
||||
editor.putInt("screen_width", displaymetrics.widthPixels);
|
||||
editor.putInt("screen_height", displaymetrics.heightPixels);
|
||||
editor.putFloat("density", displaymetrics.density);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static String string(int id) {
|
||||
return _resource.getString(id);
|
||||
}
|
||||
|
||||
public static String string(int id, Object... args) {
|
||||
return _resource.getString(id, args);
|
||||
}
|
||||
|
||||
public static void showToast(int message) {
|
||||
showToast(message, Toast.LENGTH_LONG, 0);
|
||||
}
|
||||
|
||||
public static void showToast(String message) {
|
||||
showToast(message, Toast.LENGTH_LONG, 0, Gravity.BOTTOM);
|
||||
}
|
||||
|
||||
public static void showToast(int message, int icon) {
|
||||
showToast(message, Toast.LENGTH_LONG, icon);
|
||||
}
|
||||
|
||||
public static void showToast(String message, int icon) {
|
||||
showToast(message, Toast.LENGTH_LONG, icon, Gravity.BOTTOM);
|
||||
}
|
||||
|
||||
public static void showToastShort(int message) {
|
||||
showToast(message, Toast.LENGTH_SHORT, 0);
|
||||
}
|
||||
|
||||
public static void showToastShort(String message) {
|
||||
showToast(message, Toast.LENGTH_SHORT, 0, Gravity.BOTTOM);
|
||||
}
|
||||
|
||||
public static void showToastShort(int message, Object... args) {
|
||||
showToast(message, Toast.LENGTH_SHORT, 0, Gravity.BOTTOM, args);
|
||||
}
|
||||
|
||||
public static void showToast(int message, int duration, int icon) {
|
||||
showToast(message, duration, icon, Gravity.BOTTOM);
|
||||
}
|
||||
|
||||
public static void showToast(int message, int duration, int icon,
|
||||
int gravity) {
|
||||
showToast(context().getString(message), duration, icon, gravity);
|
||||
}
|
||||
|
||||
public static void showToast(int message, int duration, int icon,
|
||||
int gravity, Object... args) {
|
||||
showToast(context().getString(message, args), duration, icon, gravity);
|
||||
}
|
||||
|
||||
public static void showToast(String message, int duration, int icon,
|
||||
int gravity) {
|
||||
if (message != null && !message.equalsIgnoreCase("")) {
|
||||
long time = System.currentTimeMillis();
|
||||
if (!message.equalsIgnoreCase(lastToast)
|
||||
|| Math.abs(time - lastToastTime) > 2000) {
|
||||
View view = LayoutInflater.from(context()).inflate(
|
||||
R.layout.view_toast, null);
|
||||
((TextView) view.findViewById(R.id.title_tv)).setText(message);
|
||||
if (icon != 0) {
|
||||
((ImageView) view.findViewById(R.id.icon_iv))
|
||||
.setImageResource(icon);
|
||||
((ImageView) view.findViewById(R.id.icon_iv))
|
||||
.setVisibility(View.VISIBLE);
|
||||
}
|
||||
Toast toast = new Toast(context());
|
||||
toast.setView(view);
|
||||
if (gravity == Gravity.CENTER) {
|
||||
toast.setGravity(gravity, 0, 0);
|
||||
} else {
|
||||
toast.setGravity(gravity, 0, 35);
|
||||
}
|
||||
|
||||
toast.setDuration(duration);
|
||||
toast.show();
|
||||
lastToast = message;
|
||||
lastToastTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package net.oschina.app.base;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
/**
|
||||
* Created by 火蚁 on 15/5/18.
|
||||
*/
|
||||
public class BaseNewActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package net.oschina.app.bean;
|
||||
|
||||
/**
|
||||
* Created by huanghaibin
|
||||
* on 16-5-23.
|
||||
*/
|
||||
public class Banner extends Base {
|
||||
public static final int BANNER_TYPE_URL = 0;//链接新闻
|
||||
public static final int BANNER_TYPE_SOFTWARE = 1;//
|
||||
public static final int BANNER_TYPE_POST = 2;//
|
||||
public static final int BANNER_TYPE_BLOG = 3;//
|
||||
public static final int BANNER_TYPE_TRANSLATEL = 4;//
|
||||
public static final int BANNER_TYPE_EVENT = 5;//
|
||||
public static final int BANNER_TYPE_NEWS = 6;
|
||||
private String name;
|
||||
private String detail;
|
||||
private String img;
|
||||
private String href;
|
||||
private String pubDate;
|
||||
private int type;
|
||||
private long id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public String getImg() {
|
||||
return img;
|
||||
}
|
||||
|
||||
public void setImg(String img) {
|
||||
this.img = img;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return href;
|
||||
}
|
||||
|
||||
public void setHref(String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public String getPubDate() {
|
||||
return pubDate;
|
||||
}
|
||||
|
||||
public void setPubDate(String pubDate) {
|
||||
this.pubDate = pubDate;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package net.oschina.app.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import net.oschina.app.AppException;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* 二维码扫描实体类
|
||||
* @author 火蚁 (http://my.oschina.net/LittleDY)
|
||||
* @version 1.0
|
||||
* @created 2014-3-17
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BarCode extends Entity implements Serializable{
|
||||
|
||||
public final static String NODE_REQURE_LOGIN = "require_login";
|
||||
public final static String NODE_TYPE = "type";
|
||||
public final static String NODE_URL = "url";
|
||||
public final static String NODE_TITLE = "title";
|
||||
|
||||
public final static byte LOGIN_IN = 0x00;// 登录
|
||||
public final static byte SIGN_IN = 0x01;// 签到
|
||||
|
||||
private boolean requireLogin;// 是否需要登录
|
||||
private int type;// 类型
|
||||
private String url;// url地址
|
||||
private String title;// 标题
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public boolean isRequireLogin() {
|
||||
return requireLogin;
|
||||
}
|
||||
|
||||
public void setRequireLogin(boolean requireLogin) {
|
||||
this.requireLogin = requireLogin;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public static BarCode parse(String barCodeString) throws AppException {
|
||||
BarCode barCode = new BarCode();
|
||||
try {
|
||||
// 由字符串创建json对象
|
||||
JSONObject jsonObject = new JSONObject(barCodeString);
|
||||
// 取数据操作
|
||||
if (!jsonObject.isNull(NODE_REQURE_LOGIN)) {
|
||||
barCode.setRequireLogin(jsonObject.getBoolean(NODE_REQURE_LOGIN));
|
||||
} else {
|
||||
barCode.setUrl("www.oschina.net");
|
||||
}
|
||||
if (!jsonObject.isNull(NODE_TYPE)) {
|
||||
barCode.setType(jsonObject.getInt(NODE_TYPE));
|
||||
} else {
|
||||
barCode.setType(1);
|
||||
}
|
||||
if (!jsonObject.isNull(NODE_URL)) {
|
||||
barCode.setUrl(jsonObject.getString(NODE_URL));
|
||||
} else {
|
||||
barCode.setUrl("www.oschina.net");
|
||||
}
|
||||
if (!jsonObject.isNull(NODE_TITLE)) {
|
||||
barCode.setTitle(jsonObject.getString(NODE_TITLE));
|
||||
} else {
|
||||
barCode.setTitle("");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// 抛出一个json解析错误的异常
|
||||
throw AppException.json(e);
|
||||
}
|
||||
return barCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Barcode [requireLogin=" + requireLogin + ", type=" + type
|
||||
+ ", url=" + url + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package net.oschina.app.bean;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author HuangWenwei
|
||||
*
|
||||
* @date 2014年9月29日
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@XStreamAlias("blog")
|
||||
public class Blog extends Entity {
|
||||
|
||||
public final static int DOC_TYPE_REPASTE = 0;//转帖
|
||||
public final static int DOC_TYPE_ORIGINAL = 1;//原创
|
||||
|
||||
@XStreamAlias("title")
|
||||
private String title;
|
||||
|
||||
@XStreamAlias("url")
|
||||
private String url;
|
||||
|
||||
@XStreamAlias("where")
|
||||
private String where;
|
||||
|
||||
@XStreamAlias("commentCount")
|
||||
private int commentCount;
|
||||
|
||||
@XStreamAlias("body")
|
||||
private String body;
|
||||
|
||||
@XStreamAlias("author")
|
||||
private String authorname;
|
||||
|
||||
@XStreamAlias("authorid")
|
||||
private int authoruid;
|
||||
|
||||
@XStreamAlias("documentType")
|
||||
private int documentType;
|
||||
|
||||
@XStreamAlias("pubDate")
|
||||
private String pubDate;
|
||||
|
||||
@XStreamAlias("favorite")
|
||||
private int favorite;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getWhere() {
|
||||
return where;
|
||||
}
|
||||
|
||||
public void setWhere(String where) {
|
||||
this.where = where;
|
||||
}
|
||||
|
||||
public int getCommentCount() {
|
||||
return commentCount;
|
||||
}
|
||||
|
||||
public void setCommentCount(int commentCount) {
|
||||
this.commentCount = commentCount;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return authorname;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.authorname = author;
|
||||
}
|
||||
|
||||
public int getAuthorId() {
|
||||
return authoruid;
|
||||
}
|
||||
|
||||
public void setAuthorId(int authorId) {
|
||||
this.authoruid = authorId;
|
||||
}
|
||||
|
||||
public int getDocumenttype() {
|
||||
return documentType;
|
||||
}
|
||||
|
||||
public void setDocumenttype(int documenttype) {
|
||||
this.documentType = documenttype;
|
||||
}
|
||||
|
||||
public String getPubDate() {
|
||||
return pubDate;
|
||||
}
|
||||
|
||||
public void setPubDate(String pubDate) {
|
||||
this.pubDate = pubDate;
|
||||
}
|
||||
|
||||
public int getFavorite() {
|
||||
return favorite;
|
||||
}
|
||||
|
||||
public void setFavorite(int favorite) {
|
||||
this.favorite = favorite;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue