Repository: salecoding/WanAndroid Branch: master Commit: d4d3ed1a4255 Files: 158 Total size: 10.7 MB Directory structure: gitextract_ys2tdhgz/ ├── .gitignore ├── .idea/ │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ ├── release/ │ │ ├── app-release.apk │ │ └── output.json │ ├── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── lw/ │ │ │ └── wanandroid/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── lw/ │ │ │ │ └── wanandroid/ │ │ │ │ ├── MainActivity.java │ │ │ │ ├── base/ │ │ │ │ │ ├── App.java │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ ├── BaseContract.java │ │ │ │ │ ├── BaseFragment.java │ │ │ │ │ └── BasePresenter.java │ │ │ │ ├── bean/ │ │ │ │ │ ├── Article.java │ │ │ │ │ ├── Banner.java │ │ │ │ │ ├── DataResponse.java │ │ │ │ │ ├── Friend.java │ │ │ │ │ ├── HotKey.java │ │ │ │ │ ├── KnowledgeSystem.java │ │ │ │ │ └── User.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── Constant.java │ │ │ │ │ └── LoadType.java │ │ │ │ ├── db/ │ │ │ │ │ ├── AppDatabase.java │ │ │ │ │ └── HistoryModel.java │ │ │ │ ├── di/ │ │ │ │ │ ├── component/ │ │ │ │ │ │ ├── ActivityComponent.java │ │ │ │ │ │ ├── ApplicationComponent.java │ │ │ │ │ │ ├── FragmentComponent.java │ │ │ │ │ │ └── ServiceComponent.java │ │ │ │ │ ├── module/ │ │ │ │ │ │ ├── ActivityModule.java │ │ │ │ │ │ ├── ApplicationModule.java │ │ │ │ │ │ ├── FragmentModule.java │ │ │ │ │ │ └── ServiceModule.java │ │ │ │ │ └── scope/ │ │ │ │ │ ├── ContextLife.java │ │ │ │ │ ├── PerActivity.java │ │ │ │ │ ├── PerApp.java │ │ │ │ │ ├── PerFragment.java │ │ │ │ │ └── PerService.java │ │ │ │ ├── event/ │ │ │ │ │ └── LoginEvent.java │ │ │ │ ├── net/ │ │ │ │ │ ├── ApiService.java │ │ │ │ │ ├── CookiesManager.java │ │ │ │ │ ├── OkHttpCookies.java │ │ │ │ │ ├── PersistentCookieStore.java │ │ │ │ │ └── RetrofitManager.java │ │ │ │ ├── ui/ │ │ │ │ │ ├── article/ │ │ │ │ │ │ ├── ArticleAdapter.java │ │ │ │ │ │ ├── ArticleContentActivity.java │ │ │ │ │ │ ├── ArticleContentContract.java │ │ │ │ │ │ ├── ArticleContentPresenter.java │ │ │ │ │ │ ├── ArticleListContract.java │ │ │ │ │ │ ├── ArticleListFragment.java │ │ │ │ │ │ ├── ArticleListPresenter.java │ │ │ │ │ │ ├── ArticleTypeActivity.java │ │ │ │ │ │ └── ArticleTypeFragmentPagerAdapter.java │ │ │ │ │ ├── home/ │ │ │ │ │ │ ├── HomeContract.java │ │ │ │ │ │ ├── HomeFragment.java │ │ │ │ │ │ └── HomePresenter.java │ │ │ │ │ ├── hotsearch/ │ │ │ │ │ │ ├── CommonHotAdapter.java │ │ │ │ │ │ ├── HistoryAdapter.java │ │ │ │ │ │ ├── HotAdapter.java │ │ │ │ │ │ ├── HotContract.java │ │ │ │ │ │ ├── HotFragment.java │ │ │ │ │ │ ├── HotPresenter.java │ │ │ │ │ │ ├── SearchActivity.java │ │ │ │ │ │ ├── SearchContract.java │ │ │ │ │ │ └── SearchPresenter.java │ │ │ │ │ ├── knowledgesystem/ │ │ │ │ │ │ ├── KnowledgeSystemAdapter.java │ │ │ │ │ │ ├── KnowledgeSystemContract.java │ │ │ │ │ │ ├── KnowledgeSystemFragment.java │ │ │ │ │ │ └── KnowledgeSystemPresenter.java │ │ │ │ │ ├── my/ │ │ │ │ │ │ ├── LoginActivity.java │ │ │ │ │ │ ├── LoginContract.java │ │ │ │ │ │ ├── LoginPresenter.java │ │ │ │ │ │ ├── MyBookmarkActivity.java │ │ │ │ │ │ ├── MyBookmarkContract.java │ │ │ │ │ │ ├── MyBookmarkPresenter.java │ │ │ │ │ │ ├── MyCollectionActivity.java │ │ │ │ │ │ ├── MyCollectionContract.java │ │ │ │ │ │ ├── MyCollectionPresenter.java │ │ │ │ │ │ ├── MyContract.java │ │ │ │ │ │ ├── MyFragment.java │ │ │ │ │ │ ├── MyPresenter.java │ │ │ │ │ │ └── RegisterActivity.java │ │ │ │ │ └── setting/ │ │ │ │ │ ├── SettingActivity.java │ │ │ │ │ ├── SettingFragment.java │ │ │ │ │ └── TestPageActivity.java │ │ │ │ └── utils/ │ │ │ │ ├── ArticleUtils.java │ │ │ │ ├── GlideImageLoader.java │ │ │ │ ├── GsonUtils.java │ │ │ │ ├── RxBus.java │ │ │ │ └── RxSchedulers.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ ├── ic_action_browser.xml │ │ │ │ ├── ic_action_hot.xml │ │ │ │ ├── ic_action_like.xml │ │ │ │ ├── ic_action_no_like.xml │ │ │ │ ├── ic_action_search.xml │ │ │ │ ├── ic_action_share.xml │ │ │ │ ├── ic_action_white_like.xml │ │ │ │ ├── ic_chevron_right_black_24dp.xml │ │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ │ ├── ic_home_black_24dp.xml │ │ │ │ ├── ic_my_black_24dp.xml │ │ │ │ ├── item_selector.xml │ │ │ │ └── item_selector_hot.xml │ │ │ ├── drawable-v21/ │ │ │ │ └── item_selector_hot.xml │ │ │ ├── layout/ │ │ │ │ ├── activity_article_content.xml │ │ │ │ ├── activity_article_type.xml │ │ │ │ ├── activity_login.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_my_bookmark.xml │ │ │ │ ├── activity_my_collection.xml │ │ │ │ ├── activity_register.xml │ │ │ │ ├── activity_search.xml │ │ │ │ ├── activity_setting.xml │ │ │ │ ├── activity_test_page.xml │ │ │ │ ├── fragment_article_list.xml │ │ │ │ ├── fragment_home.xml │ │ │ │ ├── fragment_hot.xml │ │ │ │ ├── fragment_knowledge_system.xml │ │ │ │ ├── fragment_my.xml │ │ │ │ ├── item_article.xml │ │ │ │ ├── item_history.xml │ │ │ │ ├── item_hot.xml │ │ │ │ ├── item_knowledge_system.xml │ │ │ │ ├── layout_empty_view.xml │ │ │ │ ├── layout_error_view.xml │ │ │ │ ├── layout_home_banner_head.xml │ │ │ │ ├── layout_hot_head.xml │ │ │ │ └── layout_search_head.xml │ │ │ ├── menu/ │ │ │ │ ├── menu_content.xml │ │ │ │ ├── menu_main.xml │ │ │ │ ├── menu_search.xml │ │ │ │ ├── menu_type_content.xml │ │ │ │ └── navigation.xml │ │ │ ├── values/ │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── fonts.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ └── settings_preference_fragment.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── lw/ │ │ └── wanandroid/ │ │ └── ExampleUnitTest.java │ └── wanandroid.jks ├── build.gradle ├── config.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ #WanAndroid Try to build a www.wanandroid.com client # APK [app-release.apk](https://coding.net/u/salecoding/p/WanAndroid/git/raw/master/app/release/app-release.apk) # 接口 [玩Android接口](http://www.wanandroid.com/blog/show/2) # 代码结构 ### MVP + RxJava + Retrofit2 + Dagger2 + Glide * [RxJava2](https://github.com/ReactiveX/RxJava) * [Dagger2](https://github.com/google/dagger) * [Retrofit2](https://github.com/square/retrofit) * [鸿神的Flowlayout](https://github.com/hongyangAndroid/FlowLayout) * [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper) * [Agentweb](https://github.com/Justson/AgentWeb) * [Glide](https://github.com/bumptech/glide) # 项目运行截图 # 参考项目 WanAndroidClient https://github.com/wangzailfm/WanAndroidClient 微阅 https://github.com/Will-Ls/WeiYue 非常感谢以上开源项目的作者!谢谢! [Github](https://github.com/salecoding) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { signingConfigs { config { keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_KEY_PASSWORD storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD } } compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.android.buildToolsVersion defaultConfig { applicationId rootProject.ext.android.applicationId minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } multiDexEnabled true } buildTypes { release { minifyEnabled false signingConfig signingConfigs.config proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { buildConfigField "boolean", "LOG_DEBUG", "true" minifyEnabled false signingConfig signingConfigs.config proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } buildToolsVersion '26.0.2' } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile rootProject.ext.dependencies["appcompat-v7"] compile rootProject.ext.dependencies["support-v4"] compile rootProject.ext.dependencies["cardview"] compile rootProject.ext.dependencies["design"] compile rootProject.ext.dependencies["recyclerview"] compile rootProject.ext.dependencies["preference"] compile rootProject.ext.dependencies["retrofit2"] compile rootProject.ext.dependencies["converter-scalars"] compile rootProject.ext.dependencies["converter-gson"] compile rootProject.ext.dependencies["adapter-rxjava2"] compile rootProject.ext.dependencies["rxlifecycle2"] compile rootProject.ext.dependencies["rxlifecomponents"] compile rootProject.ext.dependencies["dagger"] annotationProcessor rootProject.ext.dependencies["dagger-compiler"] compile rootProject.ext.dependencies["rxjava"] compile rootProject.ext.dependencies["rxandroid"] compile rootProject.ext.dependencies["rxbinding2"] compile rootProject.ext.dependencies["constraint-layout"] compile rootProject.ext.dependencies["butterknife"] annotationProcessor rootProject.ext.dependencies["butterknife-compiler"] compile rootProject.ext.dependencies["BaseAdapterHelper"] compile rootProject.ext.dependencies["glide"] compile rootProject.ext.dependencies["banner"] compile rootProject.ext.dependencies["circleimageview"] compile rootProject.ext.dependencies["utilcode"] compile rootProject.ext.dependencies["agentweb"] compile rootProject.ext.dependencies["flowlayout"] compile rootProject.ext.dependencies["fragmentation"] compile rootProject.ext.dependencies["arouter-api"] annotationProcessor rootProject.ext.dependencies["arouter-compiler"] annotationProcessor rootProject.ext.dependencies["dbflow-processor"] compile rootProject.ext.dependencies["dbflow-core"] compile rootProject.ext.dependencies["dbflow"] compile rootProject.ext.dependencies["dbflow-sqlcipher"] testCompile 'junit:junit:4.12' } repositories { mavenCentral() } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in D:\Android\SDK/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/release/app-release.apk ================================================ [File too large to display: 10.5 MB] ================================================ FILE: app/release/output.json ================================================ [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.will.weiyue","split":"","minSdkVersion":"19"}}] ================================================ FILE: app/src/androidTest/java/com/lw/wanandroid/ExampleInstrumentedTest.java ================================================ package com.lw.wanandroid; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.lw.wanandroid", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/lw/wanandroid/MainActivity.java ================================================ package com.lw.wanandroid; import android.support.annotation.NonNull; import android.support.design.widget.BottomNavigationView; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.ToastUtils; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.ui.home.HomeFragment; import com.lw.wanandroid.ui.hotsearch.HotFragment; import com.lw.wanandroid.ui.knowledgesystem.KnowledgeSystemFragment; import com.lw.wanandroid.ui.my.MyFragment; import butterknife.BindView; import me.yokeyword.fragmentation.ISupportFragment; @Route(path = "/wanandroid/MainActivity") public class MainActivity extends BaseActivity implements BottomNavigationView.OnNavigationItemSelectedListener { @BindView(R.id.navigation) BottomNavigationView mNavigation; private ISupportFragment[] mFragments = new ISupportFragment[4]; private long mExitTime; private int preIndex; @Override protected int getLayoutId() { return R.layout.activity_main; } @Override protected void initInjector() { } @Override protected void initView() { mNavigation.setOnNavigationItemSelectedListener(this); ISupportFragment homeFragment = findFragment(HomeFragment.class); if (homeFragment == null) { mFragments[0] = HomeFragment.newInstance(); mFragments[1] = KnowledgeSystemFragment.newInstance(); mFragments[2] = MyFragment.newInstance(); mFragments[3] = HotFragment.newInstance(); loadMultipleRootFragment(R.id.layout_fragment, 0, mFragments[0], mFragments[1], mFragments[2], mFragments[3]); } else { // 这里我们需要拿到mFragments的引用 mFragments[0] = homeFragment; mFragments[1] = findFragment(KnowledgeSystemFragment.class); mFragments[2] = findFragment(MyFragment.class); mFragments[3] = findFragment(HotFragment.class); } } @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: mToolbar.setTitle(R.string.app_name); showHideFragment(mFragments[0], mFragments[preIndex]); preIndex = 0; break; case R.id.navigation_knowledgesystem: mToolbar.setTitle(R.string.title_knowledgesystem); showHideFragment(mFragments[1], mFragments[preIndex]); preIndex = 1; break; case R.id.navigation_my: mToolbar.setTitle(R.string.title_my); showHideFragment(mFragments[2], mFragments[preIndex]); preIndex = 2; break; } return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menuHot) { mToolbar.setTitle(R.string.hot_title); showHideFragment(mFragments[3], mFragments[preIndex]); preIndex = 3; } else if (item.getItemId() == R.id.menuSearch) { ARouter.getInstance().build("/hotsearch/SearchActivity").navigation(); } return super.onOptionsItemSelected(item); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if ((System.currentTimeMillis() - mExitTime) > 2000) { ToastUtils.showShort(R.string.exit_system); mExitTime = System.currentTimeMillis(); } else { System.exit(0); } return true; } return super.onKeyDown(keyCode, event); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/base/App.java ================================================ package com.lw.wanandroid.base; import android.app.Application; import android.content.Context; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.Utils; import com.lw.wanandroid.BuildConfig; import com.lw.wanandroid.di.component.ApplicationComponent; import com.lw.wanandroid.di.component.DaggerApplicationComponent; import com.lw.wanandroid.di.module.ApplicationModule; import com.raizlabs.android.dbflow.config.FlowManager; import me.yokeyword.fragmentation.Fragmentation; /** * Created by lw on 2018/1/18. */ public class App extends Application { private ApplicationComponent mApplicationComponent; private static App mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; initApplicationComponent(); Utils.init(this); intARouter(); FlowManager.init(this); Fragmentation.builder() .stackViewMode(Fragmentation.BUBBLE) // 显示悬浮球 ; 其他Mode:SHAKE: 摇一摇唤出 NONE:隐藏 .debug(BuildConfig.DEBUG) .install(); } /** * 初始化路由 */ private void intARouter() { if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } ARouter.init(this); // 尽可能早,推荐在Application中初始化 } /** * 初始化ApplicationComponent */ private void initApplicationComponent() { mApplicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build(); } public ApplicationComponent getApplicationComponent() { return mApplicationComponent; } public static Context getAppContext() { return mInstance.getApplicationContext(); } public static App getInstance() { return mInstance; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/base/BaseActivity.java ================================================ package com.lw.wanandroid.base; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.MotionEvent; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.NetworkUtils; import com.blankj.utilcode.util.ToastUtils; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.di.component.ActivityComponent; import com.lw.wanandroid.di.component.DaggerActivityComponent; import com.lw.wanandroid.di.module.ActivityModule; import com.trello.rxlifecycle2.LifecycleTransformer; import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; import java.util.List; import javax.inject.Inject; import butterknife.ButterKnife; import butterknife.Unbinder; import me.yokeyword.fragmentation.ExtraTransaction; import me.yokeyword.fragmentation.ISupportActivity; import me.yokeyword.fragmentation.ISupportFragment; import me.yokeyword.fragmentation.SupportActivityDelegate; import me.yokeyword.fragmentation.SupportFragment; import me.yokeyword.fragmentation.SupportHelper; import me.yokeyword.fragmentation.anim.FragmentAnimator; /** * Created by lw on 2018/1/18. */ public abstract class BaseActivity extends RxAppCompatActivity implements ISupportActivity, BaseContract.BaseView { @Nullable @Inject protected T mPresenter; protected ActivityComponent mActivityComponent; @Nullable protected Toolbar mToolbar; private Unbinder unbinder; protected abstract int getLayoutId(); protected abstract void initInjector(); protected abstract void initView(); /** * 是否显示返回键 * * @return */ protected boolean showHomeAsUp() { return false; } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDelegate.onCreate(savedInstanceState); initActivityComponent(); ARouter.getInstance().inject(this); int layoutId = getLayoutId(); setContentView(layoutId); initInjector(); unbinder = ButterKnife.bind(this); initToolBar(); attachView(); initView(); if (!NetworkUtils.isConnected()) showNoNet(); } @Override protected void onDestroy() { super.onDestroy(); mDelegate.onDestroy(); unbinder.unbind(); detachView(); } @Override public void showLoading() { } @Override public void hideLoading() { } @Override public void showSuccess(String successMsg) { ToastUtils.showShort(successMsg); } @Override public void showFaild(String errorMsg) { ToastUtils.showShort(errorMsg); } @Override public void showNoNet() { ToastUtils.showShort(R.string.no_network_connection); } @Override public void onRetry() { } @Override public LifecycleTransformer bindToLife() { return this.bindToLifecycle(); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case android.R.id.home: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { finishAfterTransition(); } else { finish(); } break; } return true; } final SupportActivityDelegate mDelegate = new SupportActivityDelegate(this); @Override public SupportActivityDelegate getSupportDelegate() { return mDelegate; } /** * Perform some extra transactions. * 额外的事务:自定义Tag,添加SharedElement动画,操作非回退栈Fragment */ @Override public ExtraTransaction extraTransaction() { return mDelegate.extraTransaction(); } @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mDelegate.onPostCreate(savedInstanceState); } /** * Note: return mDelegate.dispatchTouchEvent(ev) || super.dispatchTouchEvent(ev); */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { return mDelegate.dispatchTouchEvent(ev) || super.dispatchTouchEvent(ev); } /** * 不建议复写该方法,请使用 {@link #onBackPressedSupport} 代替 */ @Override final public void onBackPressed() { mDelegate.onBackPressed(); } /** * 该方法回调时机为,Activity回退栈内Fragment的数量 小于等于1 时,默认finish Activity * 请尽量复写该方法,避免复写onBackPress(),以保证SupportFragment内的onBackPressedSupport()回退事件正常执行 */ @Override public void onBackPressedSupport() { mDelegate.onBackPressedSupport(); } /** * 获取设置的全局动画 copy * * @return FragmentAnimator */ @Override public FragmentAnimator getFragmentAnimator() { return mDelegate.getFragmentAnimator(); } /** * Set all fragments animation. * 设置Fragment内的全局动画 */ @Override public void setFragmentAnimator(FragmentAnimator fragmentAnimator) { mDelegate.setFragmentAnimator(fragmentAnimator); } /** * Set all fragments animation. * 构建Fragment转场动画 *

