the original version

master
SantiagoMunz 9 years ago
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,51 @@
<<<<<<< HEAD
# OSChina Android [客户端](http://www.oschina.net/app/)
##写在前面的话
从2.3版本开始项目已经完成了gradle化完全迁移到了android studio如果想使用eclipse进行该项目的学习可以clone [tag v2.2.1](http://git.oschina.net/oschina/android-app/tree/v2.2.1/)不过需要注意的是eclipse需要按照开发环境中提到的进行butterknife注解设置
##开发环境
由于使用了较多的Eclipse项目Library项目目前使用的是Eclipse。需要提示的是由于butterknife注解特性Eclipse需要开启注解功能详细方法参考[这里](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0102/2247.html)。对于使用Android Studio的开发者可能你们需要等待一段时间项目目前正在Gradle化。当然我们也欢迎由你来转换项目并通过PullRequest提交给我们充分发挥社区化协作的优势。
##项目简述
1. 底部导航
* 主界面的底部TAB导航采用[FragmentTabHost](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/ui/MainTab.java)点击底部按钮时切换Fragment。中间的快捷操作按钮使用的是[自定义dialog](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/ui/QuickOptionDialog.java),通过点击时加入动画效果实现。
2. 一级界面
* 包括资讯、动弹两个模块,采用[ViewPagerFragment](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/viewpagerfragment/NewsViewPagerFragment.java)根据滑动到不同页面显示不同信息。
3. 详情界面
* 详情界面包括[博客详情](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/fragment/BlogDetailFragment.java)[动弹详情](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/fragment/TweetDetailFragment.java)[新闻详情](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/fragment/NewsDetailFragment.java)[帖子详情](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/fragment/PostDetailFragment.java) [活动详情](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/fragment/EventDetailFragment.java)等……是通过在Fragment中的WebView直接loadData()加载一段html数据并显示。
* 而详情Fragment的显示则是通过一个外部DetailActivity来根据传入的参数不同来加载不同的Fragment。
4. 链接跳转
* 整个应用打开链接的规则都定义在UIHelper.openBrowser()方法中本方法会根据不同的url去解析如果是www.oschina.net的链接则会调用相应的界面去展示如果是git.oschina.net我们目前会使用手机自带的浏览器打开(之后会改为使用[OscGit客户端](http://git.oschina.net/oschina/git-osc-android-project)打开)如果不是oschina的站内链接则使用内置浏览器打开。
5. 侧滑菜单
* [侧滑菜单](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/ui/NavigationDrawerFragment.java)采用系统的DrawerLayout实现。关于很多朋友好奇的左上角箭头是采用的开源控件[DrawerArrowDrawable](http://git.oschina.net/oschina/osc-android-app/blob/master/osc-android-app/src/net/oschina/app/widget/DrawerArrowDrawable.java)(准确的说不应该是控件而是一个Drawable)
##依赖包介绍
1. jar包依赖
* 网络请求库 **android-async-http** http://loopj.com/android-async-http/
* 注解绑定控件 **butterknife** http://jakewharton.github.io/butterknife/
* 网络图片加载库 **KJFrameForAndroid** http://git.oschina.net/kymjs/KJFrameForAndroid
* XML解析库 **xstream** http://xstream.codehaus.org/
2. 源码依赖
* **PhotoView-library** :用于图片预览界面展示
* **UmengShareLib** :用于分享到第三方平台
##开源协议
Copyright (C) 2014, The OSChina Open Source Project
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=======
#osc-android-app
>>>>>>> 096f8630d9ff1ecb7e413fdf8860848fc381ad9f

@ -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']
}
}
// linterror
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'
}

@ -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,393 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="net.oschina.app">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- suppress DeprecatedClassUsageInspection -->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:name=".AppContext"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppBaseTheme_Light"
tools:replace="android:allowBackup">
<activity
android:name=".AppStart"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppStartLoad">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.oschina.net"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.oschina.net"
android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="my.oschina.net"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="my.oschina.net"
android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".ui.TweetPubActivity"
android:label="弹一弹"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name=".ui.FindUserActivity"
android:screenOrientation="portrait" />
<activity
android:name=".ui.LoginActivity"
android:configChanges="keyboardHidden|orientation"
android:label="登录"
android:screenOrientation="portrait" />
<activity
android:name=".ui.LoginBindActivityChooseActivity"
android:label="登录"
android:screenOrientation="portrait" />
<activity
android:name=".ui.LoginAccountBindOpenIdActivity"
android:label="账号绑定"
android:screenOrientation="portrait" />
<activity
android:name=".ui.DetailActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".ui.SimpleBackActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|stateUnchanged" />
<activity
android:name=".team.ui.TeamMainActivity"
android:screenOrientation="portrait" />
<activity
android:name=".team.ui.TeamNewActiveActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".team.ui.TeamNewIssueActivity"
android:configChanges="keyboardHidden|orientation"
android:label="新团队任务"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name="com.dtr.zxing.activity.CaptureActivity"
android:label="@string/actionbar_title_qr_scan"
android:screenOrientation="portrait" />
<activity
android:name=".ui.ShakeActivity"
android:label="@string/actionbar_title_shake"
android:screenOrientation="portrait" />
<activity
android:name=".ui.SelectFriendsActivity"
android:label="@string/actionbar_title_select_friends"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".ui.OSCPhotosActivity"
android:label=""
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/ThemePhotoBrowse" />
<service android:name=".LogUploadService" />
<service android:name=".service.ServerTaskService">
<intent-filter>
<action android:name="net.oschina.app.ACTION_PUB_BLOG_COMMENT" />
</intent-filter>
<intent-filter>
<action android:name="net.oschina.app.ACTION_PUB_COMMENT" />
</intent-filter>
<intent-filter>
<action android:name="net.oschina.app.ACTION_PUB_POST" />
</intent-filter>
<intent-filter>
<action android:name="net.oschina.app.ACTION_PUB_TWEET" />
</intent-filter>
<intent-filter>
<action android:name="net.oschina.app.ACTION_PUB_SOFTWARE_TWEET" />
</intent-filter>
</service>
<receiver android:name=".broadcast.AlarmReceiver" />
<service
android:name=".service.NoticeService"
android:process=":notice" />
<service android:name=".service.DownloadService" />
<meta-data
android:name="UMENG_APPKEY"
android:value="53cb520c56240bbd7d076ce5" />
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL}" />
<!-- ############ QQ空间和QQ SSO授权的Activity注册 ############ -->
<activity
android:name="com.tencent.tauth.AuthActivity"
android:launchMode="singleTask"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- 100424468如果你使用的公用账号则不需要修改否则修改成你在QQ开放平台申请的 APP ID -->
<data android:scheme="tencent100424468" />
</intent-filter>
</activity>
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name=".wxapi.WXEntryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/Theme.Transparent" />
<activity
android:name=".improve.detail.activities.BlogDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="博客详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.QuestionDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="问答详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.tweet.activities.TweetDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="动弹详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.EventDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="活动详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.NewsDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="资讯详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.SoftwareDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="软件详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.TranslateDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="翻译详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.comment.QuestionAnswerDetailActivity"
android:configChanges="keyboardHidden|orientation"
android:label="问答答案详情"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.comment.CommentsActivity"
android:configChanges="keyboardHidden|orientation"
android:label="评论列表"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.tweet.activities.SoftwareTweetActivity"
android:configChanges="keyboardHidden|orientation"
android:label="软件动态列表"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.comment.CommentExsActivity"
android:configChanges="keyboardHidden|orientation"
android:label="评论列表"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
android:name=".improve.detail.activities.SchemeUrlActivity"
android:configChanges="keyboardHidden|orientation"
android:label="WebUrl"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<data
android:host="www.oschina.net"
android:path="/launch/app"
android:scheme="oscapp" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<activity
android:name=".improve.tweet.activities.TweetPublishActivity"
android:label="弹一弹"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Tweet.Publish">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
<activity
android:name=".improve.user.activities.OtherUserHomeActivity"
android:configChanges="keyboardHidden|orientation"
android:label="用户主页"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Base" />
<activity
android:name=".improve.media.SelectImageActivity"
android:configChanges="keyboardHidden|orientation"
android:label="选择图片"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Base.NoTitle" />
<activity
android:name=".improve.media.ImageGalleryActivity"
android:configChanges="keyboardHidden|orientation"
android:label="图片预览"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.Dialog.NoTitle.Translucent" />
<service
android:name=".improve.tweet.service.TweetPublishService"
android:exported="false"
android:process="net.oschina.app.tweet.TweetPublishService" />
</application>
</manifest>

@ -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,226 @@
/*
* 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.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Point;
import android.hardware.Camera;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
*
* : 1076559197@qq.com | tauchen1990@gmail.com
*
* :
*
* : 2014820
*
* :
*
*/
public final class CameraConfigurationManager {
private static final String TAG = "CameraConfiguration";
private static final int MIN_PREVIEW_PIXELS = 480 * 320;
private static final double MAX_ASPECT_DISTORTION = 0.15;
private final Context context;
// 屏幕分辨率
private Point screenResolution;
// 相机分辨率
private Point cameraResolution;
public CameraConfigurationManager(Context context) {
this.context = context;
}
public void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
Point theScreenResolution = new Point();
theScreenResolution = getDisplaySize(display);
screenResolution = theScreenResolution;
Log.i(TAG, "Screen resolution: " + screenResolution);
/** 因为换成了竖屏显示,所以不替换屏幕宽高得出的预览图是变形的 */
Point screenResolutionForCamera = new Point();
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
if (screenResolution.x < screenResolution.y) {
screenResolutionForCamera.x = screenResolution.y;
screenResolutionForCamera.y = screenResolution.x;
}
cameraResolution = findBestPreviewSizeValue(parameters, screenResolutionForCamera);
Log.i(TAG, "Camera resolution x: " + cameraResolution.x);
Log.i(TAG, "Camera resolution y: " + cameraResolution.y);
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private Point getDisplaySize(final Display display) {
final Point point = new Point();
try {
display.getSize(point);
} catch (NoSuchMethodError ignore) {
point.x = display.getWidth();
point.y = display.getHeight();
}
return point;
}
public void setDesiredCameraParameters(Camera camera, boolean safeMode) {
Camera.Parameters parameters = camera.getParameters();
if (parameters == null) {
Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
return;
}
Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
if (safeMode) {
Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
}
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
camera.setParameters(parameters);
Camera.Parameters afterParameters = camera.getParameters();
Camera.Size afterSize = afterParameters.getPreviewSize();
if (afterSize != null && (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize.height)) {
Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x' + cameraResolution.y + ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
cameraResolution.x = afterSize.width;
cameraResolution.y = afterSize.height;
}
/** 设置相机预览为竖屏 */
camera.setDisplayOrientation(90);
}
public Point getCameraResolution() {
return cameraResolution;
}
public Point getScreenResolution() {
return screenResolution;
}
/**
*
*
* @param parameters
* @param screenResolution
* @return
*/
private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
Log.w(TAG, "Device returned no supported preview sizes; using default");
Camera.Size defaultSize = parameters.getPreviewSize();
return new Point(defaultSize.width, defaultSize.height);
}
// Sort by size, descending
List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
if (Log.isLoggable(TAG, Log.INFO)) {
StringBuilder previewSizesString = new StringBuilder();
for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
previewSizesString.append(supportedPreviewSize.width).append('x').append(supportedPreviewSize.height).append(' ');
}
Log.i(TAG, "Supported preview sizes: " + previewSizesString);
}
double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
// Remove sizes that are unsuitable
Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewSize = it.next();
int realWidth = supportedPreviewSize.width;
int realHeight = supportedPreviewSize.height;
if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
it.remove();
continue;
}
boolean isCandidatePortrait = realWidth < realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
Point exactPoint = new Point(realWidth, realHeight);
Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
return exactPoint;
}
}
// If no exact match, use largest preview size. This was not a great
// idea on older devices because
// of the additional computation needed. We're likely to get here on
// newer Android 4+ devices, where
// the CPU is much more powerful.
if (!supportedPreviewSizes.isEmpty()) {
Camera.Size largestPreview = supportedPreviewSizes.get(0);
Point largestSize = new Point(largestPreview.width, largestPreview.height);
Log.i(TAG, "Using largest suitable preview size: " + largestSize);
return largestSize;
}
// If there is nothing at all suitable, return current preview size
Camera.Size defaultPreview = parameters.getPreviewSize();
Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
return defaultSize;
}
}

@ -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,159 @@
/*
* 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 android.graphics.Bitmap;
import android.graphics.Rect;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.dtr.zxing.activity.CaptureActivity;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import net.oschina.app.R;
public class DecodeHandler extends Handler {
private final CaptureActivity activity;
private final MultiFormatReader multiFormatReader;
private boolean running = true;
public DecodeHandler(CaptureActivity activity, Map<DecodeHintType, Object> hints) {
multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(hints);
this.activity = activity;
}
@Override
public void handleMessage(Message message) {
if (!running) {
return;
}
switch (message.what) {
case R.id.decode:
decode((byte[]) message.obj, message.arg1, message.arg2);
break;
case R.id.quit:
running = false;
Looper.myLooper().quit();
break;
}
}
/**
* Decode the data within the viewfinder rectangle, and time how long it
* took. For efficiency, reuse the same reader objects from one decode to
* the next.
*
* @param data
* The YUV preview frame.
* @param width
* The width of the preview frame.
* @param height
* The height of the preview frame.
*/
private void decode(byte[] data, int width, int height) {
Size size = activity.getCameraManager().getPreviewSize();
// 这里需要将获取的data翻转一下因为相机默认拿的的横屏的数据
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < size.height; y++) {
for (int x = 0; x < size.width; x++)
rotatedData[x * size.height + size.height - y - 1] = data[x + y * size.width];
}
// 宽高也要调整
int tmp = size.width;
size.width = size.height;
size.height = tmp;
Result rawResult = null;
PlanarYUVLuminanceSource source = buildLuminanceSource(rotatedData, size.width, size.height);
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
}
}
Handler handler = activity.getHandler();
if (rawResult != null) {
// Don't log the barcode contents for security.
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle();
bundleThumbnail(source, bundle);
message.setData(bundle);
message.sendToTarget();
}
} else {
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_failed);
message.sendToTarget();
}
}
}
private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
int[] pixels = source.renderThumbnail();
int width = source.getThumbnailWidth();
int height = source.getThumbnailHeight();
Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
}
/**
* A factory method to build the appropriate LuminanceSource object based on
* the format of the preview buffers, as described by Camera.Parameters.
*
* @param data
* A preview frame.
* @param width
* The width of the image.
* @param height
* The height of the image.
* @return A PlanarYUVLuminanceSource instance.
*/
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
Rect rect = activity.getCropRect();
if (rect == null) {
return null;
}
// Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
}
}

