├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── AndroidBlog.iml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── likebamboo │ │ └── osa │ │ └── android │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── fontawesome-webfont.ttf │ ├── java │ └── com │ │ └── likebamboo │ │ └── osa │ │ └── android │ │ ├── OsaApplication.java │ │ ├── cache │ │ └── BitmapLruCache.java │ │ ├── entity │ │ ├── AuthorList.java │ │ ├── BaseRsp.java │ │ ├── BlogList.java │ │ ├── CategoryList.java │ │ ├── Feedback.java │ │ ├── IssueList.java │ │ ├── LDialogItem.java │ │ └── TagList.java │ │ ├── exception │ │ ├── ErrorTrans.java │ │ └── OsaException.java │ │ ├── impl │ │ ├── BaseOnItemClickListener.java │ │ └── ImageLoaderListener.java │ │ ├── interfaces │ │ └── IOnItemClickListener.java │ │ ├── request │ │ ├── BaseRequest.java │ │ ├── JsonRequest.java │ │ ├── RequestManager.java │ │ ├── RequestParams.java │ │ └── RequestUrl.java │ │ ├── ui │ │ ├── AboutActivity.java │ │ ├── AuthorActivity.java │ │ ├── AuthorBlogActivity.java │ │ ├── BaseActivity.java │ │ ├── BlogActivity.java │ │ ├── BlogListActivity.java │ │ ├── CategoryActivity.java │ │ ├── CategoryBlogActivity.java │ │ ├── EndlessActivity.java │ │ ├── FavoriteActivity.java │ │ ├── MainActivity.java │ │ ├── NavigationActivity.java │ │ ├── SearchActivity.java │ │ ├── SearchResultActivity.java │ │ ├── TagBlogActivity.java │ │ ├── WebViewActivity.java │ │ ├── adapter │ │ │ ├── AuthorAdapter.java │ │ │ ├── BaseAdapter.java │ │ │ ├── BaseRecycleAdapter.java │ │ │ ├── BlogAdapter.java │ │ │ ├── CategoryAdapter.java │ │ │ └── ChoiceAdapter.java │ │ ├── fragments │ │ │ ├── AuthorInfoFragment.java │ │ │ ├── BlogInfoFragment.java │ │ │ ├── FeedbackFragment.java │ │ │ ├── NavigationDrawerFragment.java │ │ │ ├── SettingsFragment.java │ │ │ └── SimpleListDialog.java │ │ ├── nav │ │ │ └── ActivityNavigator.java │ │ └── view │ │ │ ├── CircleImageView.java │ │ │ ├── CommonWebView.java │ │ │ ├── FilterFooter.java │ │ │ ├── LoadingLayout.java │ │ │ ├── ObservedWebView.java │ │ │ ├── TagGroup.java │ │ │ ├── WebViewToolBar.java │ │ │ ├── blur │ │ │ ├── Blur.java │ │ │ ├── BlurBehind.java │ │ │ ├── BlurDialogFragmentHelper.java │ │ │ ├── EtsyActionBarDrawerToggle.java │ │ │ ├── OnBlurCompleteListener.java │ │ │ └── Util.java │ │ │ ├── fa │ │ │ ├── ButtonAwesome.java │ │ │ ├── DrawableAwesome.java │ │ │ └── TextAwesome.java │ │ │ ├── fab │ │ │ ├── DirectionScrollListener.java │ │ │ ├── FabToolbar.java │ │ │ ├── FloatingActionButton.java │ │ │ └── FloatingView.java │ │ │ └── fastscroll │ │ │ ├── BubbleTextGetter.java │ │ │ └── FastScroller.java │ │ └── utils │ │ ├── DateUtil.java │ │ ├── DeviceUtil.java │ │ ├── NetworkUtil.java │ │ ├── PreferencesUtil.java │ │ ├── ToastUtil.java │ │ ├── UrlDetect.java │ │ └── ValidateUtil.java │ └── res │ ├── anim │ ├── fade_in.xml │ ├── fade_out.xml │ ├── slide_down_dialog.xml │ └── slide_up_dialog.xml │ ├── drawable-hdpi │ ├── bg_card.9.png │ ├── bg_card_active.9.png │ ├── default_avatar.png │ ├── ic_add_white.png │ ├── ic_launcher.png │ └── ic_up.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── ic_launcher.png │ └── ic_up.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── drawable │ ├── bg_card_selector.xml │ ├── bg_feedback_submit_selector.xml │ ├── bg_list_item_selector.xml │ ├── bg_tag_selector.xml │ ├── bg_translucent_gradient.xml │ ├── list_divider_padded_vertical.xml │ ├── recycler_view_fast_scroller__bubble.xml │ ├── recycler_view_fast_scroller__handle.xml │ └── webview_progress_bar.xml │ ├── layout │ ├── activity_about.xml │ ├── activity_author.xml │ ├── activity_blog.xml │ ├── activity_blog_list.xml │ ├── activity_category.xml │ ├── activity_navigation.xml │ ├── activity_search.xml │ ├── activity_webview.xml │ ├── common_webview.xml │ ├── fab_tool_bar.xml │ ├── footer_loading_layout.xml │ ├── fragment_author_info.xml │ ├── fragment_blog_info.xml │ ├── fragment_feedback.xml │ ├── fragment_navigation_drawer.xml │ ├── fragment_settings.xml │ ├── item_author.xml │ ├── item_blog.xml │ ├── item_category.xml │ ├── item_nav_menu.xml │ ├── item_simple_list_dialog.xml │ ├── list_filter_footer.xml │ ├── recycler_view_fast_scroller.xml │ ├── simple_list_dialog.xml │ ├── simple_text.xml │ ├── space_loading_layout.xml │ └── webview_toolbar.xml │ ├── menu │ ├── edit.xml │ ├── global.xml │ └── main.xml │ └── values │ ├── arrays.xml │ ├── attrs.xml │ ├── attrs_fab.xml │ ├── colors.xml │ ├── dimens.xml │ ├── font_awesome.xml │ ├── ids.xml │ ├── integers.xml │ ├── strings.xml │ └── styles.xml ├── art ├── author.png ├── blog.png ├── home.png ├── info.png ├── menu.png └── menu2.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── osa-android.iml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | .idea 30 | /local.properties 31 | /.idea/workspace.xml 32 | /.idea/libraries 33 | .DS_Store 34 | /captures 35 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | AndroidBlog -------------------------------------------------------------------------------- /.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 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Android 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AndroidBlog.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Android博客客户端 2 | --- 3 | 这是一个简单的android博客阅读应用,遵循 Android 设计风格,UI借鉴我个人非常喜欢的一款应用[Etsy](https://www.etsy.com/mobile/?ref=ftr). 4 | 5 | ##构建与支持 6 | 项目使用 Android Studio (v1.2) + Gradle (v2.2.1) 构建 7 | 该项目支持 Android 2.3+ ( api level 9) 8 | 9 | ## 效果图 10 | image1 image2 11 | image3image4 12 | image5 13 | 14 | ##使用到的开源项目 15 | [AndroidStaggeredGrid](https://github.com/etsy/AndroidStaggeredGrid) Esty 开源的瀑布流布局库 16 | [Sugar](https://github.com/satyan/sugar) 一个简单的ORM框架 17 | [Volley](https://android.googlesource.com/platform/frameworks/volley) Google官方发布的网络请求库 18 | [ButterKnife](http://jakewharton.github.io/butterknife/) 一个专注于Android系统的View注入框架 19 | [NineOldAndroid](http://nineoldandroids.com/) 动画支持库 20 | [Jackson](https://github.com/FasterXML/jackson) json解析库 21 | ... 22 | 23 | ## 意见与建议 24 | 欢迎通过Pull Requests 方式向本项目贡献代码 25 | 如果发现程序的任何BUG,或对本项目有任何意见,可以直接创建 [Issue](https://github.com/likebamboo/AndroidBlog/issues) 告知我。 26 | 27 | ## TODO 28 | - [x] 博客收藏(本地)(完成时间2015-06-13). 29 | - [x] 博客排序(完成时间2015-06-16). 30 | - [x] 下拉刷新(完成时间2015-06-22). 31 | - [x] 前端网站(完成时间2015-06-26). 32 | - [x] 博客错误反馈(完成时间2015-07-16,1.0.0正式版发布,此处应该有掌声~). 33 | - [ ] 博客图片大图浏览与保存. 34 | - [ ] 程序优化. 35 | 36 | ## 关于 37 | 本项目的前端和后端均由我一人开发,后端代码暂不开源,主要是因为本人不太会写后端,后端代码太烂了(其实前端也不怎么样)。 38 | 本项目的Apk安装包暂时不会发布到任何应用市场,请直接在[release](https://github.com/likebamboo/AndroidBlog/releases)下载。 39 | 如果本项目收集到的博客侵犯到了您的著作权,请邮件([likebamboo@163.com](mailto:likebamboo@163.com))告知我,我将在第一时间将相应的博文删除。 40 | 41 | ## 前端网站已经上线啦(2015-06-26) 42 | [http://likebamboo.com/AndroidBlog/](http://likebamboo.com/AndroidBlog/) 43 | 使用 [hexo](http://hexo.io) 开发,对移动设备友好哟. 44 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | gradle.properties 3 | osa.key.jks -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion '19.1.0' 6 | 7 | defaultConfig { 8 | applicationId 'com.likebamboo.osa.android' 9 | minSdkVersion 9 10 | targetSdkVersion 22 11 | versionCode 11 12 | versionName '1.0.1' 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | packagingOptions { 21 | exclude 'META-INF/NOTICE' 22 | exclude 'META-INF/LICENSE' 23 | } 24 | 25 | lintOptions { 26 | abortOnError false 27 | } 28 | 29 | signingConfigs { 30 | release { 31 | try { 32 | storeFile file("osa.key.jks") 33 | storePassword KEYSTORE_PASSWORD 34 | keyAlias "androidblog" 35 | keyPassword KEY_PASSWORD 36 | } 37 | catch (ex) { 38 | throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.") 39 | } 40 | } 41 | } 42 | 43 | buildTypes{ 44 | release { 45 | signingConfig signingConfigs.release 46 | } 47 | } 48 | 49 | } 50 | 51 | dependencies { 52 | compile fileTree(include: ['*.jar'], dir: 'libs') 53 | compile 'com.android.support:appcompat-v7:22.+' 54 | // recyclerview 55 | compile 'com.android.support:recyclerview-v7:22.+' 56 | // volley 网络请求库 57 | compile 'com.mcxiaoke.volley:library:1.0.+@aar' 58 | // jackson,json解析包 https://github.com/FasterXML/jackson 59 | compile 'com.fasterxml.jackson.core:jackson-databind:2.3.1' 60 | // etsy 瀑布流 https://github.com/etsy/AndroidStaggeredGrid 61 | compile 'com.etsy.android.grid:library:1.0.5' 62 | // 依赖注入框架 https://github.com/JakeWharton/butterknife 63 | compile 'com.jakewharton:butterknife:6.1.0' 64 | // orm 框架 65 | compile 'com.github.satyan:sugar:1.3.1' 66 | // nineold 动画支持库 67 | compile 'com.nineoldandroids:library:2.4.0' 68 | // 69 | compile 'com.github.ozodrukh:CircularReveal:1.0.6@aar' 70 | } 71 | -------------------------------------------------------------------------------- /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 E:\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 | 19 | -keep class butterknife.** { *; } 20 | -dontwarn butterknife.internal.** 21 | -keep class **$$ViewInjector { *; } 22 | 23 | -keepclasseswithmembernames class * { 24 | @butterknife.* ; 25 | } 26 | 27 | -keepclasseswithmembernames class * { 28 | @butterknife.* ; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/likebamboo/osa/android/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 24 | 27 | 30 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 54 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/assets/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/assets/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/OsaApplication.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android; 2 | 3 | import com.likebamboo.osa.android.request.RequestManager; 4 | import com.orm.SugarApp; 5 | 6 | /** 7 | * Created by likebamboo on 2015/5/10. 8 | */ 9 | public class OsaApplication extends SugarApp { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | init(); 15 | } 16 | 17 | 18 | /** 19 | * 初始化 20 | */ 21 | private void init() { 22 | // 初始化volley请求管理 23 | RequestManager.init(this); 24 | } 25 | 26 | @Override 27 | public void onLowMemory() { 28 | super.onLowMemory(); 29 | } 30 | 31 | 32 | @Override 33 | public void onTerminate() { 34 | super.onTerminate(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/cache/BitmapLruCache.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.cache; 2 | 3 | import android.graphics.Bitmap; 4 | import android.support.v4.util.LruCache; 5 | import android.util.Log; 6 | 7 | import com.android.volley.toolbox.ImageLoader; 8 | 9 | /** 10 | * Basic LRU Memory cache. 11 | * 12 | * @author Trey Robinson 13 | */ 14 | public class BitmapLruCache extends LruCache implements ImageLoader.ImageCache { 15 | 16 | private final String TAG = this.getClass().getSimpleName(); 17 | 18 | public BitmapLruCache(int maxSize) { 19 | super(maxSize); 20 | } 21 | 22 | @Override 23 | protected int sizeOf(String key, Bitmap value) { 24 | return value.getRowBytes() * value.getHeight(); 25 | } 26 | 27 | @Override 28 | public Bitmap getBitmap(String url) { 29 | Log.v(TAG, "Retrieved item from Mem Cache"); 30 | return get(url); 31 | } 32 | 33 | @Override 34 | public void putBitmap(String url, Bitmap bitmap) { 35 | Log.v(TAG, "Added item to Mem Cache"); 36 | put(url, bitmap); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/BaseRsp.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | 5 | /** 6 | * Created by wentaoli on 2015/5/12. 7 | */ 8 | @JsonIgnoreProperties(ignoreUnknown = true) 9 | public class BaseRsp { 10 | /** 11 | * 返回信息 12 | */ 13 | private String message = ""; 14 | 15 | /** 16 | * 错误码 17 | */ 18 | private int errorCode = -1; 19 | 20 | @Override 21 | public String toString() { 22 | return "BaseRsp{" + 23 | "errorCode=" + errorCode + 24 | ", message='" + message + '\'' + 25 | '}'; 26 | } 27 | 28 | public int getErrorCode() { 29 | return errorCode; 30 | } 31 | 32 | public void setErrorCode(int errorCode) { 33 | this.errorCode = errorCode; 34 | } 35 | 36 | public String getMessage() { 37 | return message; 38 | } 39 | 40 | public void setMessage(String message) { 41 | this.message = message; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/CategoryList.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | 9 | import java.util.ArrayList; 10 | 11 | /** 12 | * 类别 13 | * Created by likebamboo 2015/05/14 14 | * 15 | * @author likebamboo 16 | */ 17 | public class CategoryList extends BaseRsp { 18 | 19 | @JsonProperty("result") 20 | private ArrayList mList = null; 21 | 22 | public ArrayList getList() { 23 | return mList; 24 | } 25 | 26 | public void setList(ArrayList mList) { 27 | this.mList = mList; 28 | } 29 | 30 | // 忽略未知属性 31 | @JsonIgnoreProperties(ignoreUnknown = true) 32 | public static class Category implements Parcelable { 33 | /** 34 | * 类别id 35 | */ 36 | private int id = 0; 37 | 38 | /** 39 | * 类别名称 40 | */ 41 | private String name = ""; 42 | 43 | /** 44 | * 类别描述 45 | */ 46 | private String description = ""; 47 | 48 | /** 49 | * 添加时间 50 | */ 51 | @JsonProperty("add_time") 52 | private String addTime = ""; 53 | 54 | /** 55 | * 封面图 56 | */ 57 | private String cover = ""; 58 | 59 | public int getId() { 60 | return id; 61 | } 62 | 63 | public void setId(int id) { 64 | this.id = id; 65 | } 66 | 67 | public String getAddTime() { 68 | return addTime; 69 | } 70 | 71 | public void setAddTime(String addTime) { 72 | this.addTime = addTime; 73 | } 74 | 75 | public String getName() { 76 | return name; 77 | } 78 | 79 | public void setName(String name) { 80 | this.name = name; 81 | } 82 | 83 | public String getDescription() { 84 | return description; 85 | } 86 | 87 | public void setDescription(String description) { 88 | this.description = description; 89 | } 90 | 91 | public String getCover() { 92 | return cover; 93 | } 94 | 95 | public void setCover(String cover) { 96 | this.cover = cover; 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "CategoryList [name=" + name + "]"; 102 | } 103 | 104 | @Override 105 | public int describeContents() { 106 | return 0; 107 | } 108 | 109 | @Override 110 | public void writeToParcel(Parcel dest, int flags) { 111 | dest.writeString(this.name); 112 | dest.writeString(this.description); 113 | dest.writeString(this.addTime); 114 | dest.writeString(this.cover); 115 | } 116 | 117 | public Category() { 118 | } 119 | 120 | private Category(Parcel in) { 121 | this.name = in.readString(); 122 | this.description = in.readString(); 123 | this.addTime = in.readString(); 124 | this.cover = in.readString(); 125 | } 126 | 127 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 128 | public Category createFromParcel(Parcel source) { 129 | return new Category(source); 130 | } 131 | 132 | public Category[] newArray(int size) { 133 | return new Category[size]; 134 | } 135 | }; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/Feedback.java: -------------------------------------------------------------------------------- 1 | 2 | package com.likebamboo.osa.android.entity; 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * 问题表 11 | * 12 | * @author wentaoli 13 | */ 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class Feedback { 16 | /** 17 | * 联系方式 18 | */ 19 | private String contact = ""; 20 | 21 | /** 22 | * 描述 23 | */ 24 | private String description = ""; 25 | 26 | /** 27 | * 添加时间 28 | */ 29 | @JsonProperty("add_time") 30 | private String addTime = ""; 31 | 32 | /** 33 | * 反馈的问题列表 34 | */ 35 | @JsonProperty("issue") 36 | private ArrayList issues = null; 37 | 38 | @JsonProperty("blogId") 39 | private Long blogId = 0L; 40 | 41 | public String getAddTime() { 42 | return addTime; 43 | } 44 | 45 | public void setAddTime(String addTime) { 46 | this.addTime = addTime; 47 | } 48 | 49 | public String getContact() { 50 | return contact; 51 | } 52 | 53 | public void setContact(String contact) { 54 | this.contact = contact; 55 | } 56 | 57 | public String getDescription() { 58 | return description; 59 | } 60 | 61 | public void setDescription(String description) { 62 | this.description = description; 63 | } 64 | 65 | public ArrayList getIssues() { 66 | return issues; 67 | } 68 | 69 | public void setIssues(ArrayList issuesArr) { 70 | this.issues = issuesArr; 71 | } 72 | 73 | public Long getBlogId() { 74 | return blogId; 75 | } 76 | 77 | public void setBlogId(Long blogId) { 78 | this.blogId = blogId; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "Feedback [contact=" + contact + ", description=" + description + ", addTime=" + addTime + ", issues=" + issues + "]"; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/IssueList.java: -------------------------------------------------------------------------------- 1 | 2 | package com.likebamboo.osa.android.entity; 3 | 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * Created by wentaoli on 2015/7/16. 14 | */ 15 | public class IssueList extends BaseRsp { 16 | 17 | @JsonProperty("result") 18 | private ArrayList mList = null; 19 | 20 | public ArrayList getList() { 21 | return mList; 22 | } 23 | 24 | public void setList(ArrayList list) { 25 | this.mList = list; 26 | } 27 | 28 | /** 29 | * 问题表 30 | * 31 | * @author likebamboo 32 | */ 33 | @JsonIgnoreProperties(ignoreUnknown = true) 34 | public static class Issue implements Parcelable { 35 | 36 | /** 37 | * 名称 38 | */ 39 | private String name = ""; 40 | 41 | /** 42 | * 描述 43 | */ 44 | private String description = ""; 45 | 46 | /** 47 | * 添加时间 48 | */ 49 | private String addTime = ""; 50 | 51 | public String getAddTime() { 52 | return addTime; 53 | } 54 | 55 | public void setAddTime(String addTime) { 56 | this.addTime = addTime; 57 | } 58 | 59 | public String getName() { 60 | return name; 61 | } 62 | 63 | public void setName(String name) { 64 | this.name = name; 65 | } 66 | 67 | public String getDescription() { 68 | return description; 69 | } 70 | 71 | public void setDescription(String description) { 72 | this.description = description; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "issue [name=" + name + "]"; 78 | } 79 | 80 | @Override 81 | public int describeContents() { 82 | return 0; 83 | } 84 | 85 | @Override 86 | public void writeToParcel(Parcel dest, int flags) { 87 | dest.writeString(this.name); 88 | dest.writeString(this.description); 89 | dest.writeString(this.addTime); 90 | } 91 | 92 | public Issue() { 93 | } 94 | 95 | private Issue(Parcel in) { 96 | this.name = in.readString(); 97 | this.description = in.readString(); 98 | this.addTime = in.readString(); 99 | } 100 | 101 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 102 | public Issue createFromParcel(Parcel source) { 103 | return new Issue(source); 104 | } 105 | 106 | public Issue[] newArray(int size) { 107 | return new Issue[size]; 108 | } 109 | }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/LDialogItem.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * {@link com.likebamboo.osa.android.ui.fragments.SimpleListDialog} Item项 8 | * 9 | * @author likebamboo 10 | * @version 2015-06-15 11 | * @see [相关类/方法] 12 | * @since [产品/模块版本] 13 | */ 14 | public class LDialogItem implements Parcelable { 15 | 16 | private boolean selected = false; 17 | 18 | private String name = ""; 19 | 20 | private String value = ""; 21 | 22 | public LDialogItem() { 23 | } 24 | 25 | public LDialogItem(boolean selected, String name) { 26 | this.selected = selected; 27 | this.name = name; 28 | } 29 | 30 | public String getValue() { 31 | return value; 32 | } 33 | 34 | public void setValue(String value) { 35 | this.value = value; 36 | } 37 | 38 | public boolean isSelected() { 39 | return selected; 40 | } 41 | 42 | public void setSelected(boolean selected) { 43 | this.selected = selected; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "LDialogItem [selected=" + selected + ", name=" + name + "]"; 57 | } 58 | 59 | @Override 60 | public int describeContents() { 61 | return 0; 62 | } 63 | 64 | @Override 65 | public void writeToParcel(Parcel dest, int flags) { 66 | dest.writeByte(selected ? (byte) 1 : (byte) 0); 67 | dest.writeString(this.name); 68 | dest.writeString(this.value); 69 | } 70 | 71 | private LDialogItem(Parcel in) { 72 | this.selected = in.readByte() != 0; 73 | this.name = in.readString(); 74 | this.value = in.readString(); 75 | } 76 | 77 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 78 | public LDialogItem createFromParcel(Parcel source) { 79 | return new LDialogItem(source); 80 | } 81 | 82 | public LDialogItem[] newArray(int size) { 83 | return new LDialogItem[size]; 84 | } 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/entity/TagList.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.text.TextUtils; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import com.orm.SugarRecord; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by likebamboo on 2015/5/19. 16 | */ 17 | public class TagList extends BaseRsp { 18 | 19 | @JsonProperty("result") 20 | private ArrayList mList = null; 21 | 22 | public ArrayList getList() { 23 | return mList; 24 | } 25 | 26 | public void setList(ArrayList list) { 27 | this.mList = list; 28 | } 29 | 30 | /** 31 | * 标签 32 | * 33 | * @author likebamboo 34 | */ 35 | // 忽略未知属性 36 | @JsonIgnoreProperties(ignoreUnknown = true) 37 | public static class Tag extends SugarRecord implements Parcelable { 38 | 39 | /** 40 | * id 41 | */ 42 | @JsonProperty("id") 43 | private long _id = 0L; 44 | 45 | /** 46 | * 标题 47 | */ 48 | private String name = ""; 49 | 50 | /** 51 | * 摘要 52 | */ 53 | private String desc = ""; 54 | 55 | /** 56 | * 添加时间 57 | */ 58 | @JsonIgnoreProperties 59 | private long addTime = 0; 60 | 61 | public String getDesc() { 62 | return desc; 63 | } 64 | 65 | public void setDesc(String desc) { 66 | this.desc = desc; 67 | } 68 | 69 | public String getName() { 70 | return name; 71 | } 72 | 73 | public void setName(String name) { 74 | this.name = name; 75 | } 76 | 77 | public long getTagId() { 78 | return _id; 79 | } 80 | 81 | public void setTagId(long id) { 82 | this._id = id; 83 | } 84 | 85 | public long getAddTime() { 86 | return addTime; 87 | } 88 | 89 | public void setAddTime(long addTime) { 90 | this.addTime = addTime; 91 | } 92 | 93 | /** 94 | * 删除标签 95 | * 96 | * @param name 97 | */ 98 | public static void delete(String name) { 99 | if (TextUtils.isEmpty(name)) { 100 | return; 101 | } 102 | deleteAll(Tag.class, " name like ? ", name); 103 | } 104 | 105 | /** 106 | * 查找标签 107 | * 108 | * @param name 109 | */ 110 | public static Tag findTagByName(String name) { 111 | if (TextUtils.isEmpty(name)) { 112 | return null; 113 | } 114 | List tags = find(Tag.class, " name like ? ", name); 115 | if (tags == null || tags.isEmpty()) { 116 | return null; 117 | } 118 | for (Tag t : tags) { 119 | if (t != null && name.equals(t.getName())) { 120 | return t; 121 | } 122 | } 123 | return null; 124 | } 125 | 126 | @Override 127 | public int describeContents() { 128 | return 0; 129 | } 130 | 131 | @Override 132 | public void writeToParcel(Parcel dest, int flags) { 133 | dest.writeLong(this.id); 134 | dest.writeString(this.name); 135 | dest.writeString(this.desc); 136 | dest.writeLong(this.addTime); 137 | } 138 | 139 | public Tag() { 140 | } 141 | 142 | private Tag(Parcel in) { 143 | this.id = in.readLong(); 144 | this.name = in.readString(); 145 | this.desc = in.readString(); 146 | this.addTime = in.readLong(); 147 | } 148 | 149 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 150 | public Tag createFromParcel(Parcel source) { 151 | return new Tag(source); 152 | } 153 | 154 | public Tag[] newArray(int size) { 155 | return new Tag[size]; 156 | } 157 | }; 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/exception/ErrorTrans.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.exception; 2 | 3 | import com.android.volley.AuthFailureError; 4 | import com.android.volley.NetworkError; 5 | import com.android.volley.NoConnectionError; 6 | import com.android.volley.ParseError; 7 | import com.android.volley.ServerError; 8 | import com.android.volley.TimeoutError; 9 | 10 | 11 | /** 12 | * Created by wentaoli on 2015/5/14. 13 | */ 14 | public class ErrorTrans { 15 | 16 | public static OsaException transToOsaException(Throwable a) { 17 | if (a == null) { 18 | return new OsaException("未知错误"); 19 | } 20 | if (a instanceof ParseError) { 21 | return new OsaException("数据解析错误", a); 22 | } 23 | if (a instanceof TimeoutError) { 24 | return new OsaException("请求超时", a); 25 | } 26 | if (a instanceof ServerError) { 27 | return new OsaException("服务器错误", a); 28 | } 29 | if (a instanceof AuthFailureError) { 30 | return new OsaException("请求认证错误", a); 31 | } 32 | if (a instanceof NoConnectionError) { 33 | return new OsaException("网络未连接,请检查网络状态", a); 34 | } 35 | if (a instanceof NetworkError) { 36 | return new OsaException("网络连接异常", a); 37 | } 38 | return new OsaException("未知错误"); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/exception/OsaException.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.exception; 2 | 3 | /** 4 | * Created by wentaoli on 2015/5/14. 5 | */ 6 | public class OsaException extends Exception { 7 | public OsaException() { 8 | } 9 | 10 | public OsaException(String detailMessage) { 11 | super(detailMessage); 12 | } 13 | 14 | public OsaException(String detailMessage, Throwable throwable) { 15 | super(detailMessage, throwable); 16 | } 17 | 18 | public OsaException(Throwable throwable) { 19 | super(throwable); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/impl/BaseOnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.impl; 2 | 3 | import com.likebamboo.osa.android.interfaces.IOnItemClickListener; 4 | 5 | /** 6 | * Created by likebamboo on 2015/6/11. 7 | */ 8 | public class BaseOnItemClickListener implements IOnItemClickListener { 9 | 10 | @Override 11 | public void onItemClick(int postion, T item) { 12 | // nothing 13 | } 14 | 15 | @Override 16 | public void onItemLongClick(int postion, T item) { 17 | // nothing 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/impl/ImageLoaderListener.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.impl; 2 | 3 | import android.widget.ImageView; 4 | 5 | import com.android.volley.VolleyError; 6 | import com.android.volley.toolbox.ImageLoader; 7 | 8 | /** 9 | * volley 图片下载回调 10 | * Created by wentaoli on 2015/6/4. 11 | */ 12 | public class ImageLoaderListener implements ImageLoader.ImageListener { 13 | 14 | private ImageView imageView = null; 15 | 16 | private int defaultResId = 0; 17 | 18 | public ImageLoaderListener(final ImageView imageView, final String imageUrl, final int defaultResId) { 19 | this.defaultResId = defaultResId; 20 | this.imageView = imageView; 21 | if (imageView != null) { 22 | imageView.setTag(imageUrl); 23 | imageView.setImageResource(defaultResId); 24 | } 25 | } 26 | 27 | @Override 28 | public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) { 29 | if (imageContainer == null || imageContainer.getBitmap() == null) { 30 | return; 31 | } 32 | if (("" + imageView.getTag()).equals(imageContainer.getRequestUrl())) { 33 | imageView.setImageBitmap(imageContainer.getBitmap()); 34 | } 35 | } 36 | 37 | @Override 38 | public void onErrorResponse(VolleyError volleyError) { 39 | imageView.setImageResource(defaultResId); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/interfaces/IOnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.interfaces; 2 | 3 | /** 4 | * Created by wentaoli on 2015/5/14. 5 | */ 6 | public interface IOnItemClickListener { 7 | 8 | /** 9 | * listView 点击事件回调 10 | * 11 | * @param position 12 | * @param item 13 | */ 14 | void onItemClick(int position, T item); 15 | 16 | /** 17 | * listView 长按事件回调 18 | * 19 | * @param position 20 | * @param item 21 | */ 22 | void onItemLongClick(int position, T item); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/request/BaseRequest.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.request; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.android.volley.Request; 6 | import com.android.volley.Response; 7 | 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | 11 | import java.util.Map; 12 | 13 | /** 14 | * Created by likebamboo on 2015/5/12. 15 | */ 16 | public abstract class BaseRequest extends Request { 17 | 18 | public BaseRequest(int method, String url, Response.ErrorListener listener) { 19 | super(method, url, listener); 20 | } 21 | 22 | /** 23 | * 返回json数据中的result字段 24 | * 25 | * @param data 26 | * @return 27 | */ 28 | protected String getResult(String data) { 29 | if (TextUtils.isEmpty(data)) { 30 | return null; 31 | } 32 | JSONObject json = null; 33 | try { 34 | json = new JSONObject(data); 35 | return json.getString("result"); 36 | } catch (JSONException e) { 37 | e.printStackTrace(); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | return data; 42 | } 43 | 44 | /** 45 | * 拼接URL(仅针对get方法) 46 | * 47 | * @param method 48 | * @param baseUrl 49 | * @param params 50 | * @return 51 | */ 52 | public static String formatUrl(int method, String baseUrl, Map params) { 53 | if (method != Method.GET) { 54 | return baseUrl; 55 | } 56 | return formatUrl(baseUrl, params); 57 | } 58 | 59 | /** 60 | * 拼接URL(仅针对get方法) 61 | * 62 | * @param baseUrl 63 | * @param params 64 | * @return 65 | */ 66 | public static String formatUrl(String baseUrl, Map params) { 67 | if (TextUtils.isEmpty(baseUrl) || params == null || params.isEmpty()) { 68 | return baseUrl; 69 | } 70 | StringBuilder sb = new StringBuilder(baseUrl); 71 | if (sb.toString().contains("?")) { 72 | // 不以问号结尾 73 | if (!sb.toString().endsWith("?")) { 74 | sb.append("&"); 75 | } 76 | } else {// 没有问号,添加问号 77 | sb.append("?"); 78 | } 79 | String split = ""; 80 | for (String key : params.keySet()) { 81 | sb.append(split).append(key).append("=").append(params.get(key)); 82 | split = "&"; 83 | } 84 | 85 | return sb.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/request/JsonRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Storm Zhang, Feb 11, 2014. 3 | */ 4 | 5 | package com.likebamboo.osa.android.request; 6 | 7 | import android.text.TextUtils; 8 | 9 | import com.android.volley.AuthFailureError; 10 | import com.android.volley.NetworkResponse; 11 | import com.android.volley.ParseError; 12 | import com.android.volley.Response; 13 | import com.android.volley.Response.ErrorListener; 14 | import com.android.volley.Response.Listener; 15 | import com.android.volley.VolleyError; 16 | import com.android.volley.toolbox.HttpHeaderParser; 17 | import com.fasterxml.jackson.core.JsonParseException; 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | 20 | import java.io.IOException; 21 | import java.io.UnsupportedEncodingException; 22 | import java.util.Map; 23 | 24 | /** 25 | * JSON请求 26 | * 27 | * @param 28 | */ 29 | public class JsonRequest extends BaseRequest { 30 | protected final ObjectMapper mapper = new ObjectMapper(); 31 | protected final Class mClazz; 32 | private final Listener mListener; 33 | protected final Map mParams; 34 | 35 | /** 36 | * 是否仅仅解析返回数据中 result 字段的数据 37 | */ 38 | private boolean justResult = false; 39 | 40 | public JsonRequest(String url, Class clazz, Listener listener, ErrorListener errorListener) { 41 | this(url, clazz, null, listener, errorListener); 42 | } 43 | 44 | public JsonRequest(String url, Class clazz, Map params, Listener listener, ErrorListener errorListener) { 45 | super(Method.GET, formatUrl(url, params), errorListener); 46 | this.mClazz = clazz; 47 | this.mParams = params; 48 | this.mListener = listener; 49 | } 50 | 51 | public JsonRequest(int method, String url, Class clazz, Map params, Listener listener, ErrorListener errorListener) { 52 | super(method, formatUrl(method, url, params), errorListener); 53 | this.mClazz = clazz; 54 | this.mParams = params; 55 | this.mListener = listener; 56 | } 57 | 58 | @Override 59 | public Map getParams() throws AuthFailureError { 60 | return mParams != null ? mParams : super.getParams(); 61 | } 62 | 63 | public boolean isJustResult() { 64 | return justResult; 65 | } 66 | 67 | public void setJustResult(boolean justResult) { 68 | this.justResult = justResult; 69 | } 70 | 71 | @Override 72 | protected void deliverResponse(T response) { 73 | if (mListener == null) { 74 | return; 75 | } 76 | mListener.onResponse(response); 77 | } 78 | 79 | @Override 80 | protected Response parseNetworkResponse(NetworkResponse response) { 81 | try { 82 | String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 83 | if (TextUtils.isEmpty(json)) { 84 | return Response.error(new VolleyError("返回数据为空")); 85 | } 86 | if (justResult) { 87 | json = getResult(json); 88 | } 89 | return Response.success(mapper.readValue(json, mClazz), HttpHeaderParser.parseCacheHeaders(response)); 90 | } catch (UnsupportedEncodingException e) { 91 | e.printStackTrace(); 92 | return Response.error(new ParseError(e)); 93 | } catch (JsonParseException e) { 94 | e.printStackTrace(); 95 | return Response.error(new ParseError(e)); 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | return Response.error(new ParseError(e)); 99 | } catch (Exception e) { 100 | e.printStackTrace(); 101 | return Response.error(new ParseError(e)); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/request/RequestManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Storm Zhang, Feb 11, 2014. 3 | */ 4 | 5 | package com.likebamboo.osa.android.request; 6 | 7 | import android.app.ActivityManager; 8 | import android.content.Context; 9 | 10 | import com.android.volley.Request; 11 | import com.android.volley.RequestQueue; 12 | import com.android.volley.toolbox.ImageLoader; 13 | import com.android.volley.toolbox.Volley; 14 | import com.likebamboo.osa.android.cache.BitmapLruCache; 15 | 16 | /** 17 | * volley请求管理 18 | */ 19 | public class RequestManager { 20 | private static RequestQueue mRequestQueue; 21 | private static ImageLoader mImageLoader; 22 | 23 | private RequestManager() { 24 | // no instances 25 | } 26 | 27 | /** 28 | * 初始化 29 | * 30 | * @param context 31 | */ 32 | public static void init(Context context) { 33 | mRequestQueue = Volley.newRequestQueue(context); 34 | int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); 35 | // Use 1/8th of the available memory for this memory cache. 36 | int cacheSize = 1024 * 1024 * memClass / 8; 37 | mImageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache(cacheSize)); 38 | } 39 | 40 | /** 41 | * 获取请求队列 42 | * 43 | * @return 44 | */ 45 | public static RequestQueue getRequestQueue() { 46 | if (mRequestQueue != null) { 47 | return mRequestQueue; 48 | } else { 49 | throw new IllegalStateException("RequestQueue not initialized"); 50 | } 51 | } 52 | 53 | /** 54 | * 添加请求 55 | * 56 | * @param request 57 | * @param tag 58 | */ 59 | public static void addRequest(Request request, Object... tag) { 60 | if (tag != null && tag.length > 0) { 61 | request.setTag(tag[0]); 62 | } 63 | mRequestQueue.add(request); 64 | } 65 | 66 | /** 67 | * 取消请求 68 | * 69 | * @param tag 70 | */ 71 | public static void cancelAll(Object tag) { 72 | mRequestQueue.cancelAll(tag); 73 | } 74 | 75 | /** 76 | * Returns instance of ImageLoader initialized with {@see FakeImageCache} 77 | * which effectively means that no memory caching is used. This is useful 78 | * for images that you know that will be show only once. 79 | * 80 | * @return 81 | */ 82 | public static ImageLoader getImageLoader() { 83 | if (mImageLoader != null) { 84 | return mImageLoader; 85 | } else { 86 | throw new IllegalStateException("ImageLoader not initialized"); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/request/RequestParams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Storm Zhang, Feb 13, 2014. 3 | */ 4 | 5 | package com.likebamboo.osa.android.request; 6 | 7 | import java.util.HashMap; 8 | 9 | public class RequestParams extends HashMap { 10 | private static final long serialVersionUID = 8112047472727256876L; 11 | 12 | public RequestParams add(String key, String value) { 13 | put(key, value); 14 | return this; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/request/RequestUrl.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.request; 2 | 3 | /** 4 | * Created by wentaoli on 2015/5/12. 5 | */ 6 | public class RequestUrl { 7 | 8 | public static final boolean isTest = false; 9 | 10 | /** 11 | * BaseUrl 12 | */ 13 | public static final String BASE_URL = isTest ? "http://192.168.45.38:8080/osa-mobile/" : "http://120.24.93.248/"; 14 | 15 | /** 16 | * BLOG,博客 17 | */ 18 | public static final String BLOG_URL = BASE_URL + "blog"; 19 | 20 | /** 21 | * BLOG搜索,博客搜索 22 | */ 23 | public static final String BLOG_SEARCH_URL = BLOG_URL + "/s"; 24 | 25 | /** 26 | * category,分类 27 | */ 28 | public static final String CATEGORY_URL = BASE_URL + "category"; 29 | 30 | /** 31 | * autor,作者 32 | */ 33 | public static final String AUTHOR_URL = BASE_URL + "author"; 34 | 35 | /** 36 | * 分类 BLOG, 参数: 类别id 37 | */ 38 | public static final String CATEGORY_BLOG_URL = BLOG_URL + "/category/%s"; 39 | 40 | /** 41 | * 作者的 BLOG, 参数:作者名称 (why名称,不是id? 具体看代码) 42 | */ 43 | public static final String AUTHOR_BLOG_URL = BLOG_URL + "/author/%s/"; 44 | 45 | /** 46 | * BLOG detail展示URL 47 | */ 48 | public static final String BLOG_VIEW_URL = BLOG_URL + "/%s"; 49 | 50 | /** 51 | * BLOG detail info 52 | */ 53 | public static final String BLOG_INFO_URL = BLOG_URL + "/info/%s"; 54 | 55 | /** 56 | * 标签 BLOG, 参数: 标签名称 57 | */ 58 | public static final String TAG_BLOG_URL = BLOG_URL + "/tag/%s/"; 59 | 60 | /** 61 | * issues 62 | */ 63 | public static final String ISSUES_LIST_URL = BASE_URL + "issue/"; 64 | 65 | /** 66 | * feedback.save 67 | */ 68 | public static final String FEEDBACK_SAVE_URL = BASE_URL + "feedback/save?random=%s"; 69 | 70 | /** 71 | * 关于我 72 | */ 73 | public static final String ABOUT_ME_URL = "http://likebamboo.com/about.html"; 74 | 75 | /** 76 | * issues 77 | */ 78 | public static final String ISSUES_URL = "https://github.com/likebamboo/AndroidBlog/issues"; 79 | 80 | /** 81 | * 关于app 82 | */ 83 | public static final String ABOUT_APP_URL = "https://github.com/likebamboo/AndroidBlog"; 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.ActionBar; 5 | import android.text.Html; 6 | import android.text.method.LinkMovementMethod; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.likebamboo.osa.android.R; 11 | import com.likebamboo.osa.android.request.RequestUrl; 12 | import com.likebamboo.osa.android.ui.nav.ActivityNavigator; 13 | import com.likebamboo.osa.android.utils.DeviceUtil; 14 | 15 | import butterknife.ButterKnife; 16 | import butterknife.InjectView; 17 | 18 | /** 19 | * 关于界面 20 | * 21 | * @author likebamboo 22 | */ 23 | public class AboutActivity extends BaseActivity { 24 | 25 | @InjectView(R.id.app_version_tv) 26 | TextView mVersionTv; 27 | 28 | @InjectView(R.id.app_desc_tv) 29 | TextView mDescTv; 30 | 31 | @InjectView(R.id.app_detail_tv) 32 | TextView mDetailTv; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_about); 38 | ButterKnife.inject(this); 39 | 40 | ActionBar actionBar = getSupportActionBar(); 41 | actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE); 42 | actionBar.setHomeAsUpIndicator(R.drawable.ic_up); 43 | 44 | mVersionTv.setText(getString(R.string.version, DeviceUtil.getVersionName(this))); 45 | mDescTv.setMovementMethod(LinkMovementMethod.getInstance()); 46 | mDescTv.setText(Html.fromHtml(getString(R.string.about_app))); 47 | 48 | mDetailTv.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View view) { 51 | ActivityNavigator.openWebView(AboutActivity.this, null, RequestUrl.ABOUT_APP_URL); 52 | } 53 | }); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/AuthorBlogActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | import android.text.TextUtils; 5 | 6 | import com.likebamboo.osa.android.request.RequestParams; 7 | import com.likebamboo.osa.android.request.RequestUrl; 8 | 9 | import java.net.URLEncoder; 10 | 11 | /** 12 | * 作者博客列表界面 13 | */ 14 | public class AuthorBlogActivity extends BlogListActivity { 15 | 16 | /** 17 | * 作者名称 18 | */ 19 | public static final String EXTRA_AUTHOR_NAME = "extra_author_name"; 20 | 21 | /** 22 | * 作者名称 23 | */ 24 | private String mAuthorName = ""; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | if (getIntent() != null && getIntent().hasExtra(EXTRA_AUTHOR_NAME)) { 29 | mAuthorName = getIntent().getStringExtra(EXTRA_AUTHOR_NAME); 30 | } 31 | super.onCreate(savedInstanceState); 32 | } 33 | 34 | @Override 35 | public void addParams(RequestParams params) { 36 | } 37 | 38 | @Override 39 | public String getRequestUrl() { 40 | if (!TextUtils.isEmpty(mAuthorName)) { 41 | try { 42 | mAuthorName = URLEncoder.encode(mAuthorName, "UTF-8"); 43 | } catch (Exception e) { 44 | } 45 | } 46 | // 改为搜索url 47 | return String.format(RequestUrl.AUTHOR_BLOG_URL, mAuthorName + ""); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.ActionBar; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.MenuItem; 7 | 8 | /** 9 | * Created by likebamboo on 2015/5/11. 10 | */ 11 | public class BaseActivity extends AppCompatActivity { 12 | 13 | /** 14 | * 标题 15 | */ 16 | public static final String EXTRA_TITLE = "extra_title"; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | if (getIntent().hasExtra(EXTRA_TITLE)) { 22 | setTitle(getIntent().getStringExtra(EXTRA_TITLE)); 23 | } 24 | ActionBar actionBar = getSupportActionBar(); 25 | actionBar.setTitle(getTitle()); 26 | } 27 | 28 | @Override 29 | protected void onDestroy() { 30 | super.onDestroy(); 31 | } 32 | 33 | @Override 34 | public void onBackPressed() { 35 | super.onBackPressed(); 36 | } 37 | 38 | @Override 39 | protected void onPause() { 40 | super.onPause(); 41 | } 42 | 43 | @Override 44 | protected void onResume() { 45 | super.onResume(); 46 | } 47 | 48 | @Override 49 | public boolean onOptionsItemSelected(MenuItem item) { 50 | switch (item.getItemId()) { 51 | case android.R.id.home: 52 | finish(); 53 | break; 54 | } 55 | return super.onOptionsItemSelected(item); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/CategoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import com.etsy.android.grid.StaggeredGridView; 7 | import com.likebamboo.osa.android.R; 8 | import com.likebamboo.osa.android.entity.CategoryList; 9 | import com.likebamboo.osa.android.impl.BaseOnItemClickListener; 10 | import com.likebamboo.osa.android.request.JsonRequest; 11 | import com.likebamboo.osa.android.request.RequestManager; 12 | import com.likebamboo.osa.android.request.RequestParams; 13 | import com.likebamboo.osa.android.request.RequestUrl; 14 | import com.likebamboo.osa.android.ui.adapter.CategoryAdapter; 15 | import com.likebamboo.osa.android.ui.nav.ActivityNavigator; 16 | 17 | import butterknife.InjectView; 18 | 19 | /** 20 | * 分类界面 21 | */ 22 | public class CategoryActivity extends EndlessActivity { 23 | 24 | @InjectView(R.id.list_view) 25 | StaggeredGridView mListView; 26 | 27 | @Override 28 | protected int getLayoutId() { 29 | return R.layout.activity_category; 30 | } 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | // 设置空态页面 36 | mListView.setEmptyView(mLoadingLayout); 37 | // 添加footer 38 | mListView.addFooterView(mFooterView); 39 | 40 | // 设置适配器 41 | mAdapter = new CategoryAdapter(this); 42 | mListView.setAdapter(mAdapter); 43 | ((CategoryAdapter) mAdapter).setOnItemClickListener(new BaseOnItemClickListener() { 44 | @Override 45 | public void onItemClick(int postion, CategoryList.Category item) { 46 | if (item == null) { 47 | return; 48 | } 49 | // 开始搜索 50 | Intent i = new Intent(CategoryActivity.this, CategoryBlogActivity.class); 51 | // 搜索关键字 52 | i.putExtra(CategoryBlogActivity.EXTRA_CATEGORY_ID, item.getId()); 53 | // 设置不显示抽屉导航 54 | i.putExtra(NavigationActivity.EXTRA_SHOULD_DISABLE_DRAWER, true); 55 | // 设置标题 56 | i.putExtra(EXTRA_TITLE, item.getName()); 57 | ActivityNavigator.withAnim(i, ActivityNavigator.AnimationMode.DEFAULT).startActivity(CategoryActivity.this, i); 58 | } 59 | }); 60 | // 设置加载更多 61 | mListView.setOnScrollListener(this); 62 | 63 | // 加载数据 64 | loadDatas(); 65 | } 66 | 67 | /** 68 | * 加载数据 69 | */ 70 | @Override 71 | protected void loadDatas(RequestParams params) { 72 | // 加载数据 73 | RequestManager.addRequest(new JsonRequest(RequestUrl.CATEGORY_URL, CategoryList.class, params, responseListener(), errorListener()), 74 | this.getClass().getName()); 75 | } 76 | 77 | @Override 78 | protected void doOnSuccess(CategoryList data) { 79 | if (data == null || data.getList() == null) { 80 | showMessage(data); 81 | return; 82 | } 83 | ++mPageIndex; 84 | if (data.getList().size() < mPageSize) { 85 | mHasMore = false; 86 | } else { 87 | mHasMore = true; 88 | } 89 | // 如果数据为空,显示没有更多数据了 90 | if (data.getList().isEmpty()) { 91 | showMessage(data); 92 | return; 93 | } 94 | mAdapter.addDatas(data.getList()); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/CategoryBlogActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.likebamboo.osa.android.request.RequestParams; 6 | import com.likebamboo.osa.android.request.RequestUrl; 7 | 8 | /** 9 | * 分类博客列表界面 10 | */ 11 | public class CategoryBlogActivity extends BlogListActivity { 12 | 13 | /** 14 | * 类别id 15 | */ 16 | public static final String EXTRA_CATEGORY_ID = "extra_category_id"; 17 | 18 | /** 19 | * 博客类别 20 | */ 21 | private int mCategoryId = 0; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | if (getIntent() != null && getIntent().hasExtra(EXTRA_CATEGORY_ID)) { 26 | mCategoryId = getIntent().getIntExtra(EXTRA_CATEGORY_ID, 0); 27 | } 28 | super.onCreate(savedInstanceState); 29 | } 30 | 31 | @Override 32 | public void addParams(RequestParams params) { 33 | } 34 | 35 | @Override 36 | public String getRequestUrl() { 37 | // 改为搜索url 38 | return String.format(RequestUrl.CATEGORY_BLOG_URL, mCategoryId + ""); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | 6 | import com.likebamboo.osa.android.request.RequestParams; 7 | import com.likebamboo.osa.android.request.RequestUrl; 8 | import com.likebamboo.osa.android.utils.PreferencesUtil; 9 | import com.orm.SugarConfig; 10 | import com.orm.SugarDb; 11 | 12 | /** 13 | * 主界面 14 | */ 15 | public class MainActivity extends BlogListActivity { 16 | public static final String TAG = MainActivity.class.getSimpleName(); 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | // 本次数据库版本 22 | int dbVersion = SugarConfig.getDatabaseVersion(this); 23 | // 保存的数据库版本 24 | int savedDbVersion = PreferencesUtil.getInstance(this).getInt(PreferencesUtil.PREF_DB_VERSION, 0); 25 | if (dbVersion > savedDbVersion) { // 如果数据库有升级。 26 | // 27 | new Thread(new Runnable() { 28 | @Override 29 | public void run() { 30 | if (isFinishing()) { 31 | return; 32 | } 33 | try { 34 | Log.e(TAG, System.currentTimeMillis() + "====start"); 35 | // 居然是个耗时的操作,以下语句执行了3秒钟左右。 可见sugar 效率不高啊,不知道新版是否有改进。 36 | int newVersion = (new SugarDb(MainActivity.this)).getReadableDatabase().getVersion(); 37 | PreferencesUtil.getInstance(MainActivity.this).putInt(PreferencesUtil.PREF_DB_VERSION, newVersion); 38 | Log.e(TAG, System.currentTimeMillis() + "====end"); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | }).start(); 44 | } 45 | } 46 | 47 | @Override 48 | public void addParams(RequestParams params) { 49 | } 50 | 51 | @Override 52 | public String getRequestUrl() { 53 | return RequestUrl.BLOG_URL; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/SearchResultActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | import android.text.TextUtils; 5 | 6 | import com.likebamboo.osa.android.request.RequestParams; 7 | import com.likebamboo.osa.android.request.RequestUrl; 8 | 9 | import java.net.URLEncoder; 10 | 11 | /** 12 | * 搜索结果界面 13 | */ 14 | public class SearchResultActivity extends BlogListActivity { 15 | 16 | /** 17 | * 搜索关键字 18 | */ 19 | public static final String EXTRA_SEARCH_KEY = "extra_search_key"; 20 | 21 | /** 22 | * 搜索博客的关键字 23 | */ 24 | private String mSearchKey = ""; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | if (getIntent() != null && getIntent().hasExtra(EXTRA_SEARCH_KEY)) { 29 | mSearchKey = getIntent().getStringExtra(EXTRA_SEARCH_KEY); 30 | } 31 | super.onCreate(savedInstanceState); 32 | } 33 | 34 | @Override 35 | public void addParams(RequestParams params) { 36 | // 关键字 37 | if (!TextUtils.isEmpty(mSearchKey)) { 38 | // fix 使用一个新的局部变量key,防止下拉刷新或者加载更多的时候mSearchKey不同。 39 | String key = mSearchKey; 40 | try { 41 | key = URLEncoder.encode(mSearchKey, "UTF-8"); 42 | } catch (Exception e) { 43 | } 44 | params.add("key", key); 45 | } 46 | } 47 | 48 | @Override 49 | public String getRequestUrl() { 50 | // 改为搜索url 51 | return RequestUrl.BLOG_SEARCH_URL; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/TagBlogActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.likebamboo.osa.android.request.RequestParams; 6 | import com.likebamboo.osa.android.request.RequestUrl; 7 | 8 | /** 9 | * 标签博客列表界面 10 | */ 11 | public class TagBlogActivity extends BlogListActivity { 12 | 13 | /** 14 | * 类别id 15 | */ 16 | public static final String EXTRA_TAG_NAME = "extra_tag_name"; 17 | 18 | /** 19 | * tag标签名称 20 | */ 21 | private String mTagName = ""; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | if (getIntent() != null && getIntent().hasExtra(EXTRA_TAG_NAME)) { 26 | mTagName = getIntent().getStringExtra(EXTRA_TAG_NAME); 27 | } 28 | super.onCreate(savedInstanceState); 29 | } 30 | 31 | @Override 32 | public void addParams(RequestParams params) { 33 | } 34 | 35 | @Override 36 | public String getRequestUrl() { 37 | // 改为搜索url 38 | return String.format(RequestUrl.TAG_BLOG_URL, mTagName); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBar; 7 | import android.text.TextUtils; 8 | 9 | import com.likebamboo.osa.android.R; 10 | import com.likebamboo.osa.android.ui.view.CommonWebView; 11 | 12 | import butterknife.ButterKnife; 13 | import butterknife.InjectView; 14 | 15 | /** 16 | * 普通WebView界面 17 | */ 18 | public class WebViewActivity extends BaseActivity { 19 | 20 | /** 21 | * 博客URL 22 | */ 23 | public static final String EXTRA_URL = "extra_url"; 24 | 25 | /** 26 | * actionbar 27 | */ 28 | private ActionBar mActionBar; 29 | 30 | @InjectView(R.id.webview) 31 | CommonWebView mWebView = null; 32 | 33 | /** 34 | * 打开web页面的URL 35 | */ 36 | private String mUrl = ""; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_webview); 42 | ButterKnife.inject(this); 43 | 44 | // 初始化actionBar 45 | initActionBar(); 46 | 47 | // 初始化View 48 | initView(); 49 | 50 | // 添加监听器 51 | addListener(); 52 | 53 | mUrl = getIntent().getStringExtra(EXTRA_URL); 54 | if (TextUtils.isEmpty(mUrl)) { 55 | // 容错处理 56 | finish(); 57 | return; 58 | } 59 | // 开始加载页面 60 | startLoading(mUrl); 61 | } 62 | 63 | /** 64 | * 初始化控件 65 | */ 66 | private void initView() { 67 | } 68 | 69 | /** 70 | * 添加监听器 71 | */ 72 | private void addListener() { 73 | mWebView.setStatusListener(new CommonWebView.IWebViewStatusListener() { 74 | @Override 75 | public void onPageStarted(String url) { 76 | } 77 | 78 | @Override 79 | public void onPageFinished(String url) { 80 | } 81 | 82 | @Override 83 | public void onReceiveTitle(String title) { 84 | if (mActionBar != null) { 85 | mActionBar.setTitle(title); 86 | } 87 | } 88 | 89 | @Override 90 | public boolean shouldOverrideUrl(String url) { 91 | return false; 92 | } 93 | }); 94 | } 95 | 96 | /** 97 | * 初始化actionBar布局 98 | */ 99 | private void initActionBar() { 100 | mActionBar = getSupportActionBar(); 101 | mActionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE); 102 | mActionBar.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 103 | mActionBar.setHomeAsUpIndicator(R.drawable.ic_up); 104 | } 105 | 106 | /** 107 | * 加载url 108 | * 109 | * @see [类、类#方法、类#成员] 110 | */ 111 | private void startLoading(String url) { 112 | if (TextUtils.isEmpty(url)) { 113 | return; 114 | } 115 | // 开始loading web页面 116 | mWebView.loadUrl(url); 117 | } 118 | 119 | @Override 120 | public void onBackPressed() { 121 | if (mWebView != null && mWebView.goBack()) { 122 | return; 123 | } 124 | super.onBackPressed(); 125 | } 126 | 127 | @Override 128 | public void finish() { 129 | super.finish(); 130 | overridePendingTransition(0, R.anim.fade_out); 131 | } 132 | 133 | @Override 134 | protected void onResume() { 135 | super.onResume(); 136 | mWebView.onResume(); 137 | } 138 | 139 | @Override 140 | protected void onPause() { 141 | super.onPause(); 142 | mWebView.onPause(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/adapter/BaseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * ListView 适配器基类 11 | * 12 | * @author wentaoli 13 | * @version [版本号, 2015年5月12日] 14 | * @see [相关类/方法] 15 | * @since [产品/模块版本] 16 | */ 17 | public abstract class BaseAdapter extends android.widget.BaseAdapter { 18 | 19 | /** 20 | * 上下文对象 21 | */ 22 | protected Context mContext = null; 23 | 24 | /** 25 | * 数据源 26 | */ 27 | protected ArrayList mDatas = new ArrayList(); 28 | 29 | public BaseAdapter(final Context ctx) { 30 | super(); 31 | this.mContext = ctx; 32 | } 33 | 34 | public BaseAdapter(final Context ctx, final ArrayList datas) { 35 | super(); 36 | this.mContext = ctx; 37 | this.mDatas = datas; 38 | } 39 | 40 | /** 41 | * 获取数据源 42 | * 43 | * @return 44 | */ 45 | public ArrayList getDatas() { 46 | return mDatas; 47 | } 48 | 49 | /** 50 | * 添加数据 51 | * 52 | * @param datas 53 | */ 54 | public void addDatas(final ArrayList datas) { 55 | if (mDatas == null) { 56 | mDatas = new ArrayList(); 57 | } 58 | mDatas.addAll(datas); 59 | notifyDataSetChanged(); 60 | } 61 | 62 | /** 63 | * 添加数据 64 | * 65 | * @param datas 66 | * @param index 67 | */ 68 | public void addDatas(final ArrayList datas, int index) { 69 | if (mDatas == null) { 70 | mDatas = new ArrayList(); 71 | } 72 | mDatas.addAll(index, datas); 73 | notifyDataSetChanged(); 74 | } 75 | 76 | /** 77 | * 添加数据 78 | * 79 | * @param datas 80 | */ 81 | public void addData(T datas) { 82 | if (mDatas == null) { 83 | mDatas = new ArrayList(); 84 | } 85 | mDatas.add(datas); 86 | } 87 | 88 | /** 89 | * 添加数据 90 | * 91 | * @param datas 92 | * @param index 93 | */ 94 | public void addData(T datas, int index) { 95 | if (mDatas == null) { 96 | mDatas = new ArrayList(); 97 | } 98 | mDatas.add(index, datas); 99 | } 100 | 101 | /** 102 | * 删除数据 103 | * 104 | * @param datas 105 | */ 106 | public void removeDatas(final ArrayList datas) { 107 | if (mDatas == null) { 108 | return; 109 | } 110 | mDatas.removeAll(datas); 111 | notifyDataSetChanged(); 112 | } 113 | 114 | /** 115 | * 删除数据 116 | * 117 | * @param index 118 | */ 119 | public void removeData(int index) { 120 | if (mDatas == null || index >= mDatas.size()) { 121 | return; 122 | } 123 | mDatas.remove(index); 124 | } 125 | 126 | /** 127 | * 删除数据 128 | * 129 | * @param data 130 | */ 131 | public void removeData(T data) { 132 | if (mDatas == null || data == null) { 133 | return; 134 | } 135 | mDatas.remove(data); 136 | } 137 | 138 | 139 | /** 140 | * 清空数据 141 | */ 142 | public void clear() { 143 | if (mDatas != null) { 144 | mDatas.clear(); 145 | } 146 | notifyDataSetChanged(); 147 | } 148 | 149 | @Override 150 | public int getCount() { 151 | if (mDatas == null) { 152 | return 0; 153 | } 154 | int count = mDatas.size(); 155 | return count; 156 | } 157 | 158 | @Override 159 | public T getItem(int arg0) { 160 | if (mDatas == null) { 161 | return null; 162 | } 163 | if (arg0 < 0 || arg0 >= mDatas.size()) { 164 | return null; 165 | } 166 | return mDatas.get(arg0); 167 | } 168 | 169 | @Override 170 | public long getItemId(int arg0) { 171 | return arg0; 172 | } 173 | 174 | 175 | @Override 176 | public abstract View getView(int position, View v, ViewGroup parent); 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/adapter/BlogAdapter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.adapter; 2 | 3 | import android.content.Context; 4 | import android.text.Html; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.likebamboo.osa.android.R; 11 | import com.likebamboo.osa.android.entity.BlogList; 12 | import com.likebamboo.osa.android.entity.BlogList.Blog; 13 | import com.likebamboo.osa.android.interfaces.IOnItemClickListener; 14 | import com.likebamboo.osa.android.ui.view.fa.TextAwesome; 15 | import com.likebamboo.osa.android.utils.DateUtil; 16 | 17 | import java.util.ArrayList; 18 | 19 | import butterknife.ButterKnife; 20 | import butterknife.InjectView; 21 | 22 | /** 23 | * Created by wentaoli on 2015/5/12. 24 | */ 25 | public class BlogAdapter extends ChoiceAdapter { 26 | 27 | private IOnItemClickListener mItemClickListener = null; 28 | 29 | /** 30 | * 设置回调 31 | * 32 | * @param l 33 | */ 34 | public void setOnItemClickListener(IOnItemClickListener l) { 35 | this.mItemClickListener = l; 36 | } 37 | 38 | public BlogAdapter(Context ctx) { 39 | super(ctx); 40 | } 41 | 42 | public BlogAdapter(Context ctx, ArrayList datas) { 43 | super(ctx, datas); 44 | } 45 | 46 | @Override 47 | public void removeData(Blog data) { 48 | if (data == null) { 49 | return; 50 | } 51 | // 找到相同的数据 52 | Blog temp = null; 53 | for (BlogList.Blog b : mDatas) { 54 | if (("" + b.getUrl()).equals(data.getUrl())) { 55 | temp = b; 56 | break; 57 | } 58 | } 59 | super.removeData(temp); 60 | } 61 | 62 | @Override 63 | public View getView(int position, View view, ViewGroup parent) { 64 | ViewHolder holder; 65 | if (view != null) { 66 | holder = (ViewHolder) view.getTag(); 67 | } else { 68 | view = LayoutInflater.from(mContext).inflate(R.layout.item_blog, parent, false); 69 | holder = new ViewHolder(view); 70 | view.setTag(holder); 71 | } 72 | 73 | Blog item = getItem(position); 74 | if (item == null) { 75 | return view; 76 | } 77 | 78 | view.setId(position); 79 | view.setOnClickListener(new View.OnClickListener() { 80 | @Override 81 | public void onClick(View view) { 82 | int pos = view.getId(); 83 | if (mItemClickListener != null) { 84 | mItemClickListener.onItemClick(pos, getItem(pos)); 85 | } 86 | } 87 | }); 88 | view.setOnLongClickListener(new View.OnLongClickListener() { 89 | @Override 90 | public boolean onLongClick(View view) { 91 | int pos = view.getId(); 92 | if (mItemClickListener != null) { 93 | mItemClickListener.onItemLongClick(pos, getItem(pos)); 94 | } 95 | return true; 96 | } 97 | }); 98 | 99 | // 是否选中某一项 100 | if (isItemSelected(position)) { 101 | holder.cover.setSelected(true); 102 | } else { 103 | holder.cover.setSelected(false); 104 | } 105 | 106 | /** 107 | * PS:为什么在Adapter中设置View的OnClick事件,而不是直接使用 108 | * @see android.widget.AdapterView#setOnItemClickListener(AdapterView.OnItemClickListener) 109 | * 的方式来实现? 110 | * 因为用 setOnItemClickListener 的方式 设置 ListView 的item 的background没有效果,不知道什么鬼, 111 | * 各位大神如果有解决方案,望告知~ 112 | */ 113 | holder.titleTv.setText(item.getTitle()); 114 | holder.abstractsTv.setText(Html.fromHtml(item.getAbstracts().replace("\n", "
"))); 115 | holder.tagTv.setText(R.string.fa_filter, item.getCategorys()); 116 | holder.timeTv.setText(R.string.fa_calendar, DateUtil.parseDate(item.getAddTime())); 117 | return view; 118 | } 119 | 120 | public static class ViewHolder { 121 | @InjectView(R.id.blog_title_tv) 122 | public TextView titleTv; 123 | @InjectView(R.id.blog_author_tv) 124 | public TextView authorTv; 125 | @InjectView(R.id.blog_abstracts_tv) 126 | public TextView abstractsTv; 127 | @InjectView(R.id.blog_time_tv) 128 | public TextAwesome timeTv; 129 | @InjectView(R.id.blog_tag_tv) 130 | public TextAwesome tagTv; 131 | @InjectView(R.id.item_cover) 132 | public View cover; 133 | 134 | public ViewHolder(View view) { 135 | ButterKnife.inject(this, view); 136 | } 137 | 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/adapter/CategoryAdapter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.adapter; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.android.volley.VolleyError; 11 | import com.android.volley.toolbox.ImageLoader; 12 | import com.etsy.android.grid.util.DynamicHeightImageView; 13 | import com.likebamboo.osa.android.R; 14 | import com.likebamboo.osa.android.entity.CategoryList; 15 | import com.likebamboo.osa.android.interfaces.IOnItemClickListener; 16 | import com.likebamboo.osa.android.request.RequestManager; 17 | import com.likebamboo.osa.android.request.RequestUrl; 18 | 19 | import java.util.ArrayList; 20 | 21 | import butterknife.ButterKnife; 22 | import butterknife.InjectView; 23 | 24 | /** 25 | * Created by wentaoli on 2015/5/12. 26 | */ 27 | public class CategoryAdapter extends BaseAdapter { 28 | 29 | private IOnItemClickListener mItemClickListener = null; 30 | 31 | /** 32 | * 设置回调 33 | * 34 | * @param l 35 | */ 36 | public void setOnItemClickListener(IOnItemClickListener l) { 37 | this.mItemClickListener = l; 38 | } 39 | 40 | public CategoryAdapter(Context ctx) { 41 | super(ctx); 42 | } 43 | 44 | public CategoryAdapter(Context ctx, ArrayList datas) { 45 | super(ctx, datas); 46 | } 47 | 48 | @Override 49 | public View getView(int position, View view, ViewGroup parent) { 50 | final ViewHolder holder; 51 | if (view != null) { 52 | holder = (ViewHolder) view.getTag(); 53 | } else { 54 | view = LayoutInflater.from(mContext).inflate(R.layout.item_category, parent, false); 55 | holder = new ViewHolder(view); 56 | view.setTag(holder); 57 | } 58 | 59 | CategoryList.Category item = getItem(position); 60 | if (item == null) { 61 | return view; 62 | } 63 | 64 | // 图片 65 | holder.coverIv.setImageResource(R.drawable.ic_launcher); 66 | // 加载图片 67 | ImageLoader imageLoader = RequestManager.getImageLoader(); 68 | holder.coverIv.setHeightRatio(0.8); 69 | if (!TextUtils.isEmpty(item.getCover())) { 70 | String url = RequestUrl.BASE_URL + item.getCover(); 71 | holder.coverIv.setTag(url); 72 | imageLoader.get(url, new ImageLoader.ImageListener() { 73 | @Override 74 | public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) { 75 | if (imageContainer == null || imageContainer.getBitmap() == null) { 76 | onErrorResponse(null); 77 | return; 78 | } 79 | if (("" + holder.coverIv.getTag()).equals(imageContainer.getRequestUrl())) { 80 | holder.coverIv.setHeightRatio(imageContainer.getBitmap().getHeight() / (double) imageContainer.getBitmap().getWidth()); 81 | holder.coverIv.setImageBitmap(imageContainer.getBitmap()); 82 | } 83 | } 84 | 85 | @Override 86 | public void onErrorResponse(VolleyError volleyError) { 87 | holder.coverIv.setImageResource(R.drawable.ic_launcher); 88 | } 89 | }); 90 | } 91 | 92 | view.setId(position); 93 | view.setOnClickListener(new View.OnClickListener() { 94 | @Override 95 | public void onClick(View view) { 96 | int pos = view.getId(); 97 | if (mItemClickListener != null) { 98 | mItemClickListener.onItemClick(pos, getItem(pos)); 99 | } 100 | } 101 | }); 102 | // set data 103 | holder.titleTv.setText(item.getName()); 104 | holder.descTv.setText(item.getDescription()); 105 | 106 | return view; 107 | } 108 | 109 | public static class ViewHolder { 110 | @InjectView(R.id.category_title_tv) 111 | public TextView titleTv; 112 | @InjectView(R.id.category_desc_tv) 113 | public TextView descTv; 114 | @InjectView(R.id.category_cover_iv) 115 | public DynamicHeightImageView coverIv; 116 | 117 | public ViewHolder(View view) { 118 | ButterKnife.inject(this, view); 119 | } 120 | 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/adapter/ChoiceAdapter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.adapter; 2 | 3 | import android.content.Context; 4 | import android.util.SparseBooleanArray; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * 带选择的adapter 10 | * Created by wentaoli on 2015/6/12. 11 | */ 12 | public abstract class ChoiceAdapter extends BaseAdapter { 13 | 14 | /** 15 | * 选中项 16 | */ 17 | protected SparseBooleanArray mSelectedPositions = new SparseBooleanArray(); 18 | 19 | public ChoiceAdapter(Context ctx) { 20 | super(ctx); 21 | } 22 | 23 | public ChoiceAdapter(Context ctx, ArrayList datas) { 24 | super(ctx, datas); 25 | } 26 | 27 | /** 28 | * 判断某一项是否选中 29 | * 30 | * @param position 位置 31 | * @return 32 | */ 33 | public boolean isItemSelected(int position) { 34 | return mSelectedPositions.get(position, false); 35 | } 36 | 37 | /** 38 | * 设置选中项 39 | * 40 | * @param position 位置 41 | * @param selected 选中or未选中 42 | */ 43 | public void setItemSelected(int position, boolean selected) { 44 | boolean sel = mSelectedPositions.get(position, false); 45 | if (sel == selected) { 46 | return; 47 | } 48 | 49 | // 选中 50 | if (selected) { 51 | mSelectedPositions.put(position, true); 52 | } else { 53 | // 取消选中 54 | mSelectedPositions.delete(position); 55 | } 56 | notifyDataSetChanged(); 57 | } 58 | 59 | /** 60 | * 设置全选or全部取消 61 | * 62 | * @param selectedAll 63 | */ 64 | public void setSelectedAll(boolean selectedAll) { 65 | // 全选 66 | if (selectedAll) { 67 | for (int i = 0; i < getCount(); i++) { 68 | mSelectedPositions.put(i, true); 69 | } 70 | } else { 71 | // 全不选 72 | mSelectedPositions.clear(); 73 | } 74 | notifyDataSetChanged(); 75 | } 76 | 77 | /** 78 | * 获取选中的项数。 79 | * 80 | * @return 81 | */ 82 | public int getSelectedCount() { 83 | int result = 0; 84 | for (int i = 0; i < getCount(); i++) { 85 | if (mSelectedPositions.get(i, false)) { 86 | ++result; 87 | } 88 | } 89 | return result; 90 | } 91 | 92 | /** 93 | * 获取选中的项的id 94 | * 95 | * @return 96 | */ 97 | public ArrayList getSelectedPositions() { 98 | ArrayList result = new ArrayList<>(); 99 | for (int i = 0; i < getCount(); i++) { 100 | if (mSelectedPositions.get(i, false)) { 101 | result.add(i); 102 | } 103 | } 104 | return result; 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/fragments/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.fragments; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.DialogFragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.AdapterView; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.ListView; 13 | 14 | import com.likebamboo.osa.android.R; 15 | import com.likebamboo.osa.android.request.RequestUrl; 16 | import com.likebamboo.osa.android.ui.AboutActivity; 17 | import com.likebamboo.osa.android.ui.BaseActivity; 18 | import com.likebamboo.osa.android.ui.nav.ActivityNavigator; 19 | import com.likebamboo.osa.android.ui.view.blur.BlurDialogFragmentHelper; 20 | 21 | /** 22 | * 关于Fragment 23 | * 24 | * @author likebamboo 25 | */ 26 | public class SettingsFragment extends DialogFragment { 27 | 28 | private BlurDialogFragmentHelper mHelper; 29 | 30 | public static SettingsFragment newInstance() { 31 | SettingsFragment fragment = new SettingsFragment(); 32 | return fragment; 33 | } 34 | 35 | @Override 36 | public void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | mHelper = new BlurDialogFragmentHelper(this); 39 | mHelper.onCreate(); 40 | } 41 | 42 | @Override 43 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 44 | View v = inflater.inflate(R.layout.fragment_settings, container, false); 45 | 46 | ListView listView = (ListView) v.findViewById(R.id.dialog_content); 47 | listView.setAdapter(new ArrayAdapter<>( 48 | getActivity(), R.layout.simple_text, 49 | android.R.id.text1, 50 | getResources().getStringArray(R.array.about_list) 51 | )); 52 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 53 | @Override 54 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 55 | if (getActivity() == null) { 56 | return; 57 | } 58 | switch (i) { 59 | case 0:// 意见反馈 60 | ActivityNavigator.openWebView(getActivity(), null, RequestUrl.ISSUES_URL); 61 | break; 62 | case 1:// 关于作者 63 | ActivityNavigator.openWebView(getActivity(), null, RequestUrl.ABOUT_ME_URL); 64 | break; 65 | case 2:// 关于app 66 | Intent intent = new Intent(getActivity(), AboutActivity.class); 67 | intent.putExtra(BaseActivity.EXTRA_TITLE, getString(R.string.about)); 68 | ActivityNavigator.startActivity(getActivity(), intent); 69 | break; 70 | } 71 | } 72 | }); 73 | return v; 74 | } 75 | 76 | public void onActivityCreated(Bundle savedInstanceState) { 77 | super.onActivityCreated(savedInstanceState); 78 | mHelper.onActivityCreated(); 79 | } 80 | 81 | @Override 82 | public void onStart() { 83 | super.onStart(); 84 | mHelper.onStart(); 85 | } 86 | 87 | @Override 88 | public void onDismiss(DialogInterface dialog) { 89 | mHelper.onDismiss(); 90 | super.onDismiss(dialog); 91 | } 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/FilterFooter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Point; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.view.Display; 8 | import android.view.View; 9 | import android.view.WindowManager; 10 | import android.view.animation.AccelerateInterpolator; 11 | import android.view.animation.Interpolator; 12 | import android.widget.FrameLayout; 13 | import android.widget.TextView; 14 | 15 | import com.likebamboo.osa.android.R; 16 | import com.likebamboo.osa.android.ui.view.fab.FloatingView; 17 | import com.nineoldandroids.animation.ObjectAnimator; 18 | import com.nineoldandroids.view.ViewHelper; 19 | 20 | import butterknife.ButterKnife; 21 | import butterknife.InjectView; 22 | 23 | /** 24 | * listView底部过滤器布局 25 | *

26 | * Created by likebamboo on 2015/6/15. 27 | */ 28 | public class FilterFooter extends FrameLayout implements FloatingView { 29 | 30 | /** 31 | * 加速器 32 | */ 33 | private Interpolator mInterpolator = new AccelerateInterpolator(); 34 | /** 35 | * 是否隐藏 36 | */ 37 | private boolean mHidden = false; 38 | 39 | /** 40 | * 隐藏时候的Y值 41 | */ 42 | private float mYHidden = -1; 43 | /** 44 | * 显示时候的Y值 45 | */ 46 | private float mYDisplayed = -1; 47 | 48 | @InjectView(R.id.filter_category_tv) 49 | TextView mCategoryTv = null; 50 | 51 | @InjectView(R.id.filter_sort_tv) 52 | TextView mSortTv = null; 53 | 54 | /** 55 | * 点击事件回调 56 | */ 57 | private IOnFilterClickListener mFilterClickListener = null; 58 | 59 | public FilterFooter(Context context) { 60 | this(context, null); 61 | } 62 | 63 | public FilterFooter(Context context, AttributeSet attrs) { 64 | this(context, attrs, 0); 65 | } 66 | 67 | public FilterFooter(Context context, AttributeSet attrs, int defStyleAttr) { 68 | super(context, attrs, defStyleAttr); 69 | WindowManager mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 70 | Display display = mWindowManager.getDefaultDisplay(); 71 | Point size = new Point(); 72 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 73 | display.getSize(size); 74 | mYHidden = size.y; 75 | } else { 76 | mYHidden = display.getHeight(); 77 | } 78 | } 79 | 80 | @Override 81 | protected void onFinishInflate() { 82 | super.onFinishInflate(); 83 | if (isInEditMode()) { 84 | return; 85 | } 86 | ButterKnife.inject(this); 87 | mCategoryTv.setOnClickListener(new OnClickListener() { 88 | @Override 89 | public void onClick(View view) { 90 | if (mFilterClickListener == null) { 91 | return; 92 | } 93 | mFilterClickListener.onCategoryClick(); 94 | } 95 | }); 96 | mSortTv.setOnClickListener(new OnClickListener() { 97 | @Override 98 | public void onClick(View view) { 99 | if (mFilterClickListener == null) { 100 | return; 101 | } 102 | mFilterClickListener.onSortClick(); 103 | } 104 | }); 105 | } 106 | 107 | @Override 108 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 109 | // Perform the default behavior 110 | super.onLayout(changed, left, top, right, bottom); 111 | // Store the FAB button's displayed Y position if we are not already aware of it 112 | if (mYDisplayed == -1) { 113 | mYDisplayed = ViewHelper.getY(this); 114 | } 115 | } 116 | 117 | @Override 118 | public void hide() { 119 | hide(true); 120 | } 121 | 122 | @Override 123 | public void show() { 124 | hide(false); 125 | } 126 | 127 | /** 128 | * 设置点击事件回调 129 | * 130 | * @param l 131 | */ 132 | public void setFilterClickListener(IOnFilterClickListener l) { 133 | this.mFilterClickListener = l; 134 | } 135 | 136 | private void hide(boolean hide) { 137 | // If the hidden state is being updated 138 | if (mHidden != hide) { 139 | // Store the new hidden state 140 | mHidden = hide; 141 | 142 | // Animate the FAB to it's new Y position 143 | ObjectAnimator animator = ObjectAnimator.ofFloat(this, "y", mHidden ? mYHidden : mYDisplayed).setDuration(500); 144 | animator.setInterpolator(mInterpolator); 145 | animator.start(); 146 | } 147 | } 148 | 149 | /** 150 | * 点击事件回调 151 | */ 152 | public interface IOnFilterClickListener { 153 | /** 154 | * 点击排序 155 | */ 156 | void onSortClick(); 157 | 158 | /** 159 | * 点击分类 160 | */ 161 | void onCategoryClick(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/LoadingLayout.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | import android.util.AttributeSet; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | import android.widget.TextView; 10 | 11 | import com.likebamboo.osa.android.R; 12 | 13 | import butterknife.ButterKnife; 14 | import butterknife.InjectView; 15 | 16 | /** 17 | * @author likebamboo 18 | * @date 2015/5/13. 19 | * @desc

描述: Loading(整块区域或者加载更多时loading)
 20 |  */
 21 | public class LoadingLayout extends LinearLayout {
 22 |     /**
 23 |      * 正在加载view
 24 |      */
 25 |     @InjectView(R.id.loading_layout)
 26 |     View mLoadingView = null;
 27 | 
 28 |     /**
 29 |      * Loading 提示TextView
 30 |      */
 31 |     @InjectView(R.id.loading_tv)
 32 |     TextView mLoadingTv = null;
 33 | 
 34 |     /**
 35 |      * 重试布局
 36 |      */
 37 |     @InjectView(R.id.loading_fail_layout)
 38 |     View mRetryLayout = null;
 39 | 
 40 |     /**
 41 |      * 错误提示TextView
 42 |      */
 43 |     @InjectView(R.id.loading_fail_tv)
 44 |     TextView mErrorTv = null;
 45 | 
 46 |     /**
 47 |      * 重试接口
 48 |      */
 49 |     private IRetryListener mRetryListener = null;
 50 | 
 51 |     /**
 52 |      * 是否可以重试
 53 |      */
 54 |     private boolean canRetry = true;
 55 | 
 56 |     public interface IRetryListener {
 57 |         void onRetry();
 58 |     }
 59 | 
 60 |     public LoadingLayout(Context context) {
 61 |         this(context, null);
 62 |     }
 63 | 
 64 |     public LoadingLayout(Context context, AttributeSet attrs) {
 65 |         super(context, attrs);
 66 |     }
 67 | 
 68 |     @SuppressLint("NewApi")
 69 |     public LoadingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
 70 |         super(context, attrs, defStyleAttr);
 71 |     }
 72 | 
 73 |     @Override
 74 |     protected void onFinishInflate() {
 75 |         super.onFinishInflate();
 76 |         if (isInEditMode()) {
 77 |             return;
 78 |         }
 79 |         ButterKnife.inject(this);
 80 |         mRetryLayout.setOnClickListener(new View.OnClickListener() {
 81 |             @Override
 82 |             public void onClick(View view) {
 83 |                 if (!canRetry) {
 84 |                     return;
 85 |                 }
 86 |                 if (mRetryListener != null) {
 87 |                     mRetryListener.onRetry();
 88 |                 }
 89 |             }
 90 |         });
 91 |     }
 92 | 
 93 |     /**
 94 |      * 显示/隐藏正在加载中。。。
 95 |      *
 96 |      * @param show
 97 |      * @see [类、类#方法、类#成员]
 98 |      */
 99 |     public void showLoading(boolean show) {
100 |         showLoading(show, null);
101 |     }
102 | 
103 |     /**
104 |      * 显示/隐藏正在加载中。。。
105 |      *
106 |      * @param show
107 |      * @param text
108 |      * @see [类、类#方法、类#成员]
109 |      */
110 |     public void showLoading(boolean show, String text) {
111 |         if (show) {
112 |             setVisibility(View.VISIBLE);
113 |             mLoadingView.setVisibility(View.VISIBLE);
114 |             mRetryLayout.setVisibility(View.GONE);
115 |             if (!TextUtils.isEmpty(text)) {
116 |                 mLoadingTv.setText(text);
117 |             }
118 |         } else {
119 |             setVisibility(View.GONE);
120 |         }
121 |     }
122 | 
123 |     /**
124 |      * 显示加载失败信息
125 |      *
126 |      * @param msg
127 |      * @see [类、类#方法、类#成员]
128 |      */
129 |     public void showError(String msg) {
130 |         setVisibility(View.VISIBLE);
131 |         mLoadingView.setVisibility(View.GONE);
132 |         mRetryLayout.setVisibility(View.VISIBLE);
133 |         if (!TextUtils.isEmpty(msg)) {
134 |             mErrorTv.setText(msg);
135 |         }
136 |         canRetry = true;
137 |     }
138 | 
139 |     /**
140 |      * 显示"空"信息
141 |      *
142 |      * @param msg
143 |      */
144 |     public void showEmpty(String msg) {
145 |         showError(msg);
146 |         // 不允许重试
147 |         canRetry = false;
148 |     }
149 | 
150 |     /**
151 |      * 设置重试监听器。
152 |      *
153 |      * @param listener
154 |      */
155 |     public void setRetryListener(IRetryListener listener) {
156 |         this.mRetryListener = listener;
157 |     }
158 | 
159 | }
160 | 