* 如果是在Activity内实现,则构建的是Activity内所有Fragment的转场动画, * 如果是在Fragment内实现,则构建的是该Fragment的转场动画,此时优先级 > Activity的onCreateFragmentAnimator() * * @return FragmentAnimator对象 */ @Override public FragmentAnimator onCreateFragmentAnimator() { return mDelegate.onCreateFragmentAnimator(); } /** * Causes the Runnable r to be added to the action queue. *

* The runnable will be run after all the previous action has been run. *

* 前面的事务全部执行后 执行该Action */ @Override public void post(Runnable runnable) { mDelegate.post(runnable); } protected void setToolbarTitle(String title) { getSupportActionBar().setTitle(title); } /** * 设置加载数据结果 * * @param baseQuickAdapter * @param refreshLayout * @param list * @param loadType */ protected void setLoadDataResult(BaseQuickAdapter baseQuickAdapter, SwipeRefreshLayout refreshLayout, List list, @LoadType.checker int loadType) { switch (loadType) { case LoadType.TYPE_REFRESH_SUCCESS: baseQuickAdapter.setNewData(list); refreshLayout.setRefreshing(false); break; case LoadType.TYPE_REFRESH_ERROR: refreshLayout.setRefreshing(false); break; case LoadType.TYPE_LOAD_MORE_SUCCESS: if (list != null) baseQuickAdapter.addData(list); break; case LoadType.TYPE_LOAD_MORE_ERROR: baseQuickAdapter.loadMoreFail(); break; } if (list == null || list.isEmpty() || list.size() < Constant.PAGE_SIZE) { baseQuickAdapter.loadMoreEnd(false); } else { baseQuickAdapter.loadMoreComplete(); } } // 选择性拓展其他方法 public void loadRootFragment(int containerId, @NonNull ISupportFragment toFragment) { mDelegate.loadRootFragment(containerId, toFragment); } public void start(ISupportFragment toFragment) { mDelegate.start(toFragment); } /** * @param launchMode Same as Activity's LaunchMode. */ public void start(ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode) { mDelegate.start(toFragment, launchMode); } /** * It is recommended to use {@link SupportFragment#startWithPopTo(ISupportFragment, Class, boolean)}. * * @see #popTo(Class, boolean) * + * @see #start(ISupportFragment) */ public void startWithPopTo(ISupportFragment toFragment, Class targetFragmentClass, boolean includeTargetFragment) { mDelegate.startWithPopTo(toFragment, targetFragmentClass, includeTargetFragment); } /** * Pop the fragment. */ public void pop() { mDelegate.pop(); } /** * Pop the last fragment transition from the manager's fragment * back stack. */ public void popTo(Class targetFragmentClass, boolean includeTargetFragment) { mDelegate.popTo(targetFragmentClass, includeTargetFragment); } /** * If you want to begin another FragmentTransaction immediately after popTo(), use this method. * 如果你想在出栈后, 立刻进行FragmentTransaction操作,请使用该方法 */ public void popTo(Class targetFragmentClass, boolean includeTargetFragment, Runnable afterPopTransactionRunnable) { mDelegate.popTo(targetFragmentClass, includeTargetFragment, afterPopTransactionRunnable); } public void popTo(Class targetFragmentClass, boolean includeTargetFragment, Runnable afterPopTransactionRunnable, int popAnim) { mDelegate.popTo(targetFragmentClass, includeTargetFragment, afterPopTransactionRunnable, popAnim); } /** * 得到位于栈顶Fragment */ public ISupportFragment getTopFragment() { return SupportHelper.getTopFragment(getSupportFragmentManager()); } /** * 获取栈内的fragment对象 */ public T findFragment(Class fragmentClass) { return SupportHelper.findFragment(getSupportFragmentManager(), fragmentClass); } /** * 加载多个同级根Fragment,类似Wechat, QQ主页的场景 */ public void loadMultipleRootFragment(int containerId, int showPosition, ISupportFragment... toFragments) { mDelegate.loadMultipleRootFragment(containerId, showPosition, toFragments); } /** * show一个Fragment,hide其他同栈所有Fragment * 使用该方法时,要确保同级栈内无多余的Fragment,(只有通过loadMultipleRootFragment()载入的Fragment) *