@ -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,167 @@
package net.oschina.app;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014925 5:29:00
*/
public class AppConfig {
private final static String APP_CONFIG = "config";
public final static String CONF_COOKIE = "cookie";
public final static String CONF_APP_UNIQUEID = "APP_UNIQUEID";
public static final String KEY_LOAD_IMAGE = "KEY_LOAD_IMAGE";
public static final String KEY_NOTIFICATION_ACCEPT = "KEY_NOTIFICATION_ACCEPT";
public static final String KEY_NOTIFICATION_SOUND = "KEY_NOTIFICATION_SOUND";
public static final String KEY_NOTIFICATION_VIBRATION = "KEY_NOTIFICATION_VIBRATION";
public static final String KEY_NOTIFICATION_DISABLE_WHEN_EXIT = "KEY_NOTIFICATION_DISABLE_WHEN_EXIT";
public static final String KEY_CHECK_UPDATE = "KEY_CHECK_UPDATE";
public static final String KEY_DOUBLE_CLICK_EXIT = "KEY_DOUBLE_CLICK_EXIT";
public static final String KEY_TWEET_DRAFT = "KEY_TWEET_DRAFT";
public static final String KEY_NOTE_DRAFT = "KEY_NOTE_DRAFT";
public static final String KEY_FRITST_START = "KEY_FRIST_START";
public static final String KEY_NIGHT_MODE_SWITCH = "night_mode_switch";
public static final String APP_QQ_KEY = "100942993";
// 默认存放图片的路径
public final static String DEFAULT_SAVE_IMAGE_PATH = Environment
.getExternalStorageDirectory()
+ File.separator
+ "OSChina"
+ File.separator + "osc_img" + File.separator;
// 默认存放文件下载的路径
public final static String DEFAULT_SAVE_FILE_PATH = Environment
.getExternalStorageDirectory()
+ File.separator
+ "OSChina"
+ File.separator + "download" + File.separator;
private Context mContext;
private static AppConfig appConfig;
public static AppConfig getAppConfig(Context context) {
if (appConfig == null) {
appConfig = new AppConfig();
appConfig.mContext = context;
}
return appConfig;
}
/**
* Preference
*/
public static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
public String get(String key) {
Properties props = get();
return (props != null) ? props.getProperty(key) : null;
}
public Properties get() {
FileInputStream fis = null;
Properties props = new Properties();
try {
// 读取files目录下的config
// fis = activity.openFileInput(APP_CONFIG);
// 读取app_config目录下的config
File dirConf = mContext.getDir(APP_CONFIG, Context.MODE_PRIVATE);
fis = new FileInputStream(dirConf.getPath() + File.separator
+ APP_CONFIG);
props.load(fis);
} catch (Exception e) {
} finally {
try {
fis.close();
} catch (Exception e) {
}
}
return props;
}
private void setProps(Properties p) {
FileOutputStream fos = null;
try {
// 把config建在files目录下
// fos = activity.openFileOutput(APP_CONFIG, Context.MODE_PRIVATE);
// 把config建在(自定义)app_config的目录下
File dirConf = mContext.getDir(APP_CONFIG, Context.MODE_PRIVATE);
File conf = new File(dirConf, APP_CONFIG);
fos = new FileOutputStream(conf);
p.store(fos, null);
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (Exception e) {
}
}
}
public void set(Properties ps) {
Properties props = get();
props.putAll(ps);
setProps(props);
}
public void set(String key, String value) {
Properties props = get();
props.setProperty(key, value);
setProps(props);
}
public void remove(String... key) {
Properties props = get();
for (String k : key)
props.remove(k);
setProps(props);
}
/**
* 1
* @param time time
* @return isExpiryDate true or false
*/
public static boolean isExpiryDate(String time) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
Date date = format.parse(time);
long delta = new Date().getTime() - date.getTime();
if (delta < 24L * 3600000L)
return true;
return false;
} catch (ParseException e) {
e.printStackTrace();
return false;
}
}
}

@ -0,0 +1,358 @@
package net.oschina.app;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import com.google.gson.Gson;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.PersistentCookieStore;
import net.oschina.app.api.ApiHttpClient;
import net.oschina.app.base.BaseApplication;
import net.oschina.app.bean.Constants;
import net.oschina.app.bean.User;
import net.oschina.app.cache.DataCleanManager;
import net.oschina.app.util.CyptoUtils;
import net.oschina.app.util.MethodsCompat;
import net.oschina.app.util.StringUtils;
import net.oschina.app.util.TLog;
import net.oschina.app.util.UIHelper;
import org.kymjs.kjframe.Core;
import org.kymjs.kjframe.http.HttpConfig;
import org.kymjs.kjframe.utils.KJLoger;
import java.util.Properties;
import java.util.UUID;
import static net.oschina.app.AppConfig.KEY_FRITST_START;
import static net.oschina.app.AppConfig.KEY_LOAD_IMAGE;
import static net.oschina.app.AppConfig.KEY_NIGHT_MODE_SWITCH;
import static net.oschina.app.AppConfig.KEY_TWEET_DRAFT;
/**
* 访
*
* @author (http://my.oschina.net/LittleDY)
* @version 1.0
* @created 2014-04-22
*/
public class AppContext extends BaseApplication {
public static final int PAGE_SIZE = 20;// 默认分页大小
private static AppContext instance;
private int loginUid;
private boolean login;
@Override
public void onCreate() {
super.onCreate();
instance = this;
init();
initLogin();
/*
// AppException 取消
Thread.setDefaultUncaughtExceptionHandler(AppException
.getAppExceptionHandler(this));
*/
UIHelper.sendBroadcastForNotice(this);
}
private void init() {
// 初始化网络请求
AsyncHttpClient client = new AsyncHttpClient();
PersistentCookieStore myCookieStore = new PersistentCookieStore(this);
client.setCookieStore(myCookieStore);
ApiHttpClient.setHttpClient(client);
ApiHttpClient.setCookie(ApiHttpClient.getCookie(this));
// Log控制器
KJLoger.openDebutLog(BuildConfig.DEBUG);
TLog.DEBUG = BuildConfig.DEBUG;
// Bitmap缓存地址
HttpConfig.CACHEPATH = "OSChina/imagecache";
}
private void initLogin() {
User user = getLoginUser();
if (null != user && user.getId() > 0) {
login = true;
loginUid = user.getId();
} else {
this.cleanLoginInfo();
}
}
/**
* appAppContext
*
* @return
*/
public static AppContext getInstance() {
return instance;
}
public boolean containsProperty(String key) {
Properties props = getProperties();
return props.containsKey(key);
}
public void setProperties(Properties ps) {
AppConfig.getAppConfig(this).set(ps);
}
public Properties getProperties() {
return AppConfig.getAppConfig(this).get();
}
public void setProperty(String key, String value) {
AppConfig.getAppConfig(this).set(key, value);
}
/**
* cookieAppConfig.CONF_COOKIE
*
* @param key
* @return
*/
public String getProperty(String key) {
String res = AppConfig.getAppConfig(this).get(key);
return res;
}
public void removeProperty(String... key) {
AppConfig.getAppConfig(this).remove(key);
}
/**
* App
*
* @return
*/
public String getAppId() {
String uniqueID = getProperty(AppConfig.CONF_APP_UNIQUEID);
if (StringUtils.isEmpty(uniqueID)) {
uniqueID = UUID.randomUUID().toString();
setProperty(AppConfig.CONF_APP_UNIQUEID, uniqueID);
}
return uniqueID;
}
/**
* App
*
* @return
*/
public PackageInfo getPackageInfo() {
PackageInfo info = null;
try {
info = getPackageManager().getPackageInfo(getPackageName(), 0);
} catch (NameNotFoundException e) {
e.printStackTrace(System.err);
}
if (info == null)
info = new PackageInfo();
return info;
}
/**
*
*
* @param user
*/
@SuppressWarnings("serial")
public void saveUserInfo(final User user) {
this.loginUid = user.getId();
this.login = true;
setProperties(new Properties() {
{
setProperty("user.uid", String.valueOf(user.getId()));
setProperty("user.name", user.getName());
setProperty("user.face", user.getPortrait());// 用户头像-文件名
setProperty("user.account", user.getAccount());
setProperty("user.pwd",
CyptoUtils.encode("oschinaApp", user.getPwd()));
setProperty("user.location", user.getLocation());
setProperty("user.followers", String.valueOf(user.getFollowers()));
setProperty("user.fans", String.valueOf(user.getFans()));
setProperty("user.score", String.valueOf(user.getScore()));
setProperty("user.favoritecount",
String.valueOf(user.getFavoritecount()));
setProperty("user.gender", String.valueOf(user.getGender()));
setProperty("user.isRememberMe",
String.valueOf(user.isRememberMe()));// 是否记住我的信息
}
});
}
/**
*
*
* @param user
*/
@SuppressWarnings("serial")
public void updateUserInfo(final User user) {
setProperties(new Properties() {
{
setProperty("user.name", user.getName());
setProperty("user.face", user.getPortrait());// 用户头像-文件名
setProperty("user.followers",
String.valueOf(user.getFollowers()));
setProperty("user.fans", String.valueOf(user.getFans()));
setProperty("user.score", String.valueOf(user.getScore()));
setProperty("user.favoritecount",
String.valueOf(user.getFavoritecount()));
setProperty("user.gender", String.valueOf(user.getGender()));
}
});
}
/**
*
*
* @return
*/
public User getLoginUser() {
User user = new User();
user.setId(StringUtils.toInt(getProperty("user.uid"), 0));
user.setName(getProperty("user.name"));
user.setPortrait(getProperty("user.face"));
user.setAccount(getProperty("user.account"));
user.setLocation(getProperty("user.location"));
user.setFollowers(StringUtils.toInt(getProperty("user.followers"), 0));
user.setFans(StringUtils.toInt(getProperty("user.fans"), 0));
user.setScore(StringUtils.toInt(getProperty("user.score"), 0));
user.setFavoritecount(StringUtils.toInt(
getProperty("user.favoritecount"), 0));
user.setRememberMe(StringUtils.toBool(getProperty("user.isRememberMe")));
user.setGender(getProperty("user.gender"));
return user;
}
/**
*
*/
public void cleanLoginInfo() {
this.loginUid = 0;
this.login = false;
removeProperty("user.uid", "user.name", "user.face", "user.location",
"user.followers", "user.fans", "user.score",
"user.isRememberMe", "user.gender", "user.favoritecount");
}
public int getLoginUid() {
return loginUid;
}
public boolean isLogin() {
return login;
}
/**
*
*/
public void Logout() {
cleanLoginInfo();
ApiHttpClient.cleanCookie();
this.cleanCookie();
this.login = false;
this.loginUid = 0;
Intent intent = new Intent(Constants.INTENT_ACTION_LOGOUT);
sendBroadcast(intent);
}
/**
*
*/
public void cleanCookie() {
removeProperty(AppConfig.CONF_COOKIE);
}
/**
* app
*/
public void clearAppCache() {
DataCleanManager.cleanDatabases(this);
// 清除数据缓存
DataCleanManager.cleanInternalCache(this);
// 2.2版本才有将应用缓存转移到sd卡的功能
if (isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)) {
DataCleanManager.cleanCustomCache(MethodsCompat
.getExternalCacheDir(this));
}
// 清除编辑器保存的临时内容
Properties props = getProperties();
for (Object key : props.keySet()) {
String _key = key.toString();
if (_key.startsWith("temp"))
removeProperty(_key);
}
Core.getKJBitmap().cleanCache();
}
public static void setLoadImage(boolean flag) {
set(KEY_LOAD_IMAGE, flag);
}
/**
*
*
* @param VersionCode
* @return
*/
public static boolean isMethodsCompat(int VersionCode) {
int currentVersion = android.os.Build.VERSION.SDK_INT;
return currentVersion >= VersionCode;
}
public static String getTweetDraft() {
return getPreferences().getString(
KEY_TWEET_DRAFT + getInstance().getLoginUid(), "");
}
public static void setTweetDraft(String draft) {
set(KEY_TWEET_DRAFT + getInstance().getLoginUid(), draft);
}
public static String getNoteDraft() {
return getPreferences().getString(
AppConfig.KEY_NOTE_DRAFT + getInstance().getLoginUid(), "");
}
public static void setNoteDraft(String draft) {
set(AppConfig.KEY_NOTE_DRAFT + getInstance().getLoginUid(), draft);
}
public static boolean isFristStart() {
return getPreferences().getBoolean(KEY_FRITST_START, true);
}
public static void setFristStart(boolean frist) {
set(KEY_FRITST_START, frist);
}
//夜间模式
public static boolean getNightModeSwitch() {
//return getPreferences().getBoolean(KEY_NIGHT_MODE_SWITCH, false);
return false;
}
// 设置夜间模式
public static void setNightModeSwitch(boolean on) {
set(KEY_NIGHT_MODE_SWITCH, on);
}
public static Gson createGson() {
com.google.gson.GsonBuilder gsonBuilder = new com.google.gson.GsonBuilder();
//gsonBuilder.setExclusionStrategies(new SpecificClassExclusionStrategy(null, Model.class));
gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss");
return gsonBuilder.create();
}
}