--------------------------------------------------------------------------------
/app/src/main/java/com/likebamboo/osa/android/ui/view/ObservedWebView.java:
--------------------------------------------------------------------------------
 1 | package com.likebamboo.osa.android.ui.view;
 2 | 
 3 | import android.content.Context;
 4 | import android.util.AttributeSet;
 5 | import android.webkit.WebView;
 6 | 
 7 | /**
 8 |  * 可监听滚动的WebView
 9 |  * 

10 | * Created by wentaoli on 2015/5/22. 11 | */ 12 | public class ObservedWebView extends WebView { 13 | private OnScrollChangedCallback mOnScrollChangedCallback; 14 | 15 | public ObservedWebView(final Context context) { 16 | super(context); 17 | } 18 | 19 | public ObservedWebView(final Context context, final AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public ObservedWebView(final Context context, final AttributeSet attrs, final int defStyle) { 24 | super(context, attrs, defStyle); 25 | } 26 | 27 | @Override 28 | protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) { 29 | super.onScrollChanged(l, t, oldl, oldt); 30 | if (mOnScrollChangedCallback != null) { 31 | mOnScrollChangedCallback.onScroll(l, t); 32 | } 33 | } 34 | 35 | public OnScrollChangedCallback getOnScrollChangedCallback() { 36 | return mOnScrollChangedCallback; 37 | } 38 | 39 | public void setOnScrollChangedCallback(final OnScrollChangedCallback onScrollChangedCallback) { 40 | mOnScrollChangedCallback = onScrollChangedCallback; 41 | } 42 | 43 | /** 44 | * Impliment in the activity/fragment/view that you want to listen to the webview 45 | */ 46 | public interface OnScrollChangedCallback { 47 | void onScroll(int l, int t); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/WebViewToolBar.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.webkit.WebView; 8 | import android.widget.RelativeLayout; 9 | 10 | import com.likebamboo.osa.android.R; 11 | 12 | /** 13 | * WebView 操作栏封装,提供 back ,refresh ,forward 功能 14 | * 15 | * @author likebamboo 16 | * @version [版本号, 2015年5月20日] 17 | * @see [相关类/方法] 18 | * @since [产品/模块版本] 19 | */ 20 | public class WebViewToolBar extends RelativeLayout implements View.OnClickListener 21 | { 22 | 23 | /** 24 | * 后退 25 | */ 26 | private View mWebviewGoBack; 27 | 28 | /** 29 | * 刷新 30 | */ 31 | private View mWebviewRefresh; 32 | 33 | /** 34 | * 前进 35 | */ 36 | private View mWebviewGoForward; 37 | 38 | /** 39 | * 目标WebView 40 | */ 41 | private WebView mTargetView = null; 42 | 43 | public WebViewToolBar(Context context) 44 | { 45 | this(context, null); 46 | } 47 | 48 | public WebViewToolBar(Context context, AttributeSet attrs) 49 | { 50 | this(context, attrs, 0); 51 | } 52 | 53 | public WebViewToolBar(Context context, AttributeSet attrs, int defStyleAttr) 54 | { 55 | super(context, attrs, defStyleAttr); 56 | LayoutInflater.from(context).inflate(R.layout.webview_toolbar, this, true); 57 | if (!isInEditMode()) 58 | { 59 | // 初始化 60 | initView(); 61 | // 添加监听器 62 | addListener(); 63 | } 64 | } 65 | 66 | /** 67 | * 初始化界面元素 68 | * 69 | * @see [类、类#方法、类#成员] 70 | */ 71 | private void initView() 72 | { 73 | mWebviewGoBack = findViewById(R.id.webviewGoBack); 74 | mWebviewGoBack.setEnabled(false); 75 | mWebviewRefresh = findViewById(R.id.webviewRefresh); 76 | mWebviewRefresh.setEnabled(false); 77 | mWebviewGoForward = findViewById(R.id.webviewGoForward); 78 | mWebviewGoForward.setEnabled(false); 79 | } 80 | 81 | /** 82 | * 添加监听器 83 | * 84 | * @see [类、类#方法、类#成员] 85 | */ 86 | private void addListener() 87 | { 88 | mWebviewGoBack.setOnClickListener(this); 89 | mWebviewRefresh.setOnClickListener(this); 90 | mWebviewGoForward.setOnClickListener(this); 91 | } 92 | 93 | @Override 94 | public void onClick(View v) 95 | { 96 | if (v == null || mTargetView == null) 97 | { 98 | return; 99 | } 100 | switch (v.getId()) 101 | { 102 | case R.id.webviewGoBack:// 后退 103 | mTargetView.goBack(); 104 | break; 105 | case R.id.webviewRefresh:// 刷新 106 | mTargetView.reload(); 107 | break; 108 | case R.id.webviewGoForward:// 向前 109 | mTargetView.goForward(); 110 | break; 111 | default: 112 | break; 113 | } 114 | } 115 | 116 | /** 117 | * 将本控件绑定到具体的webView 上 118 | * 119 | * @see [类、类#方法、类#成员] 120 | */ 121 | public void attachToWebView(WebView target) 122 | { 123 | mTargetView = target; 124 | } 125 | 126 | /** 127 | * 更新状态 128 | * 129 | * @see [类、类#方法、类#成员] 130 | */ 131 | public void updateStatus() 132 | { 133 | if (mTargetView == null) 134 | { 135 | return; 136 | } 137 | mWebviewGoBack.setEnabled(mTargetView.canGoBack()); 138 | mWebviewRefresh.setEnabled(true); 139 | mWebviewGoForward.setEnabled(mTargetView.canGoForward()); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/blur/BlurBehind.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.blur; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.graphics.PorterDuff; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.os.AsyncTask; 8 | import android.support.v4.util.LruCache; 9 | import android.view.View; 10 | 11 | /** 12 | * READ ME 13 | *

14 | * Created by wentaoli on 2015/5/18. 15 | */ 16 | public class BlurBehind { 17 | 18 | private static final String KEY_CACHE_BLURRED_BACKGROUND_IMAGE = "KEY_CACHE_BLURRED_BACKGROUND_IMAGE"; 19 | private static final int CONSTANT_BLUR_RADIUS = 10; 20 | private static final int CONSTANT_DEFAULT_ALPHA = 100; 21 | 22 | private static final LruCache mImageCache = new LruCache(1); 23 | private static CacheBlurBehindAndExecuteTask cacheBlurBehindAndExecuteTask; 24 | 25 | private int mAlpha = CONSTANT_DEFAULT_ALPHA; 26 | private int mFilterColor = -1; 27 | 28 | private enum State { 29 | READY, 30 | EXECUTING 31 | } 32 | 33 | private State mState = State.READY; 34 | 35 | private static BlurBehind mInstance; 36 | 37 | public static BlurBehind getInstance() { 38 | if (mInstance == null) { 39 | mInstance = new BlurBehind(); 40 | } 41 | return mInstance; 42 | } 43 | 44 | public void execute(Activity activity, OnBlurCompleteListener onBlurCompleteListener) { 45 | if (mState.equals(State.READY)) { 46 | mState = State.EXECUTING; 47 | cacheBlurBehindAndExecuteTask = new CacheBlurBehindAndExecuteTask(activity, onBlurCompleteListener); 48 | cacheBlurBehindAndExecuteTask.execute(); 49 | } 50 | } 51 | 52 | public BlurBehind withAlpha(int alpha) { 53 | this.mAlpha = alpha; 54 | return this; 55 | } 56 | 57 | public BlurBehind withFilterColor(int filterColor) { 58 | this.mFilterColor = filterColor; 59 | return this; 60 | } 61 | 62 | public void setBackground(Activity activity) { 63 | if (mImageCache.size() != 0) { 64 | BitmapDrawable bd = new BitmapDrawable(activity.getResources(), mImageCache.get(KEY_CACHE_BLURRED_BACKGROUND_IMAGE)); 65 | bd.setAlpha(mAlpha); 66 | if (mFilterColor != -1) { 67 | bd.setColorFilter(mFilterColor, PorterDuff.Mode.DST_ATOP); 68 | } 69 | activity.getWindow().setBackgroundDrawable(bd); 70 | mImageCache.remove(KEY_CACHE_BLURRED_BACKGROUND_IMAGE); 71 | cacheBlurBehindAndExecuteTask = null; 72 | } 73 | } 74 | 75 | private class CacheBlurBehindAndExecuteTask extends AsyncTask { 76 | private Activity activity; 77 | private OnBlurCompleteListener onBlurCompleteListener; 78 | 79 | private View decorView; 80 | private Bitmap image; 81 | 82 | public CacheBlurBehindAndExecuteTask(Activity activity, OnBlurCompleteListener onBlurCompleteListener) { 83 | this.activity = activity; 84 | this.onBlurCompleteListener = onBlurCompleteListener; 85 | } 86 | 87 | @Override 88 | protected void onPreExecute() { 89 | super.onPreExecute(); 90 | 91 | decorView = activity.getWindow().getDecorView(); 92 | decorView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW); 93 | decorView.setDrawingCacheEnabled(true); 94 | decorView.buildDrawingCache(); 95 | 96 | image = decorView.getDrawingCache(); 97 | } 98 | 99 | @Override 100 | protected Void doInBackground(Void... params) { 101 | Bitmap blurredBitmap = Blur.apply(activity, image, 0.5F, CONSTANT_BLUR_RADIUS); 102 | mImageCache.put(KEY_CACHE_BLURRED_BACKGROUND_IMAGE, blurredBitmap); 103 | 104 | return null; 105 | } 106 | 107 | @Override 108 | protected void onPostExecute(Void aVoid) { 109 | super.onPostExecute(aVoid); 110 | 111 | decorView.destroyDrawingCache(); 112 | decorView.setDrawingCacheEnabled(false); 113 | 114 | activity = null; 115 | 116 | onBlurCompleteListener.onBlurComplete(); 117 | 118 | mState = State.READY; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/blur/EtsyActionBarDrawerToggle.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.blur; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.support.v4.widget.DrawerLayout; 6 | import android.support.v7.app.ActionBarDrawerToggle; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | 11 | import com.likebamboo.osa.android.R; 12 | 13 | /** 14 | * EtsyActionBarDrawerToggle.java 15 | * 16 | * @author Manabu-GT on 6/12/14. 17 | */ 18 | public class EtsyActionBarDrawerToggle extends ActionBarDrawerToggle { 19 | 20 | private static final int DEFAULT_RADIUS = 10; 21 | private static final int DEFAULT_DOWN_SAMPLING = 3; 22 | 23 | private Activity mActivity; 24 | 25 | private View mContainer; 26 | private ImageView mBlurImage; 27 | 28 | private int mBlurRadius; 29 | private int mDownSampling; 30 | 31 | public EtsyActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, 32 | int openDrawerContentDescRes, int closeDrawerContentDescRes) { 33 | super(activity, drawerLayout, openDrawerContentDescRes, closeDrawerContentDescRes); 34 | init(activity); 35 | } 36 | 37 | public EtsyActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar, 38 | int openDrawerContentDescRes, int closeDrawerContentDescRes) { 39 | super(activity, drawerLayout, toolbar, openDrawerContentDescRes, closeDrawerContentDescRes); 40 | init(activity); 41 | } 42 | 43 | public void setBlurImage() { 44 | mBlurImage.setImageBitmap(null); 45 | mBlurImage.setVisibility(View.VISIBLE); 46 | // do the downscaling for faster processing 47 | Bitmap downScaled = Util.drawViewToBitmap(mContainer, 48 | mContainer.getWidth(), mContainer.getHeight(), mDownSampling); 49 | if (downScaled == null) { 50 | return; 51 | } 52 | // apply the blur using the renderscript 53 | Bitmap blurred = Blur.apply(mActivity, downScaled, mBlurRadius); 54 | if (blurred == null) { 55 | return; 56 | } 57 | mBlurImage.setImageBitmap(blurred); 58 | downScaled.recycle(); 59 | } 60 | 61 | public void clearBlurImage() { 62 | mBlurImage.setVisibility(View.GONE); 63 | mBlurImage.setImageBitmap(null); 64 | } 65 | 66 | public void setBlurRadius(int blurRadius) { 67 | if (0 < blurRadius && blurRadius <= 25) { 68 | mBlurRadius = blurRadius; 69 | } 70 | } 71 | 72 | public void setDownSampling(int downSampling) { 73 | mDownSampling = downSampling; 74 | } 75 | 76 | private void init(Activity activity) { 77 | mActivity = activity; 78 | mBlurRadius = DEFAULT_RADIUS; 79 | mDownSampling = DEFAULT_DOWN_SAMPLING; 80 | 81 | mContainer = activity.findViewById(R.id.container); 82 | mBlurImage = (ImageView) activity.findViewById(R.id.blur_view); 83 | } 84 | 85 | private void setBlurAlpha(float slideOffset) { 86 | if (mBlurImage.getVisibility() != View.VISIBLE) { 87 | setBlurImage(); 88 | } 89 | Util.setAlpha(mBlurImage, slideOffset); 90 | } 91 | 92 | @Override 93 | public void onDrawerSlide(final View drawerView, final float slideOffset) { 94 | super.onDrawerSlide(drawerView, slideOffset); 95 | if (slideOffset > 0f) { 96 | setBlurAlpha(slideOffset); 97 | } else { 98 | clearBlurImage(); 99 | } 100 | } 101 | 102 | @Override 103 | public void onDrawerClosed(View drawerView) { 104 | super.onDrawerClosed(drawerView); 105 | clearBlurImage(); 106 | } 107 | } -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/blur/OnBlurCompleteListener.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.blur; 2 | 3 | /** 4 | * Created by wentaoli on 2015/5/18. 5 | */ 6 | public interface OnBlurCompleteListener { 7 | void onBlurComplete(); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/blur/Util.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.blur; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.annotation.TargetApi; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.os.Build; 9 | import android.os.Handler; 10 | import android.view.View; 11 | import android.view.ViewPropertyAnimator; 12 | import android.view.animation.AlphaAnimation; 13 | import android.view.animation.Animation; 14 | 15 | /** 16 | * READ ME 17 | * Util.java 18 | * 19 | * @author Manabu-GT on 6/12/14. 20 | */ 21 | public class Util { 22 | 23 | public static Bitmap drawViewToBitmap(View view, int width, int height, int downSampling) { 24 | return drawViewToBitmap(view, width, height, 0f, 0f, downSampling); 25 | } 26 | 27 | public static Bitmap drawViewToBitmap(View view, int width, int height, float translateX, 28 | float translateY, int downSampling) { 29 | if (width * height == 0) { 30 | return null; 31 | } 32 | float scale = 1f / downSampling; 33 | int bmpWidth = (int) (width * scale - translateX / downSampling); 34 | int bmpHeight = (int) (height * scale - translateY / downSampling); 35 | Bitmap dest = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); 36 | Canvas c = new Canvas(dest); 37 | c.translate(-translateX / downSampling, -translateY / downSampling); 38 | if (downSampling > 1) { 39 | c.scale(scale, scale); 40 | } 41 | view.draw(c); 42 | return dest; 43 | } 44 | 45 | public static boolean isPostHoneycomb() { 46 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; 47 | } 48 | 49 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 50 | public static void setAlpha(View view, float alpha) { 51 | if (isPostHoneycomb()) { 52 | view.setAlpha(alpha); 53 | } else { 54 | AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha); 55 | // make it instant 56 | alphaAnimation.setDuration(0); 57 | alphaAnimation.setFillAfter(true); 58 | view.startAnimation(alphaAnimation); 59 | } 60 | } 61 | 62 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 63 | public static void animateAlpha(final View view, float fromAlpha, float toAlpha, int duration, final Runnable endAction) { 64 | if (isPostHoneycomb()) { 65 | ViewPropertyAnimator animator = view.animate().alpha(toAlpha).setDuration(duration); 66 | if (endAction != null) { 67 | animator.setListener(new AnimatorListenerAdapter() { 68 | public void onAnimationEnd(Animator animation) { 69 | endAction.run(); 70 | } 71 | }); 72 | } 73 | } else { 74 | AlphaAnimation alphaAnimation = new AlphaAnimation(fromAlpha, toAlpha); 75 | alphaAnimation.setDuration(duration); 76 | alphaAnimation.setFillAfter(true); 77 | if (endAction != null) { 78 | alphaAnimation.setAnimationListener(new Animation.AnimationListener() { 79 | @Override 80 | public void onAnimationEnd(Animation animation) { 81 | // fixes the crash bug while removing views 82 | Handler handler = new Handler(); 83 | handler.post(endAction); 84 | } 85 | @Override 86 | public void onAnimationStart(Animation animation) { } 87 | @Override 88 | public void onAnimationRepeat(Animation animation) { } 89 | }); 90 | } 91 | view.startAnimation(alphaAnimation); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/fa/ButtonAwesome.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.fa; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.support.v4.util.LruCache; 6 | import android.util.AttributeSet; 7 | import android.widget.Button; 8 | 9 | /** 10 | * ButtonAwesome 11 | * 12 | * @see FontAwesomeAndroid 13 | */ 14 | public class ButtonAwesome extends Button { 15 | private final static String NAME = "FONTAWESOME"; 16 | private static LruCache sTypefaceCache = new LruCache(12); 17 | 18 | public ButtonAwesome(Context context) { 19 | super(context); 20 | init(); 21 | 22 | } 23 | 24 | public ButtonAwesome(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(); 27 | } 28 | 29 | public ButtonAwesome(Context context, AttributeSet attrs, int defStyle) { 30 | super(context, attrs, defStyle); 31 | init(); 32 | } 33 | 34 | public synchronized void init() { 35 | Typeface typeface = sTypefaceCache.get(NAME); 36 | if (typeface == null) { 37 | typeface = Typeface.createFromAsset(getContext().getAssets(), "fontawesome-webfont.ttf"); 38 | sTypefaceCache.put(NAME, typeface); 39 | } 40 | 41 | setTypeface(typeface); 42 | } 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/fa/TextAwesome.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.fa; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.support.v4.util.LruCache; 6 | import android.util.AttributeSet; 7 | import android.widget.TextView; 8 | 9 | /** 10 | * DrawableAwesome 11 | * 12 | * @see FontAwesomeAndroid 13 | */ 14 | public class TextAwesome extends TextView { 15 | 16 | private final static String NAME = "FONTAWESOME"; 17 | private static LruCache sTypefaceCache = new LruCache(12); 18 | 19 | public TextAwesome(Context context) { 20 | super(context); 21 | init(); 22 | } 23 | 24 | public TextAwesome(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(); 27 | } 28 | 29 | public synchronized void init() { 30 | Typeface typeface = sTypefaceCache.get(NAME); 31 | if (typeface == null) { 32 | typeface = Typeface.createFromAsset(getContext().getAssets(), "fontawesome-webfont.ttf"); 33 | sTypefaceCache.put(NAME, typeface); 34 | } 35 | setTypeface(typeface); 36 | } 37 | 38 | /** 39 | * 设置文字 40 | * 41 | * @param icon 42 | * @param text 43 | */ 44 | public void setText(int icon, String text) { 45 | String iconStr = getResources().getString(icon); 46 | setText(iconStr + " " + text); 47 | } 48 | 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/fab/DirectionScrollListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 SBG Apps 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.likebamboo.osa.android.ui.view.fab; 18 | 19 | import android.view.View; 20 | import android.widget.AbsListView; 21 | 22 | /** 23 | * Created by Stéphane on 09/07/2014. 24 | */ 25 | public class DirectionScrollListener implements AbsListView.OnScrollListener { 26 | 27 | private static final int DIRECTION_CHANGE_THRESHOLD = 1; 28 | private final FloatingView mFloatingView; 29 | private int mPrevPosition; 30 | private int mPrevTop; 31 | private boolean mUpdated; 32 | 33 | public DirectionScrollListener(FloatingView floatingView) { 34 | mFloatingView = floatingView; 35 | } 36 | 37 | @Override 38 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 39 | final View topChild = view.getChildAt(0); 40 | int firstViewTop = 0; 41 | if (topChild != null) { 42 | firstViewTop = topChild.getTop(); 43 | } 44 | boolean goingDown; 45 | boolean changed = true; 46 | if (mPrevPosition == firstVisibleItem) { 47 | final int topDelta = mPrevTop - firstViewTop; 48 | goingDown = firstViewTop < mPrevTop; 49 | changed = Math.abs(topDelta) > DIRECTION_CHANGE_THRESHOLD; 50 | } else { 51 | goingDown = firstVisibleItem > mPrevPosition; 52 | } 53 | if (changed && mUpdated) { 54 | if (goingDown) { 55 | mFloatingView.hide(); 56 | } else { 57 | mFloatingView.show(); 58 | } 59 | } 60 | mPrevPosition = firstVisibleItem; 61 | mPrevTop = firstViewTop; 62 | mUpdated = true; 63 | } 64 | 65 | @Override 66 | public void onScrollStateChanged(AbsListView view, int scrollState) { 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/fab/FloatingView.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.fab; 2 | 3 | /** 4 | * 浮动的View接口 5 | * Created by wentaoli on 2015/6/15. 6 | */ 7 | public interface FloatingView { 8 | /** 9 | * 隐藏 10 | */ 11 | void hide(); 12 | 13 | /** 14 | * 显示 15 | */ 16 | void show(); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/ui/view/fastscroll/BubbleTextGetter.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.ui.view.fastscroll; 2 | 3 | /** 4 | * 5 | */ 6 | public interface BubbleTextGetter { 7 | String getTextToShowInBubble(int pos); 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | /** 9 | * 10 | */ 11 | public class DateUtil { 12 | 13 | /** 14 | * 格式化服务器端给的时间字符串 15 | * 16 | * @param timeStr 17 | * @return 18 | */ 19 | public static String parseDate(String timeStr) { 20 | return parseDate(timeStr, "yyyy-MM-dd"); 21 | } 22 | 23 | /** 24 | * 将时间字符串转为本地显示的时间形式 25 | * 26 | * @param timeStr 27 | * @param pattern 28 | * @return 29 | */ 30 | public static String parseDate(String timeStr, String pattern) { 31 | if (TextUtils.isEmpty(timeStr) || TextUtils.isEmpty(pattern)) { 32 | return ""; 33 | } 34 | 35 | try { 36 | SimpleDateFormat sdf = new SimpleDateFormat(pattern); 37 | // 将字符串转为日期 38 | Date d = sdf.parse(timeStr); 39 | return parseDate(d.getTime()); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | return timeStr; 44 | } 45 | 46 | /** 47 | * 将时间戳转为本地显示的时间形式 48 | * 49 | * @param timeStamp 50 | * @return 51 | */ 52 | public static String parseDate(long timeStamp) { 53 | long now = System.currentTimeMillis(); 54 | SimpleDateFormat sdf = null; 55 | try { 56 | // 将字符串转为日期 57 | Date d = new Date(timeStamp); 58 | Date dd = new Date(); 59 | // 如果是同一天 60 | if ((now - timeStamp < ONE_DAY) && (d.getDate() == dd.getDate())) { 61 | // sdf = new SimpleDateFormat("HH:mm"); 62 | return "今天"; 63 | } else if (dd.getYear() != d.getYear()) {// 如果是不同年份 64 | sdf = new SimpleDateFormat("yyyy-MM-dd"); 65 | } else { 66 | sdf = new SimpleDateFormat("MM-dd"); 67 | } 68 | return sdf.format(d); 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | return ""; 73 | } 74 | 75 | public static final long ONE_DAY = 24 * 60 * 60 * 1000L; 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/DeviceUtil.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | 7 | /** 8 | * Created by likebamboo on 2015/5/30. 9 | */ 10 | public class DeviceUtil { 11 | 12 | /** 13 | * 获取软件版本名称 14 | * 15 | * @param ctx 16 | * @return 17 | */ 18 | public static String getVersionName(Context ctx) { 19 | if (ctx == null) { 20 | return ""; 21 | } 22 | 23 | try { 24 | PackageManager pm = ctx.getPackageManager(); 25 | PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 0); 26 | return pi.versionName; 27 | } catch (PackageManager.NameNotFoundException e) { 28 | e.printStackTrace(); 29 | } 30 | return ""; 31 | } 32 | 33 | /** 34 | * 获取软件版本号 35 | * 36 | * @param ctx 37 | * @return 38 | */ 39 | public static int getVersionCode(Context ctx) { 40 | if (ctx == null) { 41 | return 0; 42 | } 43 | 44 | try { 45 | PackageManager pm = ctx.getPackageManager(); 46 | PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 0); 47 | return pi.versionCode; 48 | } catch (PackageManager.NameNotFoundException e) { 49 | e.printStackTrace(); 50 | } 51 | return 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/NetworkUtil.java: -------------------------------------------------------------------------------- 1 | 2 | package com.likebamboo.osa.android.utils; 3 | 4 | import android.content.Context; 5 | import android.net.ConnectivityManager; 6 | import android.net.NetworkInfo; 7 | import android.util.Log; 8 | 9 | /** 10 | * @author likebamboo 11 | * @date 2015年5月17日 12 | */ 13 | public class NetworkUtil { 14 | 15 | /** 16 | * Returns whether the network is available 17 | * 18 | * @param context Context 19 | * @return 网络是否可用 20 | * @see [类、类#方法、类#成员] 21 | */ 22 | public static boolean isNetworkAvailable(Context context) { 23 | return getConnectedNetworkInfo(context) != null; 24 | } 25 | 26 | /** 27 | * 获取网络类型 28 | * 29 | * @param context Context 30 | * @return 网络类型 31 | * @see [类、类#方法、类#成员] 32 | */ 33 | public static int getNetworkType(Context context) { 34 | NetworkInfo networkInfo = getConnectedNetworkInfo(context); 35 | if (networkInfo != null) { 36 | return networkInfo.getType(); 37 | } 38 | 39 | return -1; 40 | } 41 | 42 | public static NetworkInfo getConnectedNetworkInfo(Context context) { 43 | try { 44 | ConnectivityManager connectivity = (ConnectivityManager) context 45 | .getSystemService(Context.CONNECTIVITY_SERVICE); 46 | if (connectivity == null) { 47 | Log.e("network", "couldn't get connectivity manager"); 48 | } else { 49 | NetworkInfo info = connectivity.getActiveNetworkInfo(); 50 | if (info != null) { 51 | return info; 52 | } 53 | } 54 | } catch (Exception e) { 55 | Log.e("network", e.toString(), e); 56 | } 57 | return null; 58 | } 59 | 60 | /** 61 | * 判断网络是不是手机网络,非wifi 62 | * 63 | * @param context Context 64 | * @return boolean 65 | * @see [类、类#方法、类#成员] 66 | */ 67 | public static boolean isMobileNetwork(Context context) { 68 | return (ConnectivityManager.TYPE_MOBILE == getNetworkType(context)); 69 | } 70 | 71 | /** 72 | * 判断网络是不是wifi 73 | * 74 | * @param context Context 75 | * @return boolean 76 | * @see [类、类#方法、类#成员] 77 | */ 78 | public static boolean isWifiNetwork(Context context) { 79 | return (ConnectivityManager.TYPE_WIFI == getNetworkType(context)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/PreferencesUtil.java: -------------------------------------------------------------------------------- 1 | 2 | package com.likebamboo.osa.android.utils; 3 | 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | 7 | /** 8 | * sharedPreferences工具类 9 | * 10 | * @author likebamboo 11 | * @version [版本号, 2015年6月8日] 12 | * @see [相关类/方法] 13 | * @since [产品/模块版本] 14 | */ 15 | public class PreferencesUtil { 16 | 17 | /** 18 | * 当前数据库版本 19 | */ 20 | public static final String PREF_DB_VERSION = "pref_db_version"; 21 | 22 | private static String PREF_NAME = "osa_simple_data"; 23 | 24 | private static PreferencesUtil mInstance = null; 25 | 26 | private SharedPreferences mSettings; 27 | 28 | private SharedPreferences.Editor mEditor; 29 | 30 | public static PreferencesUtil getInstance(Context context) { 31 | if (mInstance == null) { 32 | mInstance = new PreferencesUtil(context.getApplicationContext()); 33 | } 34 | return mInstance; 35 | } 36 | 37 | public boolean contains(String key) { 38 | return mSettings.contains(key); 39 | } 40 | 41 | public String getString(String key) { 42 | return mSettings.getString(key, ""); 43 | } 44 | 45 | public String getString(String key, String defValue) { 46 | return mSettings.getString(key, defValue); 47 | } 48 | 49 | public void putString(String key, String value) { 50 | mEditor.putString(key, value); 51 | mEditor.commit(); 52 | } 53 | 54 | public void remove(String key) { 55 | mEditor.remove(key); 56 | mEditor.commit(); 57 | } 58 | 59 | public void clear() { 60 | mEditor.clear(); 61 | mEditor.commit(); 62 | } 63 | 64 | public boolean getBoolean(String key, boolean defValue) { 65 | return mSettings.getBoolean(key, defValue); 66 | } 67 | 68 | public void putBoolean(String key, boolean value) { 69 | mEditor.putBoolean(key, value); 70 | mEditor.commit(); 71 | } 72 | 73 | public int getInt(String key, int defValue) { 74 | return mSettings.getInt(key, defValue); 75 | } 76 | 77 | public void putInt(String key, int value) { 78 | mEditor.putInt(key, value); 79 | mEditor.commit(); 80 | } 81 | 82 | public long getLong(String key, long defValue) { 83 | return mSettings.getLong(key, defValue); 84 | } 85 | 86 | public void putLong(String key, long value) { 87 | mEditor.putLong(key, value); 88 | mEditor.commit(); 89 | } 90 | 91 | private PreferencesUtil(Context c) { 92 | mSettings = c.getSharedPreferences(PREF_NAME, 0); 93 | mEditor = mSettings.edit(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.utils; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | /** 7 | * toast util 8 | * 9 | * @author likebamboo 10 | * @since 2015-06-08 11 | */ 12 | public class ToastUtil { 13 | 14 | public static void show(Context context, int resId) { 15 | show(context, context.getResources().getText(resId), Toast.LENGTH_SHORT); 16 | } 17 | 18 | public static void show(Context context, int resId, int duration) { 19 | show(context, context.getResources().getText(resId), duration); 20 | } 21 | 22 | public static void show(Context context, CharSequence text) { 23 | show(context, text, Toast.LENGTH_SHORT); 24 | } 25 | 26 | public static void show(Context context, CharSequence text, int duration) { 27 | if (context == null) { 28 | return; 29 | } 30 | Toast.makeText(context, text, duration).show(); 31 | } 32 | 33 | public static void show(Context context, int resId, Object... args) { 34 | show(context, String.format(context.getResources().getString(resId), args), Toast.LENGTH_SHORT); 35 | } 36 | 37 | public static void show(Context context, String format, Object... args) { 38 | show(context, String.format(format, args), Toast.LENGTH_SHORT); 39 | } 40 | 41 | public static void show(Context context, int resId, int duration, Object... args) { 42 | show(context, String.format(context.getResources().getString(resId), args), duration); 43 | } 44 | 45 | public static void show(Context context, String format, int duration, Object... args) { 46 | show(context, String.format(format, args), duration); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/UrlDetect.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.utils; 2 | 3 | import android.text.TextUtils; 4 | import android.webkit.URLUtil; 5 | 6 | import com.likebamboo.osa.android.request.RequestUrl; 7 | 8 | /** 9 | * URL 检测 10 | * Created by wentaoli on 2015/5/27. 11 | */ 12 | public class UrlDetect { 13 | 14 | /** 15 | * 是否为正确的url 16 | * 17 | * @param url 18 | * @return 19 | */ 20 | public static boolean isValidURL(String url) { 21 | if (TextUtils.isEmpty(url)) { 22 | return false; 23 | } 24 | return URLUtil.isValidUrl(url); 25 | } 26 | 27 | /** 28 | * 是否为本站URL 29 | * 30 | * @param url 31 | */ 32 | public static boolean isOurselvesURL(String url) { 33 | if (!isValidURL(url)) { 34 | return false; 35 | } 36 | if (!url.startsWith("http")) { 37 | url = "http://" + url; 38 | } 39 | System.out.println(url); 40 | if (url.startsWith(RequestUrl.BASE_URL)) { 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | /** 47 | * 是否是博客链接 48 | * 49 | * @param url 50 | * @return 51 | */ 52 | public static String isBlogUrl(String url) { 53 | if (!isOurselvesURL(url)) { 54 | return ""; 55 | } 56 | if (!url.startsWith("http")) { 57 | url = "http://" + url; 58 | } 59 | // 查看该链接是否以博客url开头 60 | if (!url.startsWith(RequestUrl.BLOG_URL)) { 61 | return ""; 62 | } 63 | // 去掉链接前面的内容 64 | url = url.substring((RequestUrl.BLOG_URL + "/").length()); 65 | // 如果处理后的 url 不含"/",或者只有最后一个字符是"/",说明是blog链接 66 | if (!url.contains("/") || url.indexOf("/") == url.length() - 1) { 67 | if (url.endsWith("/")) { 68 | url = url.substring(0, url.length() - 1); 69 | } 70 | return url; 71 | } 72 | return ""; 73 | } 74 | 75 | 76 | /** 77 | * 是否是标签blog列表链接 78 | * 79 | * @param url 80 | * @return 81 | */ 82 | public static String isTagBlogUrl(String url) { 83 | if (!isOurselvesURL(url)) { 84 | return ""; 85 | } 86 | if (!url.startsWith("http")) { 87 | url = "http://" + url; 88 | } 89 | // 查看该链接是否以博客url开头 90 | if (!url.startsWith(RequestUrl.BLOG_URL)) { 91 | return ""; 92 | } 93 | // 去掉链接前面的内容 94 | url = url.substring((RequestUrl.BLOG_URL + "/").length()); 95 | // 如果 url 不是以 "tag/" 开头 96 | if (!url.toLowerCase().startsWith("tag/")) { 97 | return ""; 98 | } 99 | // 截取 "tag/" 后边的内容 100 | url = url.substring(4); 101 | // 如果处理后的 url 不含"/",或者只有最后一个字符是"/",说明是 tag blog链接 102 | if (!url.contains("/") || url.indexOf("/") == url.length() - 1) { 103 | if (url.endsWith("/")) { 104 | url = url.substring(0, url.length() - 1); 105 | } 106 | return url; 107 | } 108 | return ""; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/likebamboo/osa/android/utils/ValidateUtil.java: -------------------------------------------------------------------------------- 1 | package com.likebamboo.osa.android.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * 格式验证工具类 10 | * Created by likebamboo on 2015/7/16. 11 | */ 12 | public class ValidateUtil { 13 | 14 | /** 15 | * 验证手机格式 16 | * 17 | * @param num 18 | * @return 19 | */ 20 | public static boolean isPhoneNum(String num) { 21 | if (TextUtils.isEmpty(num) || num.length() < 11) { 22 | return false; 23 | } 24 | Pattern pattern = Pattern.compile("^(13[0-9]|15[0-9]|14[7|5]|17[0-9]|18[0-9])\\d{8}$"); 25 | // 匹配手机号码 26 | Matcher matcher = pattern.matcher(num); 27 | return matcher.matches(); 28 | } 29 | 30 | /** 31 | * 验证邮箱格式 32 | * 33 | * @param email 34 | * @return 35 | */ 36 | public static boolean isEmail(String email) { 37 | String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"; 38 | Pattern p = Pattern.compile(str); 39 | Matcher m = p.matcher(email); 40 | 41 | return m.matches(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_down_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_up_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bg_card.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/bg_card.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bg_card_active.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/bg_card_active.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/default_avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/ic_add_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-hdpi/ic_up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-xhdpi/ic_up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_card_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_feedback_submit_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_list_item_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_tag_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_translucent_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/list_divider_padded_vertical.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recycler_view_fast_scroller__bubble.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recycler_view_fast_scroller__handle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/webview_progress_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 25 | 26 | 27 | 37 | 38 | 46 | 47 | 56 | 57 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_author.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | 32 | 33 | 34 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_blog.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 18 | 19 | 27 | 28 | 36 | 37 | 45 | 46 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_blog_list.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 21 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_category.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 15 | 16 | 17 | 23 | 24 | 29 | 31 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_webview.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_webview.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 25 | 26 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fab_tool_bar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/footer_loading_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 32 | 33 | 34 | 40 | 41 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_author_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 25 | 26 | 27 | 37 | 38 | 45 | 46 | 49 | 50 | 54 | 55 | 56 | 63 | 64 | 67 | 68 | 74 | 75 | 76 | 82 | 83 | 86 | 87 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_feedback.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 29 | 30 | 38 | 39 | 47 | 48 | 57 | 58 | 63 | 64 | 73 | 74 | 84 | 85 | 95 | 96 | 97 | 98 | 99 | 103 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_navigation_drawer.xml: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_author.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 27 | 28 | 34 | 35 | 45 | 46 | 56 | 57 | 58 | 69 | 70 | 71 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_blog.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 27 | 28 | 37 | 38 | 39 | 43 | 44 | 54 | 55 | 56 | 62 | 63 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | 90 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 20 | 21 | 32 | 33 | 43 | 44 | 45 | 46 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_simple_list_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_filter_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 24 | 25 | 35 | 36 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recycler_view_fast_scroller.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/simple_list_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 27 | 28 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/simple_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/space_loading_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 31 | 32 | 33 | 39 | 40 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/layout/webview_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 23 | 24 | 33 | 34 | 35 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/menu/edit.xml: -------------------------------------------------------------------------------- 1 |

5 | 6 | 13 | 14 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/menu/global.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |     发现 6 |     分类 7 |     作者 8 |     收藏 9 |     设置 10 | 11 | 12 | 13 | 14 |     意见反馈 15 |     关于作者 16 |     关于APP 17 | 18 | 19 | 20 | 21 | 默认排序 22 | 发表时间 23 | 标题 24 | 浏览量 25 | 26 | 27 | 28 | 29 | 30 | post_time-desc 31 | title-asc 32 | viewCount-desc 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs_fab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #eeeeee 5 | 6 | #34322d 7 | 8 | #e25600 9 | 10 | #88ffffff 11 | 12 | #faffffff 13 | 14 | 15 | #55000000 16 | 17 | 18 | #e0ffffff 19 | 20 | 21 | #1a000000 22 | 23 | #ff202020 24 | 25 | #ffe0e0e0 26 | #ffe9e9e9 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 8 | 240dp 9 | 10 | 16.0dip 11 | 24.0dip 12 | 8.0dip 13 | 12.0dip 14 | 4.0dip 15 | 6.0dip 16 | 2.0dip 17 | 1.0dip 18 | 32.0dip 19 | 48.0dip 20 | 21 | 16.0dip 22 | 24.0dip 23 | 8.0dip 24 | 12.0dip 25 | 4.0dip 26 | 2.0dip 27 | 32.0dip 28 | 36.0dip 29 | 48.0dip 30 | 31 | 32 | 16.0dip 33 | 18.0dip 34 | 14.0dip 35 | 12.0dip 36 | 10.0dip 37 | 20.0dip 38 | 8.0dip 39 | 22.0dip 40 | 24.0dip 41 | 28.0dip 42 | 43 | 56dp 44 | 16dp 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 4 | 3 5 | 1000 6 | 2000 7 | 3 8 | 2 9 | 2000 10 | 0 11 | 2 12 | 0 13 | 300 14 | 400 15 | 350 16 | 200 17 | 3 18 | 2 19 | 60 20 | 1800 21 | 6587000 22 | 2 23 | 3 24 | 2 25 | 2 26 | 2 27 | 2 28 | 1 29 | 2 30 | 300 31 | 600 32 | 5 33 | 200 34 | 5 35 | 3 36 | 4 37 | 1 38 | 3 39 | 2 40 | 20 41 | 200 42 | 3 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Android博客 4 | 5 | Open navigation drawer 6 | Close navigation drawer 7 | 数据加载中... 8 | 没有啦... 9 | 没有找到想要的数据 10 | 请重试 11 | 12 | 请输入关键字 13 | 14 | 历史记录 15 | %1$s 记录已删除 16 | 网络连接错误,请检查网络状态 17 | 18 | 反馈 19 | 详情 20 | issue 21 | 收藏 22 | 收藏成功 23 | 取消收藏 24 | 取消收藏成功 25 | 未收藏任何博客 26 | 27 | 译者 28 | 作者 29 | 分类 30 | 原文作者 31 | 发表时间 32 | 原文发表时间 33 | 来源链接 34 | 原文链接 35 | 36 | 获取博客信息失败 37 | 38 | 关于 39 | 版本:%1$s 40 | Etsy 。
42 | AndroidBlog网站已经上线。 43 | ]]> 44 |
45 | 更多... 46 | 47 | 全选 48 | 全不选 49 | 删除 50 | 51 | 排序 52 | 53 | 联系方式: 54 | 问题: 55 | 请填写邮箱/手机号 56 | 请填写正确的邮箱或手机号 57 | 请选择问题类型 58 | 描述: 59 | 请填写问题详细信息 60 | 提交 61 | 62 | 您的反馈信息我们已经收到,感谢使用~ 63 |
64 | -------------------------------------------------------------------------------- /art/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/author.png -------------------------------------------------------------------------------- /art/blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/blog.png -------------------------------------------------------------------------------- /art/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/home.png -------------------------------------------------------------------------------- /art/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/info.png -------------------------------------------------------------------------------- /art/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/menu.png -------------------------------------------------------------------------------- /art/menu2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/likebamboo/AndroidBlog/c5db942056fc179b2a45fb1410e5ab9c0f431f37/art/menu2.png -------------------------------------------------------------------------------- /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:1.2.2' 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.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 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.2.1-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /osa-android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------