* 建议使用更明确的{@link #showHideFragment(ISupportFragment, ISupportFragment)} * * @param showFragment 需要show的Fragment */ public void showHideFragment(ISupportFragment showFragment) { mDelegate.showHideFragment(showFragment); } /** * show一个Fragment,hide一个Fragment ; 主要用于类似微信主页那种 切换tab的情况 */ public void showHideFragment(ISupportFragment showFragment, ISupportFragment hideFragment) { mDelegate.showHideFragment(showFragment, hideFragment); } /** * 初始化ActivityComponent */ private void initActivityComponent() { mActivityComponent = DaggerActivityComponent.builder() .applicationComponent(((App) getApplication()).getApplicationComponent()) .activityModule(new ActivityModule(this)) .build(); } /** * 初始化toolbar */ private void initToolBar() { mToolbar = (Toolbar) findViewById(R.id.toolbar); if (mToolbar == null) { throw new NullPointerException("toolbar can not be null"); } setSupportActionBar(mToolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(showHomeAsUp()); /**toolbar除掉阴影*/ getSupportActionBar().setElevation(0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mToolbar.setElevation(0); } } /** * 贴上view */ private void attachView() { if (mPresenter != null) { mPresenter.attachView(this); } } /** * 分离view */ private void detachView() { if (mPresenter != null) { mPresenter.detachView(); } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/base/BaseContract.java ================================================ package com.lw.wanandroid.base; import com.trello.rxlifecycle2.LifecycleTransformer; /** * desc: * author: Will . * date: 2017/9/2 . */ public interface BaseContract { interface BasePresenter { void attachView(T view); void detachView(); } interface BaseView { //显示进度中 void showLoading(); //隐藏进度 void hideLoading(); //显示请求成功 void showSuccess(String message); //失败重试 void showFaild(String message); //显示当前网络不可用 void showNoNet(); //重试 void onRetry(); /** * 绑定生命周期 * * @param * @return */ LifecycleTransformer bindToLife(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/base/BaseFragment.java ================================================ package com.lw.wanandroid.base; import android.app.Activity; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.support.v4.widget.SwipeRefreshLayout; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.NetworkUtils; import com.blankj.utilcode.util.ToastUtils; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.di.component.DaggerFragmentComponent; import com.lw.wanandroid.di.component.FragmentComponent; import com.lw.wanandroid.di.module.FragmentModule; import com.trello.rxlifecycle2.LifecycleTransformer; import com.trello.rxlifecycle2.components.support.RxFragment; import java.util.List; import javax.inject.Inject; import butterknife.ButterKnife; import butterknife.Unbinder; import me.yokeyword.fragmentation.ExtraTransaction; import me.yokeyword.fragmentation.ISupportFragment; import me.yokeyword.fragmentation.SupportFragmentDelegate; import me.yokeyword.fragmentation.SupportHelper; import me.yokeyword.fragmentation.anim.FragmentAnimator; /** * Created by lw on 2018/1/18. */ public abstract class BaseFragment extends RxFragment implements ISupportFragment, BaseContract.BaseView { private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN"; @Nullable @Inject protected T mPresenter; protected FragmentComponent mFragmentComponent; private Unbinder unbinder; private View mRootView, mErrorView, mEmptyView; protected abstract int getLayoutId(); protected abstract void initInjector(); protected abstract void initView(View view); @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDelegate.onCreate(savedInstanceState); initFragmentComponent(); ARouter.getInstance().inject(this); initInjector(); attachView(); if (!NetworkUtils.isConnected()) showNoNet(); if (savedInstanceState != null) { boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN); FragmentTransaction ft = getFragmentManager().beginTransaction(); if (isSupportHidden) { ft.hide(this); } else { ft.show(this); } ft.commit(); } } @Override public void onSaveInstanceState(Bundle outState) { mDelegate.onSaveInstanceState(outState); outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden()); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { inflaterView(inflater, container); unbinder = ButterKnife.bind(this, mRootView); initView(mRootView); return mRootView; } @Override public void onDestroy() { super.onDestroy(); mDelegate.onDestroy(); unbinder.unbind(); detachView(); } @Override public void showLoading() { ToastUtils.showShort("showLoading"); } @Override public void hideLoading() { ToastUtils.showShort("hideLoading"); } @Override public void showSuccess(String successMsg) { ToastUtils.showShort(successMsg); } @Override public void showFaild(String errorMsg) { ToastUtils.showShort(errorMsg); } @Override public void showNoNet() { ToastUtils.showShort(R.string.no_network_connection); } @Override public void onRetry() { ToastUtils.showShort("onRetry"); } @Override public LifecycleTransformer bindToLife() { return this.bindToLifecycle(); } /** * 设置加载数据结果 * * @param baseQuickAdapter * @param refreshLayout * @param list * @param loadType */ protected void setLoadDataResult(BaseQuickAdapter baseQuickAdapter, SwipeRefreshLayout refreshLayout, List list, @LoadType.checker int loadType) { switch (loadType) { case LoadType.TYPE_REFRESH_SUCCESS: baseQuickAdapter.setNewData(list); refreshLayout.setRefreshing(false); break; case LoadType.TYPE_REFRESH_ERROR: refreshLayout.setRefreshing(false); break; case LoadType.TYPE_LOAD_MORE_SUCCESS: if (list != null) baseQuickAdapter.addData(list); break; case LoadType.TYPE_LOAD_MORE_ERROR: baseQuickAdapter.loadMoreFail(); break; } if (list == null || list.isEmpty() || list.size() < Constant.PAGE_SIZE) { baseQuickAdapter.loadMoreEnd(false); } else { baseQuickAdapter.loadMoreComplete(); } } /** * 初始化FragmentComponent */ private void initFragmentComponent() { mFragmentComponent = DaggerFragmentComponent.builder() .applicationComponent(((App) getActivity().getApplication()).getApplicationComponent()) .fragmentModule(new FragmentModule(this)) .build(); } /** * 贴上view */ private void attachView() { if (mPresenter != null) { mPresenter.attachView(this); } } /** * 分离view */ private void detachView() { if (mPresenter != null) { mPresenter.detachView(); } } /** * 设置View * * @param inflater * @param container */ private void inflaterView(LayoutInflater inflater, @Nullable ViewGroup container) { if (mRootView == null) { mRootView = inflater.inflate(getLayoutId(), container, false); } } final SupportFragmentDelegate mDelegate = new SupportFragmentDelegate(this); protected FragmentActivity _mActivity; @Override public SupportFragmentDelegate getSupportDelegate() { return mDelegate; } /** * Perform some extra transactions. * 额外的事务:自定义Tag,添加SharedElement动画,操作非回退栈Fragment */ @Override public ExtraTransaction extraTransaction() { return mDelegate.extraTransaction(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); mDelegate.onAttach(activity); _mActivity = mDelegate.getActivity(); } @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { return mDelegate.onCreateAnimation(transit, enter, nextAnim); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mDelegate.onActivityCreated(savedInstanceState); } @Override public void onResume() { super.onResume(); mDelegate.onResume(); } @Override public void onPause() { super.onPause(); mDelegate.onPause(); } @Override public void onDestroyView() { mDelegate.onDestroyView(); super.onDestroyView(); } @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); mDelegate.onHiddenChanged(hidden); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); mDelegate.setUserVisibleHint(isVisibleToUser); } /** * Causes the Runnable r to be added to the action queue. *

* The runnable will be run after all the previous action has been run. *

* 前面的事务全部执行后 执行该Action * * @deprecated Use {@link #post(Runnable)} instead. */ @Deprecated @Override public void enqueueAction(Runnable runnable) { mDelegate.enqueueAction(runnable); } /** * Causes the Runnable r to be added to the action queue. *

* The runnable will be run after all the previous action has been run. *

* 前面的事务全部执行后 执行该Action */ @Override public void post(Runnable runnable) { mDelegate.post(runnable); } /** * Called when the enter-animation end. * 入栈动画 结束时,回调 */ @Override public void onEnterAnimationEnd(Bundle savedInstanceState) { mDelegate.onEnterAnimationEnd(savedInstanceState); } /** * Lazy initial,Called when fragment is first called. *

* 同级下的 懒加载 + ViewPager下的懒加载 的结合回调方法 */ @Override public void onLazyInitView(@Nullable Bundle savedInstanceState) { mDelegate.onLazyInitView(savedInstanceState); } /** * Called when the fragment is visible. * 当Fragment对用户可见时回调 *

* Is the combination of [onHiddenChanged() + onResume()/onPause() + setUserVisibleHint()] */ @Override public void onSupportVisible() { mDelegate.onSupportVisible(); } /** * Called when the fragment is invivible. *

* Is the combination of [onHiddenChanged() + onResume()/onPause() + setUserVisibleHint()] */ @Override public void onSupportInvisible() { mDelegate.onSupportInvisible(); } /** * Return true if the fragment has been supportVisible. */ @Override final public boolean isSupportVisible() { return mDelegate.isSupportVisible(); } /** * Set fragment animation with a higher priority than the ISupportActivity * 设定当前Fragmemt动画,优先级比在SupportActivity里高 */ @Override public FragmentAnimator onCreateFragmentAnimator() { return mDelegate.onCreateFragmentAnimator(); } /** * 获取设置的全局动画 copy * * @return FragmentAnimator */ @Override public FragmentAnimator getFragmentAnimator() { return mDelegate.getFragmentAnimator(); } /** * 设置Fragment内的全局动画 */ @Override public void setFragmentAnimator(FragmentAnimator fragmentAnimator) { mDelegate.setFragmentAnimator(fragmentAnimator); } /** * 按返回键触发,前提是SupportActivity的onBackPressed()方法能被调用 * * @return false则继续向上传递, true则消费掉该事件 */ @Override public boolean onBackPressedSupport() { return mDelegate.onBackPressedSupport(); } /** * 类似 *

* Similar to * * @see #startForResult(ISupportFragment, int) */ @Override public void setFragmentResult(int resultCode, Bundle bundle) { mDelegate.setFragmentResult(resultCode, bundle); } /** * 类似 *

* Similar to * * @see #startForResult(ISupportFragment, int) */ @Override public void onFragmentResult(int requestCode, int resultCode, Bundle data) { mDelegate.onFragmentResult(requestCode, resultCode, data); } /** * 在start(TargetFragment,LaunchMode)时,启动模式为SingleTask/SingleTop, 回调TargetFragment的该方法 * 类似 *

* Similar to * * @param args putNewBundle(Bundle newBundle) * @see #start(ISupportFragment, int) */ @Override public void onNewBundle(Bundle args) { mDelegate.onNewBundle(args); } /** * 添加NewBundle,用于启动模式为SingleTask/SingleTop时 * * @see #start(ISupportFragment, int) */ @Override public void putNewBundle(Bundle newBundle) { mDelegate.putNewBundle(newBundle); } /****************************************以下为可选方法(Optional methods)******************************************************/ // 自定制Support时,可移除不必要的方法 /** * 隐藏软键盘 */ protected void hideSoftInput() { mDelegate.hideSoftInput(); } /** * 显示软键盘,调用该方法后,会在onPause时自动隐藏软键盘 */ protected void showSoftInput(final View view) { mDelegate.showSoftInput(view); } /** * 加载根Fragment, 即Activity内的第一个Fragment 或 Fragment内的第一个子Fragment * * @param containerId 容器id * @param toFragment 目标Fragment */ public void loadRootFragment(int containerId, ISupportFragment toFragment) { mDelegate.loadRootFragment(containerId, toFragment); } public void loadRootFragment(int containerId, ISupportFragment toFragment, boolean addToBackStack, boolean allowAnim) { mDelegate.loadRootFragment(containerId, toFragment, addToBackStack, allowAnim); } public void start(ISupportFragment toFragment) { mDelegate.start(toFragment); } /** * @param launchMode Similar to Activity's LaunchMode. */ public void start(final ISupportFragment toFragment, @LaunchMode int launchMode) { mDelegate.start(toFragment, launchMode); } /** * Launch an fragment for which you would like a result when it poped. */ public void startForResult(ISupportFragment toFragment, int requestCode) { mDelegate.startForResult(toFragment, requestCode); } /** * Start the target Fragment and pop itself */ public void startWithPop(ISupportFragment toFragment) { mDelegate.startWithPop(toFragment); } /** * @see #popTo(Class, boolean) * + * @see #start(ISupportFragment) */ public void startWithPopTo(ISupportFragment toFragment, Class targetFragmentClass, boolean includeTargetFragment) { mDelegate.startWithPopTo(toFragment, targetFragmentClass, includeTargetFragment); } public void replaceFragment(ISupportFragment toFragment, boolean addToBackStack) { mDelegate.replaceFragment(toFragment, addToBackStack); } public void pop() { mDelegate.pop(); } /** * Pop the last fragment transition from the manager's fragment * back stack. *

* 出栈到目标fragment * * @param targetFragmentClass 目标fragment * @param includeTargetFragment 是否包含该fragment */ public void popTo(Class targetFragmentClass, boolean includeTargetFragment) { mDelegate.popTo(targetFragmentClass, includeTargetFragment); } /** * 获取栈内的fragment对象 */ public T findChildFragment(Class fragmentClass) { return SupportHelper.findFragment(getChildFragmentManager(), fragmentClass); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/base/BasePresenter.java ================================================ package com.lw.wanandroid.base; /** * desc: * author: Will . * date: 2017/9/2 . */ public class BasePresenter implements BaseContract.BasePresenter { protected T mView; @Override public void attachView(T view) { this.mView = view; } @Override public void detachView() { if (mView != null) { mView = null; } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/Article.java ================================================ package com.lw.wanandroid.bean; import java.util.List; /** * Created by lw on 2018/1/18. */ public class Article { private int offset; private int size; private int total; private int pageCount; private int curPage; private boolean over; private List datas; public int getOffset() { return offset; } public void setOffset(int offset) { this.offset = offset; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public int getPageCount() { return pageCount; } public void setPageCount(int pageCount) { this.pageCount = pageCount; } public int getCurPage() { return curPage; } public void setCurPage(int curPage) { this.curPage = curPage; } public boolean isOver() { return over; } public void setOver(boolean over) { this.over = over; } public List getDatas() { return datas; } public void setDatas(List datas) { this.datas = datas; } public static class DatasBean { /** * id : 1578 * title : 这些 Drawable 的小技巧,你都了解吗? * chapterId : 168 * chapterName : Drawable * envelopePic : null * link : https://juejin.im/post/5a28b2d0f265da431c703153 * author : 承香墨影 * origin : null * publishTime : 1512660849000 * zan : null * desc : null * visible : 1 * niceDate : 2017-12-07 * courseId : 13 * collect : false */ private int id; private String title; private int chapterId; private String chapterName; private String envelopePic; private String link; private String author; private String origin; private long publishTime; private String zan; private String desc; private int visible; private String niceDate; private int courseId; private boolean collect; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getChapterId() { return chapterId; } public void setChapterId(int chapterId) { this.chapterId = chapterId; } public String getChapterName() { return chapterName; } public void setChapterName(String chapterName) { this.chapterName = chapterName; } public String getEnvelopePic() { return envelopePic; } public void setEnvelopePic(String envelopePic) { this.envelopePic = envelopePic; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } public long getPublishTime() { return publishTime; } public void setPublishTime(long publishTime) { this.publishTime = publishTime; } public String getZan() { return zan; } public void setZan(String zan) { this.zan = zan; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public String getNiceDate() { return niceDate; } public void setNiceDate(String niceDate) { this.niceDate = niceDate; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public boolean isCollect() { return collect; } public void setCollect(boolean collect) { this.collect = collect; } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/Banner.java ================================================ package com.lw.wanandroid.bean; /** * Created by lw on 2018/1/19. */ public class Banner { private int id; private String url; private String imagePath; private String title; private String desc; private int isVisible; private int order; private int type; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getImagePath() { return imagePath; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public int getIsVisible() { return isVisible; } public void setIsVisible(int isVisible) { this.isVisible = isVisible; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getType() { return type; } public void setType(int type) { this.type = type; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/DataResponse.java ================================================ package com.lw.wanandroid.bean; /** * Created by lw on 2018/1/19. */ public class DataResponse { private int errorCode; private Object errorMsg; private T data; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public Object getErrorMsg() { return errorMsg; } public void setErrorMsg(Object errorMsg) { this.errorMsg = errorMsg; } public T getData() { return data; } public void setData(T data) { this.data = data; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/Friend.java ================================================ package com.lw.wanandroid.bean; /** * Created by lw on 2018/1/23. */ public class Friend { private int id; private String name; private String link; private int visible; private int order; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/HotKey.java ================================================ package com.lw.wanandroid.bean; /** * Created by lw on 2018/1/23. */ public class HotKey { private int id; private String name; private String link; private int visible; private int order; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/KnowledgeSystem.java ================================================ package com.lw.wanandroid.bean; import android.content.Context; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.facade.service.SerializationService; import com.lw.wanandroid.utils.GsonUtils; import java.lang.reflect.Type; import java.util.List; /** * Created by lw on 2018/1/22. */ @Route(path = "/service/json") public class KnowledgeSystem implements SerializationService { private int id; private String name; private int courseId; private int parentChapterId; private int order; private int visible; private List children; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public int getParentChapterId() { return parentChapterId; } public void setParentChapterId(int parentChapterId) { this.parentChapterId = parentChapterId; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } @Override public T json2Object(String input, Class clazz) { return GsonUtils.convertObj(input, clazz); } @Override public String object2Json(Object instance) { return GsonUtils.toJson(instance); } @Override public T parseObject(String input, Type clazz) { return GsonUtils.convertObj(input, clazz); } @Override public void init(Context context) { } public static class ChildrenBean implements SerializationService { /** * id : 60 * name : Android Studio相关 * courseId : 13 * parentChapterId : 150 * order : 1000 * visible : 1 * children : [] */ private int id; private String name; private int courseId; private int parentChapterId; private int order; private int visible; private List children; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCourseId() { return courseId; } public void setCourseId(int courseId) { this.courseId = courseId; } public int getParentChapterId() { return parentChapterId; } public void setParentChapterId(int parentChapterId) { this.parentChapterId = parentChapterId; } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public int getVisible() { return visible; } public void setVisible(int visible) { this.visible = visible; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } @Override public T json2Object(String input, Class clazz) { return GsonUtils.convertObj(input, clazz); } @Override public String object2Json(Object instance) { return GsonUtils.toJson(instance); } @Override public T parseObject(String input, Type clazz) { return GsonUtils.convertObj(input, clazz); } @Override public void init(Context context) { } public ChildrenBean(int id, String name) { this.id = id; this.name = name; } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/bean/User.java ================================================ package com.lw.wanandroid.bean; import java.util.List; /** * Created by lw on 2018/1/24. */ public class User { private int id; private String username; private String password; private String icon; private int type; private List collectIds; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public int getType() { return type; } public void setType(int type) { this.type = type; } public List getCollectIds() { return collectIds; } public void setCollectIds(List collectIds) { this.collectIds = collectIds; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/constant/Constant.java ================================================ package com.lw.wanandroid.constant; /** * Created by lw on 2018/1/19. */ public class Constant { public static final String REQUEST_BASE_URL = "http://wanandroid.com/"; /** * 每页数量 */ public static final int PAGE_SIZE = 20; /** * url key */ public static final String CONTENT_URL_KEY = "url"; /** * title key */ public static final String CONTENT_TITLE_KEY = "title"; /** * id key */ public static final String CONTENT_ID_KEY = "id"; /** * cid key */ public static final String CONTENT_CID_KEY = "cid"; public static final String CONTENT_AUTHOR_KEY = "author"; /** * childrenData key */ public static final String CONTENT_CHILDREN_DATA_KEY = "childrenData"; /** * hotFriend key */ public static final String CONTENT_HOT_FRIEND_KEY = "hotFriend"; /** * hot key */ public static final String CONTENT_HOT_KEY = "hotKey"; /** * hot key */ public static final String CONTENT_HOT_NAME_KEY = "hotNameKey"; public static final String SAVE_USER_LOGIN_KEY = "user/login"; public static final String SAVE_USER_REGISTER_KEY = "user/register"; public static final String SET_COOKIE_KEY = "set-cookie"; public static final String SHARED_NAME = "_preferences"; public static final String USERNAME_KEY = "username"; public static final String PASSWORD_KEY = "password"; public static final String LOGIN_KEY = "login"; public static final String USER_KEY = "user"; public static final String BANNER_KEY = "banner"; public static final String ARTICLE_KEY = "article"; } ================================================ FILE: app/src/main/java/com/lw/wanandroid/constant/LoadType.java ================================================ package com.lw.wanandroid.constant; import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Created by lw on 2017-04-06. */ public class LoadType { public static final int TYPE_REFRESH_SUCCESS = 1; public static final int TYPE_REFRESH_ERROR = 2; public static final int TYPE_LOAD_MORE_SUCCESS = 3; public static final int TYPE_LOAD_MORE_ERROR = 4; @IntDef({TYPE_REFRESH_SUCCESS, TYPE_REFRESH_ERROR, TYPE_LOAD_MORE_SUCCESS, TYPE_LOAD_MORE_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface checker { } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/db/AppDatabase.java ================================================ package com.lw.wanandroid.db; import com.raizlabs.android.dbflow.annotation.Database; /** * Created by lw on 2018/2/2. */ @Database(name = AppDatabase.NAME, version = AppDatabase.VERSION) public class AppDatabase { public static final String NAME = "WanAndroid-db"; public static final int VERSION = 1; } ================================================ FILE: app/src/main/java/com/lw/wanandroid/db/HistoryModel.java ================================================ package com.lw.wanandroid.db; import com.raizlabs.android.dbflow.annotation.Column; import com.raizlabs.android.dbflow.annotation.PrimaryKey; import com.raizlabs.android.dbflow.annotation.Table; import com.raizlabs.android.dbflow.structure.BaseModel; import java.util.Date; /** * Created by lw on 2018/2/2. */ @Table(database = AppDatabase.class) public class HistoryModel extends BaseModel { @PrimaryKey(autoincrement = true) private long id; @Column private String name; @Column private Date date; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/component/ActivityComponent.java ================================================ package com.lw.wanandroid.di.component; import android.app.Activity; import android.content.Context; import com.lw.wanandroid.di.module.ActivityModule; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerActivity; import com.lw.wanandroid.ui.article.ArticleContentActivity; import com.lw.wanandroid.ui.hotsearch.SearchActivity; import com.lw.wanandroid.ui.my.LoginActivity; import com.lw.wanandroid.ui.my.MyBookmarkActivity; import com.lw.wanandroid.ui.my.MyCollectionActivity; import dagger.Component; /** * Created by lw on 2017/1/19. */ @PerActivity @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { @ContextLife("Activity") Context getActivityContext(); @ContextLife("Application") Context getApplicationContext(); Activity getActivity(); void inject(SearchActivity activity); void inject(LoginActivity activity); void inject(ArticleContentActivity activity); void inject(MyCollectionActivity activity); void inject(MyBookmarkActivity activity); } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/component/ApplicationComponent.java ================================================ package com.lw.wanandroid.di.component; import android.content.Context; import com.lw.wanandroid.di.module.ApplicationModule; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerApp; import dagger.Component; /** * Created by lw on 2017/1/19. */ @PerApp @Component(modules = ApplicationModule.class) public interface ApplicationComponent { @ContextLife("Application") Context getApplication(); } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/component/FragmentComponent.java ================================================ package com.lw.wanandroid.di.component; import android.app.Activity; import android.content.Context; import com.lw.wanandroid.di.module.FragmentModule; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerFragment; import com.lw.wanandroid.ui.article.ArticleListFragment; import com.lw.wanandroid.ui.home.HomeFragment; import com.lw.wanandroid.ui.hotsearch.HotFragment; import com.lw.wanandroid.ui.knowledgesystem.KnowledgeSystemFragment; import com.lw.wanandroid.ui.my.MyFragment; import dagger.Component; /** * Created by lw on 2017/1/19. */ @PerFragment @Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class) public interface FragmentComponent { @ContextLife("Activity") Context getActivityContext(); @ContextLife("Application") Context getApplicationContext(); Activity getActivity(); void inject(HomeFragment fragment); void inject(KnowledgeSystemFragment fragment); void inject(MyFragment fragment); void inject(ArticleListFragment fragment); void inject(HotFragment fragment); } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/component/ServiceComponent.java ================================================ package com.lw.wanandroid.di.component; import android.content.Context; import com.lw.wanandroid.di.module.ServiceModule; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerService; import dagger.Component; /** * Created by lw on 2017/1/19. */ @PerService @Component(dependencies = ApplicationComponent.class, modules = ServiceModule.class) public interface ServiceComponent { @ContextLife("Service") Context getServiceContext(); @ContextLife("Application") Context getApplicationContext(); } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/module/ActivityModule.java ================================================ package com.lw.wanandroid.di.module; import android.app.Activity; import android.content.Context; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerActivity; import dagger.Module; import dagger.Provides; /** * Created by lw on 2017/1/19. */ @Module public class ActivityModule { private Activity mActivity; public ActivityModule(Activity activity) { mActivity = activity; } @Provides @PerActivity @ContextLife("Activity") public Context provideActivityContext() { return mActivity; } @Provides @PerActivity public Activity provideActivity() { return mActivity; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/module/ApplicationModule.java ================================================ package com.lw.wanandroid.di.module; import android.content.Context; import com.lw.wanandroid.base.App; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerApp; import dagger.Module; import dagger.Provides; /** * Created by lw on 2017/1/19. */ @Module public class ApplicationModule { private App mApplication; public ApplicationModule(App application) { mApplication = application; } @Provides @PerApp @ContextLife("Application") public Context provideApplicationContext() { return mApplication.getApplicationContext(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/module/FragmentModule.java ================================================ package com.lw.wanandroid.di.module; import android.app.Activity; import android.content.Context; import android.support.v4.app.Fragment; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerFragment; import dagger.Module; import dagger.Provides; /** * Created by lw on 2017/1/19. */ @Module public class FragmentModule { private Fragment mFragment; public FragmentModule(Fragment fragment) { mFragment = fragment; } @Provides @PerFragment @ContextLife("Activity") public Context provideActivityContext() { return mFragment.getActivity(); } @Provides @PerFragment public Activity provideActivity() { return mFragment.getActivity(); } @Provides @PerFragment public Fragment provideFragment() { return mFragment; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/module/ServiceModule.java ================================================ package com.lw.wanandroid.di.module; import android.app.Service; import android.content.Context; import com.lw.wanandroid.di.scope.ContextLife; import com.lw.wanandroid.di.scope.PerService; import dagger.Module; import dagger.Provides; /** * Created by lw on 2017/1/19. */ @Module public class ServiceModule { private Service mService; public ServiceModule(Service service) { mService = service; } @Provides @PerService @ContextLife("Service") public Context ProvideServiceContext() { return mService; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/scope/ContextLife.java ================================================ package com.lw.wanandroid.di.scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Qualifier; /** * Created by lw on 2017/1/19. */ @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) public @interface ContextLife { String value() default "Application"; } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/scope/PerActivity.java ================================================ package com.lw.wanandroid.di.scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Scope; /** * Created by lw on 2017/1/19. */ @Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PerActivity { } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/scope/PerApp.java ================================================ package com.lw.wanandroid.di.scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Scope; /** * Created by lw on 2017/1/19. */ @Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PerApp { } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/scope/PerFragment.java ================================================ package com.lw.wanandroid.di.scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Scope; /** * Created by lw on 2017/1/19. */ @Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PerFragment { } ================================================ FILE: app/src/main/java/com/lw/wanandroid/di/scope/PerService.java ================================================ package com.lw.wanandroid.di.scope; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.inject.Scope; /** * Created by lw on 2017/1/19. */ @Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PerService { } ================================================ FILE: app/src/main/java/com/lw/wanandroid/event/LoginEvent.java ================================================ package com.lw.wanandroid.event; /** * Created by lw on 2018/1/25. */ public class LoginEvent { } ================================================ FILE: app/src/main/java/com/lw/wanandroid/net/ApiService.java ================================================ package com.lw.wanandroid.net; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.Banner; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.bean.HotKey; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.bean.User; import java.util.List; import io.reactivex.Observable; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Path; import retrofit2.http.Query; /** * Created by lw on 2018/1/23. */ public interface ApiService { /** * 首页数据 * http://www.wanandroid.com/article/list/0/json * * @param page page */ @GET("/article/list/{page}/json") Observable> getHomeArticles(@Path("page") int page); /** * 首页Banner * * @return BannerResponse */ @GET("/banner/json") Observable>> getHomeBanners(); /** * 知识体系 * http://www.wanandroid.com/tree/json * * @return BannerResponse */ @GET("/tree/json") Observable>> getKnowledgeSystems(); /** * 知识体系下的文章 * http://www.wanandroid.com/article/list/0/json?cid=168 * * @param page page * @param cid cid */ @GET("/article/list/{page}/json") Observable> getKnowledgeSystemArticles(@Path("page") int page, @Query("cid") int cid); /** * 常用网站 * http://www.wanandroid.com/friend/json */ @GET("/friend/json") Observable>> getHotFriends(); /** * 大家都在搜 * http://www.wanandroid.com/hotkey/json */ @GET("/hotkey/json") Observable>> getHotKeys(); /** * 搜索 * http://www.wanandroid.com/article/query/0/json * * @param page page * @param k POST search key */ @POST("/article/query/{page}/json") @FormUrlEncoded Observable> getSearchArticles(@Path("page") int page, @Field("k") String k); /** * 登录 * * @param username username * @param password password * @return Deferred */ @POST("/user/login") @FormUrlEncoded Observable> login(@Field("username") String username, @Field("password") String password); /** * 注册 * * @param username username * @param password password * @param repassword repassword * @return Deferred */ @POST("/user/register") @FormUrlEncoded Observable> register(@Field("username") String username, @Field("password") String password, @Field("repassword") String repassword); /** * 收藏文章 * * @param id id * @return Deferred */ @POST("/lg/collect/{id}/json") Observable addCollectArticle(@Path("id") int id); /** * 收藏站外文章 * * @param title title * @param author author * @param link link * @return Deferred */ @POST("/lg/collect/add/json") @FormUrlEncoded Observable addCollectOutsideArticle(@Field("title") String title, @Field("author") String author, @Field("link") String link); /** * 删除收藏文章 * * @param id id * @param originId -1 * @return Deferred */ @POST("/lg/uncollect/{id}/json") @FormUrlEncoded Observable removeCollectArticle(@Path("id") int id, @Field("originId") int originId); /** * 获取自己收藏的文章列表 * * @param page page * @return Deferred

*/ @GET("/lg/collect/list/{page}/json") Observable> getCollectArticles(@Path("page") int page); /** * 我的书签 * http://www.wanandroid.com/lg/collect/usertools/json */ @GET("/lg/collect/usertools/json") Observable>> getBookmarks(); /** * 编辑书签 * http://www.wanandroid.com/lg/collect/updatetool/json */ @POST("/lg/collect/usertools/json") @FormUrlEncoded Observable editBookmark(@Field("id") int id, @Field("name") String name, @Field("link") String link); /** * 删除书签 * http://www.wanandroid.com/lg/collect/deletetool/json */ @POST("/lg/collect/usertools/json") @FormUrlEncoded Observable delBookmark(@Field("id") int id); } ================================================ FILE: app/src/main/java/com/lw/wanandroid/net/CookiesManager.java ================================================ package com.lw.wanandroid.net; import java.util.List; import okhttp3.Cookie; import okhttp3.CookieJar; import okhttp3.HttpUrl; /** * Created by lw on 2018/1/25. */ public class CookiesManager implements CookieJar { private static final PersistentCookieStore cookieStore = new PersistentCookieStore(); @Override public void saveFromResponse(HttpUrl url, List cookies) { if (cookies != null && cookies.size() > 0) { for (Cookie item : cookies) { cookieStore.add(url, item); } } } @Override public List loadForRequest(HttpUrl url) { List cookies = cookieStore.get(url); return cookies; } /** * 清除所有cookie */ public static void clearAllCookies() { cookieStore.removeAll(); } /** * 清除指定cookie * * @param url * @param cookie * @return */ public static boolean clearCookies(HttpUrl url, Cookie cookie) { return cookieStore.remove(url, cookie); } /** * 获取cookies * * @return */ public static List getCookies() { return cookieStore.getCookies(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/net/OkHttpCookies.java ================================================ package com.lw.wanandroid.net; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import okhttp3.Cookie; /** * Created by lw on 2018/1/25. */ public class OkHttpCookies implements Serializable { private transient final Cookie cookies; private transient Cookie clientCookies; public OkHttpCookies(Cookie cookies) { this.cookies = cookies; } public Cookie getCookies() { Cookie bestCookies = cookies; if (clientCookies != null) { bestCookies = clientCookies; } return bestCookies; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(cookies.name()); out.writeObject(cookies.value()); out.writeLong(cookies.expiresAt()); out.writeObject(cookies.domain()); out.writeObject(cookies.path()); out.writeBoolean(cookies.secure()); out.writeBoolean(cookies.httpOnly()); out.writeBoolean(cookies.hostOnly()); out.writeBoolean(cookies.persistent()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { String name = (String) in.readObject(); String value = (String) in.readObject(); long expiresAt = in.readLong(); String domain = (String) in.readObject(); String path = (String) in.readObject(); boolean secure = in.readBoolean(); boolean httpOnly = in.readBoolean(); boolean hostOnly = in.readBoolean(); boolean persistent = in.readBoolean(); Cookie.Builder builder = new Cookie.Builder(); builder = builder.name(name); builder = builder.value(value); builder = builder.expiresAt(expiresAt); builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain); builder = builder.path(path); builder = secure ? builder.secure() : builder; builder = httpOnly ? builder.httpOnly() : builder; clientCookies = builder.build(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/net/PersistentCookieStore.java ================================================ package com.lw.wanandroid.net; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; import com.lw.wanandroid.base.App; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import okhttp3.Cookie; import okhttp3.HttpUrl; /** * Created by lw on 2018/1/25. */ public class PersistentCookieStore { private static final String LOG_TAG = "PersistentCookieStore"; private static final String COOKIE_PREFS = "Cookies_Prefs"; private final Map> cookies; private final SharedPreferences cookiePrefs; public PersistentCookieStore() { cookiePrefs = App.getAppContext().getSharedPreferences(COOKIE_PREFS, 0); cookies = new HashMap<>(); //将持久化的cookies缓存到内存中 即map cookies Map prefsMap = cookiePrefs.getAll(); for (Map.Entry entry : prefsMap.entrySet()) { String[] cookieNames = TextUtils.split((String) entry.getValue(), ","); for (String name : cookieNames) { String encodedCookie = cookiePrefs.getString(name, null); if (encodedCookie != null) { Cookie decodedCookie = decodeCookie(encodedCookie); if (decodedCookie != null) { if (!cookies.containsKey(entry.getKey())) { cookies.put(entry.getKey(), new ConcurrentHashMap()); } cookies.get(entry.getKey()).put(name, decodedCookie); } } } } } protected String getCookieToken(Cookie cookie) { return cookie.name() + "@" + cookie.domain(); } public void add(HttpUrl url, Cookie cookie) { String name = getCookieToken(cookie); //将cookies缓存到内存中 如果缓存过期 就重置此cookie if (!cookie.persistent()) { if (!cookies.containsKey(url.host())) { cookies.put(url.host(), new ConcurrentHashMap()); } cookies.get(url.host()).put(name, cookie); } else { if (cookies.containsKey(url.host())) { cookies.get(url.host()).remove(name); } } //讲cookies持久化到本地 SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet())); prefsWriter.putString(name, encodeCookie(new OkHttpCookies(cookie))); prefsWriter.apply(); } public List get(HttpUrl url) { ArrayList ret = new ArrayList<>(); if (cookies.containsKey(url.host())) ret.addAll(cookies.get(url.host()).values()); return ret; } public boolean removeAll() { SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); prefsWriter.clear(); prefsWriter.apply(); cookies.clear(); return true; } public boolean remove(HttpUrl url, Cookie cookie) { String name = getCookieToken(cookie); if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) { cookies.get(url.host()).remove(name); SharedPreferences.Editor prefsWriter = cookiePrefs.edit(); if (cookiePrefs.contains(name)) { prefsWriter.remove(name); } prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet())); prefsWriter.apply(); return true; } else { return false; } } public List getCookies() { ArrayList ret = new ArrayList<>(); for (String key : cookies.keySet()) ret.addAll(cookies.get(key).values()); return ret; } /** * cookies 序列化成 string * * @param cookie 要序列化的cookie * @return 序列化之后的string */ protected String encodeCookie(OkHttpCookies cookie) { if (cookie == null) return null; ByteArrayOutputStream os = new ByteArrayOutputStream(); try { ObjectOutputStream outputStream = new ObjectOutputStream(os); outputStream.writeObject(cookie); } catch (IOException e) { Log.d(LOG_TAG, "IOException in encodeCookie", e); return null; } return byteArrayToHexString(os.toByteArray()); } /** * 将字符串反序列化成cookies * * @param cookieString cookies string * @return cookie object */ protected Cookie decodeCookie(String cookieString) { byte[] bytes = hexStringToByteArray(cookieString); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); Cookie cookie = null; try { ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); cookie = ((OkHttpCookies) objectInputStream.readObject()).getCookies(); } catch (IOException e) { Log.d(LOG_TAG, "IOException in decodeCookie", e); } catch (ClassNotFoundException e) { Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e); } return cookie; } /** * 二进制数组转十六进制字符串 * * @param bytes byte array to be converted * @return string containing hex values */ protected String byteArrayToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte element : bytes) { int v = element & 0xff; if (v < 16) { sb.append('0'); } sb.append(Integer.toHexString(v)); } return sb.toString().toUpperCase(Locale.US); } /** * 十六进制字符串转二进制数组 * * @param hexString string of hex-encoded values * @return decoded byte array */ protected byte[] hexStringToByteArray(String hexString) { int len = hexString.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return data; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/net/RetrofitManager.java ================================================ package com.lw.wanandroid.net; import com.blankj.utilcode.util.NetworkUtils; import com.lw.wanandroid.base.App; import com.lw.wanandroid.constant.Constant; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhttp3.Cache; import okhttp3.CacheControl; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; /** * Created by lw on 2017-04-01. */ public class RetrofitManager { private static long CONNECT_TIMEOUT = 60L; private static long READ_TIMEOUT = 10L; private static long WRITE_TIMEOUT = 10L; //设缓存有效期为1天 private static final long CACHE_STALE_SEC = 60 * 60 * 24 * 1; //查询缓存的Cache-Control设置,为if-only-cache时只查询缓存而不会请求服务器,max-stale可以配合设置缓存失效时间 public static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC; //查询网络的Cache-Control设置 //(假如请求了服务器并在a时刻返回响应结果,则在max-age规定的秒数内,浏览器将不会发送对应的请求到服务器,数据由缓存直接返回) public static final String CACHE_CONTROL_NETWORK = "Cache-Control: public, max-age=10"; // 避免出现 HTTP 403 Forbidden,参考:http://stackoverflow.com/questions/13670692/403-forbidden-with-java-but-not-web-browser private static final String AVOID_HTTP403_FORBIDDEN = "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"; private static volatile OkHttpClient mOkHttpClient; /** * 云端响应头拦截器,用来配置缓存策略 * Dangerous interceptor that rewrites the server's cache-control header. */ private static final Interceptor mRewriteCacheControlInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!NetworkUtils.isConnected()) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); } Response originalResponse = chain.proceed(request); if (NetworkUtils.isConnected()) { //有网的时候读接口上的@Headers里的配置,可以在这里进行统一的设置 String cacheControl = request.cacheControl().toString(); return originalResponse.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma") .build(); } else { return originalResponse.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=" + CACHE_CONTROL_CACHE) .removeHeader("Pragma") .build(); } } }; /** * 日志拦截器 */ private static final Interceptor mLoggingInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); return response; } }; /** * 获取OkHttpClient实例 * * @return */ private static OkHttpClient getOkHttpClient() { if (mOkHttpClient == null) { synchronized (RetrofitManager.class) { Cache cache = new Cache(new File(App.getAppContext().getCacheDir(), "HttpCache"), 1024 * 1024 * 100); if (mOkHttpClient == null) { mOkHttpClient = new OkHttpClient.Builder().cache(cache) .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS) .addInterceptor(mRewriteCacheControlInterceptor) .addInterceptor(mLoggingInterceptor) .cookieJar(new CookiesManager()) .build(); } } } return mOkHttpClient; } /** * 获取Service * * @param clazz * @param * @return */ public static T create(Class clazz) { Retrofit retrofit = new Retrofit.Builder().baseUrl(Constant.REQUEST_BASE_URL) .client(getOkHttpClient()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build(); return retrofit.create(clazz); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleAdapter.java ================================================ package com.lw.wanandroid.ui.article; import android.text.Html; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.lw.wanandroid.R; import com.lw.wanandroid.bean.Article; import javax.inject.Inject; /** * Created by lw on 2018/1/19. */ public class ArticleAdapter extends BaseQuickAdapter { private boolean mChapterNameVisible = true; private boolean mIsMyColection = false; @Inject public ArticleAdapter() { super(R.layout.item_article, null); } @Override protected void convert(BaseViewHolder helper, Article.DatasBean item) { helper.setText(R.id.tvAuthor, item.getAuthor()); helper.setText(R.id.tvNiceDate, item.getNiceDate()); helper.setText(R.id.tvTitle, Html.fromHtml(item.getTitle())); helper.setText(R.id.tvChapterName, item.getChapterName()); if (mIsMyColection) item.setCollect(mIsMyColection); helper.setImageResource(R.id.ivCollect, item.isCollect() ? R.drawable.ic_action_like : R.drawable.ic_action_no_like); helper.addOnClickListener(R.id.tvChapterName); helper.addOnClickListener(R.id.ivCollect); helper.setVisible(R.id.tvChapterName, mChapterNameVisible); } public void setChapterNameVisible(boolean chapterNameVisible) { this.mChapterNameVisible = chapterNameVisible; } public void isMyColection(boolean isMyColection) { this.mIsMyColection = isMyColection; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleContentActivity.java ================================================ package com.lw.wanandroid.ui.article; import android.content.Intent; import android.net.Uri; import android.view.Menu; import android.view.MenuItem; import android.webkit.WebView; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.alibaba.android.arouter.facade.annotation.Autowired; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.just.agentweb.AgentWeb; import com.just.agentweb.ChromeClientCallbackManager; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.constant.Constant; import butterknife.BindView; /** * Created by lw on 2018/1/22. */ @Route(path = "/article/ArticleContentActivity") public class ArticleContentActivity extends BaseActivity implements ArticleContentContract.View { @Autowired public int id; @Autowired public String url; @Autowired public String title; @Autowired public String author; @BindView(R.id.webContent) FrameLayout mWebContent; @Override protected int getLayoutId() { return R.layout.activity_article_content; } @Override protected void initInjector() { mActivityComponent.inject(this); } @Override protected void initView() { AgentWeb.with(this)//传入Activity or Fragment .setAgentWebParent(mWebContent, new LinearLayout.LayoutParams(-1, -1))//传入AgentWeb 的父控件 ,如果父控件为 RelativeLayout , 那么第二参数需要传入 RelativeLayout.LayoutParams ,第一个参数和第二个参数应该对应。 .useDefaultIndicator()// 使用默认进度条 .defaultProgressBarColor() // 使用默认进度条颜色 .setReceivedTitleCallback(mReceivedTitleCallback) //设置 Web 页面的 title 回调 .createAgentWeb()// .ready() .go(url); } @Override protected boolean showHomeAsUp() { return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_content, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menuShare) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_article_url, getString(R.string.app_name), title, url)); intent.setType("text/plain"); startActivity(intent); } else if (item.getItemId() == R.id.menuLike) { if (id == 0) mPresenter.collectOutsideArticle(title, author, url); else mPresenter.collectArticle(id); } else if (item.getItemId() == R.id.menuBrowser) { Intent intent = new Intent("android.intent.action.VIEW"); intent.setData(Uri.parse(url)); startActivity(intent); } return super.onOptionsItemSelected(item); } public static void start(int id, String url, String title, String author) { ARouter.getInstance().build("/article/ArticleContentActivity") .withInt(Constant.CONTENT_ID_KEY, id) .withString(Constant.CONTENT_URL_KEY, url) .withString(Constant.CONTENT_TITLE_KEY, title) .withString(Constant.CONTENT_AUTHOR_KEY, author) .navigation(); } private ChromeClientCallbackManager.ReceivedTitleCallback mReceivedTitleCallback = new ChromeClientCallbackManager.ReceivedTitleCallback() { @Override public void onReceivedTitle(WebView view, String title) { setToolbarTitle(title); } }; } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleContentContract.java ================================================ package com.lw.wanandroid.ui.article; import com.lw.wanandroid.base.BaseContract; import retrofit2.http.Field; /** * Created by lw on 2018/1/25. */ public interface ArticleContentContract { interface View extends BaseContract.BaseView { } interface Presenter extends BaseContract.BasePresenter { void collectArticle(int id); void collectOutsideArticle(String title, String author, String link); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleContentPresenter.java ================================================ package com.lw.wanandroid.ui.article; import com.blankj.utilcode.util.SPUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.App; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.ui.my.LoginActivity; import com.lw.wanandroid.utils.RxSchedulers; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/25. */ public class ArticleContentPresenter extends BasePresenter implements ArticleContentContract.Presenter { @Inject public ArticleContentPresenter() { } @Override public void collectArticle(int id) { if (SPUtils.getInstance(Constant.SHARED_NAME).getBoolean(Constant.LOGIN_KEY)) { RetrofitManager.create(ApiService.class) .addCollectArticle(id) .compose(RxSchedulers.applySchedulers()) .compose(mView.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { mView.showSuccess(App.getAppContext().getString(R.string.collection_success)); } else { mView.showFaild(App.getAppContext().getString(R.string.collection_failed, response.getErrorMsg())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } else { LoginActivity.start(); } } @Override public void collectOutsideArticle(String title, String author, String link) { if (SPUtils.getInstance(Constant.SHARED_NAME).getBoolean(Constant.LOGIN_KEY)) { RetrofitManager.create(ApiService.class) .addCollectOutsideArticle(title, author, link) .compose(RxSchedulers.applySchedulers()) .compose(mView.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { mView.showSuccess(App.getAppContext().getString(R.string.collection_success)); } else { mView.showFaild(App.getAppContext().getString(R.string.collection_failed, response.getErrorMsg())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } else { LoginActivity.start(); } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleListContract.java ================================================ package com.lw.wanandroid.ui.article; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.constant.LoadType; /** * Created by lw on 2018/1/23. */ public interface ArticleListContract { interface View extends BaseContract.BaseView { void setKnowledgeSystemArticles(Article article, @LoadType.checker int loadType); void collectArticleSuccess(int position, Article.DatasBean bean); } interface Presenter extends BaseContract.BasePresenter { void loadKnowledgeSystemArticles(int cid); void refresh(); void loadMore(); void collectArticle(int position, Article.DatasBean bean); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleListFragment.java ================================================ package com.lw.wanandroid.ui.article; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Autowired; import com.alibaba.android.arouter.facade.annotation.Route; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseFragment; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.event.LoginEvent; import com.lw.wanandroid.utils.RxBus; import javax.inject.Inject; import butterknife.BindView; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/22. */ @Route(path = "/article/ArticleListFragment") public class ArticleListFragment extends BaseFragment implements ArticleListContract.View, ArticleAdapter.OnItemClickListener, ArticleAdapter.OnItemChildClickListener, SwipeRefreshLayout.OnRefreshListener, ArticleAdapter.RequestLoadMoreListener { @BindView(R.id.rvArticleList) RecyclerView mRvArticleList; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Autowired public int cid; @Inject ArticleAdapter mArticleAdapter; @Override protected int getLayoutId() { return R.layout.fragment_article_list; } @Override protected void initInjector() { mFragmentComponent.inject(this); } @Override protected void initView(View view) { /**隐藏文章类型*/ mArticleAdapter.setChapterNameVisible(false); /**设置RecyclerView*/ mRvArticleList.setLayoutManager(new LinearLayoutManager(getContext())); mRvArticleList.setAdapter(mArticleAdapter); /**设置事件监听*/ mArticleAdapter.setOnItemClickListener(this); mArticleAdapter.setOnItemChildClickListener(this); mSwipeRefreshLayout.setOnRefreshListener(this); mArticleAdapter.setOnLoadMoreListener(this); /**请求数据*/ mPresenter.loadKnowledgeSystemArticles(cid); /**登陆成功刷新*/ RxBus.getInstance().toFlowable(LoginEvent.class) .subscribe(new Consumer() { @Override public void accept(LoginEvent event) throws Exception { mPresenter.refresh(); } }); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void onRefresh() { mPresenter.refresh(); } @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { if (view.getId() == R.id.ivCollect) { mPresenter.collectArticle(position, mArticleAdapter.getItem(position)); } } @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ArticleContentActivity.start(mArticleAdapter.getItem(position).getId(), mArticleAdapter.getItem(position).getLink(), mArticleAdapter.getItem(position).getTitle(), mArticleAdapter.getItem(position).getAuthor()); } @Override public void onLoadMoreRequested() { mPresenter.loadMore(); } @Override public void setKnowledgeSystemArticles(Article article, int loadType) { setLoadDataResult(mArticleAdapter, mSwipeRefreshLayout, article.getDatas(), loadType); } @Override public void collectArticleSuccess(int position, Article.DatasBean bean) { mArticleAdapter.setData(position, bean); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleListPresenter.java ================================================ package com.lw.wanandroid.ui.article; import com.blankj.utilcode.util.SPUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.App; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.RxSchedulers; import com.lw.wanandroid.ui.my.LoginActivity; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/23. */ public class ArticleListPresenter extends BasePresenter implements ArticleListContract.Presenter { private boolean mIsRefresh; private int mPage, mCid; @Inject public ArticleListPresenter() { this.mIsRefresh = true; } @Override public void loadKnowledgeSystemArticles(int cid) { this.mCid = cid; RetrofitManager.create(ApiService.class).getKnowledgeSystemArticles(mPage, mCid) .compose(RxSchedulers.>applySchedulers()) .compose(mView.>bindToLife()) .subscribe(new Consumer>() { @Override public void accept(DataResponse
dataResponse) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_SUCCESS : LoadType.TYPE_LOAD_MORE_SUCCESS; mView.setKnowledgeSystemArticles(dataResponse.getData(), loadType); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_ERROR : LoadType.TYPE_LOAD_MORE_ERROR; mView.setKnowledgeSystemArticles(new Article(), loadType); } }); } @Override public void refresh() { mPage = 0; mIsRefresh = true; loadKnowledgeSystemArticles(mCid); } @Override public void loadMore() { mPage++; mIsRefresh = false; loadKnowledgeSystemArticles(mCid); } @Override public void collectArticle(final int position, final Article.DatasBean bean) { if (SPUtils.getInstance(Constant.SHARED_NAME).getBoolean(Constant.LOGIN_KEY)) { if (bean.isCollect()) { RetrofitManager.create(ApiService.class).removeCollectArticle(bean.getId(), -1) .compose(RxSchedulers.applySchedulers()) .compose(mView.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { bean.setCollect(!bean.isCollect()); mView.collectArticleSuccess(position, bean); mView.showSuccess(App.getAppContext().getString(R.string.collection_cancel_success)); } else { mView.showFaild(App.getAppContext().getString(R.string.collection_cancel_failed, response.getData())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } else { RetrofitManager.create(ApiService.class).addCollectArticle(bean.getId()) .compose(RxSchedulers.applySchedulers()) .compose(mView.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { bean.setCollect(!bean.isCollect()); mView.collectArticleSuccess(position, bean); mView.showSuccess(App.getAppContext().getString(R.string.collection_success)); } else { mView.showFaild(App.getAppContext().getString(R.string.collection_failed, response.getErrorMsg())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } } else { LoginActivity.start(); } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleTypeActivity.java ================================================ package com.lw.wanandroid.ui.article; import android.content.Intent; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; import android.view.Menu; import android.view.MenuItem; import com.alibaba.android.arouter.facade.annotation.Autowired; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.bean.KnowledgeSystem; import java.util.List; import butterknife.BindView; /** * Created by lw on 2018/1/22. */ @Route(path = "/article/ArticleTypeActivity") public class ArticleTypeActivity extends BaseActivity { @Autowired public String title; @Autowired public List childrenData; @BindView(R.id.tabArticleTypes) TabLayout mTabArticleTypes; @BindView(R.id.vpArticleTypes) ViewPager mVpArticleTypes; ArticleTypeFragmentPagerAdapter mArticleTypeFragmentPagerAdapter; @Override protected int getLayoutId() { return R.layout.activity_article_type; } @Override protected void initInjector() { } @Override protected void initView() { setToolbarTitle(title); mArticleTypeFragmentPagerAdapter = new ArticleTypeFragmentPagerAdapter(getSupportFragmentManager(), childrenData); mVpArticleTypes.setAdapter(mArticleTypeFragmentPagerAdapter); mTabArticleTypes.setupWithViewPager(mVpArticleTypes); } @Override protected boolean showHomeAsUp() { return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_type_content, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menuShare) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_type_url, getString(R.string.app_name), childrenData.get(mTabArticleTypes.getSelectedTabPosition()).getName(), childrenData.get(mTabArticleTypes.getSelectedTabPosition()).getId())); intent.setType("text/plain"); startActivity(Intent.createChooser(intent, getString(R.string.share_title))); } else if (item.getItemId() == R.id.menuSearch) { ARouter.getInstance().build("/hotsearch/SearchActivity").navigation(); } return super.onOptionsItemSelected(item); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/article/ArticleTypeFragmentPagerAdapter.java ================================================ package com.lw.wanandroid.ui.article; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import com.alibaba.android.arouter.launcher.ARouter; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.constant.Constant; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * Created by lw on 2018/1/22. */ public class ArticleTypeFragmentPagerAdapter extends FragmentPagerAdapter { @Nullable private List mChildrenData; private List mArticleTypeFragments; @Inject public ArticleTypeFragmentPagerAdapter(FragmentManager fm, List childrenData) { super(fm); this.mChildrenData = childrenData; mArticleTypeFragments = new ArrayList<>(); if (mChildrenData == null) return; for (KnowledgeSystem.ChildrenBean childrenBean : mChildrenData) { ArticleListFragment articleListFragment = (ArticleListFragment) ARouter.getInstance() .build("/article/ArticleListFragment") .withInt(Constant.CONTENT_CID_KEY, childrenBean.getId()) .navigation(); mArticleTypeFragments.add(articleListFragment); } } @Override public Fragment getItem(int position) { return mArticleTypeFragments.get(position); } @Override public int getCount() { return mArticleTypeFragments.size(); } @Override public CharSequence getPageTitle(int position) { return mChildrenData.get(position).getName(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/home/HomeContract.java ================================================ package com.lw.wanandroid.ui.home; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.Banner; import com.lw.wanandroid.constant.LoadType; import java.util.List; /** * Created by lw on 2018/1/18. */ public interface HomeContract { interface View extends BaseContract.BaseView { void setHomeBanners(List banners); void setHomeArticles(Article article, @LoadType.checker int loadType); void collectArticleSuccess(int position, Article.DatasBean bean); } interface Presenter extends BaseContract.BasePresenter { void loadHomeBanners(); void loadHomeArticles(); void refresh(); void loadMore(); void collectArticle(int position, Article.DatasBean bean); void loadHomeData(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/home/HomeFragment.java ================================================ package com.lw.wanandroid.ui.home; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import com.alibaba.android.arouter.launcher.ARouter; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseFragment; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.Banner; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.event.LoginEvent; import com.lw.wanandroid.ui.article.ArticleAdapter; import com.lw.wanandroid.ui.article.ArticleContentActivity; import com.lw.wanandroid.utils.GlideImageLoader; import com.lw.wanandroid.utils.RxBus; import com.youth.banner.BannerConfig; import com.youth.banner.listener.OnBannerListener; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.BindView; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/18. */ public class HomeFragment extends BaseFragment implements HomeContract.View, ArticleAdapter.OnItemClickListener, ArticleAdapter.OnItemChildClickListener, SwipeRefreshLayout.OnRefreshListener, ArticleAdapter.RequestLoadMoreListener { @BindView(R.id.rvHomeArticles) RecyclerView mRvHomeArticles; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Inject ArticleAdapter mArticleAdapter; private com.youth.banner.Banner mBannerAds; private View mHomeBannerHeadView; @Override protected int getLayoutId() { return R.layout.fragment_home; } @Override protected void initInjector() { mFragmentComponent.inject(this); } @Override protected void initView(View view) { /**设置RecyclerView*/ mRvHomeArticles.setLayoutManager(new LinearLayoutManager(getContext())); mRvHomeArticles.setAdapter(mArticleAdapter); /**设置BannerHeadView*/ mHomeBannerHeadView = LayoutInflater.from(getContext()).inflate(R.layout.layout_home_banner_head, null); mBannerAds = (com.youth.banner.Banner) mHomeBannerHeadView.findViewById(R.id.banner_ads); mArticleAdapter.addHeaderView(mHomeBannerHeadView); /**设置事件监听*/ mArticleAdapter.setOnItemClickListener(this); mArticleAdapter.setOnItemChildClickListener(this); mSwipeRefreshLayout.setOnRefreshListener(this); mArticleAdapter.setOnLoadMoreListener(this); /**请求数据*/ mPresenter.loadHomeData(); /**登陆成功刷新*/ RxBus.getInstance().toFlowable(LoginEvent.class) .subscribe(new Consumer() { @Override public void accept(LoginEvent event) throws Exception { mPresenter.refresh(); } }); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void showFaild(String errorMsg) { mSwipeRefreshLayout.setRefreshing(false); } @Override public void setHomeBanners(final List banners) { List images = new ArrayList(); List titles = new ArrayList(); for (Banner banner : banners) { images.add(banner.getImagePath()); titles.add(banner.getTitle()); } mBannerAds.setImages(images) .setBannerTitles(titles) .setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE) .setImageLoader(new GlideImageLoader()) .start(); mBannerAds.setOnBannerListener(new OnBannerListener() { @Override public void OnBannerClick(int position) { ArticleContentActivity.start(banners.get(position).getId(), banners.get(position).getUrl(), banners.get(position).getTitle(), null); } }); } @Override public void setHomeArticles(Article article, @LoadType.checker int loadType) { setLoadDataResult(mArticleAdapter, mSwipeRefreshLayout, article.getDatas(), loadType); } @Override public void collectArticleSuccess(int position, Article.DatasBean bean) { mArticleAdapter.setData(position, bean); } @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { if (view.getId() == R.id.tvChapterName) { List childrenBeans = new ArrayList<>(); childrenBeans.add(new KnowledgeSystem.ChildrenBean(mArticleAdapter.getItem(position).getChapterId(), mArticleAdapter.getItem(position).getChapterName())); ARouter.getInstance().build("/article/ArticleTypeActivity") .withString(Constant.CONTENT_TITLE_KEY, mArticleAdapter.getItem(position).getChapterName()) .withObject(Constant.CONTENT_CHILDREN_DATA_KEY, childrenBeans) .navigation(); } else if (view.getId() == R.id.ivCollect) { mPresenter.collectArticle(position, mArticleAdapter.getItem(position)); } } @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ArticleContentActivity.start(mArticleAdapter.getItem(position).getId(), mArticleAdapter.getItem(position).getLink(), mArticleAdapter.getItem(position).getTitle(), mArticleAdapter.getItem(position).getAuthor()); } @Override public void onRefresh() { mPresenter.refresh(); } @Override public void onLoadMoreRequested() { mPresenter.loadMore(); } public static HomeFragment newInstance() { return new HomeFragment(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/home/HomePresenter.java ================================================ package com.lw.wanandroid.ui.home; import com.blankj.utilcode.util.SPUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.App; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.Banner; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.User; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.ArticleUtils; import com.lw.wanandroid.utils.RxSchedulers; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import io.reactivex.Observable; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function3; /** * Created by lw on 2018/1/18. */ public class HomePresenter extends BasePresenter implements HomeContract.Presenter { private int mPage; private boolean mIsRefresh; @Inject public HomePresenter() { this.mIsRefresh = true; } @Override public void loadHomeBanners() { RetrofitManager.create(ApiService.class) .getHomeBanners() .compose(RxSchedulers.>>applySchedulers()) .compose(mView.>>bindToLife()) .subscribe(new Consumer>>() { @Override public void accept(DataResponse> dataResponse) throws Exception { mView.setHomeBanners(dataResponse.getData()); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } @Override public void loadHomeArticles() { RetrofitManager.create(ApiService.class) .getHomeArticles(mPage) .compose(RxSchedulers.>applySchedulers()) .compose(mView.>bindToLife()) .subscribe(new Consumer>() { @Override public void accept(DataResponse
dataResponse) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_SUCCESS : LoadType.TYPE_LOAD_MORE_SUCCESS; mView.setHomeArticles(dataResponse.getData(), loadType); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_ERROR : LoadType.TYPE_LOAD_MORE_ERROR; mView.setHomeArticles(new Article(), loadType); } }); } @Override public void refresh() { mPage = 0; mIsRefresh = true; loadHomeBanners(); loadHomeArticles(); } @Override public void loadMore() { mPage++; mIsRefresh = false; loadHomeArticles(); } @Override public void collectArticle(final int position, final Article.DatasBean bean) { ArticleUtils.collectArticle(mView, position, bean); } @Override public void loadHomeData() { mView.showLoading(); String username = SPUtils.getInstance(Constant.SHARED_NAME).getString(Constant.USERNAME_KEY); String password = SPUtils.getInstance(Constant.SHARED_NAME).getString(Constant.PASSWORD_KEY); Observable> observableUser = RetrofitManager.create(ApiService.class).login(username, password); Observable>> observableBanner = RetrofitManager.create(ApiService.class).getHomeBanners(); Observable> observableArticle = RetrofitManager.create(ApiService.class).getHomeArticles(mPage); Observable.zip(observableUser, observableBanner, observableArticle, new Function3, DataResponse>, DataResponse
, Map>() { @Override public Map apply(DataResponse response, DataResponse> dataResponse, DataResponse
dataResponse2) throws Exception { Map objMap = new HashMap<>(); objMap.put(Constant.USER_KEY, response); objMap.put(Constant.BANNER_KEY, dataResponse.getData()); objMap.put(Constant.ARTICLE_KEY, dataResponse2.getData()); return objMap; } }).compose(RxSchedulers.>applySchedulers()).compose(mView.>bindToLife()).subscribe(new Consumer>() { @Override public void accept(Map map) throws Exception { DataResponse dataResponse = (DataResponse) map.get(Constant.USER_KEY); if (dataResponse.getErrorCode() == 0) { mView.showSuccess(App.getAppContext().getString(R.string.auto_login_success)); } else { mView.showFaild(String.valueOf(dataResponse.getErrorMsg())); } List banners = (List) map.get(Constant.BANNER_KEY); Article article = (Article) map.get(Constant.ARTICLE_KEY); mView.setHomeBanners(banners); mView.setHomeArticles(article, LoadType.TYPE_REFRESH_SUCCESS); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/CommonHotAdapter.java ================================================ package com.lw.wanandroid.ui.hotsearch; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.lw.wanandroid.R; import com.lw.wanandroid.bean.Friend; import javax.inject.Inject; /** * Created by lw on 2018/1/23. */ public class CommonHotAdapter extends BaseQuickAdapter { @Inject public CommonHotAdapter() { super(R.layout.item_hot, null); } @Override protected void convert(BaseViewHolder helper, Friend item) { helper.setText(R.id.tvTitle, item.getName()); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/HistoryAdapter.java ================================================ package com.lw.wanandroid.ui.hotsearch; import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import com.lw.wanandroid.R; import com.lw.wanandroid.db.HistoryModel; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagAdapter; import java.util.List; /** * Created by lw on 2018/2/2. */ public class HistoryAdapter extends TagAdapter { private Context mContext; private LayoutInflater mInflater; public HistoryAdapter(Context context, List datas) { super(datas); this.mContext = context; this.mInflater = LayoutInflater.from(mContext); } @Override public View getView(FlowLayout parent, int position, HistoryModel model) { View view = mInflater.inflate(R.layout.item_history, parent, false); TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle); int parseColor = 0; try { tvTitle.setText(model.getName()); String str = Integer.toHexString((int) (Math.random() * 16777215)); parseColor = Color.parseColor("#".concat(str)); tvTitle.setTextColor(parseColor); } catch (Exception e) { e.printStackTrace(); parseColor = mContext.getResources().getColor(R.color.colorAccent); } tvTitle.setTextColor(parseColor); return view; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/HotAdapter.java ================================================ package com.lw.wanandroid.ui.hotsearch; import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import com.lw.wanandroid.R; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagAdapter; import java.lang.reflect.Field; import java.util.List; /** * Created by lw on 2018/1/23. */ public class HotAdapter extends TagAdapter { private Context mContext; private LayoutInflater mInflater; public HotAdapter(Context context, List datas) { super(datas); this.mContext = context; this.mInflater = LayoutInflater.from(mContext); } @Override public View getView(FlowLayout parent, int position, T item) { View view = mInflater.inflate(R.layout.item_hot, parent, false); TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle); int parseColor = 0; try { String name = ""; Field[] fields = item.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (field.getName().equals("name")) { name = (String) field.get(item); break; } } tvTitle.setText(name); String str = Integer.toHexString((int) (Math.random() * 16777215)); parseColor = Color.parseColor("#".concat(str)); tvTitle.setTextColor(parseColor); } catch (Exception e) { e.printStackTrace(); parseColor = mContext.getResources().getColor(R.color.colorAccent); } tvTitle.setTextColor(parseColor); return view; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/HotContract.java ================================================ package com.lw.wanandroid.ui.hotsearch; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.bean.HotKey; import java.util.List; /** * Created by lw on 2018/1/23. */ public interface HotContract { interface View extends BaseContract.BaseView { void setHotData(List hotKeys, List friends); } interface Presenter extends BaseContract.BasePresenter { void loadHotData(); void refresh(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/HotFragment.java ================================================ package com.lw.wanandroid.ui.hotsearch; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import com.alibaba.android.arouter.launcher.ARouter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseFragment; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.bean.HotKey; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.ui.article.ArticleContentActivity; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagFlowLayout; import java.util.List; import javax.inject.Inject; import butterknife.BindView; /** * Created by lw on 2018/1/23. */ public class HotFragment extends BaseFragment implements HotContract.View, SwipeRefreshLayout.OnRefreshListener { @BindView(R.id.rvHots) RecyclerView mRvHots; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Inject public CommonHotAdapter mCommonHotAdapter; private TagFlowLayout mTtlBookMarks, mTflHotKeys, mTflHotFriends; private HotAdapter mHotKeyAdapter; private HotAdapter mHotFriendAdapter, mBookMarkAdapter; private View mHotHeadView; @Override protected int getLayoutId() { return R.layout.fragment_hot; } @Override protected void initInjector() { mFragmentComponent.inject(this); } @Override protected void initView(View view) { /**设置RecyclerView*/ mRvHots.setLayoutManager(new LinearLayoutManager(getContext())); mRvHots.setAdapter(mCommonHotAdapter); /**设置HotHeadView*/ mHotHeadView = LayoutInflater.from(getContext()).inflate(R.layout.layout_hot_head, null); mTtlBookMarks = (TagFlowLayout) mHotHeadView.findViewById(R.id.tflBookMarks); mTflHotKeys = (TagFlowLayout) mHotHeadView.findViewById(R.id.tflHotKeys); mTflHotFriends = (TagFlowLayout) mHotHeadView.findViewById(R.id.tflHotFriends); mCommonHotAdapter.addHeaderView(mHotHeadView); /**设置监听*/ setListener(); /**请求数据*/ mPresenter.loadHotData(); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void showFaild(String errorMsg) { mSwipeRefreshLayout.setRefreshing(false); } @Override public void setHotData(List hotKeys, List friends) { mHotKeyAdapter = new HotAdapter(getContext(), hotKeys); mTflHotKeys.setAdapter(mHotKeyAdapter); mHotFriendAdapter = new HotAdapter<>(getContext(), friends); mTflHotFriends.setAdapter(mHotFriendAdapter); mSwipeRefreshLayout.setRefreshing(false); } @Override public void onRefresh() { mPresenter.refresh(); } public static HotFragment newInstance() { return new HotFragment(); } private void setListener() { mSwipeRefreshLayout.setOnRefreshListener(this); mTflHotKeys.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { @Override public boolean onTagClick(View view, int position, FlowLayout parent) { String name = mHotKeyAdapter.getItem(position).getName(); ARouter.getInstance().build("/hotsearch/SearchActivity") .withString(Constant.CONTENT_HOT_NAME_KEY, name) .navigation(); return false; } }); mTflHotFriends.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { @Override public boolean onTagClick(View view, int position, FlowLayout parent) { ArticleContentActivity.start(mHotFriendAdapter.getItem(position).getId(), mHotFriendAdapter.getItem(position).getLink(), mHotFriendAdapter.getItem(position).getName(), null); return false; } }); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/HotPresenter.java ================================================ package com.lw.wanandroid.ui.hotsearch; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.bean.HotKey; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.RxSchedulers; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import io.reactivex.Observable; import io.reactivex.functions.BiFunction; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/23. */ public class HotPresenter extends BasePresenter implements HotContract.Presenter { @Inject public HotPresenter() { } @Override public void loadHotData() { mView.showLoading(); Observable>> observableFriend = RetrofitManager.create(ApiService.class).getHotFriends(); Observable>> observableHotKey = RetrofitManager.create(ApiService.class).getHotKeys(); Observable.zip(observableFriend, observableHotKey, new BiFunction>, DataResponse>, Map>() { @Override public Map apply(DataResponse> response, DataResponse> response2) throws Exception { Map objMap = new HashMap<>(); objMap.put(Constant.CONTENT_HOT_KEY, response2.getData()); objMap.put(Constant.CONTENT_HOT_FRIEND_KEY, response.getData()); return objMap; } }).compose(RxSchedulers.>applySchedulers()).compose(mView.>bindToLife()).subscribe(new Consumer>() { @Override public void accept(Map map) throws Exception { List hotKeys = (List) map.get(Constant.CONTENT_HOT_KEY); List friends = (List) map.get(Constant.CONTENT_HOT_FRIEND_KEY); mView.setHotData(hotKeys, friends); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } @Override public void refresh() { loadHotData(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/SearchActivity.java ================================================ package com.lw.wanandroid.ui.hotsearch; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Autowired; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.db.HistoryModel; import com.lw.wanandroid.event.LoginEvent; import com.lw.wanandroid.ui.article.ArticleAdapter; import com.lw.wanandroid.ui.article.ArticleContentActivity; import com.lw.wanandroid.utils.RxBus; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagFlowLayout; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.BindView; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/23. */ @Route(path = "/hotsearch/SearchActivity") public class SearchActivity extends BaseActivity implements SearchContract.View, ArticleAdapter.OnItemClickListener, ArticleAdapter.OnItemChildClickListener, SwipeRefreshLayout.OnRefreshListener, ArticleAdapter.RequestLoadMoreListener { @BindView(R.id.rvArticleList) RecyclerView mRvArticleList; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Inject ArticleAdapter mArticleAdapter; @Autowired public String hotNameKey; private HistoryAdapter mHistoryAdapter; private SearchView mSearchView; private List mHistoryModels; private View mSearchHeadView; private TagFlowLayout mTflHistorys; @Override protected int getLayoutId() { return R.layout.activity_search; } @Override protected void initInjector() { mActivityComponent.inject(this); } @Override protected void initView() { /**设置RecyclerView*/ mRvArticleList.setLayoutManager(new LinearLayoutManager(this)); mRvArticleList.setAdapter(mArticleAdapter); /**设置SearchHeadView*/ mSearchHeadView = LayoutInflater.from(this).inflate(R.layout.layout_search_head, null); mTflHistorys = (TagFlowLayout) mSearchHeadView.findViewById(R.id.tflHistorys); mArticleAdapter.addHeaderView(mSearchHeadView); /**设置事件监听*/ mArticleAdapter.setOnItemClickListener(this); mArticleAdapter.setOnItemChildClickListener(this); mSwipeRefreshLayout.setOnRefreshListener(this); mArticleAdapter.setOnLoadMoreListener(this); /**加载历史搜索记录*/ mPresenter.loadHistory(); /**登陆成功刷新*/ RxBus.getInstance().toFlowable(LoginEvent.class) .subscribe(new Consumer() { @Override public void accept(LoginEvent event) throws Exception { mPresenter.refresh(); } }); mTflHistorys.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { @Override public boolean onTagClick(View view, int position, FlowLayout parent) { String name = mHistoryAdapter.getItem(position).getName(); mSearchView.setQuery(name, false); mPresenter.loadSearchArtcles(name); return false; } }); } @Override protected boolean showHomeAsUp() { return true; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_search, menu); mSearchView = (SearchView) menu.findItem(R.id.menuSearch).getActionView(); mSearchView.setMaxWidth(1920); mSearchView.setIconified(false); mSearchView.setOnCloseListener(new SearchView.OnCloseListener() { @Override public boolean onClose() { SearchActivity.this.finish(); return true; } }); mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { mPresenter.addHistory(query); mPresenter.loadSearchArtcles(query); return true; } @Override public boolean onQueryTextChange(String newText) { return false; } }); /**是否是从hot页面过来的*/ mSearchView.setQuery(hotNameKey, true); return super.onCreateOptionsMenu(menu); } @Override public void onRefresh() { mPresenter.refresh(); } @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { if (view.getId() == R.id.tvChapterName) { List childrenBeans = new ArrayList<>(); childrenBeans.add(new KnowledgeSystem.ChildrenBean(mArticleAdapter.getItem(position).getChapterId(), mArticleAdapter.getItem(position).getChapterName())); ARouter.getInstance().build("/article/ArticleTypeActivity") .withString(Constant.CONTENT_TITLE_KEY, mArticleAdapter.getItem(position).getChapterName()) .withObject(Constant.CONTENT_CHILDREN_DATA_KEY, childrenBeans) .navigation(); } else if (view.getId() == R.id.ivCollect) { mPresenter.collectArticle(position, mArticleAdapter.getItem(position)); } } @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ArticleContentActivity.start(mArticleAdapter.getItem(position).getId(), mArticleAdapter.getItem(position).getLink(), mArticleAdapter.getItem(position).getTitle(), mArticleAdapter.getItem(position).getAuthor()); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void onLoadMoreRequested() { mPresenter.loadMore(); } @Override public void setSearchArtcles(Article article, int loadType) { setLoadDataResult(mArticleAdapter, mSwipeRefreshLayout, article.getDatas(), loadType); } @Override public void collectArticleSuccess(int position, Article.DatasBean bean) { mArticleAdapter.setData(position, bean); } @Override public void setHistory(List historyModels) { this.mHistoryModels = historyModels; mHistoryAdapter = new HistoryAdapter(this, mHistoryModels); mTflHistorys.setAdapter(mHistoryAdapter); mSwipeRefreshLayout.setRefreshing(false); } @Override public void addHistorySuccess(HistoryModel historyModel) { if (mHistoryModels != null) mHistoryModels.add(0, historyModel); mHistoryAdapter.notifyDataChanged(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/SearchContract.java ================================================ package com.lw.wanandroid.ui.hotsearch; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.db.HistoryModel; import java.util.List; /** * Created by lw on 2018/1/23. */ public interface SearchContract { interface View extends BaseContract.BaseView { void setSearchArtcles(Article article, @LoadType.checker int loadType); void collectArticleSuccess(int position, Article.DatasBean bean); void setHistory(List historyModels); void addHistorySuccess(HistoryModel historyModel); } interface Presenter extends BaseContract.BasePresenter { void loadSearchArtcles(String k); void refresh(); void loadMore(); void collectArticle(int position, Article.DatasBean bean); void loadHistory(); void addHistory(String name); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/hotsearch/SearchPresenter.java ================================================ package com.lw.wanandroid.ui.hotsearch; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.db.HistoryModel; import com.lw.wanandroid.db.HistoryModel_Table; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.ArticleUtils; import com.lw.wanandroid.utils.RxSchedulers; import com.raizlabs.android.dbflow.sql.language.SQLite; import java.util.Date; import java.util.List; import javax.inject.Inject; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/23. */ public class SearchPresenter extends BasePresenter implements SearchContract.Presenter { private int mPage; private boolean mIsRefresh; private String mK; @Inject public SearchPresenter() { this.mIsRefresh = true; } @Override public void loadSearchArtcles(String k) { this.mK = k; RetrofitManager.create(ApiService.class) .getSearchArticles(mPage, mK) .compose(RxSchedulers.>applySchedulers()) .compose(mView.>bindToLife()) .subscribe(new Consumer>() { @Override public void accept(DataResponse
dataResponse) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_SUCCESS : LoadType.TYPE_LOAD_MORE_SUCCESS; mView.setSearchArtcles(dataResponse.getData(), loadType); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_ERROR : LoadType.TYPE_LOAD_MORE_ERROR; mView.setSearchArtcles(new Article(), loadType); } }); } @Override public void refresh() { mPage = 0; mIsRefresh = true; loadSearchArtcles(mK); } @Override public void loadMore() { mPage++; mIsRefresh = false; loadSearchArtcles(mK); } @Override public void collectArticle(final int position, final Article.DatasBean bean) { ArticleUtils.collectArticle(mView, position, bean); } @Override public void loadHistory() { mView.showLoading(); Observable.create(new ObservableOnSubscribe>() { @Override public void subscribe(ObservableEmitter> e) throws Exception { List historyModels = SQLite.select().from(HistoryModel.class) .orderBy(HistoryModel_Table.date, false) .limit(10).offset(0) .queryList(); e.onNext(historyModels); } }).compose(RxSchedulers.>applySchedulers()).compose(mView.>bindToLife()).subscribe(new Consumer>() { @Override public void accept(List historyModels) throws Exception { mView.setHistory(historyModels); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } @Override public void addHistory(String name) { HistoryModel historyModel = new HistoryModel(); historyModel.setName(name); historyModel.setDate(new Date()); long id = historyModel.insert(); if (id > 0) mView.addHistorySuccess(historyModel); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/knowledgesystem/KnowledgeSystemAdapter.java ================================================ package com.lw.wanandroid.ui.knowledgesystem; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.lw.wanandroid.R; import com.lw.wanandroid.bean.KnowledgeSystem; import javax.inject.Inject; /** * Created by lw on 2018/1/22. */ public class KnowledgeSystemAdapter extends BaseQuickAdapter { @Inject public KnowledgeSystemAdapter() { super(R.layout.item_knowledge_system, null); } @Override protected void convert(BaseViewHolder helper, KnowledgeSystem item) { helper.setText(R.id.typeItemFirst, item.getName()); StringBuffer sb = new StringBuffer(); for (KnowledgeSystem.ChildrenBean childrenBean : item.getChildren()) { sb.append(childrenBean.getName() + " "); } helper.setText(R.id.typeItemSecond, sb.toString()); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/knowledgesystem/KnowledgeSystemContract.java ================================================ package com.lw.wanandroid.ui.knowledgesystem; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.KnowledgeSystem; import java.util.List; /** * Created by lw on 2018/1/19. */ public interface KnowledgeSystemContract { interface View extends BaseContract.BaseView { void setKnowledgeSystems(List knowledgeSystems); } interface Presenter extends BaseContract.BasePresenter { void loadKnowledgeSystems(); void refresh(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/knowledgesystem/KnowledgeSystemFragment.java ================================================ package com.lw.wanandroid.ui.knowledgesystem; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.alibaba.android.arouter.launcher.ARouter; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseFragment; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.constant.Constant; import java.util.List; import javax.inject.Inject; import butterknife.BindView; /** * Created by lw on 2018/1/18. */ public class KnowledgeSystemFragment extends BaseFragment implements KnowledgeSystemContract.View, KnowledgeSystemAdapter.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener { @BindView(R.id.rvKnowledgeSystems) RecyclerView mRvKnowledgeSystems; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Inject KnowledgeSystemAdapter mKnowledgeSystemAdapter; @Override protected int getLayoutId() { return R.layout.fragment_knowledge_system; } @Override protected void initInjector() { mFragmentComponent.inject(this); } @Override protected void initView(View view) { /**设置RecyclerView*/ mRvKnowledgeSystems.setLayoutManager(new LinearLayoutManager(getContext())); mRvKnowledgeSystems.setAdapter(mKnowledgeSystemAdapter); /**设置事件监听*/ mKnowledgeSystemAdapter.setOnItemClickListener(this); mSwipeRefreshLayout.setOnRefreshListener(this); /**请求数据*/ mPresenter.loadKnowledgeSystems(); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void showFaild(String errorMsg) { mSwipeRefreshLayout.setRefreshing(false); } @Override public void setKnowledgeSystems(List knowledgeSystems) { mKnowledgeSystemAdapter.setNewData(knowledgeSystems); mSwipeRefreshLayout.setRefreshing(false); } @Override public void onRefresh() { mPresenter.refresh(); } @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ARouter.getInstance().build("/article/ArticleTypeActivity") .withString(Constant.CONTENT_TITLE_KEY, mKnowledgeSystemAdapter.getItem(position).getName()) .withObject(Constant.CONTENT_CHILDREN_DATA_KEY, mKnowledgeSystemAdapter.getItem(position).getChildren()) .navigation(); } public static KnowledgeSystemFragment newInstance() { return new KnowledgeSystemFragment(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/knowledgesystem/KnowledgeSystemPresenter.java ================================================ package com.lw.wanandroid.ui.knowledgesystem; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.RxSchedulers; import java.util.List; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/19. */ public class KnowledgeSystemPresenter extends BasePresenter implements KnowledgeSystemContract.Presenter { @Inject public KnowledgeSystemPresenter() { } @Override public void loadKnowledgeSystems() { mView.showLoading(); RetrofitManager.create(ApiService.class) .getKnowledgeSystems() .compose(RxSchedulers.>>applySchedulers()) .compose(mView.>>bindToLife()) .subscribe(new Consumer>>() { @Override public void accept(DataResponse> dataResponse) throws Exception { mView.setKnowledgeSystems(dataResponse.getData()); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } @Override public void refresh() { loadKnowledgeSystems(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/LoginActivity.java ================================================ package com.lw.wanandroid.ui.my; import android.support.design.widget.TextInputEditText; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.SPUtils; import com.blankj.utilcode.util.StringUtils; import com.blankj.utilcode.util.ToastUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.bean.User; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.event.LoginEvent; import com.lw.wanandroid.utils.RxBus; import butterknife.BindView; import butterknife.OnClick; /** * Created by lw on 2018/1/24. */ @Route(path = "/my/LoginActivity") public class LoginActivity extends BaseActivity implements LoginContract.View { @BindView(R.id.etUsername) TextInputEditText mEtUsername; @BindView(R.id.etPassword) TextInputEditText mEtPassword; @Override protected int getLayoutId() { return R.layout.activity_login; } @Override protected void initInjector() { mActivityComponent.inject(this); } @Override protected void initView() { mEtUsername.setText(SPUtils.getInstance(Constant.SHARED_NAME).getString(Constant.USERNAME_KEY)); mEtPassword.setText(SPUtils.getInstance(Constant.SHARED_NAME).getString(Constant.PASSWORD_KEY)); } @Override protected boolean showHomeAsUp() { return true; } @OnClick(R.id.btnLogin) public void login() { String username = mEtUsername.getText().toString(); String password = mEtPassword.getText().toString(); if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { ToastUtils.showShort(R.string.the_username_or_password_can_not_be_empty); return; } mPresenter.login(username, password); } @Override public void loginSuccess(User user) { SPUtils.getInstance(Constant.SHARED_NAME).put(Constant.LOGIN_KEY, true); SPUtils.getInstance(Constant.SHARED_NAME).put(Constant.USERNAME_KEY, user.getUsername()); SPUtils.getInstance(Constant.SHARED_NAME).put(Constant.PASSWORD_KEY, user.getPassword()); /**登陆成功通知其他界面刷新*/ RxBus.getInstance().post(new LoginEvent()); this.finish(); } public static void start() { ARouter.getInstance().build("/my/LoginActivity").navigation(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/LoginContract.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.User; /** * Created by lw on 2018/1/24. */ public interface LoginContract { interface View extends BaseContract.BaseView { void loginSuccess(User user); } interface Presenter extends BaseContract.BasePresenter { void login(String username, String password); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/LoginPresenter.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.User; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.RxSchedulers; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/24. */ public class LoginPresenter extends BasePresenter implements LoginContract.Presenter { @Inject public LoginPresenter() { } @Override public void login(String username, String password) { RetrofitManager.create(ApiService.class) .login(username, password) .compose(RxSchedulers.>applySchedulers()) .compose(mView.>bindToLife()) .subscribe(new Consumer>() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { mView.loginSuccess(response.getData()); } else { mView.showFaild(String.valueOf(response.getErrorMsg())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyBookmarkActivity.java ================================================ package com.lw.wanandroid.ui.my; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Route; import com.blankj.utilcode.util.ToastUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.ui.article.ArticleContentActivity; import com.lw.wanandroid.ui.hotsearch.HotAdapter; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagFlowLayout; import java.util.List; import java.util.Set; import butterknife.BindView; /** * Created by lw on 2018/1/25. */ @Route(path = "/my/MyBookmarkActivity") public class MyBookmarkActivity extends BaseActivity implements MyBookmarkContract.View, SwipeRefreshLayout.OnRefreshListener { @BindView(R.id.tflMyBookmarks) TagFlowLayout mTflMyBookmarks; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; private HotAdapter mBookmarkAdapter; @Override protected int getLayoutId() { return R.layout.activity_my_bookmark; } @Override protected void initInjector() { mActivityComponent.inject(this); } @Override protected void initView() { mSwipeRefreshLayout.setOnRefreshListener(this); mPresenter.loadMyBookmarks(); mTflMyBookmarks.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { @Override public boolean onTagClick(View view, int position, FlowLayout parent) { ArticleContentActivity.start(mBookmarkAdapter.getItem(position).getId(), mBookmarkAdapter.getItem(position).getLink(), mBookmarkAdapter.getItem(position).getName(), null); return false; } }); } @Override protected boolean showHomeAsUp() { return true; } @Override public void setMyBookmarks(List bookmarks) { mBookmarkAdapter = new HotAdapter(this, bookmarks); mTflMyBookmarks.setAdapter(mBookmarkAdapter); mSwipeRefreshLayout.setRefreshing(false); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override public void onRefresh() { mPresenter.refresh(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyBookmarkContract.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Friend; import java.util.List; /** * Created by lw on 2018/2/2. */ public interface MyBookmarkContract { interface View extends BaseContract.BaseView { void setMyBookmarks(List bookmarks); } interface Presenter extends BaseContract.BasePresenter { void loadMyBookmarks(); void editBookmark(int id, String name, String link); void delBookmark(int id); void refresh(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyBookmarkPresenter.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.bean.Friend; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.RxSchedulers; import java.util.List; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/2/2. */ public class MyBookmarkPresenter extends BasePresenter implements MyBookmarkContract.Presenter { private boolean mIsRefresh; @Inject public MyBookmarkPresenter() { this.mIsRefresh = true; } @Override public void loadMyBookmarks() { mView.showLoading(); RetrofitManager.create(ApiService.class) .getBookmarks() .compose(RxSchedulers.>>applySchedulers()) .compose(mView.>>bindToLife()) .subscribe(new Consumer>>() { @Override public void accept(DataResponse> response) throws Exception { mView.setMyBookmarks(response.getData()); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { mView.showFaild(throwable.getMessage()); } }); } @Override public void editBookmark(int id, String name, String link) { } @Override public void delBookmark(int id) { } @Override public void refresh() { mIsRefresh = true; loadMyBookmarks(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyCollectionActivity.java ================================================ package com.lw.wanandroid.ui.my; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; import com.chad.library.adapter.base.BaseQuickAdapter; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.KnowledgeSystem; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.ui.article.ArticleAdapter; import com.lw.wanandroid.ui.article.ArticleContentActivity; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.BindView; /** * Created by lw on 2018/1/25. */ @Route(path = "/my/MyCollectionActivity") public class MyCollectionActivity extends BaseActivity implements MyCollectionContract.View, ArticleAdapter.OnItemClickListener, ArticleAdapter.OnItemChildClickListener, SwipeRefreshLayout.OnRefreshListener, ArticleAdapter.RequestLoadMoreListener { @BindView(R.id.rvMyCollectionArticles) RecyclerView mRvMyCollectionArticles; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Inject ArticleAdapter mArticleAdapter; @Override protected int getLayoutId() { return R.layout.activity_my_collection; } @Override protected void initInjector() { mActivityComponent.inject(this); } @Override protected void initView() { /**设置RecyclerView*/ mRvMyCollectionArticles.setLayoutManager(new LinearLayoutManager(this)); mRvMyCollectionArticles.setAdapter(mArticleAdapter); /**设置事件监听*/ mArticleAdapter.setOnItemClickListener(this); mArticleAdapter.setOnItemChildClickListener(this); mSwipeRefreshLayout.setOnRefreshListener(this); mArticleAdapter.setOnLoadMoreListener(this); mArticleAdapter.isMyColection(true); /**请求数据*/ mPresenter.loadMyCollectArticles(); } @Override public void showLoading() { mSwipeRefreshLayout.setRefreshing(true); } @Override protected boolean showHomeAsUp() { return true; } @Override public void setMyCollectArticles(Article article, int loadType) { setLoadDataResult(mArticleAdapter, mSwipeRefreshLayout, article.getDatas(), loadType); } @Override public void unCollectArticleSuccess(int position) { mArticleAdapter.remove(position); } @Override public void onRefresh() { mPresenter.refresh(); } @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { if (view.getId() == R.id.tvChapterName) { List childrenBeans = new ArrayList<>(); childrenBeans.add(new KnowledgeSystem.ChildrenBean(mArticleAdapter.getItem(position).getChapterId(), mArticleAdapter.getItem(position).getChapterName())); ARouter.getInstance().build("/article/ArticleTypeActivity") .withString(Constant.CONTENT_TITLE_KEY, mArticleAdapter.getItem(position).getChapterName()) .withObject(Constant.CONTENT_CHILDREN_DATA_KEY, childrenBeans) .navigation(); } else if (view.getId() == R.id.ivCollect) { mPresenter.unCollectArticle(position, mArticleAdapter.getItem(position)); } } @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ArticleContentActivity.start(mArticleAdapter.getItem(position).getId(), mArticleAdapter.getItem(position).getLink(), mArticleAdapter.getItem(position).getTitle(), mArticleAdapter.getItem(position).getAuthor()); } @Override public void onLoadMoreRequested() { mPresenter.loadMore(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyCollectionContract.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.constant.LoadType; /** * Created by lw on 2018/2/2. */ public interface MyCollectionContract { interface View extends BaseContract.BaseView { void setMyCollectArticles(Article article, @LoadType.checker int loadType); void unCollectArticleSuccess(int position); } interface Presenter extends BaseContract.BasePresenter { void loadMyCollectArticles(); void refresh(); void loadMore(); void unCollectArticle(int position, Article.DatasBean bean); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyCollectionPresenter.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BasePresenter; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.constant.LoadType; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.utils.ArticleUtils; import com.lw.wanandroid.utils.RxSchedulers; import javax.inject.Inject; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/2/2. */ public class MyCollectionPresenter extends BasePresenter implements MyCollectionContract.Presenter { private int mPage; private boolean mIsRefresh; @Inject public MyCollectionPresenter() { this.mIsRefresh = true; } @Override public void loadMyCollectArticles() { if (mIsRefresh) mView.showLoading(); RetrofitManager.create(ApiService.class) .getCollectArticles(mPage) .compose(RxSchedulers.>applySchedulers()) .compose(mView.>bindToLife()) .subscribe(new Consumer>() { @Override public void accept(DataResponse
dataResponse) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_SUCCESS : LoadType.TYPE_LOAD_MORE_SUCCESS; mView.setMyCollectArticles(dataResponse.getData(), loadType); } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { int loadType = mIsRefresh ? LoadType.TYPE_REFRESH_ERROR : LoadType.TYPE_LOAD_MORE_ERROR; mView.setMyCollectArticles(new Article(), loadType); } }); } @Override public void refresh() { mPage = 0; mIsRefresh = true; loadMyCollectArticles(); } @Override public void loadMore() { mPage++; mIsRefresh = false; loadMyCollectArticles(); } @Override public void unCollectArticle(final int position, final Article.DatasBean bean) { ArticleUtils.collectArticle(mView, position, bean); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyContract.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BaseContract; /** * Created by lw on 2018/1/19. */ public interface MyContract { interface View extends BaseContract.BaseView { } interface Presenter extends BaseContract.BasePresenter { } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyFragment.java ================================================ package com.lw.wanandroid.ui.my; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.alibaba.android.arouter.launcher.ARouter; import com.blankj.utilcode.util.SPUtils; import com.blankj.utilcode.util.ToastUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseFragment; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.event.LoginEvent; import com.lw.wanandroid.net.CookiesManager; import com.lw.wanandroid.utils.RxBus; import butterknife.BindView; import butterknife.OnClick; import de.hdodenhof.circleimageview.CircleImageView; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/1/18. */ public class MyFragment extends BaseFragment implements MyContract.View { @BindView(R.id.civAvatar) CircleImageView mCivAvatar; @BindView(R.id.tvNick) TextView mTvNick; @BindView(R.id.llLogout) LinearLayout mLlLogout; private boolean mIsLogin; @Override protected int getLayoutId() { return R.layout.fragment_my; } @Override protected void initInjector() { mFragmentComponent.inject(this); } @Override protected void initView(View view) { setUserStatusInfo(); /**登陆成功重新设置用户新*/ RxBus.getInstance().toFlowable(LoginEvent.class).subscribe(new Consumer() { @Override public void accept(LoginEvent event) throws Exception { setUserStatusInfo(); } }); } @OnClick({R.id.civAvatar, R.id.tvMyCollection, R.id.tvMyBookmark, R.id.tvSetting, R.id.llLogout}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.civAvatar: if (!mIsLogin) LoginActivity.start(); break; case R.id.tvMyCollection: if (mIsLogin) ARouter.getInstance().build("/my/MyCollectionActivity").navigation(); else ToastUtils.showShort(R.string.not_login); break; case R.id.tvMyBookmark: if (mIsLogin) ARouter.getInstance().build("/my/MyBookmarkActivity").navigation(); else ToastUtils.showShort(R.string.not_login); break; case R.id.tvSetting: ARouter.getInstance().build("/setting/SettingActivity").navigation(); break; case R.id.llLogout: logout(); break; } } /** * 退出登陆 */ private void logout() { /**设置退出登陆*/ SPUtils.getInstance(Constant.SHARED_NAME).clear(); setUserStatusInfo(); /**清除cookies*/ CookiesManager.clearAllCookies(); /**发送退出登陆的消息*/ RxBus.getInstance().post(new LoginEvent()); } /** * 设置用户状态信息 */ private void setUserStatusInfo() { mIsLogin = SPUtils.getInstance(Constant.SHARED_NAME).getBoolean(Constant.LOGIN_KEY); if (mIsLogin) { mCivAvatar.setImageResource(R.drawable.ic_head_portrait); mTvNick.setText(SPUtils.getInstance(Constant.SHARED_NAME).getString(Constant.USERNAME_KEY)); mLlLogout.setVisibility(View.VISIBLE); } else { mCivAvatar.setImageResource(R.drawable.ic_avatar); mTvNick.setText(R.string.click_avatar_login); mLlLogout.setVisibility(View.GONE); } } public static MyFragment newInstance() { return new MyFragment(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/MyPresenter.java ================================================ package com.lw.wanandroid.ui.my; import com.lw.wanandroid.base.BasePresenter; import javax.inject.Inject; /** * Created by lw on 2018/1/19. */ public class MyPresenter extends BasePresenter implements MyContract.Presenter { @Inject public MyPresenter() { } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/my/RegisterActivity.java ================================================ package com.lw.wanandroid.ui.my; import com.alibaba.android.arouter.facade.annotation.Route; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; /** * Created by lw on 2018/1/24. */ @Route(path = "/my/RegisterActivity") public class RegisterActivity extends BaseActivity { @Override protected int getLayoutId() { return R.layout.activity_register; } @Override protected void initInjector() { } @Override protected void initView() { } @Override protected boolean showHomeAsUp() { return true; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/setting/SettingActivity.java ================================================ package com.lw.wanandroid.ui.setting; import com.alibaba.android.arouter.facade.annotation.Route; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; /** * Created by lw on 2018/1/25. */ @Route(path = "/setting/SettingActivity") public class SettingActivity extends BaseActivity { @Override protected int getLayoutId() { return R.layout.activity_setting; } @Override protected void initInjector() { } @Override protected void initView() { getSupportFragmentManager().beginTransaction() .replace(R.id.layout_fragment, SettingFragment.newInstance()) .commit(); } @Override protected boolean showHomeAsUp() { return true; } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/setting/SettingFragment.java ================================================ package com.lw.wanandroid.ui.setting; import android.os.Bundle; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; import com.alibaba.android.arouter.launcher.ARouter; import com.lw.wanandroid.R; /** * Created by lw on 2017-09-05. */ public class SettingFragment extends PreferenceFragmentCompat { private Preference mSettingAutoUpdate, mCheckUpdate, mAbout, mTestPage; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.settings_preference_fragment); mSettingAutoUpdate = findPreference("settingAutoUpdate"); mCheckUpdate = findPreference("checkUpdate"); mAbout = findPreference("about"); mTestPage = findPreference("testPage"); mTestPage.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { ARouter.getInstance().build("/setting/TestPageActivity").navigation(); return false; } }); } public static SettingFragment newInstance() { return new SettingFragment(); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/ui/setting/TestPageActivity.java ================================================ package com.lw.wanandroid.ui.setting; import android.util.Log; import android.view.View; import android.widget.Button; import com.alibaba.android.arouter.facade.annotation.Route; import com.lw.wanandroid.R; import com.lw.wanandroid.base.BaseActivity; import java.io.IOException; import java.util.concurrent.TimeUnit; import butterknife.BindView; import butterknife.OnClick; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * Created by lw on 2018/2/12. */ @Route(path = "/setting/TestPageActivity") public class TestPageActivity extends BaseActivity { private static final String TAG = "TestPageActivity"; @BindView(R.id.btnTest1) Button mBtnTest1; @BindView(R.id.btnTest2) Button mBtnTest2; @BindView(R.id.btnTest3) Button mBtnTest3; OkHttpClient mOkHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build(); @Override protected int getLayoutId() { return R.layout.activity_test_page; } @Override protected void initInjector() { } @Override protected void initView() { } @Override protected boolean showHomeAsUp() { return true; } @OnClick({R.id.btnTest1, R.id.btnTest2, R.id.btnTest3}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.btnTest1: test1(); break; case R.id.btnTest2: break; case R.id.btnTest3: break; } } private void test1() { Request request = new Request.Builder().url("http://www.baidu.com").build(); Call call = mOkHttpClient.newCall(request); try { //Response response = call.execute(); //Log.d(TAG, "test1: " + response.body().string()); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.d(TAG, "test1: " + e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { Log.d(TAG, "test1: " + response.body().string()); } }); } catch (Exception e) { e.printStackTrace(); } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/utils/ArticleUtils.java ================================================ package com.lw.wanandroid.utils; import com.blankj.utilcode.util.SPUtils; import com.lw.wanandroid.R; import com.lw.wanandroid.base.App; import com.lw.wanandroid.base.BaseContract; import com.lw.wanandroid.bean.Article; import com.lw.wanandroid.bean.DataResponse; import com.lw.wanandroid.constant.Constant; import com.lw.wanandroid.net.ApiService; import com.lw.wanandroid.net.RetrofitManager; import com.lw.wanandroid.ui.article.ArticleListContract; import com.lw.wanandroid.ui.home.HomeContract; import com.lw.wanandroid.ui.hotsearch.SearchContract; import com.lw.wanandroid.ui.my.LoginActivity; import com.lw.wanandroid.ui.my.MyCollectionContract; import io.reactivex.functions.Consumer; /** * Created by lw on 2018/2/28. */ public class ArticleUtils { /** * 文章收藏 * * @param view * @param position * @param bean */ public static void collectArticle(final BaseContract.BaseView view, final int position, final Article.DatasBean bean) { if (SPUtils.getInstance(Constant.SHARED_NAME).getBoolean(Constant.LOGIN_KEY)) { if (bean.isCollect()) { RetrofitManager.create(ApiService.class).removeCollectArticle(bean.getId(), -1) .compose(RxSchedulers.applySchedulers()) .compose(view.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { bean.setCollect(!bean.isCollect()); if (view instanceof HomeContract.View) { ((HomeContract.View) view).collectArticleSuccess(position, bean); } else if (view instanceof ArticleListContract.View) { ((ArticleListContract.View) view).collectArticleSuccess(position, bean); } else if (view instanceof MyCollectionContract.View) { ((MyCollectionContract.View) view).unCollectArticleSuccess(position); } else if (view instanceof SearchContract.View) { ((SearchContract.View) view).collectArticleSuccess(position, bean); } view.showSuccess(App.getAppContext().getString(R.string.collection_cancel_success)); } else { view.showFaild(App.getAppContext().getString(R.string.collection_cancel_failed, response.getData())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { view.showFaild(throwable.getMessage()); } }); } else { RetrofitManager.create(ApiService.class).addCollectArticle(bean.getId()) .compose(RxSchedulers.applySchedulers()) .compose(view.bindToLife()) .subscribe(new Consumer() { @Override public void accept(DataResponse response) throws Exception { if (response.getErrorCode() == 0) { bean.setCollect(!bean.isCollect()); if (view instanceof HomeContract.View) { ((HomeContract.View) view).collectArticleSuccess(position, bean); } else if (view instanceof ArticleListContract.View) { ((ArticleListContract.View) view).collectArticleSuccess(position, bean); } else if (view instanceof MyCollectionContract.View) { ((MyCollectionContract.View) view).unCollectArticleSuccess(position); } else if (view instanceof SearchContract.View) { ((SearchContract.View) view).collectArticleSuccess(position, bean); } view.showSuccess(App.getAppContext().getString(R.string.collection_success)); } else { view.showFaild(App.getAppContext().getString(R.string.collection_failed, response.getErrorMsg())); } } }, new Consumer() { @Override public void accept(Throwable throwable) throws Exception { view.showFaild(throwable.getMessage()); } }); } } else { LoginActivity.start(); } } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/utils/GlideImageLoader.java ================================================ package com.lw.wanandroid.utils; import android.content.Context; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.youth.banner.loader.ImageLoader; /** * Created by lw on 2017-09-01. */ public class GlideImageLoader extends ImageLoader { @Override public void displayImage(Context context, Object path, ImageView imageView) { //具体方法内容自己去选择,次方法是为了减少banner过多的依赖第三方包,所以将这个权限开放给使用者去选择 Glide.with(context.getApplicationContext()) .load(path) .into(imageView); } } ================================================ FILE: app/src/main/java/com/lw/wanandroid/utils/GsonUtils.java ================================================ package com.lw.wanandroid.utils; import com.blankj.utilcode.util.StringUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; /** * Created by lw on 2017-05-04. */ public class GsonUtils { private static final Gson gson = new GsonBuilder() .disableHtmlEscaping() .create(); /** *
     * JSON字符串转换为List数组, 提供两种方式(主要解决调用的容易程度)
     * 1. TypeToken> token 参数转换
     * 2. Class cls 方式转换
     *
     * @param json
     * @return List
     *
     * 
     */
    public static  List convertList(String json, TypeToken> token) {
        if (StringUtils.isSpace(json)) {
            return new ArrayList();
        }
        return gson.fromJson(json, token.getType());
    }

    /**
     * 
     * Json格式转换, 由JSON字符串转化到制定类型T
     *
     * @param json
     * @param cls
     * @return T
     *
     * 
     */
    public static  T convertObj(String json, Class cls) {
        if (StringUtils.isSpace(json)) {
            return null;
        }
        return gson.fromJson(json, cls);
    }

    /**
     * 
     * Json格式转换, 由JSON字符串转化到制定类型T
     *
     * @param json
     * @param cls
     * @return T
     *
     * 
     */
    public static  T convertObj(String json, Type cls) {
        if (StringUtils.isSpace(json)) {
            return null;
        }
        return gson.fromJson(json, cls);
    }

    /**
     * 
     * java对象转化JSON
     *
     * @return String
     *
     * 
     */
    public static String toJson(Object obj) {
        if (obj == null) {
            return "";
        }
        return gson.toJson(obj);
    }

    public static String getJsonObjectAsString(JsonObject jsonObject, String name) {
        if (jsonObject == null || StringUtils.isSpace(name)) {
            return null;
        }
        JsonElement jsonElement = jsonObject.get(name);
        return (jsonElement == null) ? null : jsonElement.getAsString();
    }

    public static JsonObject getJsonObjectChild(JsonObject jsonObject, String name) {
        if (jsonObject == null || StringUtils.isSpace(name)) {
            return null;
        }
        JsonElement jsonElement = jsonObject.get(name);
        return (jsonElement == null) ? null : jsonElement.getAsJsonObject();
    }

    public static boolean getJsonObjectAsBoolean(JsonObject jsonObject, String name) {
        if (jsonObject == null || StringUtils.isSpace(name)) {
            return false;
        }
        JsonElement jsonElement = jsonObject.get(name);
        return (jsonElement == null) ? false : jsonElement.getAsBoolean();
    }
}


================================================
FILE: app/src/main/java/com/lw/wanandroid/utils/RxBus.java
================================================
package com.lw.wanandroid.utils;

import io.reactivex.Flowable;
import io.reactivex.processors.FlowableProcessor;
import io.reactivex.processors.PublishProcessor;

/**
 * Created by lw on 2018/1/25.
 */

public class RxBus {
    private static volatile RxBus sRxBus;
    // 主题
    private final FlowableProcessor mBus;

    // PublishSubject只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者
    public RxBus() {
        mBus = PublishProcessor.create().toSerialized();
    }

    // 单例RxBus
    public static RxBus getInstance() {
        if (sRxBus == null) {
            synchronized (RxBus.class) {
                if (sRxBus == null) {
                    sRxBus = new RxBus();
                }
            }
        }
        return sRxBus;
    }

    // 提供了一个新的事件
    public void post(Object o) {
        mBus.onNext(o);
    }

    // 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
    public  Flowable toFlowable(Class eventType) {
        return mBus.ofType(eventType);
    }
}


================================================
FILE: app/src/main/java/com/lw/wanandroid/utils/RxSchedulers.java
================================================
package com.lw.wanandroid.utils;


import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * 通用的Rx线程转换类
 * 参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0819/3327.html
 */
public class RxSchedulers {


    static final ObservableTransformer schedulersTransformer = new ObservableTransformer() {
        @Override
        public ObservableSource apply(Observable upstream) {
            return (upstream).subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
        }
    };


    public static  ObservableTransformer applySchedulers() {
        return (ObservableTransformer) schedulersTransformer;
    }
}


================================================
FILE: app/src/main/res/drawable/ic_action_browser.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_hot.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_like.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_no_like.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_search.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_share.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_action_white_like.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_chevron_right_black_24dp.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_dashboard_black_24dp.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_home_black_24dp.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/ic_my_black_24dp.xml
================================================

    



================================================
FILE: app/src/main/res/drawable/item_selector.xml
================================================


    
    


================================================
FILE: app/src/main/res/drawable/item_selector_hot.xml
================================================


    
    
    



================================================
FILE: app/src/main/res/drawable-v21/item_selector_hot.xml
================================================


    
    
        
            
            
            
        
    



================================================
FILE: app/src/main/res/layout/activity_article_content.xml
================================================



    

        
        
    

    


================================================
FILE: app/src/main/res/layout/activity_article_type.xml
================================================



    

        
        

        
    

    


================================================
FILE: app/src/main/res/layout/activity_login.xml
================================================