@ -0,0 +1,238 @@
package net.oschina.app;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Looper;
import net.oschina.app.util.UIHelper;
import org.kymjs.kjframe.utils.FileUtils;
import org.kymjs.kjframe.utils.SystemTool;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.UnknownHostException;
import cz.msebera.android.httpclient.HttpException;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @author kymjs (kymjs123@gmali.com)
* @created 2014925 5:34:05
*/
@SuppressWarnings("serial")
public class AppException extends Exception implements UncaughtExceptionHandler {
/**
*
*/
public final static byte TYPE_NETWORK = 0x01;
public final static byte TYPE_SOCKET = 0x02;
public final static byte TYPE_HTTP_CODE = 0x03;
public final static byte TYPE_HTTP_ERROR = 0x04;
public final static byte TYPE_XML = 0x05;
public final static byte TYPE_IO = 0x06;
public final static byte TYPE_RUN = 0x07;
public final static byte TYPE_JSON = 0x08;
public final static byte TYPE_FILENOTFOUND = 0x09;
private byte type;// 异常的类型
// 异常的状态码,这里一般是网络请求的状态码
private int code;
/**
* UncaughtException
*/
private AppContext mContext;
private AppException(Context context) {
this.mContext = (AppContext) context;
}
private AppException(byte type, int code, Exception excp) {
super(excp);
this.type = type;
this.code = code;
}
public int getCode() {
return this.code;
}
public int getType() {
return this.type;
}
public static AppException http(int code) {
return new AppException(TYPE_HTTP_CODE, code, null);
}
public static AppException http(Exception e) {
return new AppException(TYPE_HTTP_ERROR, 0, e);
}
public static AppException socket(Exception e) {
return new AppException(TYPE_SOCKET, 0, e);
}
public static AppException file(Exception e) {
return new AppException(TYPE_FILENOTFOUND, 0, e);
}
// io异常
public static AppException io(Exception e) {
return io(e, 0);
}
// io异常
public static AppException io(Exception e, int code) {
if (e instanceof UnknownHostException || e instanceof ConnectException) {
return new AppException(TYPE_NETWORK, code, e);
} else if (e instanceof IOException) {
return new AppException(TYPE_IO, code, e);
}
return run(e);
}
public static AppException xml(Exception e) {
return new AppException(TYPE_XML, 0, e);
}
public static AppException json(Exception e) {
return new AppException(TYPE_JSON, 0, e);
}
// 网络请求异常
public static AppException network(Exception e) {
if (e instanceof UnknownHostException || e instanceof ConnectException) {
return new AppException(TYPE_NETWORK, 0, e);
} else if (e instanceof HttpException) {
return http(e);
} else if (e instanceof SocketException) {
return socket(e);
}
return http(e);
}
public static AppException run(Exception e) {
return new AppException(TYPE_RUN, 0, e);
}
/**
* APP
*
* @param context
* @return
*/
public static AppException getAppExceptionHandler(Context context) {
return new AppException(context.getApplicationContext());
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
ex.printStackTrace();
if (!handleException(ex)) {
//System.exit(0);
}
}
/**
* :&
*
* @param ex
* @return true:;false
*/
private boolean handleException(final Throwable ex) {
if (ex == null || mContext == null) {
return false;
}
boolean success = true;
try {
success = saveToSDCard(ex);
} catch (Exception e) {
} finally {
if (!success) {
return false;
} else {
final Context context = AppManager.getAppManager()
.currentActivity();
// 显示异常信息&发送报告
new Thread() {
@Override
public void run() {
Looper.prepare();
// 拿到未捕获的异常,
UIHelper.sendAppCrashReport(context);
Looper.loop();
}
}.start();
}
}
return true;
}
private boolean saveToSDCard(Throwable ex) throws Exception {
boolean append = false;
File file = FileUtils.getSaveFile("OSChina", "OSCLog.log");
if (System.currentTimeMillis() - file.lastModified() > 5000) {
append = true;
}
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(
file, append)));
// 导出发生异常的时间
pw.println(SystemTool.getDataTime("yyyy-MM-dd-HH-mm-ss"));
// 导出手机信息
dumpPhoneInfo(pw);
pw.println();
// 导出异常的调用栈信息
ex.printStackTrace(pw);
pw.println();
pw.close();
return append;
}
private void dumpPhoneInfo(PrintWriter pw) throws NameNotFoundException {
// 应用的版本名称和版本号
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),
PackageManager.GET_ACTIVITIES);
pw.print("App Version: ");
pw.print(pi.versionName);
pw.print('_');
pw.println(pi.versionCode);
pw.println();
// android版本号
pw.print("OS Version: ");
pw.print(Build.VERSION.RELEASE);
pw.print("_");
pw.println(Build.VERSION.SDK_INT);
pw.println();
// 手机制造商
pw.print("Vendor: ");
pw.println(Build.MANUFACTURER);
pw.println();
// 手机型号
pw.print("Model: ");
pw.println(Build.MODEL);
pw.println();
// cpu架构
pw.print("CPU ABI: ");
pw.println(Build.CPU_ABI);
pw.println();
}
}

@ -0,0 +1,127 @@
package net.oschina.app;
import android.app.Activity;
import java.util.Stack;
/**
* activity
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 20141030 6:22:05
*/
public class AppManager {
private static Stack<Activity> activityStack;
private static AppManager instance;
private AppManager() {
}
/**
*
*/
public static AppManager getAppManager() {
if (instance == null) {
instance = new AppManager();
}
if (activityStack == null) {
activityStack = new Stack<Activity>();
}
return instance;
}
/**
* Activity
*
* @author kymjs
*/
public static Activity getActivity(Class<?> cls) {
if (activityStack != null)
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
return activity;
}
}
return null;
}
/**
* Activity
*/
public void addActivity(Activity activity) {
activityStack.add(activity);
}
/**
* Activity
*/
public Activity currentActivity() {
Activity activity = activityStack.lastElement();
return activity;
}
/**
* Activity
*/
public void finishActivity() {
Activity activity = activityStack.lastElement();
finishActivity(activity);
}
/**
* Activity
*/
public void finishActivity(Activity activity) {
if (activity != null && activityStack.contains(activity)) {
activityStack.remove(activity);
activity.finish();
}
}
/**
* Activity
*/
public void removeActivity(Activity activity) {
if (activity != null && activityStack.contains(activity)) {
activityStack.remove(activity);
}
}
/**
* Activity
*/
public void finishActivity(Class<?> cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
break;
}
}
}
/**
* Activity
*/
public void finishAllActivity() {
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
finishActivity(activityStack.get(i));
}
}
activityStack.clear();
}
/**
* 退
*/
public void AppExit() {
try {
finishAllActivity();
// System.exit(0);
} catch (Exception e) {
}
}
}

@ -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 2014929
*/
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,160 @@
package net.oschina.app.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.text.Html;
import android.text.Spanned;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.oschina.app.R;
import net.oschina.app.base.ListBaseAdapter;
import net.oschina.app.bean.Comment;
import net.oschina.app.bean.Comment.Refer;
import net.oschina.app.bean.Comment.Reply;
import net.oschina.app.emoji.InputHelper;
import net.oschina.app.util.PlatfromUtil;
import net.oschina.app.util.StringUtils;
import net.oschina.app.widget.AvatarView;
import net.oschina.app.widget.FloorView;
import net.oschina.app.widget.MyLinkMovementMethod;
import net.oschina.app.widget.MyURLSpan;
import net.oschina.app.widget.TweetTextView;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
public class CommentAdapter extends ListBaseAdapter<Comment> {
@SuppressLint({"InflateParams", "CutPasteId"})
@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_comment, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
try {
final Comment item = mDatas.get(position);
// 若Authorid为0则显示非会员
vh.name.setText(item.getAuthor()
+ (item.getAuthorId() == 0 ? "(非会员)" : ""));
vh.content.setMovementMethod(MyLinkMovementMethod.a());
vh.content.setFocusable(false);
vh.content.setDispatchToParent(true);
vh.content.setLongClickable(false);
Spanned span = Html.fromHtml(TweetTextView.modifyPath(item
.getContent()));
span = InputHelper.displayEmoji(parent.getContext().getResources(),
span.toString());
vh.content.setText(span);
MyURLSpan.parseLinkText(vh.content, span);
vh.time.setText(StringUtils.friendly_time(item.getPubDate()));
PlatfromUtil.setPlatFromString(vh.from, item.getAppClient());
// setup refers
setupRefers(parent.getContext(), vh, item.getRefers());
// setup replies
setupReplies(parent.getContext(), vh, item.getReplies());
vh.avatar.setAvatarUrl(item.getPortrait());
vh.avatar.setUserInfo(item.getAuthorId(), item.getAuthor());
} catch (Exception e) {
}
return convertView;
}
private void setupRefers(Context context, ViewHolder vh, List<Refer> refers) {
vh.refers.removeAllViews();
if (refers == null || refers.size() <= 0) {
vh.refers.setVisibility(View.GONE);
} else {
vh.refers.setVisibility(View.VISIBLE);
vh.refers.setComments(refers);
}
}
private void setupReplies(Context context, ViewHolder vh,
List<Reply> replies) {
vh.relies.removeAllViews();
if (replies == null || replies.size() <= 0) {
vh.relies.setVisibility(View.GONE);
} else {
vh.relies.setVisibility(View.VISIBLE);
// add count layout
View countView = getLayoutInflater(context).inflate(
R.layout.list_cell_reply_count, null, false);
TextView count = (TextView) countView
.findViewById(R.id.tv_comment_reply_count);
count.setText(context.getResources().getString(
R.string.comment_reply_count, replies.size()));
vh.relies.addView(countView);
// add reply item
for (Reply reply : replies) {
LinearLayout replyItemView = (LinearLayout) getLayoutInflater(
context).inflate(R.layout.list_cell_reply_name_content,
null, false);
replyItemView.setOrientation(LinearLayout.HORIZONTAL);
replyItemView
.setBackgroundResource(R.drawable.comment_background);
TextView name = (TextView) replyItemView
.findViewById(R.id.tv_reply_name);
name.setText(reply.rauthor + ":");
TweetTextView replyContent = (TweetTextView) replyItemView
.findViewById(R.id.tv_reply_content);
replyContent.setMovementMethod(MyLinkMovementMethod.a());
replyContent.setFocusable(false);
replyContent.setDispatchToParent(true);
replyContent.setLongClickable(false);
Spanned rcontent = Html.fromHtml(reply.rcontent);
replyContent.setText(rcontent);
MyURLSpan.parseLinkText(replyContent, rcontent);
vh.relies.addView(replyItemView);
}
}
}
static class ViewHolder {
@Bind(R.id.iv_avatar)
AvatarView avatar;
@Bind(R.id.tv_name)
TextView name;
@Bind(R.id.tv_time)
TextView time;
@Bind(R.id.tv_from)
TextView from;
@Bind(R.id.tv_content)
TweetTextView content;
@Bind(R.id.ly_relies)
LinearLayout relies;
@Bind(R.id.ly_refers)
FloorView refers;
ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}

