├── .classpath
├── .gitattributes
├── .gitignore
├── .project
├── AndroidManifest.xml
├── README.md
├── libs
└── fastjson-1.1.26-android.jar
├── lint.xml
├── proguard-project.txt
├── proguard
├── dump.txt
├── mapping.txt
├── seeds.txt
└── usage.txt
├── project.properties
├── res
├── drawable-hdpi
│ ├── action_settings.png
│ ├── btn_default_disabled_focused_holo_light.9.png
│ ├── btn_default_disabled_holo_light.9.png
│ ├── btn_default_focused_holo_light.9.png
│ ├── btn_default_normal_holo_light.9.png
│ ├── btn_default_pressed_holo_light.9.png
│ ├── coffee.png
│ ├── collections_view_as_grid.png
│ ├── collections_view_as_list.png
│ ├── default_user_head_img.png
│ ├── edit.png
│ ├── forum_new.png
│ ├── hsegmentation.png
│ ├── item_bg.9.png
│ ├── item_bg_pressed.9.png
│ ├── kanxue.png
│ ├── kanxue_splash.png
│ ├── kanxue_title.png
│ ├── lock.png
│ ├── logout_dark.png
│ ├── navigation_back.png
│ ├── navigation_collapse.png
│ ├── navigation_expand.png
│ ├── navigation_next_item.png
│ ├── navigation_previous_item.png
│ ├── navigation_refresh.png
│ ├── segmentation.png
│ ├── social_add_person_dark.png
│ ├── social_send_now.png
│ ├── social_send_now_dark.png
│ ├── spinner_ab_default_holo_light.9.png
│ ├── spinner_ab_disabled_holo_light.9.png
│ ├── spinner_ab_focused_holo_light.9.png
│ ├── spinner_ab_pressed_holo_light.9.png
│ ├── textfield_activated_holo_light.9.png
│ ├── textfield_default_holo_light.9.png
│ ├── textfield_disabled_focused_holo_light.9.png
│ ├── textfield_disabled_holo_light.9.png
│ └── xlistview_arrow.png
├── drawable-mdpi
│ ├── action_settings.png
│ ├── btn_default_disabled_focused_holo_light.9.png
│ ├── btn_default_disabled_holo_light.9.png
│ ├── btn_default_focused_holo_light.9.png
│ ├── btn_default_normal_holo_light.9.png
│ ├── btn_default_pressed_holo_light.9.png
│ ├── coffee.png
│ ├── collections_view_as_grid.png
│ ├── collections_view_as_list.png
│ ├── default_user_head_img.png
│ ├── edit.png
│ ├── forum_new.png
│ ├── kanxue.png
│ ├── kanxue_splash.png
│ ├── kanxue_title.png
│ ├── lock.png
│ ├── logout_dark.png
│ ├── navigation_back.png
│ ├── navigation_collapse.png
│ ├── navigation_expand.png
│ ├── navigation_next_item.png
│ ├── navigation_refresh.png
│ ├── social_add_person_dark.png
│ ├── social_send_now.png
│ ├── social_send_now_dark.png
│ ├── spinner_ab_default_holo_light.9.png
│ ├── spinner_ab_disabled_holo_light.9.png
│ ├── spinner_ab_focused_holo_light.9.png
│ ├── spinner_ab_pressed_holo_light.9.png
│ ├── textfield_activated_holo_light.9.png
│ ├── textfield_default_holo_light.9.png
│ ├── textfield_disabled_focused_holo_light.9.png
│ └── textfield_disabled_holo_light.9.png
├── drawable-xhdpi
│ ├── action_settings.png
│ ├── btn_default_disabled_focused_holo_light.9.png
│ ├── btn_default_disabled_holo_light.9.png
│ ├── btn_default_focused_holo_light.9.png
│ ├── btn_default_normal_holo_light.9.png
│ ├── btn_default_pressed_holo_light.9.png
│ ├── coffee.png
│ ├── collections_view_as_grid.png
│ ├── collections_view_as_list.png
│ ├── default_user_head_img.png
│ ├── edit.png
│ ├── forum_new.png
│ ├── kanxue.png
│ ├── kanxue_splash.png
│ ├── kanxue_title.png
│ ├── list_divider_line.png
│ ├── lock.png
│ ├── logout_dark.png
│ ├── navigation_back.png
│ ├── navigation_collapse.png
│ ├── navigation_expand.png
│ ├── navigation_next_item.png
│ ├── navigation_refresh.png
│ ├── social_add_person_dark.png
│ ├── social_send_now.png
│ ├── social_send_now_dark.png
│ ├── spinner_ab_default_holo_light.9.png
│ ├── spinner_ab_disabled_holo_light.9.png
│ ├── spinner_ab_focused_holo_light.9.png
│ ├── spinner_ab_pressed_holo_light.9.png
│ ├── textfield_activated_holo_light.9.png
│ ├── textfield_default_holo_light.9.png
│ ├── textfield_disabled_focused_holo_light.9.png
│ └── textfield_disabled_holo_light.9.png
├── drawable
│ ├── attament_bg.xml
│ ├── btn_default_holo_light.xml
│ ├── edit_text_holo_light.xml
│ ├── forum_item.xml
│ ├── login_btn.xml
│ ├── normal_action_btn.xml
│ ├── normal_item.xml
│ ├── quick_reply_btn.xml
│ ├── spinner_ab_holo_light.xml
│ └── tab_bg.xml
├── layout
│ ├── about_page.xml
│ ├── feedback_page.xml
│ ├── forum_display_item.xml
│ ├── forum_display_page.xml
│ ├── forum_home_item.xml
│ ├── forum_home_item_header.xml
│ ├── forum_home_page.xml
│ ├── forum_tab.xml
│ ├── login_page.xml
│ ├── main.xml
│ ├── new_thread_page.xml
│ ├── refresh_action_btn.xml
│ ├── setting_page.xml
│ ├── show_thread_item.xml
│ ├── show_thread_page.xml
│ ├── show_thread_title_header.xml
│ ├── show_user_info.xml
│ ├── splash.xml
│ ├── thread_item_footer.xml
│ ├── xlistview_footer.xml
│ └── xlistview_header.xml
└── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── src
├── android
└── util
│ ├── Base64.java
│ ├── Base64DataException.java
│ ├── Base64InputStream.java
│ └── Base64OutputStream.java
└── com
└── pediy
└── bbs
└── kanxue
├── AboutPage.java
├── App.java
├── FeedbackPage.java
├── ForumDisplayPage.java
├── ForumHomePage.java
├── LoginPage.java
├── MainActivity.java
├── NewThreadPage.java
├── SettingPage.java
├── ShowThreadPage.java
├── SplashActivity.java
├── UserInfoPage.java
├── net
├── Api.java
└── HttpClientUtil.java
├── util
├── CookieStorage.java
├── ObjStorage.java
└── SimpleHASH.java
└── widget
├── AmazingAdapter.java
├── AmazingListView.java
├── ImageViewWithCache.java
├── RefreshActionBtn.java
├── ThreadItemFooter.java
├── XListView.java
├── XListViewFooter.java
└── XListViewHeader.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .metadata
7 | bin/
8 | gen/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .settings/
16 | .loadpath
17 |
18 | # External tool builders
19 | .externalToolBuilders/
20 |
21 | # Locally stored "Eclipse launch configurations"
22 | *.launch
23 |
24 | # CDT-specific
25 | .cproject
26 |
27 | # PDT-specific
28 | .buildpath
29 |
30 |
31 | #################
32 | ## Visual Studio
33 | #################
34 |
35 | ## Ignore Visual Studio temporary files, build results, and
36 | ## files generated by popular Visual Studio add-ons.
37 |
38 | # User-specific files
39 | *.suo
40 | *.user
41 | *.sln.docstates
42 |
43 | # Build results
44 | [Dd]ebug/
45 | [Rr]elease/
46 | *_i.c
47 | *_p.c
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.vspscc
62 | .builds
63 | *.dotCover
64 |
65 | ## TODO: If you have NuGet Package Restore enabled, uncomment this
66 | #packages/
67 |
68 | # Visual C++ cache files
69 | ipch/
70 | *.aps
71 | *.ncb
72 | *.opensdf
73 | *.sdf
74 |
75 | # Visual Studio profiler
76 | *.psess
77 | *.vsp
78 |
79 | # ReSharper is a .NET coding add-in
80 | _ReSharper*
81 |
82 | # Installshield output folder
83 | [Ee]xpress
84 |
85 | # DocProject is a documentation generator add-in
86 | DocProject/buildhelp/
87 | DocProject/Help/*.HxT
88 | DocProject/Help/*.HxC
89 | DocProject/Help/*.hhc
90 | DocProject/Help/*.hhk
91 | DocProject/Help/*.hhp
92 | DocProject/Help/Html2
93 | DocProject/Help/html
94 |
95 | # Click-Once directory
96 | publish
97 |
98 | # Others
99 | [Bb]in
100 | [Oo]bj
101 | sql
102 | TestResults
103 | *.Cache
104 | ClientBin
105 | stylecop.*
106 | ~$*
107 | *.dbmdl
108 | Generated_Code #added for RIA/Silverlight projects
109 |
110 | # Backup & report files from converting an old project file to a newer
111 | # Visual Studio version. Backup files are not needed, because we have git ;-)
112 | _UpgradeReport_Files/
113 | Backup*/
114 | UpgradeLog*.XML
115 |
116 |
117 |
118 | ############
119 | ## Windows
120 | ############
121 |
122 | # Windows image file caches
123 | Thumbs.db
124 |
125 | # Folder config file
126 | Desktop.ini
127 |
128 |
129 | #############
130 | ## Python
131 | #############
132 |
133 | *.py[co]
134 |
135 | # Packages
136 | *.egg
137 | *.egg-info
138 | dist
139 | build
140 | eggs
141 | parts
142 | bin
143 | var
144 | sdist
145 | develop-eggs
146 | .installed.cfg
147 |
148 | # Installer logs
149 | pip-log.txt
150 |
151 | # Unit test / coverage reports
152 | .coverage
153 | .tox
154 |
155 | #Translations
156 | *.mo
157 |
158 | #Mr Developer
159 | .mr.developer.cfg
160 |
161 | # Mac crap
162 | .DS_Store
163 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Kanxue
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # **看雪安全论坛 Android 客户端** #
3 |
4 | 本项目基于看雪安全论坛开放的接口开发完成。可通过在看雪各页面url后添加styleid=12参数查看各页面返回的JSON数据。项目中采用了一些经过修改的第三方控件,如AmazingListView和XListView。
5 |
6 | **源码结构**
7 |
8 | > src
9 | > ├ android.util
10 | > ├ com.pediy.bbs.kanxue
11 | > ├ com.pediy.bbs.kanxue.net
12 | > ├ com.pediy.bbs.kanxue.util
13 | > ├ com.pediy.bbs.kanxue.widget
14 |
15 |
16 | - android.util — 为了兼容低版本,从安卓sdk中复制出来的base64工具包
17 | - com.pediy.bbs.kanxue — 界面包
18 | - com.pediy.bbs.kanxue.net — 网络包
19 | - com.pediy.bbs.kanxue.util — 工具包
20 | - com.pediy.bbs.kanxue.widget — 控件包
21 |
22 | > libs
23 | > └ fastjson-1.1.26-android.jar
24 |
25 | - fastjson-1.1.26-android.jar — 快速的json数据解析包
26 |
27 | **看雪开放的api文档地址:**http://bbs.pediy.com/showthread.php?t=163280
28 |
29 |
--------------------------------------------------------------------------------
/libs/fastjson-1.1.26-android.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/libs/fastjson-1.1.26-android.jar
--------------------------------------------------------------------------------
/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/proguard/dump.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/proguard/dump.txt
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-15
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/action_settings.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_default_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_default_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/btn_default_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_default_normal_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/btn_default_normal_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/btn_default_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/coffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/coffee.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/collections_view_as_grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/collections_view_as_grid.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/collections_view_as_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/collections_view_as_list.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/default_user_head_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/default_user_head_img.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/edit.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/forum_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/forum_new.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/hsegmentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/hsegmentation.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/item_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/item_bg.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/item_bg_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/item_bg_pressed.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/kanxue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/kanxue.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/kanxue_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/kanxue_splash.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/kanxue_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/kanxue_title.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/lock.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/logout_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/logout_dark.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_back.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_collapse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_collapse.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_expand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_expand.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_next_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_next_item.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_previous_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_previous_item.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/navigation_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/navigation_refresh.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/segmentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/segmentation.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/social_add_person_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/social_add_person_dark.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/social_send_now.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/social_send_now.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/social_send_now_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/social_send_now_dark.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/spinner_ab_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/textfield_activated_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/textfield_activated_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/textfield_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/textfield_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/textfield_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/textfield_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/xlistview_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-hdpi/xlistview_arrow.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/action_settings.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/btn_default_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/btn_default_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/btn_default_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/btn_default_normal_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/btn_default_normal_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/btn_default_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/coffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/coffee.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/collections_view_as_grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/collections_view_as_grid.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/collections_view_as_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/collections_view_as_list.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/default_user_head_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/default_user_head_img.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/edit.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/forum_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/forum_new.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/kanxue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/kanxue.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/kanxue_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/kanxue_splash.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/kanxue_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/kanxue_title.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/lock.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/logout_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/logout_dark.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/navigation_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/navigation_back.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/navigation_collapse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/navigation_collapse.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/navigation_expand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/navigation_expand.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/navigation_next_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/navigation_next_item.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/navigation_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/navigation_refresh.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/social_add_person_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/social_add_person_dark.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/social_send_now.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/social_send_now.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/social_send_now_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/social_send_now_dark.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/spinner_ab_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/textfield_activated_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/textfield_activated_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/textfield_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/textfield_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/textfield_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-mdpi/textfield_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/action_settings.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/btn_default_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/btn_default_normal_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/coffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/coffee.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/collections_view_as_grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/collections_view_as_grid.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/collections_view_as_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/collections_view_as_list.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/default_user_head_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/default_user_head_img.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/edit.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/forum_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/forum_new.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/kanxue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/kanxue.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/kanxue_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/kanxue_splash.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/kanxue_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/kanxue_title.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/list_divider_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/list_divider_line.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/lock.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/logout_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/logout_dark.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/navigation_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/navigation_back.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/navigation_collapse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/navigation_collapse.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/navigation_expand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/navigation_expand.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/navigation_next_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/navigation_next_item.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/navigation_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/navigation_refresh.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/social_add_person_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/social_add_person_dark.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/social_send_now.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/social_send_now.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/social_send_now_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/social_send_now_dark.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/textfield_activated_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/textfield_activated_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/textfield_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/textfield_default_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/textfield_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kanxue-team/kanxue-android-app/41dafc491e9513f31c55de13ebb43a63e3e59827/res/drawable-xhdpi/textfield_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/res/drawable/attament_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/drawable/btn_default_holo_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
33 |
--------------------------------------------------------------------------------
/res/drawable/edit_text_holo_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/res/drawable/forum_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/drawable/login_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/drawable/normal_action_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/drawable/normal_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/drawable/quick_reply_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/drawable/spinner_ab_holo_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
20 |
22 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/res/drawable/tab_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/layout/about_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
16 |
17 |
26 |
27 |
33 |
34 |
43 |
44 |
45 |
46 |
50 |
51 |
56 |
57 |
64 |
65 |
71 |
72 |
78 |
79 |
84 |
85 |
86 |
87 |
94 |
95 |
100 |
101 |
108 |
109 |
116 |
117 |
118 |
119 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/res/layout/feedback_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
29 |
30 |
36 |
37 |
48 |
49 |
50 |
55 |
56 |
64 |
65 |
66 |
67 |
68 |
78 |
79 |
80 |
81 |
82 |
93 |
94 |
104 |
105 |
--------------------------------------------------------------------------------
/res/layout/forum_display_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
27 |
28 |
37 |
38 |
42 |
43 |
50 |
51 |
60 |
61 |
69 |
70 |
79 |
80 |
89 |
90 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/res/layout/forum_display_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
29 |
30 |
39 |
40 |
46 |
47 |
56 |
57 |
58 |
59 |
64 |
65 |
74 |
75 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
94 |
95 |
104 |
105 |
106 |
107 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/res/layout/forum_home_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
27 |
28 |
34 |
35 |
41 |
42 |
49 |
50 |
51 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/res/layout/forum_home_item_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/res/layout/forum_home_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
29 |
30 |
39 |
40 |
41 |
42 |
43 |
47 |
48 |
54 |
55 |
56 |
57 |
64 |
65 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/res/layout/forum_tab.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/res/layout/login_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
29 |
30 |
36 |
37 |
46 |
47 |
48 |
49 |
50 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
77 |
78 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/res/layout/new_thread_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
28 |
29 |
35 |
36 |
47 |
48 |
49 |
50 |
55 |
56 |
64 |
65 |
66 |
67 |
68 |
77 |
78 |
85 |
86 |
92 |
93 |
94 |
95 |
105 |
106 |
118 |
119 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/res/layout/refresh_action_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/res/layout/setting_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
19 |
20 |
26 |
27 |
36 |
37 |
38 |
39 |
40 |
41 |
48 |
49 |
53 |
54 |
60 |
61 |
67 |
68 |
75 |
76 |
82 |
83 |
91 |
92 |
98 |
99 |
102 |
103 |
104 |
105 |
106 |
113 |
114 |
120 |
121 |
128 |
129 |
134 |
135 |
141 |
142 |
149 |
150 |
151 |
152 |
158 |
159 |
160 |
161 |
168 |
169 |
175 |
176 |
185 |
186 |
193 |
194 |
199 |
200 |
201 |
202 |
212 |
213 |
220 |
221 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
--------------------------------------------------------------------------------
/res/layout/show_thread_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
19 |
20 |
28 |
29 |
34 |
35 |
39 |
40 |
46 |
47 |
52 |
53 |
54 |
55 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
74 |
80 |
81 |
87 |
88 |
89 |
98 |
99 |
104 |
105 |
111 |
112 |
113 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/res/layout/show_thread_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
30 |
31 |
37 |
38 |
49 |
50 |
51 |
52 |
57 |
58 |
66 |
67 |
68 |
69 |
70 |
71 |
74 |
75 |
79 |
80 |
90 |
91 |
92 |
93 |
99 |
100 |
106 |
107 |
116 |
117 |
118 |
119 |
120 |
129 |
130 |
131 |
132 |
133 |
134 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/res/layout/show_thread_title_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/res/layout/splash.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
20 |
21 |
30 |
31 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/res/layout/thread_item_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
16 |
17 |
22 |
23 |
30 |
31 |
32 |
33 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/res/layout/xlistview_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
19 |
20 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/layout/xlistview_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
19 |
20 |
25 |
26 |
30 |
31 |
38 |
39 |
44 |
45 |
46 |
47 |
55 |
56 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #E2E7EE
4 | #E0E0E0
5 | #FFFFFF
6 | #40567E
7 | #22229C
8 | #25334B
9 | #6E88B6
10 | #E5E5E5
11 | #E5E5E5
12 | #808080
13 | #00000000
14 | #DEE3E7
15 | #636973
16 | #B22222
17 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 看雪论坛
5 | Hello world!
6 | Settings
7 | 下拉刷新
8 | 松开刷新数据
9 | 正在加载...
10 | 上次更新时间:
11 | 查看更多
12 | 松开载入更多
13 | 网络连接超时
14 | 网络连接失败
15 | 数据错误
16 | 点击查看详细内容
17 | 点击查看缩略内容
18 | /kanxue/img
19 | 48
20 | 确定要退出登录吗?
21 | 用户名不能为空
22 | 密码不能为空
23 | 版面为空
24 | 您已经超出了登录失败限制次数,请等待 15 分钟后重试
25 | 发帖成功
26 | 禁止三十秒内连续发帖
27 | 禁止五分钟内发相同贴
28 | 现金不足, 无法发出高于现金数额的悬赏
29 | 这么多年来,在看雪学到许多知识,非常感谢看雪!一直希望能为看雪做点贡献,看到论坛准备开发Android产品,就希望能参加。刚好自己有些论坛app开发经验,在得到kanxue支持下,写了一个论坛客户端,希望大家喜欢。\n更新历史\nV1.0 fanhexin首次开发\nV1.1 沙加L负责维护\n
30 |
31 | - 选择话题
32 | - 【求助】
33 | - 【原创】
34 | - 【推荐】
35 | - 【转帖】
36 | - 【讨论】
37 | - 【下载】
38 | - 【分享】
39 | - 【注意】
40 | - 【翻译】
41 | - 【调查】
42 | - 【建议】
43 |
44 |
45 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
20 |
27 |
43 |
52 |
56 |
63 |
64 |
65 |
68 |
69 |
--------------------------------------------------------------------------------
/src/android/util/Base64DataException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.util;
18 |
19 | import java.io.IOException;
20 |
21 | /**
22 | * This exception is thrown by {@link Base64InputStream} or
23 | * {@link Base64OutputStream} when an error is detected in the data being
24 | * decoded. This allows problems with the base64 data to be disambiguated from
25 | * errors in the underlying streams (e.g. actual connection errors.)
26 | */
27 | public class Base64DataException extends IOException {
28 | /**
29 | *
30 | */
31 | private static final long serialVersionUID = 1L;
32 |
33 | public Base64DataException(String detailMessage) {
34 | super(detailMessage);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/android/util/Base64InputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.util;
18 |
19 | import java.io.FilterInputStream;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 |
23 | /**
24 | * An InputStream that does Base64 decoding on the data read through
25 | * it.
26 | */
27 | public class Base64InputStream extends FilterInputStream {
28 | private final Base64.Coder coder;
29 |
30 | private static byte[] EMPTY = new byte[0];
31 |
32 | private static final int BUFFER_SIZE = 2048;
33 | private boolean eof;
34 | private byte[] inputBuffer;
35 | private int outputStart;
36 | private int outputEnd;
37 |
38 | /**
39 | * An InputStream that performs Base64 decoding on the data read
40 | * from the wrapped stream.
41 | *
42 | * @param in the InputStream to read the source data from
43 | * @param flags bit flags for controlling the decoder; see the
44 | * constants in {@link Base64}
45 | */
46 | public Base64InputStream(InputStream in, int flags) {
47 | this(in, flags, false);
48 | }
49 |
50 | /**
51 | * Performs Base64 encoding or decoding on the data read from the
52 | * wrapped InputStream.
53 | *
54 | * @param in the InputStream to read the source data from
55 | * @param flags bit flags for controlling the decoder; see the
56 | * constants in {@link Base64}
57 | * @param encode true to encode, false to decode
58 | *
59 | * @hide
60 | */
61 | public Base64InputStream(InputStream in, int flags, boolean encode) {
62 | super(in);
63 | eof = false;
64 | inputBuffer = new byte[BUFFER_SIZE];
65 | if (encode) {
66 | coder = new Base64.Encoder(flags, null);
67 | } else {
68 | coder = new Base64.Decoder(flags, null);
69 | }
70 | coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)];
71 | outputStart = 0;
72 | outputEnd = 0;
73 | }
74 |
75 | @Override
76 | public boolean markSupported() {
77 | return false;
78 | }
79 |
80 | @Override
81 | public void mark(int readlimit) {
82 | throw new UnsupportedOperationException();
83 | }
84 |
85 | @Override
86 | public void reset() {
87 | throw new UnsupportedOperationException();
88 | }
89 |
90 | @Override
91 | public void close() throws IOException {
92 | in.close();
93 | inputBuffer = null;
94 | }
95 |
96 | @Override
97 | public int available() {
98 | return outputEnd - outputStart;
99 | }
100 |
101 | @Override
102 | public long skip(long n) throws IOException {
103 | if (outputStart >= outputEnd) {
104 | refill();
105 | }
106 | if (outputStart >= outputEnd) {
107 | return 0;
108 | }
109 | long bytes = Math.min(n, outputEnd-outputStart);
110 | outputStart += bytes;
111 | return bytes;
112 | }
113 |
114 | @Override
115 | public int read() throws IOException {
116 | if (outputStart >= outputEnd) {
117 | refill();
118 | }
119 | if (outputStart >= outputEnd) {
120 | return -1;
121 | } else {
122 | return coder.output[outputStart++] & 0xff;
123 | }
124 | }
125 |
126 | @Override
127 | public int read(byte[] b, int off, int len) throws IOException {
128 | if (outputStart >= outputEnd) {
129 | refill();
130 | }
131 | if (outputStart >= outputEnd) {
132 | return -1;
133 | }
134 | int bytes = Math.min(len, outputEnd-outputStart);
135 | System.arraycopy(coder.output, outputStart, b, off, bytes);
136 | outputStart += bytes;
137 | return bytes;
138 | }
139 |
140 | /**
141 | * Read data from the input stream into inputBuffer, then
142 | * decode/encode it into the empty coder.output, and reset the
143 | * outputStart and outputEnd pointers.
144 | */
145 | private void refill() throws IOException {
146 | if (eof) return;
147 | int bytesRead = in.read(inputBuffer);
148 | boolean success;
149 | if (bytesRead == -1) {
150 | eof = true;
151 | success = coder.process(EMPTY, 0, 0, true);
152 | } else {
153 | success = coder.process(inputBuffer, 0, bytesRead, false);
154 | }
155 | if (!success) {
156 | throw new Base64DataException("bad base-64");
157 | }
158 | outputEnd = coder.op;
159 | outputStart = 0;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/android/util/Base64OutputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.util;
18 |
19 | import java.io.FilterOutputStream;
20 | import java.io.IOException;
21 | import java.io.OutputStream;
22 |
23 | /**
24 | * An OutputStream that does Base64 encoding on the data written to
25 | * it, writing the resulting data to another OutputStream.
26 | */
27 | public class Base64OutputStream extends FilterOutputStream {
28 | private final Base64.Coder coder;
29 | private final int flags;
30 |
31 | private byte[] buffer = null;
32 | private int bpos = 0;
33 |
34 | private static byte[] EMPTY = new byte[0];
35 |
36 | /**
37 | * Performs Base64 encoding on the data written to the stream,
38 | * writing the encoded data to another OutputStream.
39 | *
40 | * @param out the OutputStream to write the encoded data to
41 | * @param flags bit flags for controlling the encoder; see the
42 | * constants in {@link Base64}
43 | */
44 | public Base64OutputStream(OutputStream out, int flags) {
45 | this(out, flags, true);
46 | }
47 |
48 | /**
49 | * Performs Base64 encoding or decoding on the data written to the
50 | * stream, writing the encoded/decoded data to another
51 | * OutputStream.
52 | *
53 | * @param out the OutputStream to write the encoded data to
54 | * @param flags bit flags for controlling the encoder; see the
55 | * constants in {@link Base64}
56 | * @param encode true to encode, false to decode
57 | *
58 | * @hide
59 | */
60 | public Base64OutputStream(OutputStream out, int flags, boolean encode) {
61 | super(out);
62 | this.flags = flags;
63 | if (encode) {
64 | coder = new Base64.Encoder(flags, null);
65 | } else {
66 | coder = new Base64.Decoder(flags, null);
67 | }
68 | }
69 |
70 | @Override
71 | public void write(int b) throws IOException {
72 | // To avoid invoking the encoder/decoder routines for single
73 | // bytes, we buffer up calls to write(int) in an internal
74 | // byte array to transform them into writes of decently-sized
75 | // arrays.
76 |
77 | if (buffer == null) {
78 | buffer = new byte[1024];
79 | }
80 | if (bpos >= buffer.length) {
81 | // internal buffer full; write it out.
82 | internalWrite(buffer, 0, bpos, false);
83 | bpos = 0;
84 | }
85 | buffer[bpos++] = (byte) b;
86 | }
87 |
88 | /**
89 | * Flush any buffered data from calls to write(int). Needed
90 | * before doing a write(byte[], int, int) or a close().
91 | */
92 | private void flushBuffer() throws IOException {
93 | if (bpos > 0) {
94 | internalWrite(buffer, 0, bpos, false);
95 | bpos = 0;
96 | }
97 | }
98 |
99 | @Override
100 | public void write(byte[] b, int off, int len) throws IOException {
101 | if (len <= 0) return;
102 | flushBuffer();
103 | internalWrite(b, off, len, false);
104 | }
105 |
106 | @Override
107 | public void close() throws IOException {
108 | IOException thrown = null;
109 | try {
110 | flushBuffer();
111 | internalWrite(EMPTY, 0, 0, true);
112 | } catch (IOException e) {
113 | thrown = e;
114 | }
115 |
116 | try {
117 | if ((flags & Base64.NO_CLOSE) == 0) {
118 | out.close();
119 | } else {
120 | out.flush();
121 | }
122 | } catch (IOException e) {
123 | if (thrown != null) {
124 | thrown = e;
125 | }
126 | }
127 |
128 | if (thrown != null) {
129 | throw thrown;
130 | }
131 | }
132 |
133 | /**
134 | * Write the given bytes to the encoder/decoder.
135 | *
136 | * @param finish true if this is the last batch of input, to cause
137 | * encoder/decoder state to be finalized.
138 | */
139 | private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
140 | coder.output = embiggen(coder.output, coder.maxOutputSize(len));
141 | if (!coder.process(b, off, len, finish)) {
142 | throw new Base64DataException("bad base-64");
143 | }
144 | out.write(coder.output, 0, coder.op);
145 | }
146 |
147 | /**
148 | * If b.length is at least len, return b. Otherwise return a new
149 | * byte array of length len.
150 | */
151 | private byte[] embiggen(byte[] b, int len) {
152 | if (b == null || b.length < len) {
153 | return new byte[len];
154 | } else {
155 | return b;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/AboutPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import android.app.Activity;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.PackageManager.NameNotFoundException;
7 | import android.os.Bundle;
8 | import android.view.View;
9 | import android.widget.TextView;
10 |
11 | public class AboutPage extends Activity {
12 |
13 | @Override
14 | public void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | this.setContentView(R.layout.about_page);
17 |
18 | PackageInfo pinfo;
19 | try {
20 | pinfo = getPackageManager().getPackageInfo(this.getPackageName(),
21 | PackageManager.GET_CONFIGURATIONS);
22 | TextView tv = (TextView) this.findViewById(R.id.aboutPageVerText);
23 | tv.setText("v" + pinfo.versionName);
24 | } catch (NameNotFoundException e1) {
25 | // TODO 自动生成的 catch 块
26 | e1.printStackTrace();
27 | }
28 | }
29 |
30 | public void onBackBtnClick(View v) {
31 | finish();
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/FeedbackPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import java.util.List;
4 | import java.util.regex.Matcher;
5 | import java.util.regex.Pattern;
6 |
7 | import org.apache.http.cookie.Cookie;
8 |
9 | import android.app.Activity;
10 | import android.app.ProgressDialog;
11 | import android.os.Bundle;
12 | import android.os.Handler;
13 | import android.os.Message;
14 | import android.text.Html;
15 | import android.view.View;
16 | import android.widget.EditText;
17 | import android.widget.Toast;
18 |
19 | import com.alibaba.fastjson.JSON;
20 | import com.alibaba.fastjson.JSONObject;
21 | import com.pediy.bbs.kanxue.net.Api;
22 | import com.pediy.bbs.kanxue.net.HttpClientUtil;
23 | import com.pediy.bbs.kanxue.net.HttpClientUtil.NetClientCallback;
24 |
25 | public class FeedbackPage extends Activity {
26 | private EditText m_name;
27 | private EditText m_email;
28 | private EditText m_message;
29 | private Handler m_handler = new Handler() {
30 | @Override
31 | public void handleMessage(Message msg) {
32 | // 关闭ProgressDialog
33 |
34 | switch (msg.what) {
35 | case HttpClientUtil.NET_TIMEOUT:
36 | Toast.makeText(FeedbackPage.this, R.string.net_timeout,
37 | Toast.LENGTH_SHORT).show();
38 | break;
39 | case HttpClientUtil.NET_FAILED:
40 | Toast.makeText(FeedbackPage.this, R.string.net_failed,
41 | Toast.LENGTH_SHORT).show();
42 | break;
43 | default:
44 | break;
45 | }
46 | }
47 | };
48 |
49 | @Override
50 | public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | this.setContentView(R.layout.feedback_page);
53 | this.m_name = (EditText) this.findViewById(R.id.feedbackName);
54 | this.m_name
55 | .setText(Html.fromHtml(Api.getInstance().getLoginUserName()));
56 | this.m_email = (EditText) this.findViewById(R.id.feedbackEmail);
57 | this.m_email.setText(Api.getInstance().getEmail());
58 | this.m_message = (EditText) this.findViewById(R.id.feedbackMessage);
59 | }
60 |
61 | public void onBackBtnClick(View v) {
62 | this.finish();
63 | }
64 |
65 | public void onSendBtnClick(View v) {
66 |
67 | String name = m_name.getText().toString();
68 | if (name.length() == 0) {
69 | Toast.makeText(FeedbackPage.this, "姓名不能为空", Toast.LENGTH_SHORT)
70 | .show();
71 | this.m_name.requestFocus();
72 | return;
73 | }
74 |
75 | String email = m_email.getText().toString();
76 | if (email.length() == 0) {
77 | Toast.makeText(FeedbackPage.this, "Email不能为空", Toast.LENGTH_SHORT)
78 | .show();
79 | this.m_email.requestFocus();
80 | return;
81 | } else {
82 |
83 | Pattern pattern = Pattern.compile(
84 | "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*",
85 | Pattern.CASE_INSENSITIVE);
86 | Matcher matcher = pattern.matcher(email);
87 | if (!matcher.matches()) {
88 | Toast.makeText(FeedbackPage.this, "不是有效的Email地址",
89 | Toast.LENGTH_SHORT).show();
90 | this.m_email.requestFocus();
91 | return;
92 | }
93 | }
94 |
95 | String msg = m_message.getText().toString();
96 | if (msg.length() == 0) {
97 | Toast.makeText(FeedbackPage.this, "信息不能为空", Toast.LENGTH_SHORT)
98 | .show();
99 | this.m_message.requestFocus();
100 | return;
101 | }
102 | final ProgressDialog pd = ProgressDialog.show(this, null,
103 | "提交反馈中,请稍后……", true, true);
104 | Api.getInstance().feedback(name, email, msg, new NetClientCallback() {
105 |
106 | @Override
107 | public void execute(final int status, final String response,
108 | List cookies) {
109 | System.out.println("feedback:" + response);
110 |
111 | m_handler.sendEmptyMessage(status);
112 | m_handler.post(new Runnable() {
113 |
114 | @Override
115 | public void run() {
116 | pd.dismiss();
117 | switch (status) {
118 | case HttpClientUtil.NET_SUCCESS:
119 | if (response == null) {
120 | Toast.makeText(FeedbackPage.this, "提交反馈失败",
121 | Toast.LENGTH_SHORT).show();
122 | return;
123 | }
124 |
125 | JSONObject jsonObj = JSON.parseObject(response);
126 | if (jsonObj == null) {
127 | Toast.makeText(FeedbackPage.this, "数据错误",
128 | Toast.LENGTH_SHORT).show();
129 | return;
130 | }
131 | int ret = jsonObj.getInteger("result");
132 | if (ret != 0) {
133 | Toast.makeText(FeedbackPage.this, "提交反馈失败",
134 | Toast.LENGTH_SHORT).show();
135 | return;
136 | }
137 | Toast.makeText(FeedbackPage.this, "提交反馈成功",
138 | Toast.LENGTH_SHORT).show();
139 | FeedbackPage.this.finish();
140 | break;
141 | default:
142 | break;
143 | }
144 |
145 | }
146 |
147 | });
148 |
149 | }
150 |
151 | });
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/LoginPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.http.cookie.Cookie;
6 |
7 | import android.app.Activity;
8 | import android.app.ProgressDialog;
9 | import android.content.Intent;
10 | import android.os.Bundle;
11 | import android.os.Handler;
12 | import android.os.Message;
13 | import android.view.View;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import com.alibaba.fastjson.JSON;
18 | import com.alibaba.fastjson.JSONObject;
19 | import com.pediy.bbs.kanxue.net.Api;
20 | import com.pediy.bbs.kanxue.net.HttpClientUtil;
21 | import com.pediy.bbs.kanxue.net.HttpClientUtil.NetClientCallback;
22 |
23 | public class LoginPage extends Activity {
24 | private TextView m_userName;
25 | private TextView m_passwd;
26 | private ProgressDialog m_pd;
27 |
28 | private Handler m_handler = new Handler() {
29 | @Override
30 | public void handleMessage(Message msg) {
31 | // 关闭ProgressDialog
32 | m_pd.dismiss();
33 |
34 | switch (msg.what) {
35 | case HttpClientUtil.NET_SUCCESS:
36 | LoginPage.this.setResult(1);
37 | finish();
38 | break;
39 | case HttpClientUtil.NET_TIMEOUT:
40 | Toast.makeText(LoginPage.this, R.string.net_timeout,
41 | Toast.LENGTH_SHORT).show();
42 | break;
43 | case HttpClientUtil.NET_FAILED:
44 | Toast.makeText(LoginPage.this, R.string.net_failed,
45 | Toast.LENGTH_SHORT).show();
46 | break;
47 | default:
48 | break;
49 | }
50 | }
51 | };
52 |
53 | @Override
54 | public void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | this.setContentView(R.layout.login_page);
57 |
58 | m_userName = (TextView) this.findViewById(R.id.loginPageUsername);
59 | m_passwd = (TextView) this.findViewById(R.id.loginPagePasswd);
60 | }
61 |
62 | public void onBackBtnClick(View v) {
63 | this.finish();
64 | }
65 |
66 | public void onLoginBtnClick(View v) {
67 | String uname = m_userName.getText().toString();
68 | String passwd = m_passwd.getText().toString();
69 | if (uname.length() == 0) {
70 | Toast.makeText(this, R.string.login_username_alert,
71 | Toast.LENGTH_SHORT).show();
72 | return;
73 | }
74 |
75 | if (passwd.length() == 0) {
76 | Toast.makeText(this, R.string.login_passwd_alert,
77 | Toast.LENGTH_SHORT).show();
78 | return;
79 | }
80 | m_pd = ProgressDialog.show(this, null, "登录中,请稍后……", true, true);
81 |
82 | Api.getInstance().login(uname, passwd, new NetClientCallback() {
83 |
84 | @Override
85 | public void execute(int status, String response,
86 | List cookies) {
87 | System.out.println("login:" + response);
88 |
89 | if (status == HttpClientUtil.NET_SUCCESS) {
90 | final JSONObject retObj = JSON.parseObject(response);
91 | final int ret = retObj.getInteger("result");
92 | if (ret != Api.LOGIN_SUCCESS) {
93 | m_handler.post(new Runnable() {
94 |
95 | @Override
96 | public void run() {
97 | m_pd.dismiss();
98 | switch (ret) {
99 | case Api.LOGIN_FAIL_LESS_THAN_FIVE:
100 | String alertText = "用户名或者密码错误,还有"
101 | + (Api.ALLOW_LOGIN_USERNAME_OR_PASSWD_ERROR_NUM - retObj
102 | .getInteger("strikes"))
103 | + "尝试机会";
104 | Toast.makeText(LoginPage.this, alertText,
105 | Toast.LENGTH_SHORT).show();
106 | break;
107 | case Api.LOGIN_FAIL_MORE_THAN_FIVE:
108 | Toast.makeText(LoginPage.this,
109 | R.string.login_fail_more_than_five,
110 | Toast.LENGTH_SHORT).show();
111 | break;
112 | }
113 |
114 | }
115 |
116 | });
117 | return;
118 | }
119 | String token = retObj.getString("securitytoken");
120 | Api.getInstance().setToken(token);
121 | Api.getInstance().setLoginUserInfo(
122 | retObj.getString("username"),
123 | retObj.getInteger("userid"),
124 | retObj.getInteger("isavatar"),
125 | retObj.getString("email"));
126 |
127 | for (int i = 0; i < cookies.size(); i++) {
128 | Cookie cookie = cookies.get(i);
129 | Api.getInstance().getCookieStorage()
130 | .addCookie(cookie.getName(), cookie.getValue());
131 | }
132 | LoginPage.this.sendBroadcast(new Intent(
133 | App.LOGIN_STATE_CHANGE_ACTION));
134 | }
135 | m_handler.sendEmptyMessage(status);
136 |
137 | }
138 |
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import android.app.TabActivity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.KeyEvent;
7 | import android.view.View;
8 | import android.widget.ImageView;
9 | import android.widget.TabHost;
10 | import android.widget.TabHost.TabSpec;
11 | import android.widget.Toast;
12 |
13 | import com.pediy.bbs.kanxue.net.Api;
14 |
15 | public class MainActivity extends TabActivity {
16 | private long m_exitTime = 0;
17 | private String[] m_tabTitle = new String[] { "新贴", "主页", "安全资讯", "设置" };
18 |
19 | private Class>[] m_tabIntent = new Class>[] { ForumDisplayPage.class,
20 | ForumHomePage.class, ForumDisplayPage.class, SettingPage.class };
21 |
22 | private int[] m_tabIcon = new int[] { R.drawable.collections_view_as_list,
23 | R.drawable.collections_view_as_grid, R.drawable.coffee,
24 | R.drawable.action_settings };
25 |
26 | private Bundle[] m_data = new Bundle[] {
27 | createBundle(Api.NEW_FORUM_ID, "新贴", true), null,
28 | createBundle(Api.SECURITY_FORUM_ID, "安全资讯", true), null };
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | this.setContentView(R.layout.main);
34 |
35 | TabHost tabHost = getTabHost();
36 | for (int i = 0; i < this.m_tabTitle.length; i++) {
37 | String title = this.m_tabTitle[i];
38 | Intent intent = new Intent(this, m_tabIntent[i]);
39 | if (m_data[i] != null) {
40 | intent.putExtras(m_data[i]);
41 | }
42 | View tab = getLayoutInflater().inflate(R.layout.forum_tab, null);
43 | ImageView imgView = (ImageView) tab.findViewById(R.id.tabIcon);
44 | imgView.setImageResource(m_tabIcon[i]);
45 | TabSpec spec = tabHost.newTabSpec(title).setIndicator(tab)
46 | .setContent(intent);
47 | tabHost.addTab(spec);
48 | }
49 | // 每次进入检测更新
50 | App app = (App) this.getApplication();
51 | app.checkUpdate(this);
52 | }
53 |
54 | // 按两下返回键退出,在tabActivity中不好用
55 | /*
56 | * @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
57 | * if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() ==
58 | * KeyEvent.ACTION_DOWN) { if((System.currentTimeMillis() - m_exitTime) >
59 | * 2000) { //System.currentTimeMillis()无论何时调用,肯定大于2000 Toast.makeText(this,
60 | * "再按一次退出程序",Toast.LENGTH_SHORT).show(); m_exitTime =
61 | * System.currentTimeMillis(); } else { finish(); System.exit(0); } return
62 | * true; } return super.onKeyDown(keyCode, event); }
63 | */
64 |
65 | @Override
66 | public boolean dispatchKeyEvent(KeyEvent event) {
67 | int keyCode = event.getKeyCode();
68 | if (keyCode == KeyEvent.KEYCODE_BACK
69 | && event.getAction() == KeyEvent.ACTION_DOWN) {
70 | if ((System.currentTimeMillis() - m_exitTime) > 2000) { // System.currentTimeMillis()无论何时调用,肯定大于2000
71 | Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
72 | m_exitTime = System.currentTimeMillis();
73 | } else {
74 | finish();
75 | System.exit(0);
76 | }
77 | return true;
78 | }
79 | return super.dispatchKeyEvent(event);
80 | }
81 |
82 | private Bundle createBundle(int id, String title, boolean bHideBackBtn) {
83 | Bundle data = new Bundle();
84 | data.putInt("id", id);
85 | data.putString("title", title);
86 | data.putBoolean("isHideBackBtn", bHideBackBtn);
87 | return data;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/NewThreadPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.http.cookie.Cookie;
6 |
7 | import com.alibaba.fastjson.JSON;
8 | import com.alibaba.fastjson.JSONObject;
9 | import com.pediy.bbs.kanxue.net.HttpClientUtil;
10 | import com.pediy.bbs.kanxue.net.Api;
11 | import com.pediy.bbs.kanxue.net.HttpClientUtil.NetClientCallback;
12 |
13 | import android.app.Activity;
14 | import android.app.ProgressDialog;
15 | import android.content.Intent;
16 | import android.os.Bundle;
17 | import android.os.Handler;
18 | import android.os.Message;
19 | import android.view.View;
20 | import android.widget.AdapterView;
21 | import android.widget.AdapterView.OnItemSelectedListener;
22 | import android.widget.ArrayAdapter;
23 | import android.widget.EditText;
24 | import android.widget.Spinner;
25 | import android.widget.TextView;
26 | import android.widget.Toast;
27 |
28 | public class NewThreadPage extends Activity {
29 | private int m_id;
30 | private ProgressDialog m_pd;
31 | private EditText m_kxReward;
32 | private EditText m_subject;
33 | private int m_currentTopic;
34 |
35 | private Handler m_handler = new Handler() {
36 | @Override
37 | public void handleMessage(Message msg) {
38 | switch (msg.what) {
39 | case HttpClientUtil.NET_TIMEOUT:
40 | Toast.makeText(NewThreadPage.this, R.string.net_timeout, Toast.LENGTH_SHORT).show();
41 | break;
42 | case HttpClientUtil.NET_FAILED:
43 | Toast.makeText(NewThreadPage.this, R.string.net_failed, Toast.LENGTH_SHORT).show();
44 | break;
45 | default:
46 | break;
47 | }
48 |
49 | //关闭ProgressDialog
50 | m_pd.dismiss();
51 | }
52 | };
53 |
54 | @Override
55 | public void onCreate(Bundle savedInstanceState) {
56 | super.onCreate(savedInstanceState);
57 | this.setContentView(R.layout.new_thread_page);
58 |
59 | Bundle data = this.getIntent().getExtras();
60 | m_id = data.getInt("id");
61 | m_subject = (EditText)this.findViewById(R.id.newThreadSubject);
62 |
63 | if (m_id == Api.HELP_FORUM_ID) {
64 | m_kxReward = (EditText)this.findViewById(R.id.newThreadKxReward);
65 | m_kxReward.setVisibility(View.VISIBLE);
66 | return;
67 | } else if (m_id == Api.GET_JOB_FORUM_ID) {
68 | m_subject.setText("【招聘】");
69 | return;
70 | }
71 |
72 | View v = this.findViewById(R.id.newThreadTopic);
73 | v.setVisibility(View.VISIBLE);
74 | Spinner spinner = (Spinner)this.findViewById(R.id.newThreadTopicSelect);
75 | final String[] topics = getResources().getStringArray(R.array.topic_list);
76 | ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, topics);
77 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
78 | spinner.setAdapter(adapter);
79 | spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
80 |
81 | @Override
82 | public void onItemSelected(AdapterView> arg0, View arg1,
83 | int index, long arg3) {
84 | if (index == 0)
85 | return;
86 | m_currentTopic = index;
87 | String tmp = m_subject.getText().toString();
88 | tmp = topics[index] + tmp;
89 | m_subject.setText(tmp);
90 | }
91 |
92 | @Override
93 | public void onNothingSelected(AdapterView> arg0) {
94 | // TODO 自动生成的方法存根
95 |
96 | }
97 |
98 | });
99 | }
100 |
101 | public void onBackBtnClick(View v) {
102 | this.finish();
103 | }
104 |
105 | public void onSubmitBtnClick(View v) {
106 | EditText message = (EditText)this.findViewById(R.id.newThreadMessage);
107 | final String subjectText = m_subject.getText().toString();
108 | String msgText = message.getText().toString();
109 |
110 | if (m_subject.length() == 0) {
111 | Toast.makeText(NewThreadPage.this, "标题不能为空", Toast.LENGTH_SHORT).show();
112 | m_subject.requestFocus();
113 | return;
114 | }
115 |
116 | if (m_id == Api.HELP_FORUM_ID) {
117 | if (m_kxReward.length() == 0) {
118 | Toast.makeText(NewThreadPage.this, "悬赏金额不能为空", Toast.LENGTH_SHORT).show();
119 | m_kxReward.requestFocus();
120 | return;
121 | } else {
122 | int kx = Integer.parseInt(m_kxReward.getText().toString());
123 | if (kx < 10 || kx > 100) {
124 | Toast.makeText(NewThreadPage.this, "悬赏金额超出限定范围", Toast.LENGTH_SHORT).show();
125 | m_kxReward.requestFocus();
126 | return;
127 | }
128 | }
129 | } else {
130 | if (m_id != Api.GET_JOB_FORUM_ID) {
131 | if (m_currentTopic == 0) {
132 | Toast.makeText(NewThreadPage.this, "请选择话题", Toast.LENGTH_SHORT).show();
133 | return;
134 | }
135 | }
136 | }
137 |
138 | if (message.length() == 0) {
139 | Toast.makeText(NewThreadPage.this, "内容不能为空", Toast.LENGTH_SHORT).show();
140 | message.requestFocus();
141 | return;
142 | } else if (message.length() < Api.POST_CONTENT_SIZE_MIN) {
143 | Toast.makeText(NewThreadPage.this, "内容长度不能小于"+Api.POST_CONTENT_SIZE_MIN+"个字符", Toast.LENGTH_SHORT).show();
144 | message.requestFocus();
145 | return;
146 | }
147 |
148 | NetClientCallback callback = new NetClientCallback() {
149 |
150 | @Override
151 | public void execute(int status, final String response,
152 | List cookies) {
153 | System.out.println("post new thread return:"+response);
154 |
155 | m_handler.sendEmptyMessage(status);
156 | if (status != HttpClientUtil.NET_SUCCESS)
157 | return;
158 | m_handler.post(new Runnable() {
159 |
160 | @Override
161 | public void run() {
162 | JSONObject jsonObj = JSON.parseObject(response);
163 | int result = jsonObj.getInteger("result");
164 |
165 | switch(result) {
166 | case Api.NEW_POST_SUCCESS:
167 | Toast.makeText(NewThreadPage.this, R.string.new_post_success, Toast.LENGTH_SHORT).show();
168 | break;
169 | case Api.NEW_POST_FAIL_WITHIN_THIRTY_SECONDS:
170 | Toast.makeText(NewThreadPage.this, R.string.new_post_fail_within_thirty_seconds, Toast.LENGTH_SHORT).show();
171 | return;
172 | case Api.NEW_POST_FAIL_WITHIN_FIVE_MINUTES:
173 | Toast.makeText(NewThreadPage.this, R.string.new_post_fail_within_five_minutes, Toast.LENGTH_SHORT).show();
174 | return;
175 | case Api.NEW_POST_FAIL_NOT_ENOUGH_KX:
176 | Toast.makeText(NewThreadPage.this, R.string.new_post_fail_not_enough_kx, Toast.LENGTH_SHORT).show();
177 | return;
178 | default:
179 | break;
180 | }
181 | Bundle data = new Bundle();
182 | data.putString("subject", subjectText);
183 | int id = jsonObj.getInteger("threadid");
184 | data.putInt("id", id);
185 | Intent intent = NewThreadPage.this.getIntent();
186 | intent.putExtras(data);
187 | NewThreadPage.this.setResult(1, intent);
188 | NewThreadPage.this.finish();
189 | }
190 |
191 | });
192 |
193 | }
194 |
195 | };
196 |
197 | m_pd = ProgressDialog.show(this, null, "发送帖子内容,请稍后……", true, true);
198 | if (m_id == Api.HELP_FORUM_ID) {
199 | Api.getInstance().newThread(this.m_id, subjectText, m_kxReward.getText().toString(), msgText, callback);
200 | return;
201 | }
202 | Api.getInstance().newThread(this.m_id, subjectText, msgText, callback);
203 |
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/SettingPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.net.HttpURLConnection;
9 | import java.net.MalformedURLException;
10 | import java.net.URL;
11 | import java.util.List;
12 |
13 | import org.apache.http.cookie.Cookie;
14 |
15 | import com.alibaba.fastjson.JSON;
16 | import com.alibaba.fastjson.JSONObject;
17 | import com.pediy.bbs.kanxue.App.onCheckUpdate;
18 | import com.pediy.bbs.kanxue.net.Api;
19 | import com.pediy.bbs.kanxue.net.HttpClientUtil;
20 | import com.pediy.bbs.kanxue.net.HttpClientUtil.NetClientCallback;
21 | import com.pediy.bbs.kanxue.widget.ImageViewWithCache;
22 |
23 | import android.app.Activity;
24 | import android.app.ProgressDialog;
25 | import android.app.AlertDialog.Builder;
26 | import android.content.ActivityNotFoundException;
27 | import android.content.BroadcastReceiver;
28 | import android.content.Context;
29 | import android.content.DialogInterface;
30 | import android.content.Intent;
31 | import android.content.IntentFilter;
32 | import android.content.DialogInterface.OnClickListener;
33 | import android.content.SharedPreferences;
34 | import android.content.SharedPreferences.Editor;
35 | import android.content.pm.PackageInfo;
36 | import android.content.pm.PackageManager;
37 | import android.content.pm.PackageManager.NameNotFoundException;
38 | import android.net.Uri;
39 | import android.os.Bundle;
40 | import android.os.Environment;
41 | import android.os.Handler;
42 | import android.os.Message;
43 | import android.preference.PreferenceManager;
44 | import android.text.Html;
45 | import android.view.View;
46 | import android.widget.ImageView;
47 | import android.widget.TextView;
48 | import android.widget.Toast;
49 |
50 | public class SettingPage extends Activity {
51 | private TextView m_version;
52 | private ImageViewWithCache m_loginUserHeadImg = null;
53 | private TextView m_loginUserName = null;
54 | private ImageView m_loginIcon = null;
55 | private Handler m_handler = null;
56 | private Recv m_recv = null;
57 |
58 | @Override
59 | public void onCreate(Bundle savedInstanceState) {
60 | super.onCreate(savedInstanceState);
61 | this.setContentView(R.layout.setting_page);
62 |
63 | m_version = (TextView)this.findViewById(R.id.settingAppVersion);
64 | PackageInfo pinfo = null;
65 | try {
66 | pinfo = getPackageManager().getPackageInfo(SettingPage.this.getPackageName(), PackageManager.GET_CONFIGURATIONS);
67 | m_version.setText(pinfo.versionName);
68 | } catch (NameNotFoundException e) {
69 | // TODO 自动生成的 catch 块
70 | e.printStackTrace();
71 | }
72 |
73 | m_loginUserHeadImg = (ImageViewWithCache)this.findViewById(R.id.settingPageLoginUserHeadImg);
74 | m_loginUserName = (TextView)this.findViewById(R.id.settingPageLoginUserName);
75 | m_loginIcon = (ImageView)this.findViewById(R.id.settingPageLoginIcon);
76 | if (Api.getInstance().isLogin()) {
77 | onLoginState();
78 | }
79 | m_handler = ((App)SettingPage.this.getApplication()).getNetworkHandler();
80 |
81 | m_recv = new Recv();
82 | IntentFilter filter = new IntentFilter();
83 | filter.addAction(App.LOGIN_STATE_CHANGE_ACTION);
84 | this.registerReceiver(m_recv, filter);
85 | }
86 |
87 | @Override
88 | public void onDestroy() {
89 | super.onDestroy();
90 | this.unregisterReceiver(m_recv);
91 | }
92 |
93 | private void onLoginState() {
94 | if (Api.getInstance().getIsAvatar() == 1) {
95 | String url = Api.getInstance().getUserHeadImageUrl(Api.getInstance().getLoginUserId());
96 | try {
97 | m_loginUserHeadImg.setImageUrl(new URL(url));
98 | } catch (MalformedURLException e) {
99 | // TODO 自动生成的 catch 块
100 | e.printStackTrace();
101 | }
102 | }
103 | m_loginIcon.setImageResource(R.drawable.logout_dark);
104 | //解析昵称中可能出现的html实体字符
105 | m_loginUserName.setText(Html.fromHtml(Api.getInstance().getLoginUserName()));
106 | }
107 |
108 | private void onLogoutState() {
109 | m_loginUserHeadImg.setImageResource(R.drawable.default_user_head_img);
110 | m_loginUserName.setText("游客");
111 | m_loginIcon.setImageResource(R.drawable.social_add_person_dark);
112 | }
113 |
114 | public void onShowMyInfo(View v) {
115 |
116 | if (Api.getInstance().isLogin()) {
117 | Bundle data = new Bundle();
118 | data.putInt("user_id",Api.getInstance().getLoginUserId());
119 | Intent intent = new Intent(SettingPage.this, UserInfoPage.class);
120 | intent.putExtras(data);
121 | v.getContext().startActivity(intent);
122 | return;
123 | }
124 | else {
125 | Toast.makeText(SettingPage.this, "提示:请登入后查看用户信息。",
126 | Toast.LENGTH_SHORT).show();
127 | }
128 |
129 | }
130 | public void onLoginItemClick(View v) {
131 | if (!Api.getInstance().isLogin()) {
132 | this.startActivityForResult(new Intent(this, LoginPage.class), 0);
133 | return;
134 | }
135 |
136 | Builder builder = new Builder(this);
137 | builder.setMessage(R.string.logout_alert_msg);
138 | builder.setPositiveButton("确定", new OnClickListener() {
139 |
140 | @Override
141 | public void onClick(DialogInterface dialog, int which) {
142 | final ProgressDialog pd = ProgressDialog.show(SettingPage.this, null, "登出中,请稍后……", true, true);
143 | Api.getInstance().logout(new NetClientCallback() {
144 |
145 | @Override
146 | public void execute(int status, String response,
147 | List cookies) {
148 | System.out.println("logout renturn:"+response);
149 | if (status == HttpClientUtil.NET_SUCCESS) {
150 | m_handler.post(new Runnable() {
151 |
152 | @Override
153 | public void run() {
154 | pd.dismiss();
155 | onLogoutState();
156 | Api.getInstance().clearLoginData();
157 | SettingPage.this.sendBroadcast(new Intent(App.LOGIN_STATE_CHANGE_ACTION));
158 | }
159 |
160 | });
161 | return;
162 | }
163 | m_handler.sendEmptyMessage(status);
164 | }
165 |
166 | });
167 |
168 | }
169 |
170 | }).setNegativeButton("取消", null);
171 | builder.create().show();
172 | // TODO 登录和登出两种状态刷新主页缓存
173 | }
174 |
175 | @Override
176 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
177 | switch (resultCode) {
178 | case 1:
179 | onLoginState();
180 | break;
181 | default:
182 | break;
183 | }
184 | }
185 |
186 | public void onCheckUpdateItemClick(View v) {
187 | final ProgressDialog pd = ProgressDialog.show(this, null, "检测更新中,请稍后……", true, true);
188 | final Handler handler = new Handler();
189 | final App app = (App)this.getApplication();
190 | app.checkUpdate(this, new onCheckUpdate() {
191 |
192 | @Override
193 | public void networkComplete() {
194 | handler.post(new Runnable() {
195 |
196 | @Override
197 | public void run() {
198 | pd.dismiss();
199 | }
200 |
201 | });
202 |
203 | }
204 |
205 | @Override
206 | public void noUpdate() {
207 | handler.post(new Runnable() {
208 |
209 | @Override
210 | public void run() {
211 | Toast.makeText(SettingPage.this, "已经是最新版本", Toast.LENGTH_SHORT).show();
212 | }
213 |
214 | });
215 | }
216 |
217 | });
218 | }
219 |
220 | public void onFeedbackItemClick(View v) {
221 | if (Api.getInstance().isLogin()) {
222 | this.startActivity(new Intent(this, FeedbackPage.class));
223 | } else {
224 | Intent emailIntent=new Intent(Intent.ACTION_SEND);
225 |
226 | String subject = "意见反馈";
227 | String[] extra = new String[]{"webmaster@pediy.com"};
228 |
229 | emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
230 | emailIntent.putExtra(Intent.EXTRA_EMAIL, extra);
231 | emailIntent.setType("message/rfc822");
232 | try {
233 | startActivity(emailIntent);
234 | } catch (ActivityNotFoundException e) {
235 | // TODO 自动生成的 catch 块
236 | e.printStackTrace();
237 | Toast.makeText(SettingPage.this, "未安装邮件客户端", Toast.LENGTH_SHORT).show();
238 | }
239 | }
240 | }
241 |
242 | public void onAboutBtnClick(View v) {
243 | this.startActivity(new Intent(this, AboutPage.class));
244 | }
245 |
246 | private class Recv extends BroadcastReceiver {
247 |
248 | @Override
249 | public void onReceive(Context context, Intent intent) {
250 | //未登陆状态下点击发帖或者回帖时,弹出登陆界面,此时登陆后需通知settingpage更新登陆用户信息
251 | if (Api.getInstance().isLogin())
252 | SettingPage.this.onLoginState();
253 | }
254 |
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.content.pm.PackageInfo;
6 | import android.content.pm.PackageManager;
7 | import android.content.pm.PackageManager.NameNotFoundException;
8 | import android.os.Bundle;
9 | import android.view.View;
10 | import android.widget.TextView;
11 |
12 | public class SplashActivity extends Activity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | final View view = View.inflate(this, R.layout.splash, null);
18 | setContentView(view);
19 |
20 | PackageInfo pinfo;
21 | try {
22 | pinfo = getPackageManager().getPackageInfo(this.getPackageName(),
23 | PackageManager.GET_CONFIGURATIONS);
24 | TextView tv = (TextView) this.findViewById(R.id.splashVerText);
25 | tv.setText("v" + pinfo.versionName);
26 | } catch (NameNotFoundException e1) {
27 | // TODO 自动生成的 catch 块
28 | e1.printStackTrace();
29 | }
30 |
31 | /*
32 | * AlphaAnimation anim = new AlphaAnimation(0.3f,1.0f);
33 | * anim.setDuration(100); view.startAnimation(anim);
34 | * anim.setAnimationListener(new AnimationListener() {
35 | *
36 | * @Override public void onAnimationEnd(Animation arg0) {
37 | * gotoMainPage(); }
38 | *
39 | * @Override public void onAnimationRepeat(Animation animation) {}
40 | *
41 | * @Override public void onAnimationStart(Animation animation) {} });
42 | */
43 |
44 | new Thread() {
45 |
46 | @Override
47 | public void run() {
48 | try {
49 | Thread.sleep(2000);
50 | } catch (InterruptedException e) {
51 | // TODO 自动生成的 catch 块
52 | e.printStackTrace();
53 | }
54 | gotoMainPage();
55 | }
56 |
57 | }.start();
58 | }
59 |
60 | private void gotoMainPage() {
61 | Intent intent = (new Intent(this, MainActivity.class));
62 | startActivity(intent);
63 | finish();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/UserInfoPage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.http.cookie.Cookie;
6 |
7 | import com.alibaba.fastjson.JSON;
8 | import com.alibaba.fastjson.JSONArray;
9 | import com.alibaba.fastjson.JSONObject;
10 | import com.pediy.bbs.kanxue.net.Api;
11 | import com.pediy.bbs.kanxue.net.HttpClientUtil;
12 | import com.pediy.bbs.kanxue.net.HttpClientUtil.NetClientCallback;
13 |
14 | import android.app.Activity;
15 | import android.content.Intent;
16 | import android.os.Bundle;
17 | import android.os.Handler;
18 | import android.os.Looper;
19 | import android.os.Message;
20 | import android.text.Html;
21 | import android.view.View;
22 | import android.widget.TextView;
23 | import android.widget.Toast;
24 |
25 | public class UserInfoPage extends Activity {
26 |
27 | private TextView m_UserNameContentView;
28 | private TextView m_IDView;
29 | private TextView m_IDContentView;
30 | private TextView m_UserTitleView;
31 | private TextView m_UserTitleContentView;
32 | private TextView m_PostsView;
33 | private TextView m_PostsContentView;
34 | private TextView m_moneyView;
35 | private TextView m_MoneyContentView;
36 | private TextView m_GoodnessView;
37 | private TextView m_GoodnessContentView;
38 | private String m_UserNameContent = null;
39 | private String m_UserTitleContent = null;
40 | private int m_PostsContent = 0;
41 | private int m_MoneyContent = 0;
42 | private int m_GoodnessContent = 0;
43 | private Integer m_IDContent = 0;
44 | private JSONArray m_person = null;
45 |
46 |
47 | private int m_id = 0;
48 |
49 | private Handler m_handler = new Handler(){
50 |
51 |
52 | @Override
53 | public void handleMessage(Message msg) {
54 | switch (msg.what) {
55 | case HttpClientUtil.NET_SUCCESS:
56 |
57 | for(int i = 0; i < m_person.size(); i++){
58 | JSONObject jsonObj = m_person.getJSONObject(i);
59 | m_UserNameContent = jsonObj.getString("username");
60 | m_UserTitleContent = jsonObj.getString("usertitle");
61 | m_IDContent = jsonObj.getInteger("userid");
62 | m_PostsContent = jsonObj.getInteger("posts");
63 | m_MoneyContent = jsonObj.getInteger("money");
64 | m_GoodnessContent = jsonObj.getInteger("goodnees");
65 | m_UserNameContentView.setText(Html.fromHtml(m_UserNameContent));
66 | m_UserTitleContentView.setText(Html.fromHtml(m_UserTitleContent));
67 | m_IDContentView.setText(""+m_IDContent);
68 | m_PostsContentView.setText(""+m_PostsContent);
69 | m_MoneyContentView.setText(""+m_MoneyContent+" Kx");
70 | m_GoodnessContentView.setText(""+m_GoodnessContent);
71 |
72 | }
73 | break;
74 | case HttpClientUtil.NET_TIMEOUT:
75 | Toast.makeText(UserInfoPage.this, R.string.net_timeout, Toast.LENGTH_SHORT).show();
76 | break;
77 | case HttpClientUtil.NET_FAILED:
78 | Toast.makeText(UserInfoPage.this, R.string.net_failed, Toast.LENGTH_SHORT).show();
79 | break;
80 | default:
81 | break;
82 | }
83 | }
84 |
85 | };
86 | @Override
87 | public void onCreate(Bundle savedInstanceState) {
88 | super.onCreate(savedInstanceState);
89 | this.setContentView(R.layout.show_user_info);
90 |
91 | m_IDView = (TextView)this.findViewById(R.id.peID);
92 | m_UserNameContentView = (TextView)this.findViewById(R.id.peNameConent);
93 | m_IDContentView =(TextView)this.findViewById(R.id.peIDConent);
94 | m_UserTitleView = (TextView)this.findViewById(R.id.peUserTitle);
95 | m_UserTitleContentView = (TextView)this.findViewById(R.id.peUserTitleConent);
96 | m_PostsView = (TextView)this.findViewById(R.id.pePosts);
97 | m_PostsContentView = (TextView)this.findViewById(R.id.pePostsConent);
98 | m_moneyView = (TextView)this.findViewById(R.id.peMoneny);
99 | m_MoneyContentView = (TextView)this.findViewById(R.id.peMoneyConent);
100 | m_GoodnessView = (TextView)this.findViewById(R.id.peGoodness);
101 | m_GoodnessContentView = (TextView)this.findViewById(R.id.peGoodnessConent);
102 | Bundle bundle = this.getIntent().getExtras();
103 |
104 | /*从Bundle中获得所需查看用戶的ID*/
105 | m_id = bundle.getInt("user_id");
106 | if (Api.getInstance().isLogin()) {
107 | onLoginState();
108 | }
109 |
110 |
111 | }
112 |
113 |
114 |
115 | private void onLoginState() {
116 | // 获取用户信息列表
117 | //m_id = Api.getInstance().getLoginUserId();
118 |
119 | if (m_id > 0) {
120 | Api.getInstance().getUserInfoPage(m_id, new NetClientCallback() {
121 |
122 | @Override
123 | public void execute(int status, String response,
124 | List cookies) {
125 | Looper.prepare();
126 | if (status == HttpClientUtil.NET_SUCCESS) {
127 | JSONArray jsonArray = JSON.parseArray(response);
128 | m_person = jsonArray;
129 |
130 | } else {
131 | Toast.makeText(UserInfoPage.this, "提示:目前网络状况不佳。",
132 | Toast.LENGTH_SHORT).show();
133 | }
134 | m_handler.sendEmptyMessage(status);
135 | Looper.loop();
136 | }
137 | });
138 | }
139 | else {
140 | Toast.makeText(UserInfoPage.this, "用户ID"+m_id+"不合法",
141 | Toast.LENGTH_SHORT).show();
142 | }
143 |
144 | }
145 |
146 |
147 | public void onBackBtnClick(View v) {
148 | finish();
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/net/HttpClientUtil.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.net;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.io.OutputStream;
11 | import java.io.UnsupportedEncodingException;
12 | import java.net.SocketTimeoutException;
13 | import java.net.URLEncoder;
14 | import java.util.ArrayList;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.Map;
18 | import org.apache.http.HttpResponse;
19 | import org.apache.http.HttpStatus;
20 | import org.apache.http.HttpVersion;
21 | import org.apache.http.NameValuePair;
22 | import org.apache.http.client.ClientProtocolException;
23 | import org.apache.http.client.entity.UrlEncodedFormEntity;
24 | import org.apache.http.client.methods.HttpGet;
25 | import org.apache.http.client.methods.HttpPost;
26 | import org.apache.http.client.methods.HttpUriRequest;
27 | import org.apache.http.cookie.Cookie;
28 | import org.apache.http.impl.client.DefaultHttpClient;
29 | import org.apache.http.message.BasicNameValuePair;
30 | import org.apache.http.params.BasicHttpParams;
31 | import org.apache.http.params.HttpConnectionParams;
32 |
33 |
34 | import android.util.Log;
35 |
36 | public class HttpClientUtil {
37 | public static int METHOD_GET = 1;
38 | public static int METHOD_POST = 2;
39 |
40 | public static final int NET_SUCCESS = 1;
41 | public static final int NET_FAILED = 2;
42 | public static final int NET_TIMEOUT = 3;
43 |
44 | private String url;
45 | private int method;
46 | private NetClientCallback callback;
47 | private Map params = new HashMap();
48 | private String m_cookie = null;
49 |
50 | public HttpClientUtil(String url, int method, NetClientCallback callback){
51 | this.url = url;
52 | this.method = method;
53 | this.callback = callback;
54 | }
55 |
56 | public HttpClientUtil addParam(String name, String value){
57 | params.put(name, value);
58 | return this;
59 | }
60 |
61 | public void addCookie(String cookie) {
62 | this.m_cookie = cookie;
63 | }
64 |
65 | public void asyncDownloadTo(final String path,final boolean bForceRefresh){
66 | new Thread(new Runnable() {
67 | @Override
68 | public void run() {
69 | syncDownloadTo(path,bForceRefresh);
70 | }
71 | }).start();
72 | }
73 |
74 | public void syncDownloadTo(String path,boolean bForceRefresh){
75 |
76 | int status = NET_FAILED;
77 |
78 | OutputStream output=null;
79 | InputStream input = null;
80 | List cookies = null;
81 | try {
82 | BasicHttpParams localBasicHttpParams = new BasicHttpParams();
83 | HttpConnectionParams.setConnectionTimeout(localBasicHttpParams, 6000);
84 | HttpConnectionParams.setSoTimeout(localBasicHttpParams, 6000);
85 | DefaultHttpClient client = new DefaultHttpClient(localBasicHttpParams);
86 | client.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_1);
87 | client.getParams().setParameter("http.useragent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
88 | client.getParams().setParameter("http.protocol.expect-continue", Boolean.FALSE);
89 | client.getParams().setParameter("http.protocol.content-charset", "UTF-8");
90 |
91 | HttpUriRequest request = getRequest();
92 | HttpResponse response = client.execute(request);
93 |
94 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
95 | long filelength = response.getEntity().getContentLength();
96 | if(filelength>0){
97 | input=response.getEntity().getContent();
98 | File file=new File(path);
99 | if(!bForceRefresh && file.exists() && new FileInputStream(file).available()==filelength){
100 | if(callback!=null){
101 | cookies = client.getCookieStore().getCookies();
102 | callback.execute(NET_SUCCESS,"", cookies);
103 | }
104 | return;
105 | }
106 | output=new FileOutputStream(file);
107 |
108 | long downlength = 0;
109 |
110 | byte[] buffer=new byte[4*1024];
111 | int len = input.read(buffer);
112 |
113 | while(len!=-1 ){
114 | output.write(buffer,0,len);
115 | downlength += len;
116 | len = input.read(buffer);
117 | }
118 | output.flush();
119 | if(downlength == filelength ){
120 | status = NET_SUCCESS;
121 | cookies = client.getCookieStore().getCookies();
122 | }
123 | }
124 | }
125 | } catch (SocketTimeoutException e){
126 | status = NET_TIMEOUT;
127 | } catch (ClientProtocolException e) {
128 | e.printStackTrace();
129 | } catch(IllegalStateException e){
130 | e.printStackTrace();
131 | } catch (IOException e) {
132 | e.printStackTrace();
133 | } finally {
134 | try {
135 | if(output!=null){
136 | output.close();
137 | }
138 | } catch (IOException e) {
139 | e.printStackTrace();
140 | }
141 | try {
142 | if(input!=null){
143 | input.close();
144 | }
145 | } catch (IOException e) {
146 | e.printStackTrace();
147 | }
148 | }
149 | if(callback!=null){
150 | callback.execute(status,"", cookies);
151 | }
152 | }
153 |
154 | public void asyncConnect(){
155 | new Thread(new Runnable() {
156 | @Override
157 | public void run() {
158 | syncConnect();
159 | }
160 | }).start();
161 | }
162 |
163 | public void syncConnect(){
164 | String responseString = null;
165 | BufferedReader reader = null;
166 | int status = NET_FAILED;
167 | List cookies = null;
168 | try {
169 | BasicHttpParams localBasicHttpParams = new BasicHttpParams();
170 | HttpConnectionParams.setConnectionTimeout(localBasicHttpParams, 6000);
171 | HttpConnectionParams.setSoTimeout(localBasicHttpParams, 6000);
172 | DefaultHttpClient client = new DefaultHttpClient(localBasicHttpParams);
173 | client.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_1);
174 | client.getParams().setParameter("http.useragent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
175 | client.getParams().setParameter("http.protocol.expect-continue", Boolean.FALSE);
176 | client.getParams().setParameter("http.protocol.content-charset", "UTF-8");
177 |
178 | HttpUriRequest request = getRequest();
179 |
180 | HttpResponse response = client.execute(request);
181 |
182 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
183 | reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
184 | StringBuilder sb = new StringBuilder();
185 | for (String s = reader.readLine(); s != null; s = reader.readLine()) {
186 | sb.append(s);
187 | }
188 | responseString = sb.toString();
189 | status = NET_SUCCESS;
190 | cookies = client.getCookieStore().getCookies();
191 | }
192 | } catch (SocketTimeoutException e){
193 | status = NET_TIMEOUT;
194 | } catch (ClientProtocolException e) {
195 | Log.e("HttpClientUtil", e.getMessage(), e);
196 | } catch (IOException e) {
197 | Log.e("HttpClientUtil", e.getMessage(), e);
198 | } finally {
199 | try {
200 | if (reader != null) {
201 | reader.close();
202 | }
203 | } catch (IOException e) {
204 | // ignore me
205 | }
206 | }
207 | if(callback!=null){
208 | callback.execute(status,responseString, cookies);
209 | }
210 | }
211 |
212 | private HttpUriRequest getRequest() {
213 | if (method==METHOD_POST) {
214 | List listParams = new ArrayList();
215 | for (String name : params.keySet()) {
216 | listParams.add(new BasicNameValuePair(name, params.get(name)));
217 | }
218 | try {
219 | UrlEncodedFormEntity entity = new UrlEncodedFormEntity(listParams,"UTF-8");
220 | HttpPost request = new HttpPost(url);
221 | request.setEntity(entity);
222 | if (this.m_cookie != null) {
223 | request.addHeader("Cookie", this.m_cookie);
224 | }
225 | return request;
226 | } catch (UnsupportedEncodingException e) {
227 | throw new java.lang.RuntimeException(e.getMessage(), e);
228 | }
229 | } else {
230 | if (url.indexOf("?") < 0) {
231 | url += "?";
232 | }
233 | for (String name : params.keySet()) {
234 | try {
235 | url += "&" + name + "=" + URLEncoder.encode(params.get(name),"UTF-8");
236 | } catch (UnsupportedEncodingException e) {
237 | e.printStackTrace();
238 | }
239 | }
240 | HttpGet request = new HttpGet(url);
241 | if (this.m_cookie != null) {
242 | request.addHeader("Cookie", this.m_cookie);
243 | }
244 | return request;
245 | }
246 | }
247 |
248 | public interface NetClientCallback {
249 | public void execute(int status, String response, List cookies);
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/util/CookieStorage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.util;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 |
6 | public class CookieStorage {
7 | private ObjStorage m_objStorage = null;
8 | private HashMap m_cookies = null;
9 |
10 | public CookieStorage(ObjStorage objStorage) {
11 | this.m_objStorage = objStorage;
12 | Object obj = m_objStorage.load("cookie");
13 | this.m_cookies = (obj == null)?new HashMap():(HashMap)obj;
14 | }
15 |
16 | public void addCookie(String name, String value) {
17 | if (name == null || value == null)
18 | return;
19 |
20 | if (m_cookies.containsKey(name) && m_cookies.get(name) == value)
21 | return;
22 | m_cookies.put(name, value);
23 | m_objStorage.save("cookie", m_cookies);
24 | }
25 |
26 | public String getCookies() {
27 | if (this.m_cookies.size() == 0)
28 | return null;
29 |
30 | String cookies = "";
31 | Object[] keys = this.m_cookies.keySet().toArray();
32 | cookies += (keys[0]+"="+this.m_cookies.get(keys[0]));
33 | for (int i = 1; i < m_cookies.size(); i++) {
34 | cookies += ("; "+keys[i]+"="+this.m_cookies.get(keys[i]));
35 | }
36 | return cookies;
37 | }
38 |
39 | public boolean hasCookie(String name) {
40 | return this.m_cookies.containsKey(name);
41 | }
42 |
43 | public void clearAll() {
44 | this.m_cookies.clear();
45 | m_objStorage.remove("cookie");
46 | }
47 |
48 | public void remove(String name) throws IOException {
49 | if (null == name)
50 | return;
51 |
52 | m_cookies.remove(name);
53 | m_objStorage.save("cookie", m_cookies);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/util/ObjStorage.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.util;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.IOException;
6 | import java.io.ObjectInputStream;
7 | import java.io.ObjectOutputStream;
8 | import java.io.StreamCorruptedException;
9 |
10 | import android.content.SharedPreferences;
11 | import android.content.SharedPreferences.Editor;
12 | import android.util.Base64;
13 |
14 | public class ObjStorage {
15 | private SharedPreferences m_sharedPreferences;
16 |
17 | public ObjStorage(SharedPreferences sharedPreferences) {
18 | m_sharedPreferences = sharedPreferences;
19 | }
20 |
21 | public void save(String key, Object obj) {
22 | if (obj == null || key == null)
23 | return;
24 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
25 | ObjectOutputStream oos;
26 | try {
27 | oos = new ObjectOutputStream(baos);
28 | oos.writeObject(obj);
29 | } catch (IOException e) {
30 | // TODO 自动生成的 catch 块
31 | e.printStackTrace();
32 | return;
33 | }
34 |
35 | SharedPreferences.Editor editor = m_sharedPreferences.edit();
36 | editor.putString(key, Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT));
37 | editor.commit();
38 | }
39 |
40 | public Object load(String key) {
41 | String wordBase64 = m_sharedPreferences.getString(key, null);
42 | if (wordBase64 == null)
43 | return null;
44 | byte[] base64Bytes = Base64.decode(wordBase64.getBytes(), Base64.DEFAULT);
45 | ByteArrayInputStream bais = new ByteArrayInputStream(base64Bytes);
46 | ObjectInputStream ois = null;
47 | Object ret = null;
48 | try {
49 | ois = new ObjectInputStream(bais);
50 | ret = ois.readObject();
51 | } catch (StreamCorruptedException e) {
52 | // TODO 自动生成的 catch 块
53 | e.printStackTrace();
54 | return null;
55 | } catch (IOException e) {
56 | // TODO 自动生成的 catch 块
57 | e.printStackTrace();
58 | return null;
59 | } catch (ClassNotFoundException e) {
60 | // TODO 自动生成的 catch 块
61 | e.printStackTrace();
62 | return null;
63 | }
64 | return ret;
65 | }
66 |
67 | public void remove(String key) {
68 | Editor editor = m_sharedPreferences.edit();
69 | editor.remove(key);
70 | editor.commit();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/util/SimpleHASH.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.util;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.security.MessageDigest;
5 | import java.security.NoSuchAlgorithmException;
6 |
7 | public class SimpleHASH {
8 | private static String convertToHex(byte[] data) {
9 | StringBuilder buf = new StringBuilder();
10 | for (byte b : data) {
11 | int halfbyte = (b >>> 4) & 0x0F;
12 | int two_halfs = 0;
13 | do {
14 | buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte) : (char) ('a' + (halfbyte - 10)));
15 | halfbyte = b & 0x0F;
16 | } while (two_halfs++ < 1);
17 | }
18 | return buf.toString();
19 | }
20 |
21 | public static String sha1(String text) {
22 | return convertToHex(hash("SHA-1", text));
23 | }
24 |
25 | public static String md5(String text) {
26 | return convertToHex(hash("MD5", text));
27 | }
28 |
29 | private static byte[] hash(String hashType, String input) {
30 | MessageDigest md = null;
31 | try {
32 | md = MessageDigest.getInstance(hashType);
33 | if (md == null)
34 | return null;
35 | md.update(input.getBytes("iso-8859-1"), 0, input.length());
36 | } catch (NoSuchAlgorithmException e) {
37 | // TODO 自动生成的 catch 块
38 | e.printStackTrace();
39 | return null;
40 | } catch (UnsupportedEncodingException e) {
41 | // TODO 自动生成的 catch 块
42 | e.printStackTrace();
43 | return null;
44 | }
45 | return md.digest();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/AmazingAdapter.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import android.widget.AbsListView;
6 | import android.widget.AbsListView.OnScrollListener;
7 | import android.widget.BaseAdapter;
8 | import android.widget.SectionIndexer;
9 |
10 | public abstract class AmazingAdapter extends BaseAdapter implements SectionIndexer, OnScrollListener {
11 | public static final String TAG = AmazingAdapter.class.getSimpleName();
12 |
13 | public interface HasMorePagesListener {
14 | void noMorePages();
15 | void mayHaveMorePages();
16 | }
17 |
18 | /**
19 | * The current page, not the page that is going to be loaded.
20 | */
21 | int page = 1;
22 | int initialPage = 1;
23 | boolean automaticNextPageLoading = false;
24 | HasMorePagesListener hasMorePagesListener;
25 |
26 | void setHasMorePagesListener(HasMorePagesListener hasMorePagesListener) {
27 | this.hasMorePagesListener = hasMorePagesListener;
28 | }
29 |
30 | /**
31 | * Pinned header state: don't show the header.
32 | */
33 | public static final int PINNED_HEADER_GONE = 0;
34 |
35 | /**
36 | * Pinned header state: show the header at the top of the list.
37 | */
38 | public static final int PINNED_HEADER_VISIBLE = 1;
39 |
40 | /**
41 | * Pinned header state: show the header. If the header extends beyond
42 | * the bottom of the first shown element, push it up and clip.
43 | */
44 | public static final int PINNED_HEADER_PUSHED_UP = 2;
45 |
46 | /**
47 | * Computes the desired state of the pinned header for the given
48 | * position of the first visible list item. Allowed return values are
49 | * {@link #PINNED_HEADER_GONE}, {@link #PINNED_HEADER_VISIBLE} or
50 | * {@link #PINNED_HEADER_PUSHED_UP}.
51 | */
52 | public int getPinnedHeaderState(int position) {
53 | if (position < 0 || getCount() == 0) {
54 | return PINNED_HEADER_GONE;
55 | }
56 |
57 | // The header should get pushed up if the top item shown
58 | // is the last item in a section for a particular letter.
59 | int section = getSectionForPosition(position);
60 | int nextSectionPosition = getPositionForSection(section + 1);
61 | if (nextSectionPosition != -1 && position == nextSectionPosition - 1) {
62 | return PINNED_HEADER_PUSHED_UP;
63 | }
64 |
65 | return PINNED_HEADER_VISIBLE;
66 | }
67 |
68 | /**
69 | * Sets the initial page when {@link #resetPage()} is called.
70 | * Default is 1 (for APIs with 1-based page number).
71 | */
72 | public void setInitialPage(int initialPage) {
73 | this.initialPage = initialPage;
74 | }
75 |
76 | /**
77 | * Resets the current page to the page specified in {@link #setInitialPage(int)}.
78 | */
79 | public void resetPage() {
80 | this.page = this.initialPage;
81 | }
82 |
83 | /**
84 | * Increases the current page number.
85 | */
86 | public void nextPage() {
87 | this.page++;
88 | }
89 |
90 | @Override
91 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
92 | if (view instanceof AmazingListView) {
93 | ((AmazingListView) view).configureHeaderView(firstVisibleItem);
94 | }
95 | }
96 |
97 | @Override
98 | public void onScrollStateChanged(AbsListView view, int scrollState) {
99 | // nop
100 | }
101 |
102 | @Override
103 | public final View getView(int position, View convertView, ViewGroup parent) {
104 | View res = getAmazingView(position, convertView, parent);
105 |
106 | if (position == getCount() - 1 && automaticNextPageLoading) {
107 | onNextPageRequested(page + 1);
108 | }
109 |
110 | final int section = getSectionForPosition(position);
111 | boolean displaySectionHeaders = (getPositionForSection(section) == position);
112 |
113 | bindSectionHeader(res, position, displaySectionHeaders);
114 |
115 | return res;
116 | }
117 |
118 | public void notifyNoMorePages() {
119 | automaticNextPageLoading = false;
120 | if (hasMorePagesListener != null) hasMorePagesListener.noMorePages();
121 | }
122 |
123 | public void notifyMayHaveMorePages() {
124 | automaticNextPageLoading = true;
125 | if (hasMorePagesListener != null) hasMorePagesListener.mayHaveMorePages();
126 | }
127 |
128 | /**
129 | * The last item on the list is requested to be seen, so do the request
130 | * and call {@link AmazingListView#tellNoMoreData()} if there is no more pages.
131 | *
132 | * @param page the page number to load.
133 | */
134 | protected abstract void onNextPageRequested(int page);
135 |
136 | /**
137 | * Configure the view (a listview item) to display headers or not based on displaySectionHeader
138 | * (e.g. if displaySectionHeader header.setVisibility(VISIBLE) else header.setVisibility(GONE)).
139 | */
140 | protected abstract void bindSectionHeader(View view, int position, boolean displaySectionHeader);
141 |
142 | /**
143 | * read: get view too
144 | */
145 | public abstract View getAmazingView(int position, View convertView, ViewGroup parent);
146 |
147 | /**
148 | * Configures the pinned header view to match the first visible list item.
149 | *
150 | * @param header pinned header view.
151 | * @param position position of the first visible list item.
152 | * @param alpha fading of the header view, between 0 and 255.
153 | */
154 | public abstract void configurePinnedHeader(View header, int position, int alpha);
155 |
156 | @Override
157 | public abstract int getPositionForSection(int section);
158 |
159 | @Override
160 | public abstract int getSectionForPosition(int position);
161 |
162 | @Override
163 | public abstract Object[] getSections();
164 | }
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/AmazingListView.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import com.pediy.bbs.kanxue.widget.AmazingAdapter.HasMorePagesListener;
4 |
5 | import android.content.Context;
6 | import android.graphics.Canvas;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.widget.ListAdapter;
10 | import android.widget.ListView;
11 |
12 | public class AmazingListView extends ListView implements HasMorePagesListener {
13 | public static final String TAG = AmazingListView.class.getSimpleName();
14 |
15 | View listFooter;
16 | boolean footerViewAttached = false;
17 |
18 | private View mHeaderView;
19 | private boolean mHeaderViewVisible;
20 |
21 | private int mHeaderViewWidth;
22 | private int mHeaderViewHeight;
23 |
24 | private AmazingAdapter adapter;
25 |
26 | public void setPinnedHeaderView(View view) {
27 | mHeaderView = view;
28 |
29 | // Disable vertical fading when the pinned header is present
30 | // TODO change ListView to allow separate measures for top and bottom fading edge;
31 | // in this particular case we would like to disable the top, but not the bottom edge.
32 | if (mHeaderView != null) {
33 | setFadingEdgeLength(0);
34 | }
35 | requestLayout();
36 | }
37 |
38 | @Override
39 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
40 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
41 | if (mHeaderView != null) {
42 | measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
43 | mHeaderViewWidth = mHeaderView.getMeasuredWidth();
44 | mHeaderViewHeight = mHeaderView.getMeasuredHeight();
45 | }
46 | }
47 |
48 | @Override
49 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
50 | super.onLayout(changed, left, top, right, bottom);
51 | if (mHeaderView != null) {
52 | mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
53 | configureHeaderView(getFirstVisiblePosition());
54 | }
55 | }
56 |
57 | public void configureHeaderView(int position) {
58 | if (mHeaderView == null) {
59 | return;
60 | }
61 |
62 | int state = adapter.getPinnedHeaderState(position);
63 | switch (state) {
64 | case AmazingAdapter.PINNED_HEADER_GONE: {
65 | mHeaderViewVisible = false;
66 | break;
67 | }
68 |
69 | case AmazingAdapter.PINNED_HEADER_VISIBLE: {
70 | adapter.configurePinnedHeader(mHeaderView, position, 255);
71 | if (mHeaderView.getTop() != 0) {
72 | mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
73 | }
74 | mHeaderViewVisible = true;
75 | break;
76 | }
77 |
78 | case AmazingAdapter.PINNED_HEADER_PUSHED_UP: {
79 | View firstView = getChildAt(0);
80 | if (firstView != null) {
81 | int bottom = firstView.getBottom();
82 | int headerHeight = mHeaderView.getHeight();
83 | int y;
84 | int alpha;
85 | if (bottom < headerHeight) {
86 | y = (bottom - headerHeight);
87 | alpha = 255 * (headerHeight + y) / headerHeight;
88 | } else {
89 | y = 0;
90 | alpha = 255;
91 | }
92 | adapter.configurePinnedHeader(mHeaderView, position, alpha);
93 | if (mHeaderView.getTop() != y) {
94 | mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
95 | }
96 | mHeaderViewVisible = true;
97 | }
98 | break;
99 | }
100 | }
101 | }
102 |
103 | @Override
104 | protected void dispatchDraw(Canvas canvas) {
105 | super.dispatchDraw(canvas);
106 | if (mHeaderViewVisible) {
107 | drawChild(canvas, mHeaderView, getDrawingTime());
108 | }
109 | }
110 |
111 |
112 | public AmazingListView(Context context) {
113 | super(context);
114 | }
115 |
116 | public AmazingListView(Context context, AttributeSet attrs) {
117 | super(context, attrs);
118 | }
119 |
120 | public AmazingListView(Context context, AttributeSet attrs, int defStyle) {
121 | super(context, attrs, defStyle);
122 | }
123 |
124 | public void setLoadingView(View listFooter) {
125 | this.listFooter = listFooter;
126 | }
127 |
128 | public View getLoadingView() {
129 | return listFooter;
130 | }
131 |
132 | @Override
133 | public void setAdapter(ListAdapter adapter) {
134 | if (!(adapter instanceof AmazingAdapter)) {
135 | throw new IllegalArgumentException(AmazingListView.class.getSimpleName() + " must use adapter of type " + AmazingAdapter.class.getSimpleName());
136 | }
137 |
138 | // previous adapter
139 | if (this.adapter != null) {
140 | this.adapter.setHasMorePagesListener(null);
141 | this.setOnScrollListener(null);
142 | }
143 |
144 | this.adapter = (AmazingAdapter) adapter;
145 | ((AmazingAdapter)adapter).setHasMorePagesListener(this);
146 | this.setOnScrollListener((AmazingAdapter) adapter);
147 |
148 | View dummy = new View(getContext());
149 | super.addFooterView(dummy);
150 | super.setAdapter(adapter);
151 | super.removeFooterView(dummy);
152 | }
153 |
154 | @Override
155 | public AmazingAdapter getAdapter() {
156 | return adapter;
157 | }
158 |
159 | @Override
160 | public void noMorePages() {
161 | if (listFooter != null) {
162 | this.removeFooterView(listFooter);
163 | }
164 | footerViewAttached = false;
165 | }
166 |
167 | @Override
168 | public void mayHaveMorePages() {
169 | if (! footerViewAttached && listFooter != null) {
170 | this.addFooterView(listFooter);
171 | footerViewAttached = true;
172 | }
173 | }
174 |
175 | public boolean isLoadingViewVisible() {
176 | return footerViewAttached;
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/ImageViewWithCache.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import java.io.File;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.net.URL;
9 | import java.net.URLConnection;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 |
15 | import com.pediy.bbs.kanxue.util.SimpleHASH;
16 |
17 | import android.content.Context;
18 | import android.graphics.Bitmap;
19 | import android.graphics.BitmapFactory;
20 | import android.os.Environment;
21 | import android.os.Handler;
22 | import android.text.format.Time;
23 | import android.util.AttributeSet;
24 | import android.util.DisplayMetrics;
25 | import android.view.WindowManager;
26 | import android.widget.ImageView;
27 |
28 | public class ImageViewWithCache extends ImageView {
29 | private static ExecutorService s_threadPool = Executors.newFixedThreadPool(4);
30 | private static HashMap> s_updateQueue = new HashMap>();
31 | private static Handler s_handler = new Handler();
32 | private static final String s_cachePath = Environment.getExternalStorageDirectory() + "/kanxue/img";
33 | private URL m_currentUrl = null;
34 | private int m_ageingTime = 0; //老化时间,单位小时
35 |
36 | static {
37 | createCachePath();
38 | }
39 |
40 | public ImageViewWithCache(Context context) {
41 | super(context);
42 | }
43 |
44 | public ImageViewWithCache(Context context, AttributeSet attrs) {
45 | super(context, attrs);
46 | initWithAttrs(context, attrs);
47 | }
48 |
49 | public ImageViewWithCache(Context context, AttributeSet attrs, int defStyle) {
50 | super(context, attrs, defStyle);
51 | initWithAttrs(context, attrs);
52 | }
53 |
54 | private void initWithAttrs(Context context, AttributeSet attrs) {
55 | //从xml文件中读取cache_path字段的值
56 | int resouceId = attrs.getAttributeResourceValue(null, "ageing_time", 0);
57 | if(resouceId <= 0)
58 | return;
59 | m_ageingTime = Integer.parseInt(context.getResources().getString(resouceId));
60 | }
61 |
62 | public void setAgeingTime(int hour) {
63 | this.m_ageingTime = hour;
64 | }
65 |
66 | public URL getCurrentUrl() {
67 | return this.m_currentUrl;
68 | }
69 |
70 | private static void createCachePath() {
71 | if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
72 | return;
73 | }
74 | File file = new File(s_cachePath);
75 | if (file.exists())
76 | return;
77 | file.mkdirs();
78 | }
79 |
80 | private boolean isOld(File file) {
81 | if (this.m_ageingTime == 0)
82 | return false;
83 |
84 | long now = System.currentTimeMillis();
85 | long fileModeTime = file.lastModified();
86 | return (now - fileModeTime > this.m_ageingTime*60*60*1000);
87 | }
88 |
89 | public static Bitmap getBitmapFromUrl(URL url, String cookies) {
90 | if (url == null)
91 | return null;
92 |
93 | URLConnection connection;
94 | InputStream is = null;
95 | try {
96 | connection = url.openConnection();
97 | connection.setUseCaches(true);
98 | if (cookies != null) {
99 | connection.addRequestProperty("Cookie", cookies);
100 | }
101 | is = (InputStream)connection.getContent();
102 | } catch (IOException e) {
103 | // TODO 自动生成的 catch 块
104 | e.printStackTrace();
105 | }
106 | return BitmapFactory.decodeStream(is);
107 | }
108 |
109 | public static void loadImage(final URL url, final String cookies) {
110 | if (url == null)
111 | return;
112 |
113 | Runnable runGetImg = new Runnable() {
114 | @Override
115 | public void run() {
116 | final Bitmap bitmap = getBitmapFromUrl(url, cookies);
117 | if (bitmap == null)
118 | return;
119 |
120 | s_handler.post(new Runnable() {
121 | @Override
122 | public void run() {
123 | ArrayList imgList = s_updateQueue.get(url);
124 | for (int i = 0; i < imgList.size(); i++) {
125 | ImageViewWithCache img = imgList.get(i);
126 | if (img.getCurrentUrl().equals(url)) {
127 | bitmap.setDensity(getDensityDpi(img.getContext()));
128 | img.setImageBitmap(bitmap);
129 | }
130 | }
131 | s_updateQueue.remove(url);
132 | }
133 |
134 | });
135 | cacheBitmapFromUrl(url, bitmap);
136 | }
137 | };
138 | s_threadPool.submit(runGetImg);
139 | }
140 |
141 | public static int getDensityDpi(Context con) {
142 | DisplayMetrics metrics = new DisplayMetrics();
143 | WindowManager mWm = (WindowManager)con.getSystemService(Context.WINDOW_SERVICE);
144 | mWm.getDefaultDisplay().getMetrics(metrics);
145 | return metrics.densityDpi;
146 | }
147 |
148 | public static void cacheBitmapFromUrl(URL url, Bitmap bitmap) {
149 | if (url == null || bitmap == null)
150 | return;
151 |
152 | File tFile = new File(getImgPathFromUrl(url));
153 | try {
154 | if (tFile != null) {
155 | if (!tFile.exists()) {
156 | tFile.createNewFile();
157 | }
158 | FileOutputStream fos = new FileOutputStream(tFile);
159 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
160 | fos.flush();
161 | fos.close();
162 | }
163 | } catch (FileNotFoundException e) {
164 | // TODO 自动生成的 catch 块
165 | e.printStackTrace();
166 | } catch (IOException e) {
167 | // TODO 自动生成的 catch 块
168 | e.printStackTrace();
169 | }
170 | }
171 |
172 | public static File getCachedImgFromUrl(URL url) {
173 | if (url == null)
174 | return null;
175 |
176 | File targetFile = null;
177 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
178 | targetFile = new File(getImgPathFromUrl(url));
179 | if (targetFile.exists()) {
180 | return targetFile;
181 | }
182 | }
183 | return null;
184 | }
185 |
186 | private static String getImgPathFromUrl(URL url) {
187 | if (url == null)
188 | return null;
189 | return s_cachePath + "/" + SimpleHASH.sha1(url.toString()) + ".png";
190 | }
191 |
192 | public void setImageUrl(URL url, String cookies) {
193 | if (url == null)
194 | return;
195 |
196 | this.m_currentUrl = url;
197 |
198 | File targetFile = getCachedImgFromUrl(url);
199 | if (targetFile != null && !this.isOld(targetFile)) {
200 | Bitmap bitmap = BitmapFactory.decodeFile(targetFile.getAbsolutePath());
201 | if (bitmap == null) {
202 | System.err.println("ImageViewWithCache decodeFile failed。 url:" + url + " path:" + targetFile.getAbsolutePath());
203 | return;
204 | }
205 | bitmap.setDensity(getDensityDpi(this.getContext()));
206 | this.setImageBitmap(bitmap);
207 | return;
208 | }
209 |
210 | if (s_updateQueue.containsKey(url)) {
211 | s_updateQueue.get(url).add(this);
212 | return;
213 | }
214 | ArrayList imgList = new ArrayList();
215 | imgList.add(this);
216 | s_updateQueue.put(url, imgList);
217 | loadImage(url, cookies);
218 | }
219 |
220 | public void setImageUrl(URL url) {
221 | this.setImageUrl(url, null);
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/RefreshActionBtn.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import com.pediy.bbs.kanxue.R;
4 |
5 | import android.content.Context;
6 | import android.widget.ImageView;
7 | import android.widget.RelativeLayout;
8 | import android.util.AttributeSet;
9 | import android.view.LayoutInflater;
10 | import android.view.animation.Animation;
11 | import android.view.animation.LinearInterpolator;
12 | import android.view.animation.RotateAnimation;
13 |
14 | public class RefreshActionBtn extends RelativeLayout {
15 | private ImageView m_icon;
16 |
17 | public RefreshActionBtn(Context context) {
18 | super(context);
19 | this.init(context);
20 | }
21 |
22 | public RefreshActionBtn(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | this.init(context);
25 | }
26 |
27 | public RefreshActionBtn(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | this.init(context);
30 | }
31 |
32 | private void init(Context context) {
33 | LayoutInflater.from(context).inflate(R.layout.refresh_action_btn, this);
34 | m_icon = (ImageView)this.findViewById(R.id.refreshBtn);
35 |
36 | }
37 |
38 | public void startRefresh() {
39 | if (m_icon.getAnimation() != null && !m_icon.getAnimation().hasEnded()) {
40 | return;
41 | }
42 | float centerX = m_icon.getWidth()/2.0f;
43 | float centerY = m_icon.getHeight()/2.0f;
44 |
45 | RotateAnimation ra = new RotateAnimation(0, 360, centerX, centerY);
46 | ra.setDuration(1000);
47 | ra.setRepeatCount(-1);
48 | ra.setInterpolator(new LinearInterpolator());
49 | this.m_icon.startAnimation(ra);
50 | }
51 |
52 | public void endRefresh() {
53 | this.m_icon.clearAnimation();
54 | }
55 |
56 | public boolean isRefreshing() {
57 | Animation a = this.m_icon.getAnimation();
58 | return (a != null)?!a.hasEnded():false;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/ThreadItemFooter.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import com.pediy.bbs.kanxue.R;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.ImageView;
10 | import android.widget.LinearLayout;
11 | import android.widget.TextView;
12 |
13 | public class ThreadItemFooter extends LinearLayout {
14 | private ImageView m_TipImg;
15 | private TextView m_TipText;
16 | private View m_processBar;
17 |
18 | public ThreadItemFooter(Context context) {
19 | super(context);
20 | }
21 |
22 | public ThreadItemFooter(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | LayoutInflater.from(context).inflate(R.layout.thread_item_footer, this);
25 | m_TipImg = (ImageView)this.findViewById(R.id.threadItemFooterTipImg);
26 | m_TipText = (TextView)this.findViewById(R.id.threadItemFooterTipText);
27 | m_processBar = this.findViewById(R.id.threadItemProgressBar);
28 | }
29 |
30 | public void setExpanded() {
31 | m_TipImg.setImageResource(R.drawable.navigation_collapse);
32 | m_TipText.setText(R.string.tap_load_less);
33 | }
34 |
35 | public void setCollapsed() {
36 | m_TipImg.setImageResource(R.drawable.navigation_expand);
37 | m_TipText.setText(R.string.tap_load_more);
38 | }
39 |
40 | public void setLoading() {
41 | m_TipImg.setVisibility(View.INVISIBLE);
42 | m_processBar.setVisibility(View.VISIBLE);
43 | }
44 |
45 | public void setLoadFinish() {
46 | m_processBar.setVisibility(View.GONE);
47 | m_TipImg.setVisibility(View.VISIBLE);
48 | }
49 |
50 | public boolean isLoading() {
51 | return (m_TipImg.getVisibility() == View.INVISIBLE);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/XListViewFooter.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import com.pediy.bbs.kanxue.R;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.LinearLayout;
10 | import android.widget.TextView;
11 |
12 | public class XListViewFooter extends LinearLayout{
13 | public final static int STATE_NORMAL = 0;
14 | public final static int STATE_READY = 1;
15 | public final static int STATE_LOADING = 2;
16 |
17 | private Context mContext;
18 |
19 | private View mContentView;
20 | private View mProgressBar;
21 | private TextView mHintView;
22 |
23 | public XListViewFooter(Context context) {
24 | super(context);
25 | initView(context);
26 | }
27 |
28 | public XListViewFooter(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | initView(context);
31 | }
32 |
33 |
34 | public void setState(int state) {
35 | mHintView.setVisibility(View.INVISIBLE);
36 | mProgressBar.setVisibility(View.INVISIBLE);
37 | mHintView.setVisibility(View.INVISIBLE);
38 | if (state == STATE_READY) {
39 | mHintView.setVisibility(View.VISIBLE);
40 | mHintView.setText(R.string.xlistview_footer_hint_ready);
41 | } else if (state == STATE_LOADING) {
42 | mProgressBar.setVisibility(View.VISIBLE);
43 | } else {
44 | mHintView.setVisibility(View.VISIBLE);
45 | mHintView.setText(R.string.xlistview_footer_hint_normal);
46 | }
47 | }
48 |
49 | public void setBottomMargin(int height) {
50 | if (height < 0) return ;
51 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)mContentView.getLayoutParams();
52 | lp.bottomMargin = height;
53 | mContentView.setLayoutParams(lp);
54 | }
55 |
56 | public int getBottomMargin() {
57 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)mContentView.getLayoutParams();
58 | return lp.bottomMargin;
59 | }
60 |
61 |
62 | /**
63 | * normal status
64 | */
65 | public void normal() {
66 | mHintView.setVisibility(View.VISIBLE);
67 | mProgressBar.setVisibility(View.GONE);
68 | }
69 |
70 |
71 | /**
72 | * loading status
73 | */
74 | public void loading() {
75 | mHintView.setVisibility(View.GONE);
76 | mProgressBar.setVisibility(View.VISIBLE);
77 | }
78 |
79 | /**
80 | * hide footer when disable pull load more
81 | */
82 | public void hide() {
83 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)mContentView.getLayoutParams();
84 | lp.height = 0;
85 | mContentView.setLayoutParams(lp);
86 | }
87 |
88 | /**
89 | * show footer
90 | */
91 | public void show() {
92 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)mContentView.getLayoutParams();
93 | lp.height = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
94 | mContentView.setLayoutParams(lp);
95 | }
96 |
97 | private void initView(Context context) {
98 | mContext = context;
99 | LinearLayout moreView = (LinearLayout)LayoutInflater.from(mContext).inflate(R.layout.xlistview_footer, null);
100 | addView(moreView);
101 | moreView.setLayoutParams(new LinearLayout.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
102 |
103 | mContentView = moreView.findViewById(R.id.xlistview_footer_content);
104 | mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);
105 | mHintView = (TextView)moreView.findViewById(R.id.xlistview_footer_hint_textview);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/com/pediy/bbs/kanxue/widget/XListViewHeader.java:
--------------------------------------------------------------------------------
1 | package com.pediy.bbs.kanxue.widget;
2 |
3 | import com.pediy.bbs.kanxue.R;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.Gravity;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.animation.Animation;
11 | import android.view.animation.RotateAnimation;
12 | import android.widget.ImageView;
13 | import android.widget.LinearLayout;
14 | import android.widget.ProgressBar;
15 | import android.widget.TextView;
16 |
17 | public class XListViewHeader extends LinearLayout{
18 | private LinearLayout mContainer;
19 | private ImageView mArrowImageView;
20 | private ProgressBar mProgressBar;
21 | private TextView mHintTextView;
22 | private TextView mHeaderTimeView;
23 | private TextView mHeaderTimeLabel;
24 | private int mState = STATE_NORMAL;
25 |
26 | private Animation mRotateUpAnim;
27 | private Animation mRotateDownAnim;
28 |
29 | private final int ROTATE_ANIM_DURATION = 180;
30 |
31 | public final static int STATE_NORMAL = 0;
32 | public final static int STATE_READY = 1;
33 | public final static int STATE_REFRESHING = 2;
34 |
35 | public XListViewHeader(Context context) {
36 | super(context);
37 | initView(context);
38 | }
39 |
40 | /**
41 | * @param context
42 | * @param attrs
43 | */
44 | public XListViewHeader(Context context, AttributeSet attrs) {
45 | super(context, attrs);
46 | initView(context);
47 | }
48 |
49 | private void initView(Context context) {
50 | // 初始情况,设置下拉刷新view高度为0
51 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
52 | android.view.ViewGroup.LayoutParams.FILL_PARENT, 0);
53 | mContainer = (LinearLayout) LayoutInflater.from(context).inflate(
54 | R.layout.xlistview_header, null);
55 | addView(mContainer, lp);
56 | setGravity(Gravity.BOTTOM);
57 |
58 | mArrowImageView = (ImageView)findViewById(R.id.xlistview_header_arrow);
59 | mHintTextView = (TextView)findViewById(R.id.xlistview_header_hint_textview);
60 | mProgressBar = (ProgressBar)findViewById(R.id.xlistview_header_progressbar);
61 | mHeaderTimeView = (TextView)findViewById(R.id.xlistview_header_time);
62 | mHeaderTimeLabel = (TextView)findViewById(R.id.xlistview_header_time_label);
63 |
64 | mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
65 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
66 | 0.5f);
67 | mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
68 | mRotateUpAnim.setFillAfter(true);
69 | mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
70 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
71 | 0.5f);
72 | mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
73 | mRotateDownAnim.setFillAfter(true);
74 | }
75 |
76 | public void setState(int state) {
77 | if (state == mState) return ;
78 |
79 | if (state == STATE_REFRESHING) { // 显示进度
80 | mArrowImageView.clearAnimation();
81 | mArrowImageView.setVisibility(View.INVISIBLE);
82 | mProgressBar.setVisibility(View.VISIBLE);
83 | } else { // 显示箭头图片
84 | mArrowImageView.setVisibility(View.VISIBLE);
85 | mProgressBar.setVisibility(View.INVISIBLE);
86 | }
87 |
88 | switch(state){
89 | case STATE_NORMAL:
90 | if (mState == STATE_READY) {
91 | mArrowImageView.startAnimation(mRotateDownAnim);
92 | }
93 | if (mState == STATE_REFRESHING) {
94 | mArrowImageView.clearAnimation();
95 | }
96 | mHintTextView.setText(R.string.xlistview_header_hint_normal);
97 | break;
98 | case STATE_READY:
99 | if (mState != STATE_READY) {
100 | mArrowImageView.clearAnimation();
101 | mArrowImageView.startAnimation(mRotateUpAnim);
102 | mHintTextView.setText(R.string.xlistview_header_hint_ready);
103 | }
104 | break;
105 | case STATE_REFRESHING:
106 | mHintTextView.setText(R.string.xlistview_header_hint_loading);
107 | break;
108 | default:
109 | }
110 |
111 | mState = state;
112 | }
113 |
114 | public void setVisiableHeight(int height) {
115 | if (height < 0)
116 | height = 0;
117 | LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContainer
118 | .getLayoutParams();
119 | lp.height = height;
120 | mContainer.setLayoutParams(lp);
121 | }
122 |
123 | public int getVisiableHeight() {
124 | return mContainer.getHeight();
125 | }
126 |
127 | public void setRefreshTime(String time) {
128 | mHeaderTimeLabel.setVisibility(View.VISIBLE);
129 | mHeaderTimeView.setText(time);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------