├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── GankApp.iml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── debug.keystore ├── proguard-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── android │ │ │ └── ted │ │ │ └── gank │ │ │ ├── GankApplication.java │ │ │ ├── adapter │ │ │ ├── ArrayRecyclerAdapter.java │ │ │ ├── BenefitGoodsItemAdapter.java │ │ │ ├── GoodsItemAdapter.java │ │ │ └── MainFragmentPagerAdapter.java │ │ │ ├── config │ │ │ └── Constants.java │ │ │ ├── data │ │ │ └── ImageGoodsCache.java │ │ │ ├── db │ │ │ ├── GankMigration.java │ │ │ ├── GankRealmHelper.java │ │ │ └── Image.java │ │ │ ├── main │ │ │ ├── BaseLoadingFragment.java │ │ │ ├── BenefitListFragment.java │ │ │ ├── CommonGoodsListFragment.java │ │ │ ├── MainActivity.java │ │ │ ├── ViewerActivity.java │ │ │ └── ViewerFragment.java │ │ │ ├── manager │ │ │ └── CollectManager.java │ │ │ ├── model │ │ │ ├── BaseResult.java │ │ │ ├── DayGoods.java │ │ │ ├── DayGoodsResult.java │ │ │ ├── Goods.java │ │ │ └── GoodsResult.java │ │ │ ├── network │ │ │ └── GankCloudApi.java │ │ │ ├── service │ │ │ └── ImageImproveService.java │ │ │ ├── utils │ │ │ ├── PictUtil.java │ │ │ └── Utils.java │ │ │ └── view │ │ │ ├── RadioImageView.java │ │ │ └── TouchImageView.java │ │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_dashboard.png │ │ ├── ic_discuss.png │ │ ├── ic_done.png │ │ ├── ic_event.png │ │ ├── ic_forum.png │ │ ├── ic_headset.png │ │ └── ic_menu.png │ │ ├── drawable-mdpi │ │ ├── ic_dashboard.png │ │ ├── ic_discuss.png │ │ ├── ic_done.png │ │ ├── ic_event.png │ │ ├── ic_forum.png │ │ ├── ic_headset.png │ │ └── ic_menu.png │ │ ├── drawable-nodpi │ │ ├── cheese_1.jpg │ │ ├── cheese_2.jpg │ │ ├── cheese_3.jpg │ │ ├── cheese_4.jpg │ │ └── cheese_5.jpg │ │ ├── drawable-xhdpi │ │ ├── ic_arrow_back_white_24dp.png │ │ ├── ic_dashboard.png │ │ ├── ic_discuss.png │ │ ├── ic_done.png │ │ ├── ic_event.png │ │ ├── ic_forum.png │ │ ├── ic_headset.png │ │ └── ic_menu.png │ │ ├── drawable-xxhdpi │ │ ├── github_logo.png │ │ ├── github_logo_light.png │ │ ├── ic_arrow_back_white_24dp.png │ │ ├── ic_comment_outline_grey.png │ │ ├── ic_dashboard.png │ │ ├── ic_discuss.png │ │ ├── ic_done.png │ │ ├── ic_event.png │ │ ├── ic_forum.png │ │ ├── ic_global_menu_likes.png │ │ ├── ic_headset.png │ │ ├── ic_heart_outline_grey.png │ │ ├── ic_heart_red.png │ │ ├── ic_menu.png │ │ ├── ic_more_grey.png │ │ └── item_default_img.jpg │ │ ├── drawable-xxxhdpi │ │ ├── ic_done.png │ │ └── ic_menu.png │ │ ├── drawable │ │ └── goods_title_bg.xml │ │ ├── layout │ │ ├── activity_detail.xml │ │ ├── activity_main.xml │ │ ├── activity_viewer.xml │ │ ├── benefit_goods_item_layout.xml │ │ ├── fragment_base_loading_layout.xml │ │ ├── fragment_benifit_list.xml │ │ ├── fragment_common_list.xml │ │ ├── fragment_viewer.xml │ │ ├── goods_item_layout.xml │ │ ├── include_list_viewpager.xml │ │ └── nav_header.xml │ │ ├── menu │ │ ├── drawer_view.xml │ │ ├── menu_main.xml │ │ └── sample_actions.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── paths.xml └── ted_android.jks ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | GankApp -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GankApp.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gank.IO客户端 2 | 借助[Gank.Io](http://gank.io/)提供的API开发的一个客户端,目的纯属练手和向大神学习。 3 | 4 | ###参考项目: 5 | 6 | 1、[xingrz](http://xingrz.me/)大神的开源项目[GankMeizhi](https://github.com/xingrz/GankMeizhi) 7 | 8 | 2、[drakeet](http://drakeet.me)大神的开源项目[Meizhi](https://github.com/drakeet/Meizhi) 9 | 10 | 3、开源项目[cheesesquare](https://github.com/chrisbanes/cheesesquare) 11 | 12 | ###apk下载: 13 | ####[直接下载](http://pre.im/gankio11) 14 | 15 | ###演示视频: 16 | ####[演示视频@Youtube](https://youtu.be/JhRjWYbYpKc) 17 | 18 | ###应用截图: 19 | 20 | ![img](http://7vzsca.com1.z0.glb.clouddn.com/Gank_2.png_img500w) 21 | 22 | ###遗留功能: 23 | 1、干货收藏 24 | 25 | 2、按日期浏览 26 | 27 | ###支持版本: 28 | `minSdkVersion 19` 29 | 30 | ###依赖开源技术: 31 | ``` 32 | compile 'com.android.support:design:23.0.0' 33 | compile 'com.android.support:appcompat-v7:23.0.0' 34 | compile 'com.android.support:cardview-v7:23.0.0' 35 | compile 'com.android.support:recyclerview-v7:23.0.0' 36 | compile 'com.umeng.analytics:analytics:latest.integration' 37 | compile 'com.github.bumptech.glide:glide:3.6.0' 38 | compile 'com.squareup.retrofit:retrofit:1.9.0' 39 | compile 'com.squareup.okhttp:okhttp:2.2.0' 40 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0' 41 | compile 'io.reactivex:rxandroid:0.24.0' 42 | compile 'com.orhanobut:logger:1.11' 43 | compile 'com.jakewharton:butterknife:7.0.1' 44 | compile 'io.realm:realm-android:0.81.1' 45 | compile 'com.malinskiy:materialicons:1.0.2' 46 | compile 'com.vlonjatg.android:progress-activity:1.1.1' 47 | compile 'net.grandcentrix.tray:tray:0.9.2' 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.android.ted.gank" 9 | minSdkVersion 19 10 | targetSdkVersion 21 11 | versionCode 2 12 | versionName "2.1" 13 | 14 | } 15 | 16 | signingConfigs { 17 | 18 | debug { 19 | storeFile file("debug.keystore") 20 | } 21 | 22 | release { 23 | storeFile file("ted_android.jks") 24 | storePassword "xiongwei" 25 | keyAlias "ted_android" 26 | keyPassword "xiongwei" 27 | } 28 | } 29 | 30 | buildTypes { 31 | debug{ 32 | buildConfigField "boolean", "LOG_DEBUG", "true" 33 | } 34 | 35 | release { 36 | buildConfigField "boolean", "LOG_DEBUG", "false" 37 | minifyEnabled false 38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 39 | signingConfig signingConfigs.release 40 | } 41 | } 42 | 43 | packagingOptions { 44 | exclude 'META-INF/services/javax.annotation.processing.Processor' 45 | exclude "lib/arm64-v8a/librealm-jni.so" 46 | } 47 | } 48 | 49 | repositories { 50 | jcenter() 51 | maven { 52 | url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' 53 | } 54 | } 55 | 56 | dependencies { 57 | compile fileTree(dir: 'libs', include: ['*.jar']) 58 | compile 'com.android.support:design:23.1.1' 59 | compile 'com.android.support:appcompat-v7:23.1.1' 60 | compile 'com.android.support:cardview-v7:23.1.1' 61 | compile 'com.android.support:recyclerview-v7:23.1.1' 62 | compile 'com.umeng.analytics:analytics:5.6.4' 63 | 64 | compile 'com.github.bumptech.glide:glide:3.6.0' 65 | compile 'com.squareup.retrofit:retrofit:1.9.0' 66 | compile 'com.squareup.okhttp:okhttp:2.2.0' 67 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0' 68 | compile 'io.reactivex:rxandroid:0.24.0' 69 | compile 'com.github.xiongwei-git:Logger:1.0' 70 | compile 'com.jakewharton:butterknife:7.0.1' 71 | compile 'io.realm:realm-android:0.87.5' 72 | compile 'com.malinskiy:materialicons:1.0.2' 73 | compile 'com.vlonjatg.android:progress-activity:1.1.1' 74 | compile 'net.grandcentrix.tray:tray:0.9.2' 75 | } 76 | -------------------------------------------------------------------------------- /app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/debug.keystore -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/GankApplication.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.orhanobut.logger.Logger; 6 | 7 | public class GankApplication extends Application { 8 | 9 | private static Context context; 10 | 11 | public static Context getContext() { 12 | return context; 13 | } 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | context = getApplicationContext(); 19 | initLogger(); 20 | } 21 | 22 | private void initLogger() { 23 | Logger.init("xiongwei").hideThreadInfo().setLogValve(BuildConfig.LOG_DEBUG).methodCount(1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/adapter/ArrayRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.adapter; 19 | 20 | import android.support.annotation.NonNull; 21 | import android.support.v7.widget.RecyclerView; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Collection; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | import java.util.ListIterator; 28 | import java.util.Objects; 29 | 30 | public abstract class ArrayRecyclerAdapter 31 | extends RecyclerView.Adapter implements List { 32 | 33 | private final Object lock = new Object(); 34 | 35 | private final List list; 36 | 37 | @SuppressWarnings("unused") 38 | public ArrayRecyclerAdapter() { 39 | list = new ArrayList<>(); 40 | } 41 | 42 | @SuppressWarnings("unused") 43 | public ArrayRecyclerAdapter(int capacity) { 44 | list = new ArrayList<>(capacity); 45 | } 46 | 47 | @SuppressWarnings("unused") 48 | public ArrayRecyclerAdapter(Collection collection) { 49 | list = new ArrayList<>(collection); 50 | } 51 | 52 | @Override 53 | public int getItemCount() { 54 | return size(); 55 | } 56 | 57 | @Override 58 | public void add(int location, E object) { 59 | synchronized (lock) { 60 | list.add(location, object); 61 | notifyItemInserted(location); 62 | } 63 | } 64 | 65 | @Override 66 | public boolean add(E object) { 67 | synchronized (lock) { 68 | int lastIndex = list.size(); 69 | if (list.add(object)) { 70 | notifyItemInserted(lastIndex); 71 | return true; 72 | } else { 73 | return false; 74 | } 75 | } 76 | } 77 | 78 | @Override 79 | public boolean addAll(int location, @NonNull Collection collection) { 80 | synchronized (lock) { 81 | if (list.addAll(location, collection)) { 82 | notifyItemRangeInserted(location, collection.size()); 83 | return true; 84 | } else { 85 | return false; 86 | } 87 | } 88 | } 89 | 90 | @Override 91 | public boolean addAll(@NonNull Collection collection) { 92 | synchronized (lock) { 93 | int lastIndex = list.size(); 94 | if (list.addAll(collection)) { 95 | notifyItemRangeInserted(lastIndex, collection.size()); 96 | return true; 97 | } else { 98 | return false; 99 | } 100 | } 101 | } 102 | 103 | @Override 104 | public void clear() { 105 | synchronized (lock) { 106 | int size = list.size(); 107 | if (size > 0) { 108 | list.clear(); 109 | notifyItemRangeRemoved(0, size); 110 | } 111 | } 112 | } 113 | 114 | @Override 115 | public boolean contains(Object object) { 116 | return list.contains(object); 117 | } 118 | 119 | @Override 120 | public boolean containsAll(@NonNull Collection collection) { 121 | return list.containsAll(collection); 122 | } 123 | 124 | @Override 125 | public E get(int location) { 126 | return list.get(location); 127 | } 128 | 129 | @Override 130 | public int indexOf(Object object) { 131 | return list.indexOf(object); 132 | } 133 | 134 | @Override 135 | public boolean isEmpty() { 136 | return list.isEmpty(); 137 | } 138 | 139 | @NonNull 140 | @Override 141 | public Iterator iterator() { 142 | return list.iterator(); 143 | } 144 | 145 | @Override 146 | public int lastIndexOf(Object object) { 147 | return list.lastIndexOf(object); 148 | } 149 | 150 | @NonNull 151 | @Override 152 | public ListIterator listIterator() { 153 | return list.listIterator(); 154 | } 155 | 156 | @NonNull 157 | @Override 158 | public ListIterator listIterator(int location) { 159 | return list.listIterator(location); 160 | } 161 | 162 | @Override 163 | public E remove(int location) { 164 | synchronized (lock) { 165 | E item = list.remove(location); 166 | notifyItemRemoved(location); 167 | return item; 168 | } 169 | } 170 | 171 | @Override 172 | public boolean remove(Object object) { 173 | synchronized (lock) { 174 | int index = indexOf(object); 175 | if (list.remove(object)) { 176 | notifyItemRemoved(index); 177 | return true; 178 | } else { 179 | return false; 180 | } 181 | } 182 | } 183 | 184 | @Override 185 | public boolean removeAll(@NonNull Collection collection) { 186 | boolean modified = false; 187 | 188 | Iterator iterator = list.iterator(); 189 | while (iterator.hasNext()) { 190 | Object object = iterator.next(); 191 | if (collection.contains(object)) { 192 | synchronized (lock) { 193 | int index = indexOf(object); 194 | iterator.remove(); 195 | notifyItemRemoved(index); 196 | } 197 | 198 | modified = true; 199 | } 200 | } 201 | 202 | return modified; 203 | } 204 | 205 | @Override 206 | public boolean retainAll(@NonNull Collection collection) { 207 | boolean modified = false; 208 | 209 | Iterator iterator = list.iterator(); 210 | while (iterator.hasNext()) { 211 | Object object = iterator.next(); 212 | if (!collection.contains(object)) { 213 | synchronized (lock) { 214 | int index = indexOf(object); 215 | iterator.remove(); 216 | notifyItemRemoved(index); 217 | } 218 | 219 | modified = true; 220 | } 221 | } 222 | 223 | return modified; 224 | } 225 | 226 | @Override 227 | public E set(int location, E object) { 228 | synchronized (lock) { 229 | E origin = list.set(location, object); 230 | if (!Objects.equals(object, origin)) { 231 | notifyItemChanged(location); 232 | } 233 | return origin; 234 | } 235 | } 236 | 237 | @Override 238 | public int size() { 239 | return list.size(); 240 | } 241 | 242 | @NonNull 243 | @Override 244 | public List subList(int start, int end) { 245 | return list.subList(start, end); 246 | } 247 | 248 | @NonNull 249 | @Override 250 | public Object[] toArray() { 251 | return list.toArray(); 252 | } 253 | 254 | @NonNull 255 | @Override 256 | @SuppressWarnings("SuspiciousToArrayCall") 257 | public T[] toArray(@NonNull T[] array) { 258 | return list.toArray(array); 259 | } 260 | 261 | public void replaceWith(List data) { 262 | if (list.isEmpty() && data.isEmpty()) { 263 | return; 264 | } 265 | 266 | if (list.isEmpty()) { 267 | addAll(data); 268 | return; 269 | } 270 | 271 | if (data.isEmpty()) { 272 | clear(); 273 | return; 274 | } 275 | 276 | if (list.equals(data)) { 277 | return; 278 | } 279 | 280 | // 首先将旧列表有、新列表没有的从旧列表去除 281 | retainAll(data); 282 | 283 | // 如果列表被完全清空了,那就直接全部插入好了 284 | if (list.isEmpty()) { 285 | addAll(data); 286 | return; 287 | } 288 | 289 | // 然后遍历新列表,对旧列表的数据更新、移动、增加 290 | for (int indexNew = 0; indexNew < data.size(); indexNew++) { 291 | E item = data.get(indexNew); 292 | 293 | int indexOld = indexOf(item); 294 | 295 | if (indexOld == -1) { 296 | add(indexNew, item); 297 | } else if (indexOld == indexNew) { 298 | set(indexNew, item); 299 | } else { 300 | list.remove(indexOld); 301 | list.add(indexNew, item); 302 | notifyItemMoved(indexOld, indexNew); 303 | } 304 | } 305 | } 306 | 307 | } 308 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/adapter/BenefitGoodsItemAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.adapter; 19 | 20 | import android.content.Context; 21 | import android.support.annotation.LayoutRes; 22 | import android.support.v4.view.ViewCompat; 23 | import android.support.v7.widget.RecyclerView; 24 | import android.text.TextUtils; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import com.android.ted.gank.R; 30 | import com.android.ted.gank.db.Image; 31 | import com.android.ted.gank.view.RadioImageView; 32 | import com.bumptech.glide.Glide; 33 | 34 | import butterknife.Bind; 35 | import butterknife.ButterKnife; 36 | 37 | /*** 38 | * 39 | */ 40 | public abstract class BenefitGoodsItemAdapter extends ArrayRecyclerAdapter{ 41 | 42 | private final Context context; 43 | private final LayoutInflater inflater; 44 | 45 | public BenefitGoodsItemAdapter(Context context) { 46 | this.context = context; 47 | this.inflater = LayoutInflater.from(context); 48 | setHasStableIds(true); 49 | } 50 | 51 | @Override 52 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 53 | return new ViewHolder(R.layout.benefit_goods_item_layout, parent); 54 | } 55 | 56 | @Override 57 | public void onBindViewHolder(ViewHolder holder, int position) { 58 | Image image = get(position); 59 | holder.imageView.setOriginalSize(image.getWidth(), image.getHeight()); 60 | loadGoodsImage(holder, image); 61 | ViewCompat.setTransitionName(holder.imageView, image.getUrl()); 62 | } 63 | 64 | @Override 65 | public long getItemId(int position) { 66 | return get(position).getUrl().hashCode(); 67 | } 68 | 69 | protected abstract void onItemClick(View v, int position); 70 | 71 | public class ViewHolder extends RecyclerView.ViewHolder { 72 | 73 | @Bind(R.id.image) 74 | public RadioImageView imageView; 75 | 76 | public ViewHolder(@LayoutRes int resource, ViewGroup parent) { 77 | super(inflater.inflate(resource, parent, false)); 78 | ButterKnife.bind(this, itemView); 79 | itemView.setOnClickListener(new View.OnClickListener() { 80 | @Override 81 | public void onClick(View v) { 82 | onItemClick(v, getAdapterPosition()); 83 | } 84 | }); 85 | } 86 | 87 | } 88 | 89 | private void loadGoodsImage(ViewHolder holder, Image imgGoods) { 90 | if (null == imgGoods || TextUtils.isEmpty(imgGoods.getUrl())) { 91 | Glide.with(context) 92 | .load(R.drawable.item_default_img) 93 | .into(holder.imageView); 94 | } else { 95 | Glide.with(context) 96 | .load(imgGoods.getUrl()) 97 | .into(holder.imageView); 98 | } 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/adapter/GoodsItemAdapter.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.adapter; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.net.Uri; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.text.TextUtils; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.animation.AccelerateInterpolator; 16 | import android.view.animation.DecelerateInterpolator; 17 | import android.view.animation.OvershootInterpolator; 18 | import android.widget.ImageView; 19 | import android.widget.TextView; 20 | 21 | import com.android.ted.gank.R; 22 | import com.android.ted.gank.data.ImageGoodsCache; 23 | import com.android.ted.gank.db.Image; 24 | import com.android.ted.gank.manager.CollectManager; 25 | import com.android.ted.gank.model.Goods; 26 | import com.android.ted.gank.utils.Utils; 27 | import com.bumptech.glide.Glide; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Date; 31 | 32 | import butterknife.Bind; 33 | import butterknife.ButterKnife; 34 | 35 | 36 | /** 37 | * Created by froger_mcs on 05.11.14. 38 | */ 39 | public class GoodsItemAdapter extends RecyclerView.Adapter implements View.OnClickListener { 40 | 41 | private static final DecelerateInterpolator DECCELERATE_INTERPOLATOR = new DecelerateInterpolator(); 42 | private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator(); 43 | private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4); 44 | 45 | private static final int ANIMATED_ITEMS_COUNT = 3; 46 | 47 | private Context context; 48 | private int lastAnimatedPosition = -1; 49 | private boolean animateItems = false; 50 | 51 | private ArrayList goodsItemData; 52 | 53 | public GoodsItemAdapter(Context context) { 54 | this.context = context; 55 | goodsItemData = new ArrayList<>(); 56 | } 57 | 58 | @Override 59 | public void onClick(View view) { 60 | final int viewId = view.getId(); 61 | if(viewId == R.id.img_like_goods){ 62 | 63 | } 64 | } 65 | 66 | private View.OnClickListener mItemOnClickListener = new View.OnClickListener() { 67 | @Override 68 | public void onClick(View view) { 69 | Integer position = (Integer)view.getTag(); 70 | Goods goods = goodsItemData.get(position.intValue()); 71 | Intent intent= new Intent(); 72 | intent.setAction("android.intent.action.VIEW"); 73 | Uri content_url = Uri.parse(goods.getUrl()); 74 | intent.setData(content_url); 75 | view.getContext().startActivity(intent); 76 | } 77 | }; 78 | 79 | @Override 80 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 81 | final View view = LayoutInflater.from(context).inflate(R.layout.goods_item_layout, parent, false); 82 | final CellGoodsViewHolder cellGoodsViewHolder = new CellGoodsViewHolder(view); 83 | cellGoodsViewHolder.imgLikeGoods.setOnClickListener(this); 84 | cellGoodsViewHolder.rootView.setOnClickListener(mItemOnClickListener); 85 | return cellGoodsViewHolder; 86 | } 87 | 88 | private void runEnterAnimation(View view, int position) { 89 | if (!animateItems || position >= ANIMATED_ITEMS_COUNT - 1) { 90 | return; 91 | } 92 | if (position > lastAnimatedPosition) { 93 | lastAnimatedPosition = position; 94 | view.setTranslationY(Utils.getScreenHeight(context)); 95 | view.animate() 96 | .translationY(0) 97 | .setInterpolator(new DecelerateInterpolator(3.f)) 98 | .setDuration(700) 99 | .start(); 100 | } 101 | } 102 | 103 | @Override 104 | public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { 105 | runEnterAnimation(viewHolder.itemView, position); 106 | final CellGoodsViewHolder holder = (CellGoodsViewHolder) viewHolder; 107 | bindGoodsItem(position, holder); 108 | } 109 | 110 | private void bindGoodsItem(int position, CellGoodsViewHolder holder) { 111 | Goods goods = goodsItemData.get(position); 112 | Image image = ImageGoodsCache.getIns().getImgGoodsRandom(position); 113 | boolean hasImg = null != image; 114 | holder.txtGoodsTitle.setText("#"+goods.getDesc()); 115 | holder.txtImgAuthor.setText(hasImg?"图:"+image.getWho():""); 116 | holder.txtGoodsAuthor.setText(getGoodsAuthorInfo(goods)); 117 | loadGoodsImage(holder, image); 118 | updateHeartButton(holder, goods, false); 119 | 120 | holder.imgLikeGoods.setTag(holder); 121 | holder.rootView.setTag(position); 122 | } 123 | 124 | @Override 125 | public int getItemViewType(int position) { 126 | return position; 127 | } 128 | 129 | @Override 130 | public int getItemCount() { 131 | return goodsItemData.size(); 132 | } 133 | 134 | private String getGoodsAuthorInfo(Goods goods){ 135 | StringBuilder builder = new StringBuilder(); 136 | Date date = Utils.formatDateFromStr(goods.getPublishedAt()); 137 | String dateStr = Utils.getFormatDateStr(date); 138 | builder.append(goods.getWho()).append(TextUtils.isEmpty(dateStr)?"":"@"+dateStr); 139 | return builder.toString(); 140 | } 141 | 142 | private void loadGoodsImage(final CellGoodsViewHolder holder,Image imgGoods){ 143 | if(null == imgGoods || TextUtils.isEmpty(imgGoods.getUrl())){ 144 | Glide.with(context) 145 | .load(R.drawable.item_default_img) 146 | .centerCrop() 147 | .into(holder.imgGoodsImageBg); 148 | }else { 149 | Glide.with(context) 150 | .load(imgGoods.getUrl()) 151 | .centerCrop() 152 | .into(holder.imgGoodsImageBg); 153 | } 154 | } 155 | 156 | 157 | private void updateHeartButton(final CellGoodsViewHolder holder,Goods goods, boolean animated) { 158 | if (animated) { 159 | if (!CollectManager.getIns().isCollect(goods)) { 160 | AnimatorSet animatorSet = new AnimatorSet(); 161 | ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(holder.imgLikeGoods, "rotation", 0f, 360f); 162 | rotationAnim.setDuration(300); 163 | rotationAnim.setInterpolator(ACCELERATE_INTERPOLATOR); 164 | 165 | ObjectAnimator bounceAnimX = ObjectAnimator.ofFloat(holder.imgLikeGoods, "scaleX", 0.2f, 1f); 166 | bounceAnimX.setDuration(300); 167 | bounceAnimX.setInterpolator(OVERSHOOT_INTERPOLATOR); 168 | 169 | ObjectAnimator bounceAnimY = ObjectAnimator.ofFloat(holder.imgLikeGoods, "scaleY", 0.2f, 1f); 170 | bounceAnimY.setDuration(300); 171 | bounceAnimY.setInterpolator(OVERSHOOT_INTERPOLATOR); 172 | bounceAnimY.addListener(new AnimatorListenerAdapter() { 173 | @Override 174 | public void onAnimationStart(Animator animation) { 175 | holder.imgLikeGoods.setImageResource(R.drawable.ic_heart_red); 176 | } 177 | }); 178 | 179 | animatorSet.play(rotationAnim); 180 | animatorSet.play(bounceAnimX).with(bounceAnimY).after(rotationAnim); 181 | 182 | animatorSet.addListener(new AnimatorListenerAdapter() { 183 | @Override 184 | public void onAnimationEnd(Animator animation) { 185 | //resetLikeAnimationState(holder); 186 | } 187 | }); 188 | animatorSet.start(); 189 | }else { 190 | 191 | } 192 | } else { 193 | if (CollectManager.getIns().isCollect(goods)) { 194 | holder.imgLikeGoods.setImageResource(R.drawable.ic_heart_red); 195 | } else { 196 | holder.imgLikeGoods.setImageResource(R.drawable.ic_heart_outline_grey); 197 | } 198 | } 199 | } 200 | 201 | 202 | 203 | private void animatePhotoLike(final CellGoodsViewHolder holder) { 204 | // if (!likeAnimations.containsKey(holder)) { 205 | // holder.vBgLike.setVisibility(View.VISIBLE); 206 | // holder.ivLike.setVisibility(View.VISIBLE); 207 | // 208 | // holder.vBgLike.setScaleY(0.1f); 209 | // holder.vBgLike.setScaleX(0.1f); 210 | // holder.vBgLike.setAlpha(1f); 211 | // holder.ivLike.setScaleY(0.1f); 212 | // holder.ivLike.setScaleX(0.1f); 213 | // 214 | // AnimatorSet animatorSet = new AnimatorSet(); 215 | // likeAnimations.put(holder, animatorSet); 216 | // 217 | // ObjectAnimator bgScaleYAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleY", 0.1f, 1f); 218 | // bgScaleYAnim.setDuration(200); 219 | // bgScaleYAnim.setInterpolator(DECCELERATE_INTERPOLATOR); 220 | // ObjectAnimator bgScaleXAnim = ObjectAnimator.ofFloat(holder.vBgLike, "scaleX", 0.1f, 1f); 221 | // bgScaleXAnim.setDuration(200); 222 | // bgScaleXAnim.setInterpolator(DECCELERATE_INTERPOLATOR); 223 | // ObjectAnimator bgAlphaAnim = ObjectAnimator.ofFloat(holder.vBgLike, "alpha", 1f, 0f); 224 | // bgAlphaAnim.setDuration(200); 225 | // bgAlphaAnim.setStartDelay(150); 226 | // bgAlphaAnim.setInterpolator(DECCELERATE_INTERPOLATOR); 227 | // 228 | // ObjectAnimator imgScaleUpYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 0.1f, 1f); 229 | // imgScaleUpYAnim.setDuration(300); 230 | // imgScaleUpYAnim.setInterpolator(DECCELERATE_INTERPOLATOR); 231 | // ObjectAnimator imgScaleUpXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 0.1f, 1f); 232 | // imgScaleUpXAnim.setDuration(300); 233 | // imgScaleUpXAnim.setInterpolator(DECCELERATE_INTERPOLATOR); 234 | // 235 | // ObjectAnimator imgScaleDownYAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleY", 1f, 0f); 236 | // imgScaleDownYAnim.setDuration(300); 237 | // imgScaleDownYAnim.setInterpolator(ACCELERATE_INTERPOLATOR); 238 | // ObjectAnimator imgScaleDownXAnim = ObjectAnimator.ofFloat(holder.ivLike, "scaleX", 1f, 0f); 239 | // imgScaleDownXAnim.setDuration(300); 240 | // imgScaleDownXAnim.setInterpolator(ACCELERATE_INTERPOLATOR); 241 | // 242 | // animatorSet.playTogether(bgScaleYAnim, bgScaleXAnim, bgAlphaAnim, imgScaleUpYAnim, imgScaleUpXAnim); 243 | // animatorSet.play(imgScaleDownYAnim).with(imgScaleDownXAnim).after(imgScaleUpYAnim); 244 | // 245 | // animatorSet.addListener(new AnimatorListenerAdapter() { 246 | // @Override 247 | // public void onAnimationEnd(Animator animation) { 248 | // resetLikeAnimationState(holder); 249 | // } 250 | // }); 251 | // animatorSet.start(); 252 | // } 253 | } 254 | 255 | public void updateItems(ArrayList goods,boolean animated) { 256 | goodsItemData.clear(); 257 | goodsItemData.addAll(goods); 258 | animateItems = animated; 259 | notifyDataSetChanged(); 260 | } 261 | 262 | 263 | public static class CellGoodsViewHolder extends RecyclerView.ViewHolder { 264 | @Bind(R.id.img_goods_img) 265 | ImageView imgGoodsImageBg; 266 | @Bind(R.id.txt_img_author) 267 | TextView txtImgAuthor; 268 | @Bind(R.id.txt_goods_title) 269 | TextView txtGoodsTitle; 270 | @Bind(R.id.img_like_goods) 271 | ImageView imgLikeGoods; 272 | @Bind(R.id.txt_goods_author) 273 | TextView txtGoodsAuthor; 274 | 275 | public final View rootView; 276 | 277 | public CellGoodsViewHolder(View view) { 278 | super(view); 279 | ButterKnife.bind(this, view); 280 | rootView = view; 281 | } 282 | 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/adapter/MainFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.adapter; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by Ted on 2015/8/24. 12 | */ 13 | public class MainFragmentPagerAdapter extends FragmentPagerAdapter { 14 | private final List mFragments = new ArrayList<>(); 15 | private final List mFragmentTitles = new ArrayList<>(); 16 | 17 | public MainFragmentPagerAdapter(FragmentManager fm) { 18 | super(fm); 19 | } 20 | 21 | public void MainFragmentPagerAdapter(Fragment fragment, String title) { 22 | mFragments.add(fragment); 23 | mFragmentTitles.add(title); 24 | } 25 | 26 | public void addFragment(Fragment fragment, String title) { 27 | mFragments.add(fragment); 28 | mFragmentTitles.add(title); 29 | } 30 | 31 | @Override 32 | public Fragment getItem(int position) { 33 | return mFragments.get(position); 34 | } 35 | 36 | @Override 37 | public int getCount() { 38 | return mFragments.size(); 39 | } 40 | 41 | @Override 42 | public CharSequence getPageTitle(int position) { 43 | return mFragmentTitles.get(position); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/config/Constants.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.config; 2 | 3 | /** 4 | * Created by Ted on 2015/8/23. 5 | */ 6 | public class Constants { 7 | public static final String GANK_SERVER_IP = "https://gank.io/api"; 8 | 9 | //分类数据: http://gank.avosapps.com/api/data/数据类型/请求个数/第几页 10 | public static final String GANK_CATEGORY_IP = "data/"; 11 | 12 | //每日数据: http://gank.avosapps.com/api/day/年/月/日 13 | public static final String GANK_DAY_IP = "day/"; 14 | 15 | //随机数据:http://gank.avosapps.com/api/random/data/分类/个数 16 | public static final String GANK_RANDOM_IP = "random/data/"; 17 | 18 | public static final String GITHUB_URL = "https://github.com/xiongwei-git/GankApp"; 19 | 20 | public static final String AUTHOR_URL = "http://www.weibo.com/521213101"; 21 | 22 | public static final String GANK_URL = "http://gank.io/"; 23 | 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/data/ImageGoodsCache.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.data; 2 | 3 | import com.android.ted.gank.db.Image; 4 | import com.android.ted.gank.model.Goods; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Random; 8 | 9 | import io.realm.RealmResults; 10 | 11 | /** 12 | * Created by Ted on 2015/8/24. 13 | * 图片缓存 14 | */ 15 | public class ImageGoodsCache { 16 | private static ImageGoodsCache instance; 17 | 18 | private ArrayList mGankImageList; 19 | 20 | public ImageGoodsCache(){ 21 | mGankImageList = new ArrayList<>(); 22 | } 23 | 24 | public static ImageGoodsCache getIns() { 25 | if (null == instance) { 26 | synchronized (ImageGoodsCache.class) { 27 | if (null == instance) { 28 | instance = new ImageGoodsCache(); 29 | } 30 | } 31 | } 32 | return instance; 33 | } 34 | 35 | private void addGoodsToImage(Goods goods){ 36 | Image image = new Image(); 37 | Image.updateDbGoods(image,goods); 38 | mGankImageList.add(image); 39 | } 40 | 41 | public void addAllImageGoods(ArrayList list){ 42 | if(null != list && list.size() > 0){ 43 | mGankImageList.clear(); 44 | for (Goods goods:list){ 45 | addGoodsToImage(goods); 46 | } 47 | } 48 | } 49 | 50 | public void addAllImageGoods(RealmResults images){ 51 | mGankImageList.clear(); 52 | mGankImageList.addAll(images); 53 | } 54 | 55 | public ArrayList getGankImageList() { 56 | return mGankImageList; 57 | } 58 | 59 | public Image getImgGoodsRandom(int randomIndex){ 60 | int size = getGankImageList().size(); 61 | if(size == 0)return null; 62 | Random random = new Random(); 63 | int randomInt = random.nextInt(size); 64 | if(randomInt + randomIndex >= size){ 65 | return getGankImageList().get(randomInt); 66 | } 67 | return getGankImageList().get(randomInt + randomIndex); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/db/GankMigration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.db; 19 | 20 | import io.realm.DynamicRealm; 21 | import io.realm.Realm; 22 | import io.realm.RealmMigration; 23 | 24 | /** 25 | * Created by Ted on 2015/8/27. 26 | */ 27 | public class GankMigration implements RealmMigration{ 28 | 29 | @Override public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/db/GankRealmHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.db; 19 | 20 | import android.content.Context; 21 | 22 | import io.realm.Realm; 23 | import io.realm.RealmConfiguration; 24 | 25 | /** 26 | * Created by Ted on 2015/8/27. 27 | */ 28 | public class GankRealmHelper { 29 | public static Realm getRealm(Context context){ 30 | RealmConfiguration configuration = new RealmConfiguration.Builder(context) 31 | .name("gank.io") 32 | .schemaVersion(0) 33 | .build(); 34 | Realm.migrateRealm(configuration, new GankMigration()); 35 | Realm realm = Realm.getInstance(configuration); 36 | return realm; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/db/Image.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2015 TedXiong 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.android.ted.gank.db; 20 | 21 | import com.android.ted.gank.model.Goods; 22 | 23 | import io.realm.Realm; 24 | import io.realm.RealmObject; 25 | import io.realm.RealmResults; 26 | import io.realm.Sort; 27 | import io.realm.annotations.PrimaryKey; 28 | 29 | /** 30 | * Created by Ted on 2015/8/23. 31 | * 32 | * DB Item for Image {@link com.android.ted.gank.model.Goods} 33 | */ 34 | public class Image extends RealmObject { 35 | /**补充数据*/ 36 | private int width = 0; 37 | private int height = 0; 38 | private int position = 0; 39 | 40 | @PrimaryKey 41 | private String _id; 42 | 43 | private String who; 44 | private String publishedAt; 45 | private String desc; 46 | private String type; 47 | private String url; 48 | private boolean used; 49 | 50 | private String createdAt; 51 | private String _ns; 52 | 53 | public static Image queryImageById(Realm realm,String objectId){ 54 | RealmResults results = realm.where(Image.class).equalTo("_id",objectId).findAll(); 55 | if(results.size() > 0){ 56 | Image image = results.get(0); 57 | return image; 58 | } 59 | return null; 60 | } 61 | 62 | public static Image queryImageByUrl(Realm realm,String objectId){ 63 | RealmResults results = realm.where(Image.class).equalTo("_id",objectId).findAll(); 64 | if(results.size() > 0){ 65 | Image image = results.get(0); 66 | return image; 67 | } 68 | return null; 69 | } 70 | 71 | public static Image queryFirstZeroImg(Realm realm){ 72 | RealmResults results = realm.where(Image.class).equalTo("width",0) 73 | .findAllSorted("position", Sort.DESCENDING); 74 | if(results.size() > 0){ 75 | Image image = results.get(0); 76 | return image; 77 | } 78 | return null; 79 | } 80 | 81 | public static Image updateDbGoods(Image dbItem,Goods goods) { 82 | dbItem.setWho(goods.getWho()); 83 | dbItem.setPublishedAt(goods.getPublishedAt()); 84 | dbItem.setDesc(goods.getDesc()); 85 | dbItem.setType(goods.getType()); 86 | dbItem.setUrl(goods.getUrl()); 87 | dbItem.setUsed(goods.isUsed()); 88 | dbItem.set_id(goods.get_id()); 89 | dbItem.setCreatedAt(goods.getCreatedAt()); 90 | dbItem.set_ns(goods.get_ns()); 91 | return dbItem; 92 | } 93 | 94 | public String getWho() { 95 | return who; 96 | } 97 | 98 | public void setWho(String who) { 99 | this.who = who; 100 | } 101 | 102 | public String getPublishedAt() { 103 | return publishedAt; 104 | } 105 | 106 | public void setPublishedAt(String publishedAt) { 107 | this.publishedAt = publishedAt; 108 | } 109 | 110 | public String getDesc() { 111 | return desc; 112 | } 113 | 114 | public void setDesc(String desc) { 115 | this.desc = desc; 116 | } 117 | 118 | public String getType() { 119 | return type; 120 | } 121 | 122 | public void setType(String type) { 123 | this.type = type; 124 | } 125 | 126 | public String getUrl() { 127 | return url; 128 | } 129 | 130 | public void setUrl(String url) { 131 | this.url = url; 132 | } 133 | 134 | public boolean isUsed() { 135 | return used; 136 | } 137 | 138 | public void setUsed(boolean used) { 139 | this.used = used; 140 | } 141 | 142 | public String get_id() { 143 | return _id; 144 | } 145 | 146 | public void set_id(String _id) { 147 | this._id = _id; 148 | } 149 | 150 | public String getCreatedAt() { 151 | return createdAt; 152 | } 153 | 154 | public void setCreatedAt(String createdAt) { 155 | this.createdAt = createdAt; 156 | } 157 | 158 | public String get_ns() { 159 | return _ns; 160 | } 161 | 162 | public void set_ns(String updatedAt) { 163 | this._ns = updatedAt; 164 | } 165 | 166 | public int getWidth() { 167 | return width; 168 | } 169 | 170 | public void setWidth(int width) { 171 | this.width = width; 172 | } 173 | 174 | public int getHeight() { 175 | return height; 176 | } 177 | 178 | public void setHeight(int height) { 179 | this.height = height; 180 | } 181 | 182 | public int getPosition() { 183 | return position; 184 | } 185 | 186 | public void setPosition(int position) { 187 | this.position = position; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/BaseLoadingFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.main; 19 | 20 | import android.graphics.drawable.Drawable; 21 | import android.os.Bundle; 22 | import android.support.v4.app.Fragment; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | 27 | import com.android.ted.gank.R; 28 | import com.vlonjatg.progressactivity.ProgressActivity; 29 | 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | /** 34 | * Created by Ted on 2015/8/27. 35 | */ 36 | public abstract class BaseLoadingFragment extends Fragment { 37 | private ProgressActivity mProgressActivity; 38 | 39 | @Override 40 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 41 | mProgressActivity = (ProgressActivity) inflater.inflate(R.layout.fragment_base_loading_layout, container, false); 42 | mProgressActivity.addView(onCreateContentView(inflater, mProgressActivity, savedInstanceState)); 43 | return mProgressActivity; 44 | } 45 | 46 | abstract View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); 47 | 48 | protected void showLoading() { 49 | mProgressActivity.showLoading(); 50 | } 51 | 52 | protected void showContent(){ 53 | mProgressActivity.showContent(); 54 | } 55 | 56 | protected void showEmpty(Drawable emptyImageDrawable, String emptyTextTitle, String emptyTextContent,List skipIds) { 57 | if(null == skipIds)skipIds = new ArrayList<>(); 58 | mProgressActivity.showEmpty(emptyImageDrawable, emptyTextTitle, emptyTextContent,skipIds); 59 | } 60 | 61 | protected void showError(Drawable emptyImageDrawable, String emptyTextTitle, String emptyTextContent, String errorButtonText, View.OnClickListener onClickListener) { 62 | mProgressActivity.showError(emptyImageDrawable, emptyTextTitle, emptyTextContent, errorButtonText, onClickListener); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/BenefitListFragment.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2015 TedXiong 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.android.ted.gank.main; 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.graphics.drawable.Drawable; 26 | import android.os.Bundle; 27 | import android.support.v4.app.ActivityOptionsCompat; 28 | import android.support.v4.app.Fragment; 29 | import android.support.v4.widget.SwipeRefreshLayout; 30 | import android.support.v7.widget.RecyclerView; 31 | import android.support.v7.widget.StaggeredGridLayoutManager; 32 | import android.view.LayoutInflater; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import android.view.ViewTreeObserver; 36 | import android.widget.Toast; 37 | 38 | import com.android.ted.gank.R; 39 | import com.android.ted.gank.adapter.BenefitGoodsItemAdapter; 40 | import com.android.ted.gank.db.Image; 41 | import com.android.ted.gank.model.Goods; 42 | import com.android.ted.gank.model.GoodsResult; 43 | import com.android.ted.gank.network.GankCloudApi; 44 | import com.android.ted.gank.service.ImageImproveService; 45 | import com.malinskiy.materialicons.IconDrawable; 46 | import com.malinskiy.materialicons.Iconify; 47 | 48 | import java.util.ArrayList; 49 | import java.util.List; 50 | import java.util.Map; 51 | 52 | import butterknife.Bind; 53 | import butterknife.ButterKnife; 54 | import io.realm.Realm; 55 | import io.realm.RealmChangeListener; 56 | import io.realm.RealmResults; 57 | import retrofit.RetrofitError; 58 | import rx.Observer; 59 | import rx.android.schedulers.AndroidSchedulers; 60 | import rx.schedulers.Schedulers; 61 | 62 | public class BenefitListFragment extends BaseLoadingFragment implements SwipeRefreshLayout.OnRefreshListener, 63 | RealmChangeListener { 64 | 65 | @Bind(R.id.benefit_recycler_view) 66 | RecyclerView mRecyclerView; 67 | @Bind(R.id.benefit_swipe_refresh) 68 | SwipeRefreshLayout mSwipeRefreshLayout; 69 | 70 | private ArrayList mAllBenefitImage; 71 | private BenefitGoodsItemAdapter mBenefitItemAdapter; 72 | private UpdateResultReceiver updateResultReceiver = new UpdateResultReceiver(); 73 | private Realm mRealm; 74 | private StaggeredGridLayoutManager mStaggeredGridLayoutManager; 75 | 76 | //是否正在更新图片信息 77 | private boolean bImproveDoing = false; 78 | private boolean isALlLoad = false; 79 | private int hasLoadPage = 0; 80 | private boolean isLoadMore = false; 81 | 82 | 83 | private Observer getBenefitGoodsObserver = new Observer() { 84 | @Override 85 | public void onNext(final GoodsResult goodsResult) { 86 | if(mAllBenefitImage.isEmpty() && goodsResult.getResults().isEmpty()){ 87 | showNoDataView(); 88 | return; 89 | } 90 | showContent(); 91 | if(goodsResult.getResults().size() == GankCloudApi.LOAD_LIMIT){ 92 | hasLoadPage++; 93 | }else { 94 | isALlLoad = true; 95 | } 96 | if (analysisNewImage(goodsResult)) 97 | doImproveJob(); 98 | else refreshBenefitGoods(); 99 | } 100 | 101 | @Override 102 | public void onCompleted() { 103 | isLoadMore = false; 104 | mSwipeRefreshLayout.setRefreshing(false); 105 | } 106 | 107 | @Override 108 | public void onError(final Throwable error) { 109 | if (error instanceof RetrofitError) { 110 | Drawable errorDrawable = new IconDrawable(getContext(), Iconify.IconValue.zmdi_network_off) 111 | .colorRes(android.R.color.white); 112 | RetrofitError e = (RetrofitError) error; 113 | if (e.getKind() == RetrofitError.Kind.NETWORK) { 114 | showError(errorDrawable,"网络异常","好像您的网络出了点问题","重试",mErrorRetryListener); 115 | } else if (e.getKind() == RetrofitError.Kind.HTTP) { 116 | showError(errorDrawable,"服务异常","好像服务器出了点问题","再试一次",mErrorRetryListener); 117 | } else { 118 | showError(errorDrawable,"莫名异常","外星人进攻地球了?","反击",mErrorRetryListener); 119 | } 120 | } 121 | } 122 | }; 123 | 124 | private View.OnClickListener mErrorRetryListener = new View.OnClickListener() { 125 | @Override 126 | public void onClick(View v) { 127 | reloadData(); 128 | } 129 | }; 130 | 131 | @Override 132 | public void onRefresh() { 133 | reloadData(); 134 | } 135 | 136 | @Override 137 | public void onChange() { 138 | if(!bImproveDoing)return; 139 | if(!this.isVisible())return; 140 | refreshBenefitGoods(); 141 | } 142 | 143 | @Override 144 | public void onCreate(Bundle savedInstanceState) { 145 | super.onCreate(savedInstanceState); 146 | mAllBenefitImage = new ArrayList<>(); 147 | mBenefitItemAdapter = new BenefitGoodsItemAdapter(getActivity()) { 148 | @Override 149 | protected void onItemClick(View v, int position) { 150 | startViewerActivity(v,position); 151 | } 152 | }; 153 | mRealm = Realm.getInstance(getActivity()); 154 | mRealm.addChangeListener(this); 155 | } 156 | 157 | @Override 158 | View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 159 | return inflater.inflate(R.layout.fragment_benifit_list,null); 160 | } 161 | 162 | @Override 163 | public void onViewCreated(View view, Bundle savedInstanceState) { 164 | super.onViewCreated(view, savedInstanceState); 165 | ButterKnife.bind(this, view); 166 | setupBaseView(); 167 | } 168 | 169 | @Override 170 | public void onActivityCreated(Bundle savedInstanceState) { 171 | super.onActivityCreated(savedInstanceState); 172 | refreshBenefitGoods(); 173 | reloadData(); 174 | } 175 | 176 | @Override 177 | public void onResume() { 178 | super.onResume(); 179 | getActivity().registerReceiver(updateResultReceiver, 180 | new IntentFilter(ImageImproveService.ACTION_UPDATE_RESULT), 181 | ImageImproveService.PERMISSION_ACCESS_UPDATE_RESULT, null); 182 | } 183 | 184 | @Override 185 | public void onPause() { 186 | super.onPause(); 187 | getActivity().unregisterReceiver(updateResultReceiver); 188 | } 189 | 190 | @Override 191 | public void onDestroy() { 192 | super.onDestroy(); 193 | mRealm.removeChangeListener(this); 194 | mRealm.close(); 195 | } 196 | 197 | private void refreshBenefitGoods(){ 198 | mAllBenefitImage.clear(); 199 | RealmResults results = mRealm.where(Image.class).notEqualTo("width",0).findAll(); 200 | mAllBenefitImage.addAll(results); 201 | mBenefitItemAdapter.replaceWith(mAllBenefitImage); 202 | } 203 | 204 | private void setupBaseView() { 205 | mSwipeRefreshLayout.setColorSchemeColors(R.color.colorPrimary, R.color.colorPrimaryDark); 206 | mSwipeRefreshLayout.setOnRefreshListener(this); 207 | mStaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 208 | mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager); 209 | mRecyclerView.setAdapter(mBenefitItemAdapter); 210 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 211 | @Override 212 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 213 | super.onScrollStateChanged(recyclerView, newState); 214 | if (newState == RecyclerView.SCROLL_STATE_IDLE) { 215 | BenefitListFragment.this.onScrollStateChanged(); 216 | } 217 | } 218 | }); 219 | } 220 | 221 | private void onScrollStateChanged(){ 222 | int[] positions = new int[mStaggeredGridLayoutManager.getSpanCount()]; 223 | mStaggeredGridLayoutManager.findLastVisibleItemPositions(positions); 224 | for (int position : positions) { 225 | if (position == mStaggeredGridLayoutManager.getItemCount() - 1) { 226 | loadMore(); 227 | break; 228 | } 229 | } 230 | } 231 | 232 | private void loadMore(){ 233 | if(isALlLoad){ 234 | Toast.makeText(getActivity(), "全部加载完毕", Toast.LENGTH_SHORT).show(); 235 | return; 236 | } 237 | if(isLoadMore)return; 238 | isLoadMore = true; 239 | loadData(hasLoadPage + 1); 240 | } 241 | 242 | private void reloadData(){ 243 | mSwipeRefreshLayout.setRefreshing(true); 244 | mAllBenefitImage.clear(); 245 | isALlLoad = false; 246 | hasLoadPage = 0; 247 | loadData(1); 248 | } 249 | 250 | private void loadData(int startPage){ 251 | GankCloudApi.getIns() 252 | .getBenefitsGoods(GankCloudApi.LOAD_LIMIT, startPage) 253 | .cache() 254 | .subscribeOn(Schedulers.newThread()) 255 | .observeOn(AndroidSchedulers.mainThread()) 256 | .subscribe(getBenefitGoodsObserver); 257 | } 258 | 259 | 260 | /*** 261 | * 分析新的数据 262 | * 263 | * @param goodsResult 264 | * @return 是否有新数据插入 265 | */ 266 | private boolean analysisNewImage(final GoodsResult goodsResult) { 267 | mRealm.beginTransaction(); 268 | if (null != goodsResult && null != goodsResult.getResults()) { 269 | for (Goods goods : goodsResult.getResults()) { 270 | Image image = Image.queryImageById(mRealm, goods.get_id()); 271 | if(null == image)image = mRealm.createObject(Image.class); 272 | Image.updateDbGoods(image,goods); 273 | } 274 | mRealm.commitTransaction(); 275 | return true; 276 | } 277 | mRealm.cancelTransaction(); 278 | return false; 279 | } 280 | 281 | private void doImproveJob() { 282 | bImproveDoing = true; 283 | Intent intent = new Intent(getActivity(), ImageImproveService.class); 284 | intent.setAction(ImageImproveService.ACTION_IMPROVE_IMAGE); 285 | getActivity().startService(intent); 286 | } 287 | 288 | private void showNoDataView(){ 289 | Drawable emptyDrawable = new IconDrawable(getContext(), Iconify.IconValue.zmdi_shopping_cart) 290 | .colorRes(android.R.color.white); 291 | List skipIds = new ArrayList<>(); 292 | showEmpty(emptyDrawable, "数据列表为空", "没有拿到数据哎,请等一下再来玩妹子吧", skipIds); 293 | } 294 | 295 | private void startViewerActivity(View itemView, int position) { 296 | Intent intent = new Intent(getActivity(), ViewerActivity.class); 297 | intent.putExtra("index", position); 298 | // ActivityOptionsCompat options = ActivityOptionsCompat 299 | // .makeSceneTransitionAnimation(getActivity(), itemView, mBenefitItemAdapter.get(position).getUrl()); 300 | getActivity().startActivity(intent); 301 | } 302 | 303 | private class UpdateResultReceiver extends BroadcastReceiver { 304 | @Override 305 | public void onReceive(Context context, Intent intent) { 306 | bImproveDoing = false; 307 | int count = intent.getIntExtra(ImageImproveService.EXTRA_CHANGE, 0); 308 | if(count > 0) 309 | refreshBenefitGoods(); 310 | } 311 | } 312 | 313 | // public Map getActivitySharedElements(int position,Map map){ 314 | // map.put(mBenefitItemAdapter.get(position).getUrl(),mStaggeredGridLayoutManager.findViewByPosition(position)); 315 | // return map; 316 | // } 317 | // 318 | // public void onActivityReenter(Bundle bundle){ 319 | // mRecyclerView.scrollToPosition(bundle.getInt("index", 0)); 320 | // mRecyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 321 | // @Override 322 | // public boolean onPreDraw() { 323 | // mRecyclerView.getViewTreeObserver().removeOnPreDrawListener(this); 324 | // mRecyclerView.requestLayout(); 325 | // getActivity().supportStartPostponedEnterTransition(); 326 | // return true; 327 | // } 328 | // }); 329 | // } 330 | } 331 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/CommonGoodsListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 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 com.android.ted.gank.main; 18 | 19 | import android.graphics.drawable.Drawable; 20 | import android.os.Bundle; 21 | import android.support.design.widget.Snackbar; 22 | import android.support.v4.widget.SwipeRefreshLayout; 23 | import android.support.v7.widget.LinearLayoutManager; 24 | import android.support.v7.widget.RecyclerView; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import com.android.ted.gank.R; 30 | import com.android.ted.gank.adapter.GoodsItemAdapter; 31 | import com.android.ted.gank.model.Goods; 32 | import com.android.ted.gank.model.GoodsResult; 33 | import com.android.ted.gank.network.GankCloudApi; 34 | import com.malinskiy.materialicons.IconDrawable; 35 | import com.malinskiy.materialicons.Iconify; 36 | 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | import butterknife.Bind; 41 | import butterknife.ButterKnife; 42 | import retrofit.RetrofitError; 43 | import rx.Observer; 44 | import rx.android.schedulers.AndroidSchedulers; 45 | import rx.schedulers.Schedulers; 46 | 47 | public class CommonGoodsListFragment extends BaseLoadingFragment implements SwipeRefreshLayout.OnRefreshListener{ 48 | @Bind(R.id.common_recycler_view) 49 | RecyclerView mRecyclerView; 50 | @Bind(R.id.common_swipe_refresh) 51 | SwipeRefreshLayout mSwipeRefreshLayout; 52 | 53 | private ArrayList mAllCommonGoods; 54 | private GoodsItemAdapter mCommonItemAdapter; 55 | private int lastVisibleItem; 56 | private boolean isALlLoad = false; 57 | private int hasLoadPage = 0; 58 | private boolean isLoadMore = false; 59 | private String mType = "Android"; 60 | 61 | public static CommonGoodsListFragment newFragment(String type) { 62 | Bundle bundle = new Bundle(); 63 | bundle.putString("type", type); 64 | CommonGoodsListFragment fragment = new CommonGoodsListFragment(); 65 | fragment.setArguments(bundle); 66 | return fragment; 67 | } 68 | 69 | private Observer getCommonGoodsObserver = new Observer() { 70 | @Override 71 | public void onNext(final GoodsResult goodsResult) { 72 | if (null != goodsResult && null != goodsResult.getResults()) { 73 | disposeResults(goodsResult); 74 | } 75 | } 76 | 77 | @Override 78 | public void onCompleted() { 79 | mSwipeRefreshLayout.setRefreshing(false); 80 | } 81 | 82 | @Override 83 | public void onError(final Throwable error) { 84 | if (error instanceof RetrofitError) { 85 | Drawable errorDrawable = new IconDrawable(getContext(), Iconify.IconValue.zmdi_network_off) 86 | .colorRes(android.R.color.white); 87 | RetrofitError e = (RetrofitError) error; 88 | if (e.getKind() == RetrofitError.Kind.NETWORK) { 89 | showError(errorDrawable,"网络异常","好像您的网络出了点问题","重试",mErrorRetryListener); 90 | } else if (e.getKind() == RetrofitError.Kind.HTTP) { 91 | showError(errorDrawable,"服务异常","好像服务器出了点问题","再试一次",mErrorRetryListener); 92 | } else { 93 | showError(errorDrawable,"莫名异常","外星人进攻地球了?","反击",mErrorRetryListener); 94 | } 95 | } 96 | isLoadMore = false; 97 | } 98 | }; 99 | 100 | private View.OnClickListener mErrorRetryListener = new View.OnClickListener() { 101 | @Override 102 | public void onClick(View v) { 103 | reloadData(); 104 | } 105 | }; 106 | 107 | private RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { 108 | @Override 109 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 110 | super.onScrolled(recyclerView, dx, dy); 111 | lastVisibleItem = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition(); 112 | } 113 | 114 | @Override 115 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 116 | super.onScrollStateChanged(recyclerView, newState); 117 | if (newState == RecyclerView.SCROLL_STATE_IDLE 118 | && lastVisibleItem + 1 == mCommonItemAdapter.getItemCount()) { 119 | mSwipeRefreshLayout.setRefreshing(true); 120 | loadMore(); 121 | } 122 | } 123 | }; 124 | 125 | @Override 126 | public void onRefresh() { 127 | reloadData(); 128 | } 129 | 130 | @Override 131 | public void onCreate(Bundle savedInstanceState) { 132 | super.onCreate(savedInstanceState); 133 | mType = getArguments().getString("type","common"); 134 | mAllCommonGoods = new ArrayList<>(); 135 | mCommonItemAdapter = new GoodsItemAdapter(getActivity()); 136 | } 137 | 138 | @Override 139 | View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 140 | return inflater.inflate(R.layout.fragment_common_list,null); 141 | } 142 | 143 | @Override 144 | public void onViewCreated(View view, Bundle savedInstanceState) { 145 | super.onViewCreated(view, savedInstanceState); 146 | ButterKnife.bind(this, view); 147 | setupBaseView(); 148 | } 149 | 150 | @Override 151 | public void onActivityCreated(Bundle savedInstanceState) { 152 | super.onActivityCreated(savedInstanceState); 153 | showLoading(); 154 | reloadData(); 155 | } 156 | 157 | private void setupBaseView() { 158 | mRecyclerView.setLayoutManager(new LinearLayoutManager(mRecyclerView.getContext())); 159 | mRecyclerView.setAdapter(mCommonItemAdapter); 160 | mRecyclerView.addOnScrollListener(mOnScrollListener); 161 | mSwipeRefreshLayout.setColorSchemeColors(R.color.colorPrimary, R.color.colorPrimaryDark); 162 | mSwipeRefreshLayout.setOnRefreshListener(this); 163 | } 164 | 165 | private void loadMore(){ 166 | if(isLoadMore)return; 167 | if(isALlLoad){ 168 | Snackbar.make(mRecyclerView,"全部加载完毕",Snackbar.LENGTH_SHORT) 169 | .setAction("知道了",null) 170 | .show(); 171 | return; 172 | } 173 | isLoadMore = true; 174 | loadData(hasLoadPage + 1); 175 | } 176 | 177 | private void reloadData(){ 178 | mSwipeRefreshLayout.setRefreshing(true); 179 | mAllCommonGoods.clear(); 180 | isALlLoad = false; 181 | hasLoadPage = 0; 182 | loadData(1); 183 | } 184 | 185 | private void loadData(int startPage){ 186 | GankCloudApi.getIns() 187 | .getCommonGoods(mType, GankCloudApi.LOAD_LIMIT, startPage) 188 | .cache() 189 | .subscribeOn(Schedulers.newThread()) 190 | .observeOn(AndroidSchedulers.mainThread()) 191 | .subscribe(getCommonGoodsObserver); 192 | } 193 | 194 | private void disposeResults(final GoodsResult goodsResult){ 195 | if(mAllCommonGoods.isEmpty() && goodsResult.getResults().isEmpty()){ 196 | showNoDataView(); 197 | return; 198 | } 199 | showContent(); 200 | if(goodsResult.getResults().size() == GankCloudApi.LOAD_LIMIT){ 201 | hasLoadPage++; 202 | }else { 203 | isALlLoad = true; 204 | } 205 | isLoadMore = false; 206 | mAllCommonGoods.addAll(goodsResult.getResults()); 207 | mCommonItemAdapter.updateItems(mAllCommonGoods, hasLoadPage == 1); 208 | } 209 | 210 | private void showNoDataView(){ 211 | Drawable emptyDrawable = new IconDrawable(getContext(), Iconify.IconValue.zmdi_shopping_cart) 212 | .colorRes(android.R.color.white); 213 | List skipIds = new ArrayList<>(); 214 | showEmpty(emptyDrawable, "数据列表为空", "没有拿到数据哎,请等一下再来玩干货吧", skipIds); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 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 com.android.ted.gank.main; 18 | 19 | import android.content.Intent; 20 | import android.net.Uri; 21 | import android.os.Bundle; 22 | import android.support.design.widget.FloatingActionButton; 23 | import android.support.design.widget.NavigationView; 24 | import android.support.design.widget.Snackbar; 25 | import android.support.design.widget.TabLayout; 26 | import android.support.v4.app.SharedElementCallback; 27 | import android.support.v4.view.GravityCompat; 28 | import android.support.v4.view.ViewPager; 29 | import android.support.v4.widget.DrawerLayout; 30 | import android.support.v7.app.ActionBar; 31 | import android.support.v7.app.AppCompatActivity; 32 | import android.support.v7.widget.Toolbar; 33 | import android.view.MenuItem; 34 | import android.view.View; 35 | import android.widget.Toast; 36 | 37 | import com.android.ted.gank.R; 38 | import com.android.ted.gank.adapter.MainFragmentPagerAdapter; 39 | import com.android.ted.gank.config.Constants; 40 | import com.android.ted.gank.data.ImageGoodsCache; 41 | import com.android.ted.gank.db.Image; 42 | import com.android.ted.gank.model.GoodsResult; 43 | import com.android.ted.gank.network.GankCloudApi; 44 | import com.orhanobut.logger.Logger; 45 | import com.umeng.analytics.MobclickAgent; 46 | 47 | import java.util.List; 48 | import java.util.Map; 49 | 50 | import butterknife.Bind; 51 | import butterknife.ButterKnife; 52 | import io.realm.Realm; 53 | import io.realm.RealmResults; 54 | import rx.Observer; 55 | import rx.android.schedulers.AndroidSchedulers; 56 | import rx.schedulers.Schedulers; 57 | 58 | 59 | public class MainActivity extends AppCompatActivity { 60 | 61 | @Bind(R.id.toolbar) 62 | Toolbar mToolbar; 63 | @Bind(R.id.drawer_layout) 64 | DrawerLayout mDrawerLayout; 65 | @Bind(R.id.viewpager) 66 | ViewPager mViewPager; 67 | @Bind(R.id.tabs) 68 | TabLayout mTabLayout; 69 | @Bind(R.id.nav_view) 70 | NavigationView mNavigationView; 71 | @Bind(R.id.fab) 72 | FloatingActionButton mFABtn; 73 | 74 | private Realm mRealm; 75 | private Bundle mReenterState; 76 | private MainFragmentPagerAdapter mPagerAdapter; 77 | private BenefitListFragment mBenefitListFragment; 78 | 79 | /*** 80 | * 获取福利图的回调接口,拿到数据用来做背景 81 | */ 82 | private Observer getImageGoodsObserver = new Observer() { 83 | @Override 84 | public void onNext(final GoodsResult goodsResult) { 85 | if (null != goodsResult && null != goodsResult.getResults()) { 86 | ImageGoodsCache.getIns().addAllImageGoods(goodsResult.getResults()); 87 | } 88 | } 89 | 90 | @Override 91 | public void onCompleted() { 92 | Logger.d("获取背景图服务完成"); 93 | } 94 | 95 | @Override 96 | public void onError(final Throwable error) { 97 | Logger.e(error,"获取背景图服务失败"); 98 | } 99 | }; 100 | 101 | 102 | @Override 103 | protected void onCreate(Bundle savedInstanceState) { 104 | super.onCreate(savedInstanceState); 105 | setContentView(R.layout.activity_main); 106 | 107 | mRealm = Realm.getInstance(this); 108 | 109 | ButterKnife.bind(this); 110 | 111 | setSupportActionBar(mToolbar); 112 | final ActionBar ab = getSupportActionBar(); 113 | ab.setHomeAsUpIndicator(R.drawable.ic_menu); 114 | ab.setDisplayHomeAsUpEnabled(true); 115 | 116 | setupDrawerContent(mNavigationView); 117 | setupViewPager(); 118 | 119 | mTabLayout.setupWithViewPager(mViewPager); 120 | 121 | //mFABtn.setOnClickListener(new View.OnClickListener() { 122 | // @Override 123 | // public void onClick(View view) { 124 | // Snackbar.make(view, "Here's a Snackbar", Snackbar.LENGTH_LONG) 125 | // .setAction("Action", null).show(); 126 | // } 127 | //}); 128 | 129 | //setExitSharedElementCallback(mSharedElementCallback); 130 | 131 | loadAllImageGoods(); 132 | } 133 | 134 | @Override 135 | public boolean onOptionsItemSelected(MenuItem item) { 136 | switch (item.getItemId()) { 137 | case android.R.id.home: 138 | mDrawerLayout.openDrawer(GravityCompat.START); 139 | return true; 140 | } 141 | return super.onOptionsItemSelected(item); 142 | } 143 | 144 | @Override 145 | protected void onResume() { 146 | super.onResume(); 147 | MobclickAgent.onResume(this); 148 | } 149 | 150 | @Override 151 | protected void onPause() { 152 | super.onPause(); 153 | MobclickAgent.onPause(this); 154 | } 155 | 156 | @Override 157 | protected void onDestroy() { 158 | super.onDestroy(); 159 | mRealm.close(); 160 | } 161 | 162 | private void setupViewPager() { 163 | mBenefitListFragment = new BenefitListFragment(); 164 | mPagerAdapter = new MainFragmentPagerAdapter(getSupportFragmentManager()); 165 | mPagerAdapter.addFragment(CommonGoodsListFragment.newFragment("Android"), "Android"); 166 | mPagerAdapter.addFragment(CommonGoodsListFragment.newFragment("IOS"), "IOS"); 167 | mPagerAdapter.addFragment(mBenefitListFragment, "福利"); 168 | mViewPager.setAdapter(mPagerAdapter); 169 | } 170 | 171 | private void setupDrawerContent(NavigationView navigationView) { 172 | navigationView.getMenu().findItem(R.id.nav_home).setChecked(true); 173 | navigationView.setNavigationItemSelectedListener( 174 | new NavigationView.OnNavigationItemSelectedListener() { 175 | @Override 176 | public boolean onNavigationItemSelected(MenuItem menuItem) { 177 | menuItem.setChecked(true); 178 | mDrawerLayout.closeDrawers(); 179 | disposeMenuAction(menuItem); 180 | return true; 181 | } 182 | }); 183 | //navigationView.findViewById(R.id.menu_header).setOnClickListener(new View.OnClickListener() { 184 | // @Override 185 | // public void onClick(View v) { 186 | // callWebView(Constants.GANK_URL); 187 | // } 188 | //}); 189 | } 190 | 191 | private void disposeMenuAction(MenuItem item){ 192 | switch (item.getItemId()){ 193 | case R.id.nav_collect: 194 | case R.id.nav_time: 195 | Toast.makeText(this,"功能开发中",Toast.LENGTH_SHORT).show(); 196 | break; 197 | case R.id.nav_code: 198 | callWebView(Constants.GITHUB_URL); 199 | break; 200 | case R.id.nav_author: 201 | callWebView(Constants.AUTHOR_URL); 202 | break; 203 | } 204 | } 205 | 206 | private void loadAllImageGoods() { 207 | RealmResults allImage = mRealm.where(Image.class).findAll(); 208 | if (allImage.size() == 0) { 209 | GankCloudApi.getIns() 210 | .getBenefitsGoods(GankCloudApi.LOAD_LIMIT, 1) 211 | .cache() 212 | .subscribeOn(Schedulers.newThread()) 213 | .observeOn(AndroidSchedulers.mainThread()) 214 | .subscribe(getImageGoodsObserver); 215 | } else { 216 | ImageGoodsCache.getIns().addAllImageGoods(allImage); 217 | } 218 | } 219 | 220 | 221 | // private SharedElementCallback mSharedElementCallback = new SharedElementCallback() { 222 | // @Override 223 | // public void onMapSharedElements(List names, Map sharedElements) { 224 | // if (mReenterState != null) { 225 | // int i = mReenterState.getInt("index", 0); 226 | // sharedElements.clear(); 227 | // mBenefitListFragment.getActivitySharedElements(i,sharedElements); 228 | // mReenterState = null; 229 | // } 230 | // } 231 | // }; 232 | // 233 | // @Override 234 | // public void onActivityReenter(int resultCode, Intent data) { 235 | // super.onActivityReenter(resultCode, data); 236 | // supportPostponeEnterTransition(); 237 | // mReenterState = new Bundle(data.getExtras()); 238 | // mBenefitListFragment.onActivityReenter(new Bundle(data.getExtras())); 239 | // } 240 | 241 | private void callWebView(String url){ 242 | Intent intent= new Intent(); 243 | intent.setAction("android.intent.action.VIEW"); 244 | Uri content_url = Uri.parse(url); 245 | intent.setData(content_url); 246 | startActivity(intent); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/ViewerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.main; 19 | 20 | import android.content.Intent; 21 | import android.os.Build; 22 | import android.os.Bundle; 23 | import android.support.design.widget.Snackbar; 24 | import android.support.v4.app.Fragment; 25 | import android.support.v4.app.FragmentStatePagerAdapter; 26 | import android.support.v4.app.SharedElementCallback; 27 | import android.support.v4.view.ViewPager; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.support.v7.widget.Toolbar; 30 | import android.view.View; 31 | import android.widget.Toast; 32 | 33 | import com.android.ted.gank.R; 34 | import com.android.ted.gank.db.Image; 35 | import com.umeng.analytics.MobclickAgent; 36 | 37 | import net.grandcentrix.tray.TrayAppPreferences; 38 | 39 | import java.util.ArrayList; 40 | import java.util.List; 41 | import java.util.Map; 42 | 43 | import butterknife.Bind; 44 | import butterknife.ButterKnife; 45 | import butterknife.OnPageChange; 46 | import io.realm.Realm; 47 | import io.realm.RealmChangeListener; 48 | import io.realm.RealmResults; 49 | 50 | public class ViewerActivity extends AppCompatActivity implements RealmChangeListener,ViewPager.OnPageChangeListener{ 51 | public static final int REQUEST_CODE_SET_WALLPAPER = 0x1001; 52 | public static final String KEY_SET_WALLPAPER_TIPS = "key_set_wallpaper_tips"; 53 | 54 | private static final int SYSTEM_UI_BASE_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_STABLE 55 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 56 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 57 | 58 | private static final int SYSTEM_UI_IMMERSIVE = View.SYSTEM_UI_FLAG_IMMERSIVE 59 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 60 | | View.SYSTEM_UI_FLAG_FULLSCREEN; 61 | 62 | @Bind(R.id.toolbar) 63 | Toolbar toolbar; 64 | 65 | @Bind(R.id.pager) 66 | ViewPager pager; 67 | 68 | private int index; 69 | 70 | private Realm mRealm; 71 | 72 | private ArrayList images; 73 | 74 | private PagerAdapter adapter; 75 | 76 | private TrayAppPreferences mAppPreferences; 77 | 78 | @Override 79 | protected void onCreate(Bundle savedInstanceState) { 80 | super.onCreate(savedInstanceState); 81 | setContentView(R.layout.activity_viewer); 82 | ButterKnife.bind(this); 83 | mAppPreferences = new TrayAppPreferences(this); 84 | images = new ArrayList<>(); 85 | mRealm = Realm.getInstance(this); 86 | mRealm.addChangeListener(this); 87 | loadAllImage(); 88 | 89 | setSupportActionBar(toolbar); 90 | toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp); 91 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | supportFinishAfterTransition(); 95 | } 96 | }); 97 | 98 | index = getIntent().getIntExtra("index", 0); 99 | 100 | adapter = new PagerAdapter(); 101 | 102 | pager.setAdapter(adapter); 103 | pager.setCurrentItem(index); 104 | pager.addOnPageChangeListener(this); 105 | 106 | // 避免图片在进行 Shared Element Transition 时盖过 Toolbar 107 | // if (Build.VERSION.SDK_INT >= 21) { 108 | // getWindow().setSharedElementsUseOverlay(false); 109 | // } 110 | // 111 | // setEnterSharedElementCallback(new SharedElementCallback() { 112 | // @Override 113 | // public void onMapSharedElements(List names, Map sharedElements) { 114 | // Image image = images.get(pager.getCurrentItem()); 115 | // ViewerFragment fragment = (ViewerFragment) adapter.instantiateItem(pager, pager.getCurrentItem()); 116 | // 117 | // sharedElements.clear(); 118 | // sharedElements.put(image.getUrl(), fragment.getSharedElement()); 119 | // } 120 | // }); 121 | hideSystemUi(); 122 | showTips(); 123 | } 124 | 125 | @Override 126 | protected void onResume() { 127 | super.onResume(); 128 | MobclickAgent.onResume(this); 129 | } 130 | 131 | @Override 132 | protected void onPause() { 133 | super.onPause(); 134 | MobclickAgent.onPause(this); 135 | } 136 | 137 | @Override 138 | protected void onDestroy() { 139 | super.onDestroy(); 140 | mRealm.removeChangeListener(this); 141 | mRealm.close(); 142 | } 143 | 144 | @Override 145 | public void onChange() { 146 | loadAllImage(); 147 | adapter.notifyDataSetChanged(); 148 | } 149 | 150 | @Override 151 | public void onPageScrollStateChanged(int state) { 152 | if (state == ViewPager.SCROLL_STATE_DRAGGING) { 153 | hideSystemUi(); 154 | } 155 | } 156 | 157 | 158 | @Override 159 | public void onPageSelected(int position) { 160 | 161 | } 162 | 163 | @Override 164 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 165 | 166 | } 167 | 168 | private void loadAllImage() { 169 | RealmResults results = mRealm.where(Image.class).notEqualTo("width", 0).findAll(); 170 | images.addAll(results); 171 | } 172 | 173 | public void toggleToolbar() { 174 | if (toolbar.getTranslationY() == 0) { 175 | hideSystemUi(); 176 | } else { 177 | showSystemUi(); 178 | } 179 | } 180 | 181 | private void showSystemUi() { 182 | pager.setSystemUiVisibility(SYSTEM_UI_BASE_VISIBILITY); 183 | toolbar.animate() 184 | .translationY(0) 185 | .setDuration(400) 186 | .start(); 187 | } 188 | 189 | private void hideSystemUi() { 190 | pager.setSystemUiVisibility(SYSTEM_UI_BASE_VISIBILITY | SYSTEM_UI_IMMERSIVE); 191 | toolbar.animate() 192 | .translationY(-toolbar.getHeight()) 193 | .setDuration(400) 194 | .start(); 195 | } 196 | 197 | private void showTips(){ 198 | if(mAppPreferences.getBoolean(KEY_SET_WALLPAPER_TIPS,true)){ 199 | Snackbar.make(toolbar,"长按妹子图即可设置为壁纸",Snackbar.LENGTH_LONG).setAction("知道了", new View.OnClickListener() { 200 | @Override 201 | public void onClick(View v) { 202 | mAppPreferences.put(KEY_SET_WALLPAPER_TIPS,false); 203 | } 204 | }).show(); 205 | } 206 | } 207 | 208 | @Override 209 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 210 | super.onActivityResult(requestCode, resultCode, data); 211 | if(resultCode == RESULT_OK){ 212 | if(requestCode == REQUEST_CODE_SET_WALLPAPER) 213 | Toast.makeText(this,"壁纸设置成功",Toast.LENGTH_SHORT).show(); 214 | } 215 | } 216 | 217 | @Override 218 | public void supportFinishAfterTransition() { 219 | Intent data = new Intent(); 220 | data.putExtra("index", pager.getCurrentItem()); 221 | setResult(RESULT_OK, data); 222 | showSystemUi(); 223 | super.supportFinishAfterTransition(); 224 | } 225 | 226 | private class PagerAdapter extends FragmentStatePagerAdapter { 227 | 228 | public PagerAdapter() { 229 | super(getSupportFragmentManager()); 230 | } 231 | 232 | @Override 233 | public int getCount() { 234 | return images.size(); 235 | } 236 | 237 | @Override 238 | public Fragment getItem(int position) { 239 | return ViewerFragment.newFragment(images.get(position).getUrl(), position == index); 240 | } 241 | } 242 | 243 | } 244 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/main/ViewerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 XiNGRZ 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 com.android.ted.gank.main; 18 | 19 | import android.app.WallpaperManager; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.graphics.Bitmap; 23 | import android.graphics.drawable.Drawable; 24 | import android.net.Uri; 25 | import android.os.Bundle; 26 | import android.support.annotation.Nullable; 27 | import android.support.v4.app.Fragment; 28 | import android.support.v4.content.FileProvider; 29 | import android.support.v4.view.ViewCompat; 30 | import android.view.LayoutInflater; 31 | import android.view.View; 32 | import android.view.ViewGroup; 33 | import android.widget.Toast; 34 | 35 | import com.android.ted.gank.R; 36 | import com.android.ted.gank.utils.PictUtil; 37 | import com.android.ted.gank.view.TouchImageView; 38 | import com.bumptech.glide.Glide; 39 | import com.bumptech.glide.request.animation.GlideAnimation; 40 | import com.bumptech.glide.request.target.SimpleTarget; 41 | import com.orhanobut.logger.Logger; 42 | 43 | import java.io.File; 44 | import java.io.IOException; 45 | 46 | import butterknife.Bind; 47 | import butterknife.ButterKnife; 48 | import butterknife.OnClick; 49 | import butterknife.OnLongClick; 50 | 51 | public class ViewerFragment extends Fragment{ 52 | 53 | @Bind(R.id.image) 54 | TouchImageView image; 55 | 56 | private ViewerActivity activity; 57 | 58 | private String url; 59 | private boolean initialShown; 60 | private Bitmap mBitmap; 61 | 62 | public static ViewerFragment newFragment(String url, boolean initialShown) { 63 | Bundle bundle = new Bundle(); 64 | bundle.putString("url", url); 65 | bundle.putBoolean("initial_shown", initialShown); 66 | 67 | ViewerFragment fragment = new ViewerFragment(); 68 | fragment.setArguments(bundle); 69 | 70 | return fragment; 71 | } 72 | 73 | @Override 74 | public void onAttach(Context context) { 75 | super.onAttach(context); 76 | if(context instanceof ViewerActivity){ 77 | this.activity = (ViewerActivity)context; 78 | } 79 | } 80 | 81 | @Override 82 | public void onCreate(@Nullable Bundle savedInstanceState) { 83 | super.onCreate(savedInstanceState); 84 | url = getArguments().getString("url"); 85 | initialShown = getArguments().getBoolean("initial_shown", false); 86 | 87 | Logger.d("onResourceReady"); 88 | } 89 | 90 | @Override 91 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 92 | return inflater.inflate(R.layout.fragment_viewer, container, false); 93 | } 94 | 95 | @Override 96 | public void onViewCreated(View view, Bundle savedInstanceState) { 97 | ButterKnife.bind(this, view); 98 | //ViewCompat.setTransitionName(image, url); 99 | } 100 | 101 | @Override 102 | public void onDestroy() { 103 | super.onDestroy(); 104 | if(null != mBitmap && !mBitmap.isRecycled()){ 105 | mBitmap.recycle(); 106 | mBitmap = null; 107 | } 108 | } 109 | 110 | @Override 111 | public void onResume() { 112 | super.onResume(); 113 | Glide.with(this).load(url).asBitmap().into(new SimpleTarget() { 114 | @Override 115 | public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 116 | Logger.d("onResourceReady"); 117 | if(null != resource){ 118 | image.setImageBitmap(resource); 119 | //maybeStartPostponedEnterTransition(); 120 | }else { 121 | //getActivity().supportFinishAfterTransition(); 122 | } 123 | } 124 | 125 | @Override 126 | public void onLoadFailed(Exception e, Drawable errorDrawable) { 127 | super.onLoadFailed(e, errorDrawable); 128 | Logger.d("onLoadFailed"); 129 | // maybeStartPostponedEnterTransition(); 130 | // getActivity().supportFinishAfterTransition(); 131 | } 132 | }); 133 | } 134 | 135 | // private void maybeStartPostponedEnterTransition() { 136 | // if (initialShown) { 137 | // activity.supportStartPostponedEnterTransition(); 138 | // } 139 | // } 140 | 141 | @OnClick(R.id.image) 142 | @SuppressWarnings("unused") 143 | void toggleToolbar() { 144 | activity.toggleToolbar(); 145 | } 146 | 147 | @OnLongClick(R.id.image) 148 | @SuppressWarnings("unused") 149 | boolean setImageToWallpaper(){ 150 | if(!PictUtil.hasSDCard()){ 151 | Toast.makeText(getActivity(),"不支持下载文件",Toast.LENGTH_SHORT).show(); 152 | return false; 153 | } 154 | Glide.with(this).load(url).asBitmap().into(new SimpleTarget() { 155 | @Override 156 | public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 157 | mBitmap = resource; 158 | saveImgFileToLocal(); 159 | } 160 | 161 | @Override 162 | public void onLoadFailed(Exception e, Drawable errorDrawable) { 163 | super.onLoadFailed(e, errorDrawable); 164 | mBitmap = null; 165 | Toast.makeText(getActivity(),"下载图片失败,请重试",Toast.LENGTH_SHORT).show(); 166 | } 167 | }); 168 | return false; 169 | } 170 | 171 | private void saveImgFileToLocal(){ 172 | if(null != mBitmap){ 173 | //create a temporary directory within the cache folder 174 | File dir = new File(getActivity().getCacheDir() + "/images"); 175 | if (!dir.exists()) { 176 | dir.mkdirs(); 177 | } 178 | //create the file 179 | File file = new File(dir, PictUtil.getImageFileName(url)); 180 | try { 181 | if (!file.exists()) { 182 | file.createNewFile(); 183 | } 184 | PictUtil.saveToFile(file,mBitmap); 185 | }catch (IOException e){ 186 | Logger.e(e,"下载图片失败"); 187 | Toast.makeText(getActivity(),"下载图片失败,请重试",Toast.LENGTH_SHORT).show(); 188 | }finally { 189 | checkFileAndSetWallPaper(file); 190 | } 191 | }else { 192 | Toast.makeText(getActivity(),"下载图片失败,请重试",Toast.LENGTH_SHORT).show(); 193 | } 194 | } 195 | 196 | private void checkFileAndSetWallPaper(File file){ 197 | if(null != file && file.exists()){ 198 | //get the contentUri for this file and start the intent 199 | Uri contentUri = FileProvider.getUriForFile(getActivity(), "com.android.ted.gank.fileprovider", file); 200 | //get crop intent 201 | Intent intent = WallpaperManager.getInstance(getActivity()).getCropAndSetWallpaperIntent(contentUri); 202 | //start activity for result so we can animate if we finish 203 | getActivity().startActivityForResult(intent, ViewerActivity.REQUEST_CODE_SET_WALLPAPER); 204 | } 205 | } 206 | 207 | // View getSharedElement() { 208 | // return image; 209 | // } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/manager/CollectManager.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.manager; 2 | 3 | import com.android.ted.gank.model.Goods; 4 | 5 | /** 6 | * Created by Ted on 2015/8/25. 7 | */ 8 | public class CollectManager { 9 | private static CollectManager instance; 10 | 11 | public static CollectManager getIns() { 12 | if (null == instance) { 13 | synchronized (CollectManager.class) { 14 | if (null == instance) { 15 | instance = new CollectManager(); 16 | } 17 | } 18 | } 19 | return instance; 20 | } 21 | 22 | public boolean isCollect(Goods goods){ 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/model/BaseResult.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.model; 2 | 3 | /** 4 | * Created by Ted on 2015/8/24. 5 | */ 6 | public class BaseResult { 7 | private boolean error; 8 | 9 | public boolean isError() { 10 | return error; 11 | } 12 | 13 | public void setError(boolean error) { 14 | this.error = error; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/model/DayGoods.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class DayGoods { 8 | 9 | @SerializedName("iOS") 10 | private ArrayList iosGoods; 11 | @SerializedName("Android") 12 | private ArrayList androidGoods; 13 | @SerializedName("瞎推荐") 14 | private ArrayList recommend; 15 | @SerializedName("福利") 16 | private ArrayList benefit; 17 | @SerializedName("休息视频") 18 | private ArrayList restVideo; 19 | @SerializedName("拓展资源") 20 | private ArrayList expandRes; 21 | 22 | public ArrayList getIosGoods() { 23 | return iosGoods; 24 | } 25 | 26 | public void setIosGoods(ArrayList iosGoods) { 27 | this.iosGoods = iosGoods; 28 | } 29 | 30 | public ArrayList getAndroidGoods() { 31 | return androidGoods; 32 | } 33 | 34 | public void setAndroidGoods(ArrayList androidGoods) { 35 | this.androidGoods = androidGoods; 36 | } 37 | 38 | public ArrayList getRecommend() { 39 | return recommend; 40 | } 41 | 42 | public void setRecommend(ArrayList recommend) { 43 | this.recommend = recommend; 44 | } 45 | 46 | public ArrayList getBenefit() { 47 | return benefit; 48 | } 49 | 50 | public void setBenefit(ArrayList benefit) { 51 | this.benefit = benefit; 52 | } 53 | 54 | public ArrayList getRestVideo() { 55 | return restVideo; 56 | } 57 | 58 | public void setRestVideo(ArrayList restVideo) { 59 | this.restVideo = restVideo; 60 | } 61 | 62 | public ArrayList getExpandRes() { 63 | return expandRes; 64 | } 65 | 66 | public void setExpandRes(ArrayList expandRes) { 67 | this.expandRes = expandRes; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/model/DayGoodsResult.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.model; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by Ted on 2015/8/24. 7 | */ 8 | public class DayGoodsResult extends BaseResult{ 9 | private DayGoods results; 10 | private ArrayList category; 11 | 12 | public DayGoods getResults() { 13 | return results; 14 | } 15 | 16 | public void setResults(DayGoods results) { 17 | this.results = results; 18 | } 19 | 20 | public ArrayList getCategory() { 21 | return category; 22 | } 23 | 24 | public void setCategory(ArrayList category) { 25 | this.category = category; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/model/Goods.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.model; 2 | 3 | /** 4 | * Created by Ted on 2015/8/23. 5 | * 干货实体类 6 | */ 7 | public class Goods{ 8 | 9 | // "who":"Jason", 10 | // "publishedAt":"2015-08-21T04:09:13.777Z", 11 | // "desc":"着色的加载视图库", 12 | // "type":"Android", 13 | // "url":"https://github.com/recruit-lifestyle/ColoringLoading", 14 | // "used":true, 15 | // "objectId":"55d5fd6b00b0af5bde3a9f82", 16 | // "createdAt":"2015-08-20T16:16:43.941Z", 17 | // "updatedAt":"2015-08-21T04:09:14.358Z" 18 | private String who; 19 | private String publishedAt; 20 | private String desc; 21 | private String type; 22 | private String url; 23 | private boolean used; 24 | private String _id; 25 | private String createdAt; 26 | private String _ns; 27 | private String source; 28 | 29 | public String getWho() { 30 | return who; 31 | } 32 | 33 | public void setWho(String who) { 34 | this.who = who; 35 | } 36 | 37 | public String getPublishedAt() { 38 | return publishedAt; 39 | } 40 | 41 | public void setPublishedAt(String publishedAt) { 42 | this.publishedAt = publishedAt; 43 | } 44 | 45 | public String getDesc() { 46 | return desc; 47 | } 48 | 49 | public void setDesc(String desc) { 50 | this.desc = desc; 51 | } 52 | 53 | public String getType() { 54 | return type; 55 | } 56 | 57 | public void setType(String type) { 58 | this.type = type; 59 | } 60 | 61 | public String getUrl() { 62 | return url; 63 | } 64 | 65 | public void setUrl(String url) { 66 | this.url = url; 67 | } 68 | 69 | public boolean isUsed() { 70 | return used; 71 | } 72 | 73 | public void setUsed(boolean used) { 74 | this.used = used; 75 | } 76 | 77 | public String get_id() { 78 | return _id; 79 | } 80 | 81 | public void set_id(String _id) { 82 | this._id = _id; 83 | } 84 | 85 | public String getCreatedAt() { 86 | return createdAt; 87 | } 88 | 89 | public void setCreatedAt(String createdAt) { 90 | this.createdAt = createdAt; 91 | } 92 | 93 | public String get_ns() { 94 | return _ns; 95 | } 96 | 97 | public void set_ns(String _ns) { 98 | this._ns = _ns; 99 | } 100 | 101 | public String getSource() { 102 | return source; 103 | } 104 | 105 | public void setSource(String source) { 106 | this.source = source; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/model/GoodsResult.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.model; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class GoodsResult extends BaseResult{ 6 | 7 | private ArrayList results; 8 | 9 | public GoodsResult(ArrayList results) { 10 | this.results = results; 11 | } 12 | 13 | public ArrayList getResults() { 14 | return results; 15 | } 16 | 17 | public void setResults(ArrayList results) { 18 | this.results = results; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/network/GankCloudApi.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.network; 2 | 3 | 4 | import com.android.ted.gank.GankApplication; 5 | import com.android.ted.gank.config.Constants; 6 | import com.android.ted.gank.model.DayGoodsResult; 7 | import com.android.ted.gank.model.GoodsResult; 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | import com.squareup.okhttp.Cache; 11 | import com.squareup.okhttp.OkHttpClient; 12 | 13 | import java.io.File; 14 | 15 | import retrofit.RequestInterceptor; 16 | import retrofit.RestAdapter; 17 | import retrofit.client.OkClient; 18 | import retrofit.converter.GsonConverter; 19 | import retrofit.http.GET; 20 | import retrofit.http.Path; 21 | import rx.Observable; 22 | 23 | public class GankCloudApi { 24 | public static GankCloudApi instance; 25 | 26 | public static GankCloudApi getIns() { 27 | if (null == instance) { 28 | synchronized (GankCloudApi.class) { 29 | if (null == instance) { 30 | instance = new GankCloudApi(); 31 | } 32 | } 33 | } 34 | return instance; 35 | } 36 | 37 | /**每次加载条目*/ 38 | public static final int LOAD_LIMIT = 20; 39 | /**加载起始页面*/ 40 | public static final int LOAD_START = 1; 41 | 42 | public static final String ENDPOINT = Constants.GANK_SERVER_IP; 43 | 44 | private final GankCloudService mWebService; 45 | 46 | public static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); 47 | 48 | 49 | 50 | public GankCloudApi() { 51 | Cache cache; 52 | OkHttpClient okHttpClient = null; 53 | try { 54 | File cacheDir = new File(GankApplication.getContext().getCacheDir().getPath(), "gank_cache.json"); 55 | cache = new Cache(cacheDir, 10 * 1024 * 1024); 56 | okHttpClient = new OkHttpClient(); 57 | okHttpClient.setCache(cache); 58 | } catch (Exception e) { 59 | 60 | } 61 | 62 | RestAdapter restAdapter = new RestAdapter.Builder() 63 | .setEndpoint(ENDPOINT) 64 | .setClient(new OkClient(okHttpClient)) 65 | .setConverter(new GsonConverter(gson)) 66 | .setRequestInterceptor(mRequestInterceptor) 67 | .build(); 68 | mWebService = restAdapter.create(GankCloudService.class); 69 | } 70 | 71 | private RequestInterceptor mRequestInterceptor = new RequestInterceptor() { 72 | @Override 73 | public void intercept(RequestFacade request) { 74 | request.addHeader("Cache-Control", "public, max-age=" + 60 * 60 * 4); 75 | request.addHeader("Content-Type", "application/json"); 76 | } 77 | }; 78 | 79 | public interface GankCloudService { 80 | 81 | @GET("/data/Android/{limit}/{page}") 82 | Observable getAndroidGoods( 83 | @Path("limit") int limit, 84 | @Path("page") int page 85 | ); 86 | 87 | @GET("/data/iOS/{limit}/{page}") 88 | Observable getIosGoods( 89 | @Path("limit") int limit, 90 | @Path("page") int page 91 | ); 92 | 93 | @GET("/data/all/{limit}/{page}") 94 | Observable getAllGoods( 95 | @Path("limit") int limit, 96 | @Path("page") int page 97 | ); 98 | 99 | @GET("/data/福利/{limit}/{page}") 100 | Observable getBenefitsGoods( 101 | @Path("limit") int limit, 102 | @Path("page") int page 103 | ); 104 | 105 | @GET("/day/{year}/{month}/{day}") 106 | Observable getGoodsByDay( 107 | @Path("year") int year, 108 | @Path("month") int month, 109 | @Path("day") int day 110 | ); 111 | } 112 | 113 | public Observable getCommonGoods(String type,int limit, int page) { 114 | if("Android".equalsIgnoreCase(type)){ 115 | return mWebService.getAndroidGoods(limit, page); 116 | } 117 | if("IOS".equalsIgnoreCase(type)){ 118 | return mWebService.getIosGoods(limit, page); 119 | } 120 | return mWebService.getAndroidGoods(limit, page); 121 | } 122 | 123 | public Observable getAndroidGoods(int limit, int page) { 124 | return mWebService.getAndroidGoods(limit, page); 125 | } 126 | 127 | public Observable getIosGoods(int limit, int page) { 128 | return mWebService.getIosGoods(limit, page); 129 | } 130 | 131 | public Observable getAllGoods(int limit, int page) { 132 | return mWebService.getAllGoods(limit, page); 133 | } 134 | 135 | public Observable getBenefitsGoods(int limit, int page) { 136 | return mWebService.getBenefitsGoods(limit, page); 137 | } 138 | 139 | public Observable getGoodsByDay(int year,int month,int day) { 140 | return mWebService.getGoodsByDay(year, month,day); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/service/ImageImproveService.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2015 TedXiong 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package com.android.ted.gank.service; 20 | 21 | import android.app.IntentService; 22 | import android.content.Intent; 23 | import android.graphics.BitmapFactory; 24 | import android.graphics.Point; 25 | 26 | import com.android.ted.gank.db.Image; 27 | import com.orhanobut.logger.Logger; 28 | import com.squareup.okhttp.OkHttpClient; 29 | import com.squareup.okhttp.Request; 30 | import com.squareup.okhttp.Response; 31 | 32 | import io.realm.Sort; 33 | import java.io.IOException; 34 | 35 | import io.realm.Realm; 36 | import io.realm.RealmResults; 37 | 38 | /** 39 | * 完善图片信息的服务 40 | * 41 | * @author Ted 42 | */ 43 | public class ImageImproveService extends IntentService{ 44 | 45 | public static final String ACTION_UPDATE_RESULT = "com.android.ted.UPDATE_RESULT"; 46 | 47 | public static final String EXTRA_CHANGE = "change_data"; 48 | public static final String EXTRA_ACTION = "action_name"; 49 | 50 | public static final String PERMISSION_ACCESS_UPDATE_RESULT = "com.android.ted.ACCESS_UPDATE_RESULT"; 51 | 52 | public static final String ACTION_IMPROVE_IMAGE = "com.android.ted.IMPROVE_IMAGE"; 53 | 54 | 55 | private static final String TAG = "ImageImproveService"; 56 | 57 | private final OkHttpClient client = new OkHttpClient(); 58 | 59 | public ImageImproveService() { 60 | super(TAG); 61 | } 62 | 63 | @Override 64 | protected void onHandleIntent(Intent intent) { 65 | Realm realm = Realm.getInstance(this); 66 | 67 | /**查询所有尺寸为0的图片的数目*/ 68 | int count = realm.where(Image.class).equalTo("width",0) 69 | .findAllSorted("position", Sort.DESCENDING).size(); 70 | Logger.d(count + " image need improve"); 71 | 72 | if (count == 0) { 73 | Logger.d("no new image, fresh fetch"); 74 | } else if (ACTION_IMPROVE_IMAGE.equals(intent.getAction())) { 75 | improveImageInfo(count,realm); 76 | } 77 | 78 | realm.close(); 79 | 80 | Logger.d("finished improve num:" + count); 81 | 82 | Intent broadcast = new Intent(ACTION_UPDATE_RESULT); 83 | broadcast.putExtra(EXTRA_CHANGE, count); 84 | broadcast.putExtra(EXTRA_ACTION, intent.getAction()); 85 | sendBroadcast(broadcast, PERMISSION_ACCESS_UPDATE_RESULT); 86 | } 87 | 88 | private void improveImageInfo(int count,Realm realm){ 89 | for(int i = 0;i < count;i++){ 90 | Image image = Image.queryFirstZeroImg(realm); 91 | if(null != image) 92 | saveToDb(realm,image); 93 | } 94 | } 95 | 96 | 97 | /** 98 | * 预解码图片并将抓到的图片尺寸保存至数据库 99 | * 100 | * @param realm Realm 实例 101 | * @param image 图片 102 | * @return 是否保存成功 103 | */ 104 | private boolean saveToDb(Realm realm, Image image) { 105 | realm.beginTransaction(); 106 | try { 107 | Point size = new Point(); 108 | loadImageForSize(image.getUrl(),size); 109 | image.setHeight(size.y); 110 | image.setWidth(size.x); 111 | realm.copyToRealmOrUpdate(image); 112 | } catch (IOException e) { 113 | Logger.d("Failed to fetch image"); 114 | realm.cancelTransaction(); 115 | return false; 116 | } 117 | realm.commitTransaction(); 118 | return true; 119 | } 120 | 121 | /*** 122 | * 加载图片内容计算图片大小 123 | * @param url 124 | * @param measured 125 | * @throws IOException 126 | */ 127 | public void loadImageForSize(String url, Point measured) throws IOException { 128 | Response response = client.newCall(new Request.Builder().url(url).build()).execute(); 129 | BitmapFactory.Options options = new BitmapFactory.Options(); 130 | options.inJustDecodeBounds = true; 131 | BitmapFactory.decodeStream(response.body().byteStream(), null, options); 132 | measured.x = options.outWidth; 133 | measured.y = options.outHeight; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/utils/PictUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.utils; 19 | 20 | import android.graphics.Bitmap; 21 | import android.os.Environment; 22 | 23 | import com.orhanobut.logger.Logger; 24 | 25 | import java.io.File; 26 | import java.io.FileOutputStream; 27 | import java.io.IOException; 28 | 29 | /** 30 | * Created by Ted on 2015/8/30. 31 | */ 32 | public class PictUtil { 33 | 34 | public static String getImageFileName(String url){ 35 | String fileName = url.substring(url.lastIndexOf('/') + 1, url.length()) + (url.endsWith(".jpg")?"":".jpg"); 36 | return fileName; 37 | } 38 | 39 | public static String getImageFilePath(String imageUrl){ 40 | String savePath = getSavePath().getAbsolutePath(); 41 | String fileName = getImageFileName(imageUrl); 42 | return savePath+File.separator+fileName; 43 | } 44 | 45 | public static void saveToFile(File file,Bitmap bmp)throws IOException{ 46 | FileOutputStream out = new FileOutputStream(file); 47 | bmp.compress(Bitmap.CompressFormat.JPEG, 100, out); 48 | out.flush(); 49 | out.close(); 50 | } 51 | 52 | public static boolean hasSDCard() { 53 | String status = Environment.getExternalStorageState(); 54 | return status.equals(Environment.MEDIA_MOUNTED); 55 | } 56 | 57 | public static String getSDCardPath() { 58 | File path = Environment.getExternalStorageDirectory(); 59 | return path.getAbsolutePath(); 60 | } 61 | 62 | public static File getSavePath() { 63 | File path; 64 | if (hasSDCard()) { // SD card 65 | path = new File(getSDCardPath() + "/GankDownload/"); 66 | path.mkdir(); 67 | } else { 68 | path = Environment.getDataDirectory(); 69 | } 70 | return path; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.android.ted.gank.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Point; 6 | import android.os.Build; 7 | import android.text.TextUtils; 8 | import android.view.Display; 9 | import android.view.WindowManager; 10 | 11 | import java.text.DateFormat; 12 | import java.text.SimpleDateFormat; 13 | import java.util.Date; 14 | 15 | /** 16 | * Created by froger_mcs on 05.11.14. 17 | */ 18 | public class Utils { 19 | private static int screenWidth = 0; 20 | private static int screenHeight = 0; 21 | 22 | public static int dpToPx(int dp) { 23 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density); 24 | } 25 | 26 | public static int getScreenHeight(Context c) { 27 | if (screenHeight == 0) { 28 | WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); 29 | Display display = wm.getDefaultDisplay(); 30 | Point size = new Point(); 31 | display.getSize(size); 32 | screenHeight = size.y; 33 | } 34 | 35 | return screenHeight; 36 | } 37 | 38 | public static int getScreenWidth(Context c) { 39 | if (screenWidth == 0) { 40 | WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); 41 | Display display = wm.getDefaultDisplay(); 42 | Point size = new Point(); 43 | display.getSize(size); 44 | screenWidth = size.x; 45 | } 46 | 47 | return screenWidth; 48 | } 49 | 50 | public static boolean isAndroid5() { 51 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 52 | } 53 | 54 | /*** 55 | * 获取默认格式的日期字符串 56 | * @param date 57 | * @return 58 | */ 59 | public static String getFormatDateStr(final Date date){ 60 | if(null == date)return null; 61 | return DateFormat.getDateInstance(DateFormat.DEFAULT).format(date); 62 | } 63 | 64 | public static Date formatDateFromStr(final String dateStr){ 65 | Date date = new Date(); 66 | if(!TextUtils.isEmpty(dateStr)){ 67 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'"); 68 | try { 69 | date = sdf.parse(dateStr); 70 | }catch (Exception e){ 71 | System.out.print("Error,format Date error"); 72 | } 73 | } 74 | return date; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/ted/gank/view/RadioImageView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 TedXiong 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 | 18 | package com.android.ted.gank.view; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.widget.ImageView; 23 | 24 | /** 25 | * 一个能保持比例的 ImageView 26 | * TODO: 暂时只支持维持宽度适应高度 27 | * 28 | * @author XiNGRZ 29 | */ 30 | public class RadioImageView extends ImageView { 31 | 32 | private int originalWidth; 33 | private int originalHeight; 34 | 35 | public RadioImageView(Context context) { 36 | super(context); 37 | } 38 | 39 | public RadioImageView(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | } 42 | 43 | public RadioImageView(Context context, AttributeSet attrs, int defStyleAttr) { 44 | super(context, attrs, defStyleAttr); 45 | } 46 | 47 | public void setOriginalSize(int originalWidth, int originalHeight) { 48 | this.originalWidth = originalWidth; 49 | this.originalHeight = originalHeight; 50 | } 51 | 52 | @Override 53 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 54 | if (originalWidth > 0 && originalHeight > 0) { 55 | float ratio = (float) originalWidth / (float) originalHeight; 56 | 57 | int width = MeasureSpec.getSize(widthMeasureSpec); 58 | int height = MeasureSpec.getSize(heightMeasureSpec); 59 | 60 | // TODO: 现在只支持固定宽度 61 | if (width > 0) { 62 | height = (int) ((float) width / ratio); 63 | } 64 | 65 | setMeasuredDimension(width, height); 66 | } else { 67 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_dashboard.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_discuss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_discuss.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_event.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_forum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_forum.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_headset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_headset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-hdpi/ic_menu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_dashboard.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_discuss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_discuss.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_event.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_forum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_forum.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_headset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_headset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-mdpi/ic_menu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/cheese_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-nodpi/cheese_1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/cheese_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-nodpi/cheese_2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/cheese_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-nodpi/cheese_3.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/cheese_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-nodpi/cheese_4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/cheese_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-nodpi/cheese_5.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_dashboard.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_discuss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_discuss.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_event.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_forum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_forum.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_headset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_headset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xhdpi/ic_menu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/github_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/github_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/github_logo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/github_logo_light.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_comment_outline_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_comment_outline_grey.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_dashboard.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_discuss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_discuss.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_event.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_forum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_forum.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_global_menu_likes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_global_menu_likes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_headset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_headset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_heart_outline_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_heart_outline_grey.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_heart_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_heart_red.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_menu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_more_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/ic_more_grey.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/item_default_img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxhdpi/item_default_img.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxxhdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/drawable-xxxhdpi/ic_menu.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/goods_title_bg.xml: -------------------------------------------------------------------------------- 1 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 25 | 31 | 32 | 41 | 42 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 71 | 72 | 76 | 77 | 81 | 82 | 87 | 88 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 104 | 108 | 109 | 114 | 115 | 119 | 120 | 121 | 122 | 123 | 124 | 130 | 131 | 135 | 136 | 141 | 142 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 163 | 164 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 25 | 26 | 27 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_viewer.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/benefit_goods_item_layout.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 24 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_base_loading_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_benifit_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_common_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 23 | 24 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_viewer.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/goods_item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 24 | 25 | 36 | 37 | 51 | 52 | 53 | 54 | 55 | 61 | 62 | 68 | 69 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_list_viewpager.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | 29 | 30 | 37 | 38 | 42 | 43 | 44 | 45 | 50 | 51 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 26 | 27 | 34 | 35 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/menu/drawer_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 24 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 41 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/menu/sample_actions.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #FFF5F5F5 20 | #673AB7 21 | #512DA8 22 | #D1C4E9 23 | #FF4081 24 | #FFFFFF 25 | #212121 26 | #FFFFFF 27 | #B6B6B6 28 | #212121 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 256dp 20 | 16dp 21 | 16dp 22 | 40dp 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Gank.Io 19 | 20 | 21 | %d like 22 | %d likes 23 | 24 | 25 | Jarlsberg lancashire edam. Dolcelatte hard cheese brie st. agur blue 26 | cheese caerphilly bavarian bergkase cheese and biscuits mascarpone. Cheeseburger swiss bavarian 27 | bergkase cream cheese fromage frais cheesy feet port-salut airedale. St. agur blue cheese rubber 28 | cheese caerphilly cheddar cheesecake cream cheese manchego lancashire. Roquefort squirty cheese 29 | the big cheese. 30 | 31 | 32 | Checkin 33 | Settings 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 27 | 28 | 30 | 31 | 32 | 33 | 37 | 38 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/xml/paths.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/ted_android.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/app/ted_android.jks -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.0.0-beta6' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | maven { url "https://jitpack.io" } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiongwei-git/GankApp/0dbcd3a73e33c680333c84e9ac92e58f4f16cf2a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------