@ -0,0 +1,102 @@
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.R;
import net.oschina.app.base.ListBaseAdapter;
import net.oschina.app.bean.Event;
import net.oschina.app.bean.EventList;
import org.kymjs.kjframe.Core;
import butterknife.ButterKnife;
import butterknife.Bind;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014109 6:22:54
*/
public class EventAdapter extends ListBaseAdapter<Event> {
private int eventType = EventList.EVENT_LIST_TYPE_NEW_EVENT;
static class ViewHolder {
@Bind(R.id.iv_event_status)
ImageView status;
@Bind(R.id.iv_event_img)
ImageView img;
@Bind(R.id.tv_event_title)
TextView title;
@Bind(R.id.tv_event_time)
TextView time;
@Bind(R.id.tv_event_spot)
TextView spot;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
public void setEventType(int eventType) {
this.eventType = eventType;
}
@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_event, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
Event item = mDatas.get(position);
setEventStatus(item, vh);
new Core.Builder().view(vh.img).url(item.getCover()).doTask();
vh.title.setText(item.getTitle());
vh.time.setText(item.getStartTime());
vh.spot.setText(item.getSpot());
return convertView;
}
private void setEventStatus(Event event, ViewHolder vh) {
switch (this.eventType) {
case EventList.EVENT_LIST_TYPE_NEW_EVENT:
if (event.getApplyStatus() == Event.APPLYSTATUS_CHECKING
|| event.getApplyStatus() == Event.APPLYSTATUS_CHECKED) {
vh.status
.setImageResource(R.mipmap.icon_event_status_checked);
vh.status.setVisibility(View.VISIBLE);
} else {
vh.status.setVisibility(View.GONE);
}
break;
case EventList.EVENT_LIST_TYPE_MY_EVENT:
if (event.getApplyStatus() == Event.APPLYSTATUS_ATTEND) {
vh.status.setImageResource(R.mipmap.icon_event_status_attend);
} else if (event.getStatus() == Event.EVNET_STATUS_APPLYING) {
vh.status
.setImageResource(R.mipmap.icon_event_status_checked);
} else {
vh.status.setImageResource(R.mipmap.icon_event_status_over);
}
vh.status.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
}

@ -0,0 +1,67 @@
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.Apply;
import net.oschina.app.widget.AvatarView;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 20141212 8:10:43
*
*/
public class EventApplyAdapter extends ListBaseAdapter<Apply> {
@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_friend, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
final Apply item = (Apply) mDatas.get(position);
vh.name.setText(item.getName());
vh.avatar.setUserInfo(item.getId(), item.getName());
vh.avatar.setAvatarUrl(item.getPortrait());
vh.from.setVisibility(View.GONE);
vh.desc.setText(item.getCompany() + " " + item.getJob());
return convertView;
}
static class ViewHolder {
@Bind(R.id.tv_name)
TextView name;
@Bind(R.id.tv_desc)
TextView desc;
@Bind(R.id.tv_from) TextView from;
@Bind(R.id.iv_gender)
ImageView gender;
@Bind(R.id.iv_avatar)
AvatarView avatar;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}

@ -0,0 +1,76 @@
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.User;
import net.oschina.app.widget.AvatarView;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014116 11:22:27
*
*/
public class FindUserAdapter extends ListBaseAdapter<User> {
@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_friend, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
final User item = (User) mDatas.get(position);
vh.name.setText(item.getName());
vh.from.setText(item.getFrom());
vh.desc.setVisibility(View.GONE);
int genderIcon = R.mipmap.userinfo_icon_male;
if ("女".equals(item.getGender())) {
genderIcon = R.mipmap.userinfo_icon_female;
}
vh.gender.setImageResource(genderIcon);
vh.avatar.setAvatarUrl(item.getPortrait());
vh.avatar.setUserInfo(item.getId(), item.getName());
return convertView;
}
static class ViewHolder {
@Bind(R.id.tv_name)
TextView name;
@Bind(R.id.tv_from)
TextView from;
@Bind(R.id.tv_desc)
TextView desc;
@Bind(R.id.iv_gender)
ImageView gender;
@Bind(R.id.iv_avatar)
AvatarView avatar;
public 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.R;
import net.oschina.app.base.ListBaseAdapter;
import net.oschina.app.bean.Friend;
import net.oschina.app.util.StringUtils;
import net.oschina.app.widget.AvatarView;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014116 11:22:27
*
*/
public class FriendAdapter extends ListBaseAdapter<Friend> {
@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_friend, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
final Friend item = mDatas.get(position);
vh.name.setText(item.getName());
String from = item.getFrom();
if (from != null || !StringUtils.isEmpty(from)) {
vh.from.setText(from);
} else {
vh.from.setVisibility(View.GONE);
}
String desc = item.getExpertise();
if (desc != null || !StringUtils.isEmpty(from) || !"<无>".equals(desc)) {
vh.desc.setText(item.getExpertise());
} else {
vh.desc.setVisibility(View.GONE);
}
vh.gender
.setImageResource(item.getGender() == 1 ? R.mipmap.userinfo_icon_male
: R.mipmap.userinfo_icon_female);
vh.avatar.setAvatarUrl(item.getPortrait());
vh.avatar.setUserInfo(item.getUserid(), item.getName());
return convertView;
}
static class ViewHolder {
@Bind(R.id.tv_name)
TextView name;
@Bind(R.id.tv_from)
TextView from;
@Bind(R.id.tv_desc)
TextView desc;
@Bind(R.id.iv_gender)
ImageView gender;
@Bind(R.id.iv_avatar)
AvatarView avatar;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}

@ -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,253 @@
package net.oschina.app.adapter;
import android.text.Html;
import android.text.Spanned;
import android.view.View;
import android.view.ViewGroup;
import android.widget.IconTextView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import net.oschina.app.AppContext;
import net.oschina.app.R;
import net.oschina.app.api.ApiHttpClient;
import net.oschina.app.base.ListBaseAdapter;
import net.oschina.app.bean.MessageDetail;
import net.oschina.app.emoji.InputHelper;
import net.oschina.app.ui.OSCPhotosActivity;
import net.oschina.app.util.ChatImageDisplayer;
import net.oschina.app.util.StringUtils;
import net.oschina.app.util.TDevice;
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.KJBitmap;
import org.kymjs.kjframe.bitmap.BitmapConfig;
import org.kymjs.kjframe.http.HttpConfig;
import java.lang.reflect.Field;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* 2015-09-16 ()
*/
public class MessageDetailAdapter extends ListBaseAdapter<MessageDetail> {
KJBitmap mKjBitmap = Core.getKJBitmap();
private OnRetrySendMessageListener mOnRetrySendMessageListener;
@Override
protected boolean loadMoreHasBg() {
return false;
}
public MessageDetailAdapter() {
try {
//初始化display设置图片的最大宽高和最小宽高
ChatImageDisplayer displayer = new ChatImageDisplayer(new BitmapConfig());
int maxWidth = TDevice.getDisplayMetrics().widthPixels / 2;
int maxHeight = maxWidth;
int minWidth = maxWidth / 2;
int minHeight = minWidth;
displayer.setImageSize(maxWidth, maxHeight, minWidth, minHeight);
//kjBitmap 不能设置自定义的displayer这里通过反射设置自定义的displayer
Class<?> classType = mKjBitmap.getClass();
Field field = classType.getDeclaredField("displayer");
field.setAccessible(true);
field.set(mKjBitmap, displayer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected View getRealView(int position, View convertView,
final ViewGroup parent) {
final MessageDetail item = mDatas.get(mDatas.size() - position - 1);
int itemType = 0;
if (item.getAuthorId() == AppContext.getInstance().getLoginUid()) {
itemType = 1;
}
boolean needCreateView = false;
ViewHolder vh = null;
if (convertView == null) {
needCreateView = true;
} else {
vh = (ViewHolder) convertView.getTag();
}
if (vh == null || vh.type != itemType) {
needCreateView = true;
}
if (needCreateView) {
convertView = getLayoutInflater(parent.getContext()).inflate(
itemType == 0 ? R.layout.list_cell_chat_from : R.layout.list_cell_chat_to,
null);
vh = new ViewHolder(convertView);
vh.type = itemType;
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
vh.avatar.setAvatarUrl(item.getPortrait());
vh.avatar.setUserInfo(item.getAuthorId(), item.getAuthor());
//判断是不是图片
if (item.getBtype() == 3) {
showImage(vh, item);
} else /*if(item.getBtype()==1)*/ {
//文本消息
showText(vh, item);
}
showStatus(vh, item);
//检查是否需要显示时间
if (item.isShowDate()) {
vh.time.setText(StringUtils.friendly_time3(item.getPubDate()));
vh.time.setVisibility(View.VISIBLE);
} else {
vh.time.setVisibility(View.GONE);
}
return convertView;
}
/**
*
*
* @param vh
* @param msg
*/
private void showText(ViewHolder vh, MessageDetail msg) {
vh.image.setVisibility(View.GONE);
vh.content.setVisibility(View.VISIBLE);
Spanned span = Html.fromHtml(msg.getContent());
span = InputHelper.displayEmoji(vh.content.getResources(), span);
vh.content.setText(span);
MyURLSpan.parseLinkText(vh.content, span);
}
/**
*
*
* @param vh
* @param msg
*/
private void showImage(ViewHolder vh, MessageDetail msg) {
vh.content.setVisibility(View.GONE);
vh.image.setVisibility(View.VISIBLE);
//加载图片
vh.image.setImageResource(R.mipmap.load_img_loading);
HttpConfig.sCookie = ApiHttpClient.getCookie(AppContext.getInstance());
mKjBitmap.display(vh.image, msg.getContent(), R.mipmap.load_img_error, 3000, 3000,
null);
}
/**
*
*
* @param vh
* @param msg
*/
private void showStatus(ViewHolder vh, MessageDetail msg) {
//如果msg正在发送则显示progressBar. 发送错误显示错误图标
if (msg.getStatus() != null && msg.getStatus() != MessageDetail.MessageStatus.NORMAL) {
vh.msgStatusPanel.setVisibility(View.VISIBLE);
if (msg.getStatus() == MessageDetail.MessageStatus.SENDING) {
//sending 正在发送
vh.progressBar.setVisibility(View.VISIBLE);
vh.error.setVisibility(View.GONE);
vh.error.setTag(null);
} else {
//error 发送出错
vh.progressBar.setVisibility(View.GONE);
vh.error.setVisibility(View.VISIBLE);
//设置tag为msg id以便点击重试发送
vh.error.setTag(msg.getId());
}
} else {
//注意此处隐藏要用INVISIBLE不能使用GONE
vh.msgStatusPanel.setVisibility(View.INVISIBLE);
vh.error.setTag(null);
}
}
public OnRetrySendMessageListener getOnRetrySendMessageListener() {
return mOnRetrySendMessageListener;
}
public void setOnRetrySendMessageListener(OnRetrySendMessageListener
onRetrySendMessageListener) {
this.mOnRetrySendMessageListener = onRetrySendMessageListener;
}
@Override
protected boolean hasFooterView() {
return false;
}
class ViewHolder {
int type;
@Bind(R.id.iv_avatar)
AvatarView avatar;
@Bind(R.id.tv_time)
TextView time;
@Bind(R.id.tv_content)
TweetTextView content;
@Bind(R.id.iv_img)
ImageView image;
@Bind(R.id.progress)
ProgressBar progressBar;
@Bind(R.id.rl_msg_status_panel)
RelativeLayout msgStatusPanel;
@Bind(R.id.itv_error)
IconTextView error;
ViewHolder(View view) {
ButterKnife.bind(this, view);
content.setMovementMethod(MyLinkMovementMethod.a());
content.setFocusable(false);
content.setDispatchToParent(true);
content.setLongClickable(false);
}
@OnClick(R.id.iv_img)
void viewImage(View v) {
if (v.getTag() != null) {
String url = (String) v.getTag();
OSCPhotosActivity.showImagePreview(v.getContext(), url);
}
}
/**
*
*
* @param v
*/
@OnClick(R.id.itv_error)
void retry(View v) {
if (v.getTag() != null && mOnRetrySendMessageListener != null) {
mOnRetrySendMessageListener.onRetrySendMessage((int) v.getTag());
}
}
}
public interface OnRetrySendMessageListener {
void onRetrySendMessage(int msgId);
}
}

@ -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,89 @@
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.Post;
import net.oschina.app.bean.PostList;
import net.oschina.app.util.HTMLUtil;
import net.oschina.app.util.StringUtils;
import net.oschina.app.util.ThemeSwitchUtils;
import net.oschina.app.widget.AvatarView;
import butterknife.ButterKnife;
import butterknife.Bind;
/**
* post
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014109 6:22:54
*/
public class PostAdapter extends ListBaseAdapter<Post> {
static class ViewHolder {
@Bind(R.id.tv_title)
TextView title;
@Bind(R.id.tv_description)
TextView description;
@Bind(R.id.tv_author)
TextView author;
@Bind(R.id.tv_date)
TextView time;
@Bind(R.id.tv_count)
TextView comment_count;
@Bind(R.id.iv_face)
public AvatarView face;
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_post, null);
vh = new ViewHolder(convertView);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
Post post = (Post) mDatas.get(position);
vh.face.setUserInfo(post.getAuthorId(), post.getAuthor());
vh.face.setAvatarUrl(post.getPortrait());
vh.title.setText(post.getTitle());
String body = post.getBody();
vh.description.setVisibility(View.GONE);
if (null != body || !StringUtils.isEmpty(body)) {
vh.description.setVisibility(View.VISIBLE);
vh.description.setText(HTMLUtil.replaceTag(post.getBody()).trim());
}
if (AppContext.isOnReadedPostList(PostList.PREF_READED_POST_LIST,
post.getId() + "")) {
vh.title.setTextColor(parent.getContext().getResources()
.getColor(ThemeSwitchUtils.getTitleReadedColor()));
} else {
vh.title.setTextColor(parent.getContext().getResources()
.getColor(ThemeSwitchUtils.getTitleUnReadedColor()));
}
vh.author.setText(post.getAuthor());
vh.time.setText(StringUtils.friendly_time(post.getPubDate()));
vh.comment_count.setText(post.getAnswerCount() + "回 / "
+ post.getViewCount() + "阅");
return convertView;
}
}

@ -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,145 @@
package net.oschina.app.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import net.oschina.app.R;
import net.oschina.app.widget.PagerSlidingTabStrip;
import java.util.ArrayList;
import java.util.Map;
@SuppressLint("Recycle")
public class ViewPageFragmentAdapter extends FragmentStatePagerAdapter {
private final Context mContext;
protected PagerSlidingTabStrip mPagerStrip;
private final ViewPager mViewPager;
public ArrayList<ViewPageInfo> mTabs = new ArrayList<ViewPageInfo>();
private Map<String, Fragment> mFragments = new ArrayMap<>();
public ViewPageFragmentAdapter(FragmentManager fm,
PagerSlidingTabStrip pageStrip, ViewPager pager) {
super(fm);
mContext = pager.getContext();
mPagerStrip = pageStrip;
mViewPager = pager;
mViewPager.setAdapter(this);
mPagerStrip.setViewPager(mViewPager);
}
public void addTab(String title, String tag, Class<?> clss, Bundle args) {
ViewPageInfo viewPageInfo = new ViewPageInfo(title, tag, clss, args);
addFragment(viewPageInfo);
}
public void addAllTab(ArrayList<ViewPageInfo> mTabs) {
for (ViewPageInfo viewPageInfo : mTabs) {
addFragment(viewPageInfo);
}
}
private void addFragment(ViewPageInfo info) {
if (info == null) {
return;
}
if (!TextUtils.isEmpty(info.title)) {
// 加入tab title
View v = LayoutInflater.from(mContext).inflate(
R.layout.base_viewpage_fragment_tab_item, null, false);
TextView title = (TextView) v.findViewById(R.id.tab_title);
title.setText(info.title);
mPagerStrip.addTab(v);
}
mTabs.add(info);
notifyDataSetChanged();
}
/**
*
*/
public void remove() {
remove(0);
}
/**
* tab
*
* @param index index0 tab
*/
public void remove(int index) {
if (mTabs.isEmpty()) {
return;
}
if (index < 0) {
index = 0;
}
if (index >= mTabs.size()) {
index = mTabs.size() - 1;
}
ViewPageInfo info = mTabs.get(index);
// 清理缓存
if (mFragments.containsKey(info.tag))
mFragments.remove(info.tag);
mTabs.remove(index);
mPagerStrip.removeTab(index, 1);
notifyDataSetChanged();
}
/**
* tab
*/
public void removeAll() {
if (mTabs.isEmpty()) {
return;
}
mFragments.clear();
mPagerStrip.removeAllTab();
mTabs.clear();
notifyDataSetChanged();
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
ViewPageInfo info = mTabs.get(position);
Fragment fragment = mFragments.get(info.tag);
if (fragment == null) {
fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
// 避免重复创建而进行缓存
mFragments.put(info.tag, fragment);
}
return fragment;
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).title;
}
}

@ -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 {
}
}

@ -0,0 +1,426 @@
package net.oschina.app.api.remote;
import java.io.File;
import java.io.FileNotFoundException;
import net.oschina.app.AppContext;
import net.oschina.app.api.ApiHttpClient;
import net.oschina.app.team.bean.TeamIssue;
import net.oschina.app.team.bean.TeamProject;
import android.text.TextUtils;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
/**
* osc team api
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @version 2015114 3:32:18
*
*/
public class OSChinaTeamApi {
/**
*
*
* @param teamId
* @param handler
*/
public static void getTeamProjectList(int teamId,
AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
ApiHttpClient.get("action/api/team_project_list", params, handler);
}
/**
* team
*
* @param teamId
* @param activeId
* @param pageIndex
* @param handler
*/
public static void getTeamCommentList(int teamId, int activeId,
int pageIndex, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("id", activeId);
params.put("pageIndex", pageIndex);
params.put("pageSize", 20);
ApiHttpClient.get("action/api/team_reply_list_by_activeid", params,
handler);
}
/***
*
*
* @author 2015-2-5 6:45:41
*
* @return void
* @param teamId
* @param teamProject
* @param handler
*/
public static void getTeamProjectMemberList(int teamId,
TeamProject teamProject, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("projectid", teamProject.getGit().getId());
String source = teamProject.getSource();
if (source != null && !TextUtils.isEmpty(source)) {
params.put("source", teamProject.getSource());
}
ApiHttpClient.get("action/api/team_project_member_list", params,
handler);
}
/***
*
*
* @author 2015-3-2 5:18:54
*
* @return void
* @param teamId
* @param project
* @param type
* "all"(default),"issue","code","other"
* @param page
* @param handler
*/
public static void getTeamProjectActiveList(int teamId,
TeamProject project, String type, int page,
AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("projectid", project.getGit().getId());
if (!TextUtils.isEmpty(project.getSource())) {
params.put("source", project.getSource());
}
params.put("type", type);
params.put("pageIndex", page);
ApiHttpClient.get("action/api/team_project_active_list", params,
handler);
}
/**
*
*
* @param uId
* id
* @param teamId
* id
* @param projectId
* id<=0
* @param source
* "Git@OSC","GitHub"(projectid)
*/
public static void getTeamCatalogIssueList(int uId, int teamId,
int projectId, String source, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", uId);
params.put("teamid", teamId);
params.put("projectid", projectId);
params.put("source", source);
ApiHttpClient.get("action/api/team_project_catalog_list", params,
handler);
}
/**
*
*
* @param teamId
* @param projectId
* id(-1, 0)
* @param catalogId
* id
* @param source
* "Team@OSC"(default),"Git@OSC","GitHub",projectid
*
* @param uid
* id
* @param state
* "all"(default),"opened","closed","outdate"
* @param scope
* "tome"(default,),"meto"()
* @param pageIndex
* @param pageSize
* @param handler
*/
public static void getTeamIssueList(int teamId, int projectId,
int catalogId, String source, int uid, String state, String scope,
int pageIndex, int pageSize, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("projectid", projectId);
params.put("catalogid", catalogId);
params.put("source", source);
params.put("uid", uid);
params.put("state", state);
params.put("scope", scope);
params.put("pageIndex", pageIndex);
params.put("pageSize", pageSize);
ApiHttpClient.get("action/api/team_issue_list", params, handler);
}
/***
*
*
* @author 2015-3-6 11:44:01
*
* @return void
* @param teamId
* @param issue
* @param target
* "state" : , "assignee" , "deadline" :
* @param handler
*/
public static void changeIssueState(int teamId, TeamIssue issue,
String target, AsyncHttpResponseHandler handler) {
if (issue == null)
return;
RequestParams params = new RequestParams();
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("teamid", teamId);
params.put("target", target);
params.put("issueid", issue.getId());
if (target.equals("state")) {
params.put("state", issue.getState());
} else if (target.equals("assignee")) {
params.put("assignee", issue.getToUser().getId());
} else if (target.equals("deadline")) {
params.put("deadline", issue.getDeadlineTime());
}
ApiHttpClient.post("action/api/team_issue_update", params, handler);
}
public static void pubTeamNewIssue(RequestParams params,
AsyncHttpResponseHandler handler) {
ApiHttpClient.post("action/api/team_issue_pub", params, handler);
}
/***
*
*
* @param type
* @param teamId
* @param uid
* @param pageIndex
* @param handler
*/
public static void getTeamDiscussList(String type, int teamId, int uid,
int pageIndex, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("type", type);
params.put("teamid", teamId);
params.put("uid", uid);
params.put("pageIndex", pageIndex);
params.put("pageSize", AppContext.PAGE_SIZE);
ApiHttpClient.get("action/api/team_discuss_list", params, handler);
}
/***
*
*
* @author 2015-2-2 6:19:54
*
* @return void
* @param teamId
* @param discussId
* @param handler
*/
public static void getTeamDiscussDetail(int teamId, int discussId,
AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("discussid", discussId);
ApiHttpClient.get("action/api/team_discuss_detail", params, handler);
}
/***
*
*
* @author 2015-2-3 2:42:54
*
* @return void
* @param uid
* @param teamId
* @param dicussId
* @param content
* @param handler
*/
public static void pubTeamDiscussReply(int uid, int teamId, int discussId,
String content, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", uid);
params.put("teamid", teamId);
params.put("discussid", discussId);
params.put("content", content);
ApiHttpClient.post("action/api/team_discuss_reply", params, handler);
}
/***
*
*
* @author 2015-3-6 3:31:07
*
* @return void
* @param teamId
* @param type
* -110-114 -118
* @param tweetId
* @param content
* @param handler
*/
public static void pubTeamTweetReply(int teamId, int type, long tweetId,
String content, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("type", type);
params.put("teamid", teamId);
params.put("tweetid", tweetId);
params.put("content", content);
ApiHttpClient.post("action/api/team_tweet_reply", params, handler);
}
/***
*
*
* @author 2015-1-27 7:47:17
*
*/
public static void getTeamIssueDetail(int teamId, int issueId,
AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("issueid", issueId);
ApiHttpClient.get("action/api/team_issue_detail", params, handler);
}
/***
*
*
* @param uid
* @param teamId
* @param year
* @param week
* @param pageIndex
* @param handler
*/
public static void getTeamDiaryList(int uid, int teamId, int year,
int week, int pageIndex, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", uid);
params.put("teamid", teamId);
params.put("year", year);
params.put("week", week);
params.put("pageIndex", pageIndex);
params.put("pageSize", AppContext.PAGE_SIZE);
ApiHttpClient.get("action/api/team_diary_list", params, handler);
}
/***
*
*
* @author 2015-2-2 11:39:04
*
* @return void
* @param teamId
* @param id
* @param type
* diary,discuss,issue
* @param pageIndex
* @param handler
*/
public static void getTeamReplyList(int teamId, int id, String type,
int pageIndex, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("id", id);
params.put("type", type);
params.put("pageIndex", pageIndex);
ApiHttpClient
.get("action/api/team_reply_list_by_type", params, handler);
}
/***
*
*
* @author 2015-3-9 2:46:13
*
* @return void
* @param teamId
* @param content
* @param img
* @param handler
*/
public static void pubTeamNewActive(int teamId, String content, File img,
AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("teamid", teamId);
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("msg", content);
params.put("appid", 3);
if (img != null) {
try {
params.put("img", img);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
ApiHttpClient.post("action/api/team_tweet_pub", params, handler);
}
/***
*
*
* @author 2015-3-10 4:53:49
*
* @return void
* @param teamId
* @param target
* @param childIssue
* @param handler
*/
public static void updateChildIssue(int teamId, String target,
TeamIssue childIssue, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("teamid", teamId);
params.put("childissueid", childIssue.getId());
params.put("target", target);
if (target.equals("state")) {
params.put("state", childIssue.getState());
} else {
params.put("title", childIssue.getTitle());
}
ApiHttpClient.post("action/api/team_issue_update_child_issue", params,
handler);
}
/***
*
*
* @author 2015-3-13 6:22:41
*
* @return void
* @param teamId
* @param issueId
* @param content
* @param handler
*/
public static void pubTeamIssueReply(int teamId, int issueId,
String content, AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", AppContext.getInstance().getLoginUid());
params.put("teamid", teamId);
params.put("content", content);
params.put("issueid", issueId);
ApiHttpClient.post("action/api/team_issue_reply", params, handler);
}
}

@ -0,0 +1,221 @@
package net.oschina.app.base;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import com.umeng.analytics.MobclickAgent;
import net.oschina.app.AppContext;
import net.oschina.app.AppManager;
import net.oschina.app.R;
import net.oschina.app.interf.BaseViewInterface;
import net.oschina.app.ui.dialog.CommonToast;
import net.oschina.app.ui.dialog.DialogControl;
import net.oschina.app.util.DialogHelp;
import net.oschina.app.util.TDevice;
import org.kymjs.kjframe.utils.StringUtils;
import butterknife.ButterKnife;
/**
* baseActionBar Activity
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014925 11:30:15 tonlin
*/
public abstract class BaseActivity extends AppCompatActivity implements
DialogControl, View.OnClickListener, BaseViewInterface {
public static final String INTENT_ACTION_EXIT_APP = "INTENT_ACTION_EXIT_APP";
private boolean _isVisible;
private ProgressDialog _waitDialog;
protected LayoutInflater mInflater;
protected ActionBar mActionBar;
private final String packageName4Umeng = this.getClass().getName();
private Context mContext4Umeng;
@Override
protected void onDestroy() {
super.onDestroy();
AppManager.getAppManager().finishActivity(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppContext.getNightModeSwitch()) {
setTheme(R.style.AppBaseTheme_Night);
} else {
setTheme(R.style.AppBaseTheme_Light);
}
AppManager.getAppManager().addActivity(this);
onBeforeSetContentLayout();
if (getLayoutId() != 0) {
setContentView(getLayoutId());
}
mActionBar = getSupportActionBar();
mInflater = getLayoutInflater();
if (hasActionBar()) {
initActionBar(mActionBar);
}
// 通过注解绑定控件
ButterKnife.bind(this);
init(savedInstanceState);
initView();
initData();
_isVisible = true;
//umeng analytics
MobclickAgent.setDebugMode(false);
MobclickAgent.openActivityDurationTrack(false);
MobclickAgent.setScenarioType(this, MobclickAgent.EScenarioType.E_UM_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
MobclickAgent.onPageStart(this.packageName4Umeng);
MobclickAgent.onResume(this.mContext4Umeng);
if (this.isFinishing()){
TDevice.hideSoftKeyboard(getCurrentFocus());
}
}
@Override
protected void onResume() {
super.onResume();
MobclickAgent.onPageStart(this.packageName4Umeng);
MobclickAgent.onResume(this.mContext4Umeng);
}
protected void onBeforeSetContentLayout() {
}
protected boolean hasActionBar() {
return getSupportActionBar() != null;
}
protected int getLayoutId() {
return 0;
}
protected View inflateView(int resId) {
return mInflater.inflate(resId, null);
}
protected int getActionBarTitle() {
return R.string.app_name;
}
protected boolean hasBackButton() {
return false;
}
protected void init(Bundle savedInstanceState) {
}
protected void initActionBar(ActionBar actionBar) {
if (actionBar == null)
return;
if (hasBackButton()) {
mActionBar.setDisplayHomeAsUpEnabled(true);
mActionBar.setHomeButtonEnabled(true);
} else {
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setDisplayUseLogoEnabled(false);
int titleRes = getActionBarTitle();
if (titleRes != 0) {
actionBar.setTitle(titleRes);
}
}
}
public void setActionBarTitle(int resId) {
if (resId != 0) {
setActionBarTitle(getString(resId));
}
}
public void setActionBarTitle(String title) {
if (StringUtils.isEmpty(title)) {
title = getString(R.string.app_name);
}
if (hasActionBar() && mActionBar != null) {
mActionBar.setTitle(title);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
public void showToast(int msgResid, int icon, int gravity) {
showToast(getString(msgResid), icon, gravity);
}
public void showToast(String message, int icon, int gravity) {
CommonToast toast = new CommonToast(this);
toast.setMessage(message);
toast.setMessageIc(icon);
toast.setLayoutGravity(gravity);
toast.show();
}
@Override
public ProgressDialog showWaitDialog() {
return showWaitDialog(R.string.loading);
}
@Override
public ProgressDialog showWaitDialog(int resid) {
return showWaitDialog(getString(resid));
}
@Override
public ProgressDialog showWaitDialog(String message) {
if (_isVisible) {
if (_waitDialog == null) {
_waitDialog = DialogHelp.getWaitDialog(this, message);
}
if (_waitDialog != null) {
_waitDialog.setMessage(message);
_waitDialog.show();
}
return _waitDialog;
}
return null;
}
@Override
public void hideWaitDialog() {
if (_isVisible && _waitDialog != null) {
try {
_waitDialog.dismiss();
_waitDialog = null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}

@ -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,119 @@
package net.oschina.app.base;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import net.oschina.app.AppContext;
import net.oschina.app.R;
import net.oschina.app.interf.BaseFragmentInterface;
import net.oschina.app.ui.dialog.DialogControl;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014925 11:18:46
*
*/
public class BaseFragment extends Fragment implements
android.view.View.OnClickListener, BaseFragmentInterface {
public static final int STATE_NONE = 0;
public static final int STATE_REFRESH = 1;
public static final int STATE_LOADMORE = 2;
public static final int STATE_NOMORE = 3;
public static final int STATE_PRESSNONE = 4;// 正在下拉但还没有到刷新的状态
public static int mState = STATE_NONE;
protected LayoutInflater mInflater;
public AppContext getApplication() {
return (AppContext) getActivity().getApplication();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
this.mInflater = inflater;
View view = super.onCreateView(inflater, container, savedInstanceState);
return view;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
}
protected int getLayoutId() {
return 0;
}
protected View inflateView(int resId) {
return this.mInflater.inflate(resId, null);
}
public boolean onBackPressed() {
return false;
}
protected void hideWaitDialog() {
FragmentActivity activity = getActivity();
if (activity instanceof DialogControl) {
((DialogControl) activity).hideWaitDialog();
}
}
protected ProgressDialog showWaitDialog() {
return showWaitDialog(R.string.loading);
}
protected ProgressDialog showWaitDialog(int resid) {
FragmentActivity activity = getActivity();
if (activity instanceof DialogControl) {
return ((DialogControl) activity).showWaitDialog(resid);
}
return null;
}
protected ProgressDialog showWaitDialog(String str) {
FragmentActivity activity = getActivity();
if (activity instanceof DialogControl) {
return ((DialogControl) activity).showWaitDialog(str);
}
return null;
}
@Override
public void initView(View view) {
}
@Override
public void initData() {
}
@Override
public void onClick(View v) {
}
}

@ -0,0 +1,586 @@
package net.oschina.app.base;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import com.loopj.android.http.AsyncHttpResponseHandler;
import net.oschina.app.AppContext;
import net.oschina.app.R;
import net.oschina.app.bean.Entity;
import net.oschina.app.bean.ListEntity;
import net.oschina.app.bean.Result;
import net.oschina.app.bean.ResultBean;
import net.oschina.app.cache.CacheManager;
import net.oschina.app.ui.empty.EmptyLayout;
import net.oschina.app.util.StringUtils;
import net.oschina.app.util.TDevice;
import net.oschina.app.util.ThemeSwitchUtils;
import net.oschina.app.util.XmlUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import cz.msebera.android.httpclient.Header;
@SuppressLint("NewApi")
public abstract class BaseListFragment<T extends Entity> extends BaseFragment
implements SwipeRefreshLayout.OnRefreshListener, OnItemClickListener, OnScrollListener {
public static final String BUNDLE_KEY_CATALOG = "BUNDLE_KEY_CATALOG";
@Bind(R.id.swiperefreshlayout)
protected SwipeRefreshLayout mSwipeRefreshLayout;
@Bind(R.id.listview)
protected ListView mListView;
protected ListBaseAdapter<T> mAdapter;
@Bind(R.id.error_layout)
protected EmptyLayout mErrorLayout;
protected int mStoreEmptyState = -1;
protected int mCurrentPage = 0;
protected int mCatalog = 1;
// 错误信息
protected Result mResult;
private AsyncTask<String, Void, ListEntity<T>> mCacheTask;
private ParserTask mParserTask;
protected AsyncHttpResponseHandler mHandler = new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers,
byte[] responseBytes) {
if (mCurrentPage == 0 && needAutoRefresh()) {
AppContext.putToLastRefreshTime(getCacheKey(),
StringUtils.getCurTimeStr());
}
if (isAdded()) {
if (mState == STATE_REFRESH) {
onRefreshNetworkSuccess();
}
executeParserTask(responseBytes);
} else {
executeOnLoadFinish();
}
}
@Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
Throwable arg3) {
if (isAdded()) {
readCacheData(getCacheKey());
} else {
executeOnLoadFinish();
}
}
};
@Override
protected int getLayoutId() {
return R.layout.fragment_pull_refresh_listview;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mInflater = inflater;
View view = inflater.inflate(getLayoutId(), container, false);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
initView(view);
}
@Override
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
mCatalog = args.getInt(BUNDLE_KEY_CATALOG, 0);
}
}
@Override
public void initView(View view) {
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swiperefresh_color1, R.color.swiperefresh_color2,
R.color.swiperefresh_color3, R.color.swiperefresh_color4);
mErrorLayout.setOnLayoutClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentPage = 0;
mState = STATE_REFRESH;
mErrorLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
requestData(true);
}
});
mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(this);
if (mAdapter != null) {
mListView.setAdapter(mAdapter);
mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
} else {
mAdapter = getListAdapter();
mListView.setAdapter(mAdapter);
if (requestDataIfViewCreated()) {
mErrorLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
mState = STATE_NONE;
requestData(false);
} else {
mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
}
}
if (mStoreEmptyState != -1) {
mErrorLayout.setErrorType(mStoreEmptyState);
}
}
@Override
public void onDestroyView() {
mStoreEmptyState = mErrorLayout.getErrorState();
super.onDestroyView();
}
@Override
public void onDestroy() {
cancelReadCacheTask();
cancelParserTask();
super.onDestroy();
}
protected abstract ListBaseAdapter<T> getListAdapter();
// 下拉刷新数据
@Override
public void onRefresh() {
if (mState == STATE_REFRESH) {
return;
}
// 设置顶部正在刷新
mListView.setSelection(0);
setSwipeRefreshLoadingState();
mCurrentPage = 0;
mState = STATE_REFRESH;
requestData(true);
}
protected boolean requestDataIfViewCreated() {
return true;
}
protected String getCacheKeyPrefix() {
return null;
}
protected ListEntity<T> parseList(InputStream is) throws Exception {
return null;
}
protected ListEntity<T> readList(Serializable seri) {
return null;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
}
private String getCacheKey() {
return getCacheKeyPrefix() + "_" + mCurrentPage;
}
// 是否需要自动刷新
protected boolean needAutoRefresh() {
return true;
}
/***
*
*
* @param refresh
* @return void
* @author 2015-2-9 3:16:12
*/
protected void requestData(boolean refresh) {
String key = getCacheKey();
if (isReadCacheData(refresh)) {
readCacheData(key);
} else {
// 取新的数据
sendRequestData();
}
}
/***
*
*
* @param refresh
* @return
* @author 2015-2-10 2:41:02
*/
protected boolean isReadCacheData(boolean refresh) {
String key = getCacheKey();
if (!TDevice.hasInternet()) {
return true;
}
// 第一页若不是主动刷新,缓存存在,优先取缓存的
if (CacheManager.isExistDataCache(getActivity(), key) && !refresh
&& mCurrentPage == 0) {
return true;
}
// 其他页数的,缓存存在以及还没有失效,优先取缓存的
if (CacheManager.isExistDataCache(getActivity(), key)
&& !CacheManager.isCacheDataFailure(getActivity(), key)
&& mCurrentPage != 0) {
return true;
}
return false;
}
// 是否到时间去刷新数据了
private boolean onTimeRefresh() {
String lastRefreshTime = AppContext.getLastRefreshTime(getCacheKey());
String currTime = StringUtils.getCurTimeStr();
long diff = StringUtils.calDateDifferent(lastRefreshTime, currTime);
return needAutoRefresh() && diff > getAutoRefreshTime();
}
/***
*
* <p>
*
*
* @return
* @author 2015-2-9 5:55:11
*/
protected long getAutoRefreshTime() {
return 12 * 60 * 60;
}
@Override
public void onResume() {
super.onResume();
if (onTimeRefresh()) {
onRefresh();
}
}
protected void sendRequestData() {
}
private void readCacheData(String cacheKey) {
cancelReadCacheTask();
mCacheTask = new CacheTask(getActivity()).execute(cacheKey);
}
private void cancelReadCacheTask() {
if (mCacheTask != null) {
mCacheTask.cancel(true);
mCacheTask = null;
}
}
protected void executeOnLoadDataSuccess(List<T> data) {
if (data == null) {
data = new ArrayList<T>();
}
if (mResult != null && !mResult.OK()) {
AppContext.showToast(mResult.getErrorMessage());
// 注销登陆密码已经修改cookie失效了
AppContext.getInstance().Logout();
}
mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
if (mCurrentPage == 0) {
mAdapter.clear();
}
for (int i = 0; i < data.size(); i++) {
if (compareTo(mAdapter.getData(), data.get(i))) {
data.remove(i);
i--;
}
}
int adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
if ((mAdapter.getCount() + data.size()) == 0) {
adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
} else if (data.size() == 0
|| (data.size() < getPageSize() && mCurrentPage == 0)) {
adapterState = ListBaseAdapter.STATE_NO_MORE;
mAdapter.notifyDataSetChanged();
} else {
adapterState = ListBaseAdapter.STATE_LOAD_MORE;
}
mAdapter.setState(adapterState);
mAdapter.addData(data);
// 判断等于是因为最后有一项是listview的状态
if (mAdapter.getCount() == 1) {
if (needShowEmptyNoData()) {
mErrorLayout.setErrorType(EmptyLayout.NODATA);
} else {
mAdapter.setState(ListBaseAdapter.STATE_EMPTY_ITEM);
mAdapter.notifyDataSetChanged();
}
}
}
/**
* listview
*
* @author 2015-1-27 6:18:59
*/
protected boolean needShowEmptyNoData() {
return true;
}
protected boolean compareTo(List<? extends Entity> data, Entity enity) {
int s = data.size();
if (enity != null) {
for (int i = 0; i < s; i++) {
if (enity.getId() == data.get(i).getId()) {
return true;
}
}
}
return false;
}
protected int getPageSize() {
return AppContext.PAGE_SIZE;
}
protected void onRefreshNetworkSuccess() {
}
protected void executeOnLoadDataError(String error) {
if (mCurrentPage == 0
&& !CacheManager.isExistDataCache(getActivity(), getCacheKey())) {
mErrorLayout.setErrorType(EmptyLayout.NETWORK_ERROR);
} else {
//在无网络时滚动到底部时mCurrentPage先自加了然而在失败时却
//没有减回来,如果刻意在无网络的情况下上拉,可以出现漏页问题
//find by TopJohn
mCurrentPage--;
mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
mAdapter.setState(ListBaseAdapter.STATE_NETWORK_ERROR);
mAdapter.notifyDataSetChanged();
}
}
// 完成刷新
protected void executeOnLoadFinish() {
setSwipeRefreshLoadedState();
mState = STATE_NONE;
}
/**
*
*/
protected void setSwipeRefreshLoadingState() {
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setRefreshing(true);
// 防止多次重复刷新
mSwipeRefreshLayout.setEnabled(false);
}
}
/**
*
*/
protected void setSwipeRefreshLoadedState() {
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setRefreshing(false);
mSwipeRefreshLayout.setEnabled(true);
}
}
private void executeParserTask(byte[] data) {
cancelParserTask();
mParserTask = new ParserTask(data);
mParserTask.execute();
}
private void cancelParserTask() {
if (mParserTask != null) {
mParserTask.cancel(true);
mParserTask = null;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mAdapter == null || mAdapter.getCount() == 0) {
return;
}
// 数据已经全部加载,或数据为空时,或正在加载,不处理滚动事件
if (mState == STATE_LOADMORE || mState == STATE_REFRESH) {
return;
}
// 判断是否滚动到底部
boolean scrollEnd = false;
try {
if (view.getPositionForView(mAdapter.getFooterView()) == view
.getLastVisiblePosition())
scrollEnd = true;
} catch (Exception e) {
scrollEnd = false;
}
if (mState == STATE_NONE && scrollEnd) {
if (mAdapter.getState() == ListBaseAdapter.STATE_LOAD_MORE
|| mAdapter.getState() == ListBaseAdapter.STATE_NETWORK_ERROR) {
mCurrentPage++;
mState = STATE_LOADMORE;
requestData(false);
mAdapter.setFooterViewLoading();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 数据已经全部加载,或数据为空时,或正在加载,不处理滚动事件
// if (mState == STATE_NOMORE || mState == STATE_LOADMORE
// || mState == STATE_REFRESH) {
// return;
// }
// if (mAdapter != null
// && mAdapter.getDataSize() > 0
// && mListView.getLastVisiblePosition() == (mListView.getCount() - 1))
// {
// if (mState == STATE_NONE
// && mAdapter.getState() == ListBaseAdapter.STATE_LOAD_MORE) {
// mState = STATE_LOADMORE;
// mCurrentPage++;
// requestData(true);
// }
// }
}
/**
*
*/
protected void saveToReadedList(final View view, final String prefFileName,
final String key) {
// 放入已读列表
AppContext.putReadedPostList(prefFileName, key, "true");
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
if (tvTitle != null) {
tvTitle.setTextColor(AppContext.getInstance().getResources().getColor(ThemeSwitchUtils.getTitleReadedColor()));
}
}
private class CacheTask extends AsyncTask<String, Void, ListEntity<T>> {
private final WeakReference<Context> mContext;
private CacheTask(Context context) {
mContext = new WeakReference<Context>(context);
}
@Override
protected ListEntity<T> doInBackground(String... params) {
Serializable seri = CacheManager.readObject(mContext.get(),
params[0]);
if (seri == null) {
return null;
} else {
return readList(seri);
}
}
@Override
protected void onPostExecute(ListEntity<T> list) {
super.onPostExecute(list);
if (list != null) {
executeOnLoadDataSuccess(list.getList());
} else {
executeOnLoadDataError(null);
}
executeOnLoadFinish();
}
}
class ParserTask extends AsyncTask<Void, Void, String> {
private final byte[] reponseData;
private boolean parserError;
private List<T> list;
public ParserTask(byte[] data) {
this.reponseData = data;
}
@Override
protected String doInBackground(Void... params) {
try {
final ListEntity<T> data = parseList(new ByteArrayInputStream(
reponseData));
CacheManager.saveObject(getActivity(), data, getCacheKey());
list = data.getList();
if (list == null) {
ResultBean resultBean = XmlUtils.toBean(ResultBean.class,
reponseData);
if (resultBean != null) {
mResult = resultBean.getResult();
}
}
} catch (Exception e) {
e.printStackTrace();
parserError = true;
}
return null;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if (parserError) {
readCacheData(getCacheKey());
} else {
executeOnLoadDataSuccess(list);
executeOnLoadFinish();
}
}
}
}

@ -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,90 @@
package net.oschina.app.base;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import net.oschina.app.R;
import net.oschina.app.adapter.ViewPageFragmentAdapter;
import net.oschina.app.ui.empty.EmptyLayout;
import net.oschina.app.widget.PagerSlidingTabStrip;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 2014116 4:59:50
*/
public abstract class BaseViewPagerFragment extends BaseFragment {
protected PagerSlidingTabStrip mTabStrip;
protected ViewPager mViewPager;
protected ViewPageFragmentAdapter mTabsAdapter;
protected EmptyLayout mErrorLayout;
protected View mRoot;
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mRoot == null) {
View root = inflater.inflate(R.layout.base_viewpage_fragment, null);
mTabStrip = (PagerSlidingTabStrip) root
.findViewById(R.id.pager_tabstrip);
mViewPager = (ViewPager) root.findViewById(R.id.pager);
mErrorLayout = (EmptyLayout) root.findViewById(R.id.error_layout);
mTabsAdapter = new ViewPageFragmentAdapter(getChildFragmentManager(),
mTabStrip, mViewPager);
setScreenPageLimit();
onSetupTabAdapter(mTabsAdapter);
mRoot = root;
}
return mRoot;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
/*
mTabStrip = (PagerSlidingTabStrip) view
.findViewById(R.id.pager_tabstrip);
mViewPager = (ViewPager) view.findViewById(R.id.pager);
mErrorLayout = (EmptyLayout) view.findViewById(R.id.error_layout);
mTabsAdapter = new ViewPageFragmentAdapter(getChildFragmentManager(),
mTabStrip, mViewPager);
setScreenPageLimit();
onSetupTabAdapter(mTabsAdapter);
*/
if (savedInstanceState != null) {
int pos = savedInstanceState.getInt("position");
mViewPager.setCurrentItem(pos, true);
}
}
protected void setScreenPageLimit() {
}
// @Override
// public void onSaveInstanceState(Bundle outState) {
// //No call for super(). Bug on API Level > 11.
// if (outState != null && mViewPager != null) {
// outState.putInt("position", mViewPager.getCurrentItem());
// }
// //super.onSaveInstanceState(outState);
// }
protected abstract void onSetupTabAdapter(ViewPageFragmentAdapter adapter);
}

@ -0,0 +1,178 @@
package net.oschina.app.base;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import com.loopj.android.http.AsyncHttpResponseHandler;
import net.oschina.app.bean.Entity;
import net.oschina.app.cache.CacheManager;
import net.oschina.app.ui.empty.EmptyLayout;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import butterknife.ButterKnife;
import cz.msebera.android.httpclient.Header;
/**
* headerBaseListFragment
*
* @desc
*
* BeseHaveHeaderListFragment.java
*
* @author (http://my.oschina.net/u/253900)
*
* @data 2015-1-27 3:02:42
*/
public abstract class BeseHaveHeaderListFragment<T1 extends Entity, T2 extends Serializable>
extends BaseListFragment<T1> {
protected T2 detailBean;// list 头部的详情实体类
protected Activity aty;
protected final AsyncHttpResponseHandler mDetailHandler = new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
try {
if (arg2 != null) {
T2 detail = getDetailBean(new ByteArrayInputStream(arg2));
if (detail != null) {
requstListData();
executeOnLoadDetailSuccess(detail);
new SaveCacheTask(getActivity(), detail,
getDetailCacheKey()).execute();
} else {
onFailure(arg0, arg1, arg2, null);
}
} else {
throw new RuntimeException("load detail error");
}
} catch (Exception e) {
e.printStackTrace();
onFailure(arg0, arg1, arg2, e);
}
}
@Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
Throwable arg3) {
readDetailCacheData(getDetailCacheKey());
}
};
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// 通过注解绑定控件
ButterKnife.bind(this, view);
mListView.addHeaderView(initHeaderView());
aty = getActivity();
super.initView(view);
requestDetailData(isRefresh());
}
protected boolean isRefresh() {
return false;
}
protected abstract void requestDetailData(boolean isRefresh);
protected abstract View initHeaderView();
protected abstract String getDetailCacheKey();
protected abstract void executeOnLoadDetailSuccess(T2 detailBean);
protected abstract T2 getDetailBean(ByteArrayInputStream is);
@Override
protected boolean requestDataIfViewCreated() {
return false;
}
private void requstListData() {
mState = STATE_REFRESH;
mAdapter.setState(ListBaseAdapter.STATE_LOAD_MORE);
sendRequestData();
}
/***
* header viewlistfragment
*/
@Override
protected boolean needShowEmptyNoData() {
return false;
}
protected void readDetailCacheData(String cacheKey) {
new ReadCacheTask(getActivity()).execute(cacheKey);
}
private class SaveCacheTask extends AsyncTask<Void, Void, Void> {
private final WeakReference<Context> mContext;
private final Serializable seri;
private final String key;
private SaveCacheTask(Context context, Serializable seri, String key) {
mContext = new WeakReference<Context>(context);
this.seri = seri;
this.key = key;
}
@Override
protected Void doInBackground(Void... params) {
CacheManager.saveObject(mContext.get(), seri, key);
return null;
}
}
private class ReadCacheTask extends AsyncTask<String, Void, T2> {
private final WeakReference<Context> mContext;
private ReadCacheTask(Context context) {
mContext = new WeakReference<Context>(context);
}
@Override
protected T2 doInBackground(String... params) {
if (mContext.get() != null) {
Serializable seri = CacheManager.readObject(mContext.get(),
params[0]);
if (seri == null) {
return null;
} else {
return (T2) seri;
}
}
return null;
}
@Override
protected void onPostExecute(T2 t) {
super.onPostExecute(t);
if (t != null) {
requstListData();
executeOnLoadDetailSuccess(t);
}
}
}
@Override
protected void executeOnLoadDataError(String error) {
mErrorLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
mAdapter.setState(ListBaseAdapter.STATE_NETWORK_ERROR);
mAdapter.notifyDataSetChanged();
}
@SuppressWarnings("unchecked")
protected <T extends View> T findHeaderView(View headerView, int viewId) {
return (T) headerView.findViewById(viewId);
}
}

@ -0,0 +1,588 @@
package net.oschina.app.base;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.TextHttpResponseHandler;
import net.oschina.app.AppContext;
import net.oschina.app.R;
import net.oschina.app.api.remote.OSChinaApi;
import net.oschina.app.bean.Report;
import net.oschina.app.bean.Result;
import net.oschina.app.bean.ResultBean;
import net.oschina.app.cache.CacheManager;
import net.oschina.app.emoji.OnSendClickListener;
import net.oschina.app.ui.DetailActivity;
import net.oschina.app.ui.ReportDialog;
import net.oschina.app.ui.ShareDialog;
import net.oschina.app.ui.empty.EmptyLayout;
import net.oschina.app.util.DialogHelp;
import net.oschina.app.util.FontSizeUtils;
import net.oschina.app.util.HTMLUtil;
import net.oschina.app.util.TDevice;
import net.oschina.app.util.UIHelper;
import net.oschina.app.util.XmlUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import butterknife.ButterKnife;
import cz.msebera.android.httpclient.Header;
/**
* fragment
* Created by on 15/5/25.
*/
public abstract class CommonDetailFragment<T extends Serializable> extends BaseFragment implements OnSendClickListener {
protected int mId;
protected EmptyLayout mEmptyLayout;
protected int mCommentCount = 0;
protected WebView mWebView;
protected T mDetail;
private AsyncTask<String, Void, T> mCacheTask;
private ShareDialog mDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
protected int getLayoutId() {
return R.layout.fragment_news_detail;
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container,
false);
mCommentCount = getActivity().getIntent().getIntExtra("comment_count",
0);
mId = getActivity().getIntent().getIntExtra("id", 0);
ButterKnife.bind(this, view);
initView(view);
initData();
requestData(false);
return view;
}
@Override
public void initView(View view) {
mEmptyLayout = (EmptyLayout) view.findViewById(R.id.error_layout);
setCommentCount(mCommentCount);
mWebView = (WebView) view.findViewById(R.id.webview);
UIHelper.initWebView(mWebView);
}
protected void setCommentCount(int commentCount) {
((DetailActivity) getActivity()).toolFragment
.setCommentCount(commentCount);
}
private void requestData(boolean refresh) {
String key = getCacheKey();
if (TDevice.hasInternet()
&& (!CacheManager.isExistDataCache(getActivity(), key) || refresh)) {
sendRequestDataForNet();
} else {
readCacheData(key);
}
}
@Override
public void onDestroyView() {
recycleWebView();
mDialog = null;
super.onDestroyView();
}
private void recycleWebView() {
if (mWebView != null) {
mWebView.setVisibility(View.GONE);
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
private void readCacheData(String cacheKey) {
cancelReadCache();
mCacheTask = new CacheTask(getActivity()).execute(cacheKey);
}
private void cancelReadCache() {
if (mCacheTask != null) {
mCacheTask.cancel(true);
mCacheTask = null;
}
}
protected AsyncHttpResponseHandler mDetailHeandler = new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
try {
T detail = parseData(new ByteArrayInputStream(arg2));
if (detail != null) {
mEmptyLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
executeOnLoadDataSuccess(detail);
saveCache(detail);
} else {
executeOnLoadDataError();
}
} catch (Exception e) {
e.printStackTrace();
executeOnLoadDataError();
}
}
@Override
public void onStart() {
super.onStart();
mEmptyLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
}
@Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
Throwable arg3) {
readCacheData(getCacheKey());
}
};
private class CacheTask extends AsyncTask<String, Void, T> {
private final WeakReference<Context> mContext;
private CacheTask(Context context) {
mContext = new WeakReference<Context>(context);
}
@Override
protected T doInBackground(String... params) {
if (mContext.get() != null) {
Serializable seri = CacheManager.readObject(mContext.get(),
params[0]);
if (seri == null) {
return null;
} else {
return (T) seri;
}
}
return null;
}
@Override
protected void onPostExecute(T detail) {
super.onPostExecute(detail);
mEmptyLayout.setErrorType(EmptyLayout.HIDE_LAYOUT);
if (detail != null) {
executeOnLoadDataSuccess(detail);
} else {
executeOnLoadDataError();
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
mEmptyLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
}
}
protected void executeOnLoadDataSuccess(T detail) {
this.mDetail = detail;
if (this.mDetail == null || TextUtils.isEmpty(this.getWebViewBody(detail))) {
executeOnLoadDataError();
return;
}
if (mWebView != null) {
mWebView.loadDataWithBaseURL("", this.getWebViewBody(detail), "text/html", "UTF-8", "");
// 显示存储的字体大小
mWebView.loadUrl(FontSizeUtils.getSaveFontSize());
boolean favoriteState = getFavoriteState() == 1;
setFavoriteState(favoriteState);
// 判断最新的评论数是否大于
if (getCommentCount() > mCommentCount) {
mCommentCount = getCommentCount();
}
setCommentCount(mCommentCount);
}
}
protected void executeOnLoadDataError() {
mEmptyLayout.setErrorType(EmptyLayout.NETWORK_ERROR);
mEmptyLayout.setOnLayoutClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mState = STATE_REFRESH;
mEmptyLayout.setErrorType(EmptyLayout.NETWORK_LOADING);
requestData(true);
}
});
}
protected void saveCache(T detail) {
new SaveCacheTask(getActivity(), detail, getCacheKey()).execute();
}
private class SaveCacheTask extends AsyncTask<Void, Void, Void> {
private final WeakReference<Context> mContext;
private final Serializable seri;
private final String key;
private SaveCacheTask(Context context, Serializable seri, String key) {
mContext = new WeakReference<Context>(context);
this.seri = seri;
this.key = key;
}
@Override
protected Void doInBackground(Void... params) {
CacheManager.saveObject(mContext.get(), seri, key);
return null;
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.common_detail_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
int i = 0;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh:
sendRequestDataForNet();
return false;
case R.id.font_size:
showChangeFontSize();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
AlertDialog fontSizeChange;
private void showChangeFontSize() {
final String[] items = getResources().getStringArray(
R.array.font_size);
fontSizeChange = DialogHelp.getSingleChoiceDialog(getActivity(), items, FontSizeUtils.getSaveFontSizeIndex(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// 更改字体大小
FontSizeUtils.saveFontSize(i);
mWebView.loadUrl(FontSizeUtils.getFontSize(i));
fontSizeChange.dismiss();
}
}).show();
}
// 收藏或者取消收藏
public void handleFavoriteOrNot() {
if (mDetail == null) {
return;
}
if (!TDevice.hasInternet()) {
AppContext.showToastShort(R.string.tip_no_internet);
return;
}
if (!AppContext.getInstance().isLogin()) {
UIHelper.showLoginActivity(getActivity());
return;
}
int uid = AppContext.getInstance().getLoginUid();
final boolean isFavorited = getFavoriteState() == 1 ? true : false;
AsyncHttpResponseHandler mFavoriteHandler = new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
try {
Result res = XmlUtils.toBean(ResultBean.class,
new ByteArrayInputStream(arg2)).getResult();
if (res.OK()) {
AppContext.showToast(res.getErrorMessage());
boolean newFavorited = !isFavorited;
setFavoriteState(newFavorited);
// 更新收藏的状态
updateFavoriteChanged(!newFavorited ? 0 : 1);
} else {
onFailure(arg0, arg1, arg2, null);
}
} catch (Exception e) {
e.printStackTrace();
onFailure(arg0, arg1, arg2, e);
}
}
@Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
Throwable arg3) {
AppContext.showToastShort(R.string.add_favorite_faile);
}
@Override
public void onStart() {
super.onStart();
showWaitDialog("请稍候...");
}
@Override
public void onFinish() {
super.onFinish();
hideWaitDialog();
}
};
if (isFavorited) {
OSChinaApi.delFavorite(uid, mId,
getFavoriteTargetType(), mFavoriteHandler);
} else {
OSChinaApi.addFavorite(uid, mId,
getFavoriteTargetType(), mFavoriteHandler);
}
}
private void setFavoriteState(boolean isFavorited) {
((DetailActivity) getActivity()).toolFragment.setFavorite(isFavorited);
}
// 举报帖子
public void onReportMenuClick() {
if (mId == 0 || mDetail == null) {
AppContext.showToast("正在加载,请稍等...");
}
if (!AppContext.getInstance().isLogin()) {
UIHelper.showLoginActivity(getActivity());
return;
}
int reportId = mId;
final ReportDialog dialog = new ReportDialog(getActivity(),
getRepotrUrl(), reportId, getReportType());
dialog.setCancelable(true);
dialog.setTitle(R.string.report);
dialog.setCanceledOnTouchOutside(true);
dialog.setNegativeButton(R.string.cancle, null);
final TextHttpResponseHandler mReportHandler = new TextHttpResponseHandler() {
@Override
public void onSuccess(int arg0, Header[] arg1, String arg2) {
if (TextUtils.isEmpty(arg2)) {
AppContext.showToastShort(R.string.tip_report_success);
} else {
AppContext.showToastShort(new String(arg2));
}
}
@Override
public void onFailure(int arg0, Header[] arg1, String arg2,
Throwable arg3) {
AppContext.showToastShort(R.string.tip_report_faile);
}
@Override
public void onFinish() {
hideWaitDialog();
}
};
dialog.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
Report report = null;
if ((report = dialog.getReport()) != null) {
showWaitDialog(R.string.progress_submit);
OSChinaApi.report(report, mReportHandler);
}
d.dismiss();
}
});
dialog.show();
}
// 分享
public void handleShare() {
if (mDetail == null || TextUtils.isEmpty(getShareContent())
|| TextUtils.isEmpty(getShareUrl()) || TextUtils.isEmpty(getShareTitle())) {
AppContext.showToast("内容加载失败...");
return;
}
if (mDialog == null)
mDialog = new ShareDialog(getActivity());
mDialog.setCancelable(true);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setTitle(R.string.share_to);
mDialog.setShareInfo(getShareTitle(), getShareContent(), getShareUrl());
mDialog.show();
}
// 显示评论列表
public void onCilckShowComment() {
showCommentView();
}
// 刷新数据
protected void refresh() {
sendRequestDataForNet();
}
protected AsyncHttpResponseHandler mCommentHandler = new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
try {
ResultBean rsb = XmlUtils.toBean(ResultBean.class,
new ByteArrayInputStream(arg2));
Result res = rsb.getResult();
if (res.OK()) {
hideWaitDialog();
AppContext.showToastShort(res.getErrorMessage());
// 评论成功之后评论数加1
setCommentCount(mCommentCount + 1);
} else {
hideWaitDialog();
AppContext.showToastShort(res.getErrorMessage());
}
} catch (Exception e) {
e.printStackTrace();
onFailure(arg0, arg1, arg2, e);
}
((DetailActivity) getActivity()).emojiFragment.clean();
}
@Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2,
Throwable arg3) {
hideWaitDialog();
AppContext.showToastShort(R.string.comment_publish_faile);
}
@Override
public void onFinish() {
((DetailActivity) getActivity()).emojiFragment.hideAllKeyBoard();
}
;
};
// 发表评论
@Override
public void onClickSendButton(Editable str) {
if (!TDevice.hasInternet()) {
AppContext.showToastShort(R.string.tip_network_error);
return;
}
if (!AppContext.getInstance().isLogin()) {
UIHelper.showLoginActivity(getActivity());
return;
}
if (TextUtils.isEmpty(str)) {
AppContext.showToastShort(R.string.tip_comment_content_empty);
return;
}
showWaitDialog(R.string.progress_submit);
OSChinaApi.publicComment(getCommentType(), mId, AppContext
.getInstance().getLoginUid(), str.toString(), 0,
mCommentHandler);
}
@Override
public void onClickFlagButton() {
}
/***
* htmlbody
*
* @param body
* @return
*/
protected String getFilterHtmlBody(String body) {
if (body == null)
return "";
return HTMLUtil.delHTMLTag(body.trim());
}
// 获取缓存的key
protected abstract String getCacheKey();
// 从网络中读取数据
protected abstract void sendRequestDataForNet();
// 解析数据
protected abstract T parseData(InputStream is);
// 返回填充到webview中的内容
protected abstract String getWebViewBody(T detail);
// 显示评论列表
protected abstract void showCommentView();
// 获取评论的类型
protected abstract int getCommentType();
protected abstract String getShareTitle();
protected abstract String getShareContent();
protected abstract String getShareUrl();
// 返回举报的url
protected String getRepotrUrl() {
return "";
}
// 返回举报的类型
protected byte getReportType() {
return Report.TYPE_QUESTION;
}
// 获取收藏类型(如新闻、博客、帖子)
protected abstract int getFavoriteTargetType();
protected abstract int getFavoriteState();
protected abstract void updateFavoriteChanged(int newFavoritedState);
protected abstract int getCommentCount();
public ShareDialog getDialog() {
return mDialog;
}
}

@ -0,0 +1,315 @@
package net.oschina.app.base;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.DrawableRes;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import net.oschina.app.R;
import net.oschina.app.bean.Entity;
import net.oschina.app.emoji.InputHelper;
import net.oschina.app.util.StringUtils;
import net.oschina.app.util.TDevice;
import net.oschina.app.widget.MyLinkMovementMethod;
import net.oschina.app.widget.MyURLSpan;
import net.oschina.app.widget.TweetTextView;
import java.util.ArrayList;
import java.util.List;
public class ListBaseAdapter<T extends Entity> extends BaseAdapter {
public static final int STATE_EMPTY_ITEM = 0;
public static final int STATE_LOAD_MORE = 1;
public static final int STATE_NO_MORE = 2;
public static final int STATE_NO_DATA = 3;
public static final int STATE_LESS_ONE_PAGE = 4;
public static final int STATE_NETWORK_ERROR = 5;
public static final int STATE_OTHER = 6;
protected int state = STATE_LESS_ONE_PAGE;
protected int _loadmoreText;
protected int _loadFinishText;
protected int _noDateText;
protected int mScreenWidth;
private LayoutInflater mInflater;
protected Context mContext;
public ListBaseAdapter(Context context) {
mContext = context;
}
protected LayoutInflater getLayoutInflater(Context context) {
if (mInflater == null) {
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
return mInflater;
}
public void setScreenWidth(int width) {
mScreenWidth = width;
}
public void setState(int state) {
this.state = state;
}
public int getState() {
return this.state;
}
protected ArrayList<T> mDatas = new ArrayList<T>();
public ListBaseAdapter() {
_loadmoreText = R.string.loading;
_loadFinishText = R.string.loading_no_more;
_noDateText = R.string.error_view_no_data;
}
@Override
public int getCount() {
switch (getState()) {
case STATE_EMPTY_ITEM:
return getDataSizePlus1();
case STATE_NETWORK_ERROR:
case STATE_LOAD_MORE:
return getDataSizePlus1();
case STATE_NO_DATA:
return 1;
case STATE_NO_MORE:
return getDataSizePlus1();
case STATE_LESS_ONE_PAGE:
return getDataSize();
default:
break;
}
return getDataSize();
}
public int getDataSizePlus1() {
if (hasFooterView()) {
return getDataSize() + 1;
}
return getDataSize();
}
public int getDataSize() {
return mDatas.size();
}
@Override
public T getItem(int arg0) {
if (mDatas.size() > arg0) {
return mDatas.get(arg0);
}
return null;
}
@Override
public long getItemId(int arg0) {
return arg0;
}
public void setData(ArrayList<T> data) {
mDatas = data;
notifyDataSetChanged();
}
public ArrayList<T> getData() {
return mDatas == null ? (mDatas = new ArrayList<T>()) : mDatas;
}
public void addData(List<T> data) {
if (mDatas != null && data != null && !data.isEmpty()) {
mDatas.addAll(data);
}
notifyDataSetChanged();
}
public void addItem(T obj) {
if (mDatas != null) {
mDatas.add(obj);
}
notifyDataSetChanged();
}
public void addItem(int pos, T obj) {
if (mDatas != null) {
mDatas.add(pos, obj);
}
notifyDataSetChanged();
}
public void removeItem(Object obj) {
mDatas.remove(obj);
notifyDataSetChanged();
}
public void clear() {
mDatas.clear();
notifyDataSetChanged();
}
public void setLoadmoreText(int loadmoreText) {
_loadmoreText = loadmoreText;
}
public void setLoadFinishText(int loadFinishText) {
_loadFinishText = loadFinishText;
}
public void setNoDataText(int noDataText) {
_noDateText = noDataText;
}
protected boolean loadMoreHasBg() {
return true;
}
@SuppressWarnings("deprecation")
@SuppressLint("InflateParams")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position == getCount() - 1 && hasFooterView()) {// 最后一条
// if (position < _data.size()) {
// position = getCount() - 2; // footview
// }
if (getState() == STATE_LOAD_MORE || getState() == STATE_NO_MORE
|| state == STATE_EMPTY_ITEM
|| getState() == STATE_NETWORK_ERROR) {
this.mFooterView = (LinearLayout) LayoutInflater.from(
parent.getContext()).inflate(R.layout.list_cell_footer,
null);
if (!loadMoreHasBg()) {
mFooterView.setBackgroundDrawable(null);
}
ProgressBar progress = (ProgressBar) mFooterView
.findViewById(R.id.progressbar);
TextView text = (TextView) mFooterView.findViewById(R.id.text);
switch (getState()) {
case STATE_LOAD_MORE:
setFooterViewLoading();
break;
case STATE_NO_MORE:
mFooterView.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
text.setVisibility(View.VISIBLE);
text.setText(_loadFinishText);
break;
case STATE_EMPTY_ITEM:
progress.setVisibility(View.GONE);
mFooterView.setVisibility(View.VISIBLE);
text.setText(_noDateText);
break;
case STATE_NETWORK_ERROR:
mFooterView.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
text.setVisibility(View.VISIBLE);
if (TDevice.hasInternet()) {
text.setText("加载出错了");
} else {
text.setText("没有可用的网络");
}
break;
default:
progress.setVisibility(View.GONE);
mFooterView.setVisibility(View.GONE);
text.setVisibility(View.GONE);
break;
}
return mFooterView;
}
}
if (position < 0) {
position = 0; // 若列表没有数据是没有footview/headview的
}
return getRealView(position, convertView, parent);
}
protected View getRealView(int position, View convertView, ViewGroup parent) {
return null;
}
private LinearLayout mFooterView;
protected boolean hasFooterView() {
return true;
}
public View getFooterView() {
return this.mFooterView;
}
public void setFooterViewLoading(String loadMsg) {
ProgressBar progress = (ProgressBar) mFooterView
.findViewById(R.id.progressbar);
TextView text = (TextView) mFooterView.findViewById(R.id.text);
mFooterView.setVisibility(View.VISIBLE);
progress.setVisibility(View.VISIBLE);
text.setVisibility(View.VISIBLE);
if (StringUtils.isEmpty(loadMsg)) {
text.setText(_loadmoreText);
} else {
text.setText(loadMsg);
}
}
public void setFooterViewLoading() {
setFooterViewLoading("");
}
public void setFooterViewText(String msg) {
ProgressBar progress = (ProgressBar) mFooterView
.findViewById(R.id.progressbar);
TextView text = (TextView) mFooterView.findViewById(R.id.text);
mFooterView.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
text.setVisibility(View.VISIBLE);
text.setText(msg);
}
protected void setContent(TweetTextView contentView, String content) {
contentView.setMovementMethod(MyLinkMovementMethod.a());
contentView.setFocusable(false);
contentView.setDispatchToParent(true);
contentView.setLongClickable(false);
Spanned span = Html.fromHtml(TweetTextView.modifyPath(content));
span = InputHelper.displayEmoji(contentView.getResources(),
span.toString());
contentView.setText(span);
MyURLSpan.parseLinkText(contentView, span);
}
protected void setText(TextView textView, String text, boolean needGone) {
if (text == null || TextUtils.isEmpty(text)) {
if (needGone) {
textView.setVisibility(View.GONE);
}
} else {
textView.setText(text);
}
}
protected void setImageRes(ImageView imageRes, @DrawableRes int resId) {
imageRes.setImageResource(resId);
}
protected void setText(TextView textView, String text) {
setText(textView, text, false);
}
}

@ -0,0 +1,240 @@
package net.oschina.app.bean;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 20141022 3:22:09
*
*/
@SuppressWarnings("serial")
@XStreamAlias("active")
public class Active extends Entity {
public final static int CATALOG_OTHER = 0;// 其他
public final static int CATALOG_NEWS = 1;// 新闻
public final static int CATALOG_POST = 2;// 帖子
public final static int CATALOG_TWEET = 3;// 动弹
public final static int CATALOG_BLOG = 4;// 博客
public final static int CLIENT_MOBILE = 2;
public final static int CLIENT_ANDROID = 3;
public final static int CLIENT_IPHONE = 4;
public final static int CLIENT_WINDOWS_PHONE = 5;
@XStreamAlias("portrait")
private String portrait;
@XStreamAlias("message")
private String message;
@XStreamAlias("author")
private String author;
@XStreamAlias("authorid")
private int authorId;
@XStreamAlias("activetype")
private int activeType;
@XStreamAlias("objectID")
private int objectId;
@XStreamAlias("catalog")
private int catalog;
@XStreamAlias("objecttype")
private int objectType;
@XStreamAlias("objectcatalog")
private int objectCatalog;
@XStreamAlias("objecttitle")
private String objectTitle;
@XStreamAlias("objectreply")
private ObjectReply objectReply;
@XStreamAlias("commentCount")
private int commentCount;
@XStreamAlias("pubDate")
private String pubDate;
@XStreamAlias("tweetimage")
private String tweetimage;
@XStreamAlias("tweetattach")
private String tweetattach;
@XStreamAlias("appclient")
private int appClient;
@XStreamAlias("url")
private String url;
public String getPortrait() {
return portrait;
}
public void setPortrait(String portrait) {
this.portrait = portrait;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getAuthorId() {
return authorId;
}
public void setAuthorId(int authorId) {
this.authorId = authorId;
}
public int getActiveType() {
return activeType;
}
public void setActiveType(int activeType) {
this.activeType = activeType;
}
public int getObjectId() {
return objectId;
}
public void setObjectId(int objectId) {
this.objectId = objectId;
}
public int getCatalog() {
return catalog;
}
public void setCatalog(int catalog) {
this.catalog = catalog;
}
public int getObjectType() {
return objectType;
}
public void setObjectType(int objectType) {
this.objectType = objectType;
}
public int getObjectCatalog() {
return objectCatalog;
}
public void setObjectCatalog(int objectCatalog) {
this.objectCatalog = objectCatalog;
}
public String getObjectTitle() {
return objectTitle;
}
public void setObjectTitle(String objectTitle) {
this.objectTitle = objectTitle;
}
public ObjectReply getObjectReply() {
return objectReply;
}
public void setObjectReply(ObjectReply objectReply) {
this.objectReply = objectReply;
}
public int getCommentCount() {
return commentCount;
}
public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}
public String getPubDate() {
return pubDate;
}
public void setPubDate(String pubDate) {
this.pubDate = pubDate;
}
public String getTweetattach() {
return tweetattach;
}
public void setTweetattach(String tweetattach) {
this.tweetattach = tweetattach;
}
public String getTweetimage() {
return tweetimage;
}
public void setTweetimage(String tweetimage) {
this.tweetimage = tweetimage;
}
public int getAppClient() {
return appClient;
}
public void setAppClient(int appClient) {
this.appClient = appClient;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@XStreamAlias("objectreply")
public static class ObjectReply implements Serializable {
@XStreamAlias("objectname")
public String objectName;
@XStreamAlias("objectbody")
public String objectBody;
public String getObjectName() {
return objectName;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public String getObjectBody() {
return objectBody;
}
public void setObjectBody(String objectBody) {
this.objectBody = objectBody;
}
}
}

@ -0,0 +1,60 @@
package net.oschina.app.bean;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 20141022 3:34:21
*
*/
@SuppressWarnings("serial")
@XStreamAlias("oschina")
public class ActiveList extends Entity implements ListEntity<Active> {
public final static int CATALOG_LASTEST = 1;// 最新
public final static int CATALOG_ATME = 2;// @我
public final static int CATALOG_COMMENT = 3;// 评论
public final static int CATALOG_MYSELF = 4;// 我自己
@XStreamAlias("pagesize")
private int pageSize;
@XStreamAlias("activeCount")
private int activeCount;
@XStreamAlias("activies")
private List<Active> activelist = new ArrayList<Active>();
@XStreamAlias("result")
private Result result;
public int getPageSize() {
return pageSize;
}
public int getActiveCount() {
return activeCount;
}
public List<Active> getActivelist() {
return activelist;
}
@Override
public List<Active> getList() {
return activelist;
}
public Result getResult() {
return result;
}
public void setResult(Result result) {
this.result = result;
}
}

@ -0,0 +1,61 @@
package net.oschina.app.bean;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @version 2015116 3:27:04
*
*/
@SuppressWarnings("serial")
@XStreamAlias("apply")
public class Apply extends Entity {
@XStreamAlias("uid")
private int userid;
@XStreamAlias("name")
private String name;
@XStreamAlias("portrait")
private String portrait;
@XStreamAlias("company")
private String company;
@XStreamAlias("job")
private String job;
public int getId() {
return userid;
}
public void setId(int userid) {
this.userid = userid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPortrait() {
return portrait;
}
public void setPortrait(String portrait) {
this.portrait = portrait;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}

@ -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,26 @@
package net.oschina.app.bean;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
* @author liux (http://my.oschina.net/liux)
* @version 1.0
* @created 2012-3-21
*/
@SuppressWarnings("serial")
public abstract class Base implements Serializable {
@XStreamAlias("notice")
protected Notice notice;
public Notice getNotice() {
return notice;
}
public void setNotice(Notice notice) {
this.notice = notice;
}
}

@ -0,0 +1,126 @@
package net.oschina.app.bean;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* @author HuangWenwei
*
* @date 2014929
*/
@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;
}
}

@ -0,0 +1,41 @@
package net.oschina.app.bean;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
*
* @author FireAnthttp://my.oschina.net/LittleDY
* @created 20141017 10:28:04
*
*/
@SuppressWarnings("serial")
@XStreamAlias("oschina")
public class BlogCommentList extends Entity implements ListEntity<Comment> {
@XStreamAlias("pagesize")
private int pageSize;
@XStreamAlias("allCount")
private int allCount;
@XStreamAlias("comments")
private List<Comment> commentlist = new ArrayList<Comment>();
public int getPageSize() {
return pageSize;
}
public int getAllCount() {
return allCount;
}
public List<Comment> getCommentlist() {
return commentlist;
}
@Override
public List<Comment> getList() {
return commentlist;
}
}

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

Loading…
Cancel
Save