├── settings.gradle
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_cat.png
│ │ │ │ ├── ic_fork.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_menu_about.png
│ │ │ │ ├── ic_menu_code.png
│ │ │ │ ├── ic_menu_theme.png
│ │ │ │ └── ic_menu_favorite.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_fork.png
│ │ │ │ ├── ic_hugh.png
│ │ │ │ ├── ic_cancel.png
│ │ │ │ ├── ic_github.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_menu_about.png
│ │ │ │ ├── ic_menu_code.png
│ │ │ │ ├── ic_menu_theme.png
│ │ │ │ ├── ic_github_circle.png
│ │ │ │ └── ic_menu_favorite.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── ids.xml
│ │ │ │ ├── color_red.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── color_gray.xml
│ │ │ │ ├── color_green.xml
│ │ │ │ ├── color_purple.xml
│ │ │ │ ├── drawables.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── drawable
│ │ │ │ ├── bg_rect_pressed.xml
│ │ │ │ ├── bg_rect_normal.xml
│ │ │ │ ├── shape_def_icon.xml
│ │ │ │ ├── side_nav_bar.xml
│ │ │ │ ├── layer_app_splash.xml
│ │ │ │ ├── side_nav_bar_blue.xml
│ │ │ │ ├── side_nav_bar_purple.xml
│ │ │ │ ├── side_nav_bar_red.xml
│ │ │ │ ├── ic_check_48dp.xml
│ │ │ │ ├── side_nav_bar_gray.xml
│ │ │ │ ├── bg_common_rect.xml
│ │ │ │ ├── ic_star_black_18dp.xml
│ │ │ │ ├── ic_visibility_black_20dp.xml
│ │ │ │ ├── ic_remove_red_eye_black_24dp.xml
│ │ │ │ ├── selectable_item_background.xml
│ │ │ │ ├── ic_share_black_20dp.xml
│ │ │ │ ├── custom_progress_drawable.xml
│ │ │ │ └── vector_logo_text.xml
│ │ │ ├── drawable-v21
│ │ │ │ ├── ic_menu_send.xml
│ │ │ │ ├── ic_menu_slideshow.xml
│ │ │ │ ├── ic_menu_gallery.xml
│ │ │ │ ├── ic_menu_manage.xml
│ │ │ │ ├── ic_menu_camera.xml
│ │ │ │ └── ic_menu_share.xml
│ │ │ ├── menu
│ │ │ │ ├── activity_theme_menu.xml
│ │ │ │ ├── activity_main_menu.xml
│ │ │ │ ├── menu_about.xml
│ │ │ │ ├── activity_search_menu.xml
│ │ │ │ └── activity_main_drawer.xml
│ │ │ ├── layout
│ │ │ │ ├── search_view.xml
│ │ │ │ ├── fragment_tag_cache.xml
│ │ │ │ ├── include_toolbar.xml
│ │ │ │ ├── activity_search.xml
│ │ │ │ ├── activity_theme.xml
│ │ │ │ ├── activity_favorites.xml
│ │ │ │ ├── item_other.xml
│ │ │ │ ├── fragment_repos.xml
│ │ │ │ ├── activity_languages.xml
│ │ │ │ ├── item_other_langs_header.xml
│ │ │ │ ├── content_about.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_lang.xml
│ │ │ │ ├── content_main.xml
│ │ │ │ ├── nav_header_main.xml
│ │ │ │ ├── item_theme.xml
│ │ │ │ ├── item_selected_langs_header.xml
│ │ │ │ ├── activity_webview.xml
│ │ │ │ ├── app_bar_main.xml
│ │ │ │ ├── item_trends.xml
│ │ │ │ ├── activity_about.xml
│ │ │ │ └── item_repo.xml
│ │ │ ├── values-v21
│ │ │ │ └── styles.xml
│ │ │ └── values-w820dp
│ │ │ │ └── dimens.xml
│ │ ├── java
│ │ │ └── net
│ │ │ │ └── angrycode
│ │ │ │ └── wehub
│ │ │ │ ├── event
│ │ │ │ ├── ThemeChangeEvent.java
│ │ │ │ └── LangEditedEvent.java
│ │ │ │ ├── bean
│ │ │ │ ├── Trends.java
│ │ │ │ ├── Langs.java
│ │ │ │ ├── Lang.java
│ │ │ │ ├── AppUpdate.java
│ │ │ │ ├── GsonBuilder.java
│ │ │ │ ├── Owner.java
│ │ │ │ └── TrendingRepo.java
│ │ │ │ ├── ui
│ │ │ │ ├── component
│ │ │ │ │ ├── drag
│ │ │ │ │ │ ├── OnItemMoveListener.java
│ │ │ │ │ │ ├── OnDragVHListener.java
│ │ │ │ │ │ └── ItemDragHelperCallback.java
│ │ │ │ │ ├── LangGridLayoutManager.java
│ │ │ │ │ ├── WebViewFallback.java
│ │ │ │ │ ├── WebViewActivity.java
│ │ │ │ │ └── DividerItemDecoration.java
│ │ │ │ ├── adapter
│ │ │ │ │ ├── ViewHolder.java
│ │ │ │ │ ├── MainPagerAdapter.java
│ │ │ │ │ ├── ReposAdapter.java
│ │ │ │ │ ├── TrendsAdapter.java
│ │ │ │ │ ├── BaseSimpleRecycleAdapter.java
│ │ │ │ │ └── ThemeAdapter.java
│ │ │ │ ├── fragment
│ │ │ │ │ ├── BaseFragment.java
│ │ │ │ │ ├── TagCacheFragment.java
│ │ │ │ │ └── ReposFragment.java
│ │ │ │ └── activity
│ │ │ │ │ ├── AboutActivity.java
│ │ │ │ │ ├── ThemeActivity.java
│ │ │ │ │ ├── LanguagesActivity.java
│ │ │ │ │ ├── SearchActivity.java
│ │ │ │ │ ├── FavoritesActivity.java
│ │ │ │ │ └── BaseActivity.java
│ │ │ │ ├── mvp
│ │ │ │ ├── view
│ │ │ │ │ ├── LangView.java
│ │ │ │ │ ├── MainView.java
│ │ │ │ │ ├── FavorView.java
│ │ │ │ │ ├── RepositoriesView.java
│ │ │ │ │ ├── TrendingView.java
│ │ │ │ │ └── BaseView.java
│ │ │ │ └── presenter
│ │ │ │ │ ├── Presenter.java
│ │ │ │ │ ├── FavorPresenter.java
│ │ │ │ │ ├── MainPresenter.java
│ │ │ │ │ ├── ReposPresenter.java
│ │ │ │ │ ├── LangPresenter.java
│ │ │ │ │ └── TrendsPresenter.java
│ │ │ │ ├── utils
│ │ │ │ ├── SnackbarUtils.java
│ │ │ │ ├── LogUtils.java
│ │ │ │ ├── GlideUtils.java
│ │ │ │ ├── IntentUtils.java
│ │ │ │ ├── ToastUtils.java
│ │ │ │ ├── Utils.java
│ │ │ │ ├── WebUtils.java
│ │ │ │ ├── ThemeUtils.java
│ │ │ │ ├── MD5Utils.java
│ │ │ │ ├── JsonCacheUtils.java
│ │ │ │ ├── TagPrefUtils.java
│ │ │ │ ├── AppUtils.java
│ │ │ │ ├── LangsUtils.java
│ │ │ │ ├── Keyboard.java
│ │ │ │ └── FileUtils.java
│ │ │ │ ├── api
│ │ │ │ ├── ReposApi.java
│ │ │ │ ├── CacheInterceptor.java
│ │ │ │ ├── LangApi.java
│ │ │ │ ├── FavorApi.java
│ │ │ │ ├── BaseTrendApi.java
│ │ │ │ ├── TrendingApi.java
│ │ │ │ └── BaseApi.java
│ │ │ │ └── WeCodeApp.java
│ │ ├── assets
│ │ │ └── langs
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── net
│ │ │ └── angrycode
│ │ │ └── githubtrending
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── net
│ │ └── angrycode
│ │ └── githubtrending
│ │ └── ExampleInstrumentedTest.java
├── build.gradle
└── proguard-rules.pro
├── gradle
└── wrapper
│ └── gradle-wrapper.jar
├── .gitignore
├── customtabs
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── org
│ └── chromium
│ └── customtabsclient
│ └── shared
│ ├── KeepAliveService.java
│ ├── ServiceConnectionCallback.java
│ └── ServiceConnection.java
├── README.md
└── gradlew.bat
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':customtabs'
2 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.gradle
3 | /.idea
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_cat.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_fork.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_fork.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_hugh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_hugh.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_cancel.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_github.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_menu_about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_menu_about.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_menu_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_menu_code.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_menu_theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_menu_theme.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_menu_about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_menu_about.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_menu_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_menu_code.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_menu_theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_menu_theme.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_menu_favorite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-hdpi/ic_menu_favorite.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_github_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_github_circle.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_menu_favorite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hylinux1024/WeCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_menu_favorite.png
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | /.idea
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | *.keystore
12 | *.properties
13 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/event/ThemeChangeEvent.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.event;
2 |
3 | /**
4 | * Created by lancelot on 2016/12/18.
5 | */
6 |
7 | public class ThemeChangeEvent {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/Trends.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by lancelot on 2016/12/2.
7 | */
8 |
9 | public class Trends {
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/drag/OnItemMoveListener.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.component.drag;
2 |
3 | public interface OnItemMoveListener {
4 | boolean onItemMove(int fromPosition, int toPosition);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_rect_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_rect_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_def_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_send.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_theme_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/LangView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import net.angrycode.wehub.bean.Langs;
4 |
5 | /**
6 | * Created by lancelot on 2016/12/17.
7 | */
8 |
9 | public interface LangView extends BaseView {
10 | void onGetLangsFinished(Langs langs);
11 |
12 | void onSetLangsFinished(Langs langs);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/layer_app_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar_blue.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/search_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar_purple.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar_red.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_48dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar_gray.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_common_rect.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/MainView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import net.angrycode.wehub.bean.AppUpdate;
4 | import net.angrycode.wehub.bean.Langs;
5 |
6 | /**
7 | * Created by lancelot on 2016/12/17.
8 | */
9 |
10 | public interface MainView extends BaseView {
11 |
12 | void onGetLangsFinished(Langs langs);
13 |
14 | void onCheckAppUpdateFinish(AppUpdate appUpdate);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/FavorView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import net.angrycode.wehub.bean.TrendingRepo;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by lancelot on 2016/12/18.
9 | */
10 |
11 | public interface FavorView extends BaseView {
12 |
13 | void onGetFavorListFinish(List repoList);
14 |
15 | void onDelFavorFinish(TrendingRepo repo);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/RepositoriesView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import net.angrycode.wehub.bean.Repos;
4 | import net.angrycode.wehub.bean.TrendingRepo;
5 |
6 | /**
7 | * Created by lancelot on 2016/11/13.
8 | */
9 |
10 | public interface RepositoriesView extends BaseView {
11 |
12 | void onGetRepositories(Repos repos);
13 |
14 | void onAddFavorFinish(TrendingRepo repo);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/TrendingView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import net.angrycode.wehub.bean.TrendingRepo;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by lancelot on 2016/12/3.
9 | */
10 |
11 | public interface TrendingView extends BaseView {
12 |
13 | void onGetTrendingsFinish(List repos);
14 |
15 | void onAddFavorFinish(TrendingRepo repo);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/drag/OnDragVHListener.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.component.drag;
2 |
3 | /**
4 | * ViewHolder 被选中 以及 拖拽释放 触发监听器
5 | * Created by YoKeyword on 15/12/29.
6 | */
7 | public interface OnDragVHListener {
8 | /**
9 | * Item被选中时触发
10 | */
11 | void onItemSelected();
12 |
13 |
14 | /**
15 | * Item在拖拽结束/滑动结束后触发
16 | */
17 | void onItemFinish();
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star_black_18dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/event/LangEditedEvent.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.event;
2 |
3 | import net.angrycode.wehub.bean.Langs;
4 |
5 | /**
6 | * Created by lancelot on 2016/12/17.
7 | */
8 |
9 | public class LangEditedEvent {
10 | private Langs langs;
11 |
12 | public LangEditedEvent(Langs langs) {
13 | this.langs = langs;
14 | }
15 |
16 | public Langs getLangs() {
17 | return langs;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_slideshow.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_gallery.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_about.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_manage.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/SnackbarUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.support.design.widget.Snackbar;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by lancelot on 2016/11/14.
8 | */
9 |
10 | public class SnackbarUtils {
11 | private SnackbarUtils() {
12 |
13 | }
14 |
15 | public static final void showSnack(View view, String message) {
16 | Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
17 | .show();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_red.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #E91E63
4 | #C2185B
5 | #F8BBD0
6 | #FF5252
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 10dp
7 | 160dp
8 | 16dp
9 | 200dp
10 | 16dp
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/view/BaseView.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.view;
2 |
3 | import android.app.Activity;
4 |
5 | public interface BaseView {
6 | Activity getActivity();
7 |
8 | /**
9 | * 请求中
10 | */
11 | void onRequestLoading();
12 |
13 | /**
14 | * 请求结束
15 | */
16 | void onRequestFinished();
17 |
18 | /**
19 | * 请求出错
20 | * @param code
21 | * @param message
22 | */
23 | void onRequestError(int code, String message);
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tag_cache.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_gray.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #607D8B
4 | #455A64
5 | #607D8B
6 | #607D8B
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_green.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4CAF50
4 | #388E3C
5 | #C8E6C9
6 | #8BC34A
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
--------------------------------------------------------------------------------
/app/src/test/java/net/angrycode/githubtrending/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.githubtrending;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_search_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_purple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #673AB7
4 | #512DA8
5 | #D1C4E9
6 | #E040FB
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_visibility_black_20dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/LogUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by lancelot on 2016/11/14.
7 | */
8 |
9 | public class LogUtils {
10 | private static final String TAG = "wecode";
11 |
12 | public static void log(String message) {
13 | Log.d(TAG, message);
14 | }
15 |
16 | public static void e(Exception e) {
17 | Log.e(TAG, e.getMessage());
18 | }
19 |
20 | public static void e(Throwable throwable) {
21 | Log.e(TAG, throwable.getMessage());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_remove_red_eye_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_camera.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/GlideUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.widget.ImageView;
5 |
6 | import com.bumptech.glide.Glide;
7 |
8 | import net.angrycode.wehub.R;
9 |
10 | /**
11 | * Created by lancelot on 2016/11/14.
12 | */
13 |
14 | public class GlideUtils {
15 | private GlideUtils() {
16 |
17 | }
18 |
19 | public static void loadImage(Context context, String url, ImageView imageView) {
20 | Glide.with(context).load(url).placeholder(R.mipmap.ic_github).crossFade().into(imageView);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selectable_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/include_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/IntentUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | /**
8 | * Created by lancelot on 2016/12/10.
9 | */
10 |
11 | public class IntentUtils {
12 | public static void launch(Context context, Class clazz) {
13 | Intent intent = new Intent(context, clazz);
14 | if (context instanceof Activity) {
15 |
16 | } else {
17 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
18 | }
19 | context.startActivity(intent);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 | - @android:drawable/ic_menu_camera
3 | - @android:drawable/ic_menu_gallery
4 | - @android:drawable/ic_menu_slideshow
5 | - @android:drawable/ic_menu_manage
6 | - @android:drawable/ic_menu_share
7 | - @android:drawable/ic_menu_send
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_favorites.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_other.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/ToastUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.StringRes;
5 | import android.widget.Toast;
6 |
7 | /**
8 | * Created by lancelot on 16/6/21.
9 | */
10 | public class ToastUtils {
11 | /**
12 | * @param context
13 | * @param msg
14 | */
15 | public static void show(Context context, String msg) {
16 | Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
17 | }
18 |
19 | public static void show(Context context, @StringRes int msgRes) {
20 | Toast.makeText(context, msgRes, Toast.LENGTH_SHORT).show();
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/customtabs/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | compile 'com.android.support:appcompat-v7:25.0.1'
24 | provided 'com.android.support:customtabs:25.0.1'
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ic_menu_share.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_repos.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/ReposApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import net.angrycode.wehub.bean.Repos;
4 |
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Query;
7 | import rx.Observable;
8 |
9 | /**
10 | * 受欢迎库的趋势列表
11 | * Created by lancelot on 2016/11/13.
12 | */
13 |
14 | public class ReposApi extends BaseApi {
15 | private interface ReposService {
16 | @GET("/search/repositories")
17 | Observable getRepositories(@Query("q") String query);
18 | }
19 |
20 | protected static final ReposService service = getRetrofit().create(ReposService.class);
21 |
22 | public static Observable getRepositories(String query) {
23 | return service.getRepositories(query);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share_black_20dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_languages.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_other_langs_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #2196F3
4 | #1976D2
5 | #BBDEFB
6 | #00BCD4
7 | #212121
8 | #757575
9 | #FFFFFF
10 | #BDBDBD
11 |
12 | #F7F8FB
13 | #CFD0E8
14 | #AFFFFFFF
15 | #72A1D0
16 | #7977B2
17 | #C4B4D6
18 | #C8B8B6
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/CacheInterceptor.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import java.io.IOException;
4 |
5 | import okhttp3.Interceptor;
6 | import okhttp3.Request;
7 | import okhttp3.Response;
8 |
9 | public class CacheInterceptor implements Interceptor {
10 | public CacheInterceptor() {
11 | }
12 |
13 |
14 | @Override
15 | public Response intercept(Chain chain) throws IOException {
16 | Request request = chain.request();
17 | Response response = chain.proceed(request);
18 | Response newResponse = response.newBuilder()
19 | .removeHeader("Pragma")
20 | .removeHeader("Cache-Control")
21 | //cache for 2 hours
22 | .header("Cache-Control", "max-age=" + 3600 * 2)
23 | .build();
24 | return newResponse;
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.ClipboardManager;
4 | import android.content.Context;
5 | import android.os.Build;
6 |
7 | import net.angrycode.wehub.R;
8 |
9 | /**
10 | * Created by lancelot on 2016/12/20.
11 | */
12 |
13 | public class Utils {
14 | public static void copyText(Context context,String text) {
15 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
16 | android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
17 | clipboard.setText(text);
18 | } else {
19 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
20 | clipboard.setText(text);
21 | }
22 | ToastUtils.show(context, R.string.text_copied);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/net/angrycode/githubtrending/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.githubtrending;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("net.angrycode.githubtrending", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/customtabs/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/ViewHolder.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.support.annotation.IdRes;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.util.SparseArray;
6 | import android.view.View;
7 |
8 | /**
9 | *
10 | * Created by lancelot on 2016/12/2.
11 | */
12 |
13 | public class ViewHolder extends RecyclerView.ViewHolder {
14 | View itemView;
15 | SparseArray views = new SparseArray<>();
16 | public ViewHolder(View itemView) {
17 | super(itemView);
18 | this.itemView = itemView;
19 | }
20 |
21 | @SuppressWarnings("unchecked")
22 | public T getView(@IdRes int resId) {
23 |
24 | View v = views.get(resId);
25 | if (null == v && itemView!=null) {
26 | v = itemView.findViewById(resId);
27 | views.put(resId, v);
28 | }
29 | return (T) v;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/WebUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.app.Activity;
4 | import android.net.Uri;
5 | import android.support.customtabs.CustomTabsIntent;
6 | import android.support.v4.content.ContextCompat;
7 |
8 | import net.angrycode.wehub.R;
9 | import net.angrycode.wehub.ui.component.CustomTabActivityHelper;
10 | import net.angrycode.wehub.ui.component.WebViewFallback;
11 |
12 | /**
13 | * Created by lancelot on 2016/12/4.
14 | */
15 |
16 | public class WebUtils {
17 | public static void openUrl(Activity context, String url) {
18 | CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
19 | builder.setToolbarColor(ContextCompat.getColor(context, R.color.primary));
20 | CustomTabsIntent customTabsIntent = builder.build();
21 | CustomTabActivityHelper.openCustomTab(context, customTabsIntent, Uri.parse(url), new WebViewFallback());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/ThemeUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import net.angrycode.wehub.R;
7 |
8 | /**
9 | * Created by lancelot on 2016/12/18.
10 | */
11 |
12 | public class ThemeUtils {
13 | private static final String FILE_NAME = "theme";
14 | private static final String KEY = "key";
15 |
16 | private ThemeUtils() {
17 |
18 | }
19 |
20 | public static void putTheme(Context context, int theme) {
21 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
22 | sp.edit().putInt(KEY, theme).apply();
23 | }
24 |
25 | public static int getTheme(Context context) {
26 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
27 | int theme = sp.getInt(KEY, R.style.AppTheme_Green);
28 | return theme;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/LangGridLayoutManager.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.component;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.GridLayoutManager;
5 |
6 | import net.angrycode.wehub.ui.adapter.LangAdapter;
7 |
8 | /**
9 | * Created by lancelot on 2016/12/17.
10 | */
11 |
12 | public class LangGridLayoutManager extends GridLayoutManager {
13 |
14 | public LangGridLayoutManager(Context context, final LangAdapter adapter, int spanCount) {
15 | super(context, spanCount);
16 | setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
17 | @Override
18 | public int getSpanSize(int position) {
19 | int viewType = adapter.getItemViewType(position);
20 | return viewType == LangAdapter.VIEW_TYPE_LANG_SEL_ITEM || viewType == LangAdapter.VIEW_TYPE_LANG_UNSEL_ITEM ? 1 : 4;
21 | }
22 | });
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/fragment/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import butterknife.ButterKnife;
11 |
12 | /**
13 | * Created by lancelot on 2016/11/15.
14 | */
15 |
16 | public abstract class BaseFragment extends Fragment {
17 | @Override
18 | public final View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
19 | int resLayoutId = getLayoutResource();
20 | if (resLayoutId != 0) {
21 | View view = inflater.inflate(resLayoutId, container, false);
22 | ButterKnife.bind(this, view);
23 | return view;
24 | }
25 | return null;
26 | }
27 |
28 | public abstract int getLayoutResource();
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/Langs.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Created by lancelot on 2016/12/17.
9 | */
10 |
11 | public class Langs {
12 | @SerializedName("sel")
13 | private List selList;
14 | @SerializedName("unsel")
15 | private List unselList;
16 |
17 | public Langs() {
18 | }
19 |
20 | public Langs(List selList, List unselList) {
21 | this.selList = selList;
22 | this.unselList = unselList;
23 | }
24 |
25 | public List getSelList() {
26 | return selList;
27 | }
28 |
29 | public void setSelList(List selList) {
30 | this.selList = selList;
31 | }
32 |
33 | public List getUnselList() {
34 | return unselList;
35 | }
36 |
37 | public void setUnselList(List unselList) {
38 | this.unselList = unselList;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/WeCodeApp.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub;
2 |
3 | import android.app.Application;
4 |
5 | import net.angrycode.wehub.bean.DaoMaster;
6 | import net.angrycode.wehub.bean.DaoSession;
7 |
8 | import org.greenrobot.greendao.database.Database;
9 |
10 | /**
11 | * Created by lancelot on 2016/11/13.
12 | */
13 |
14 | public class WeCodeApp extends Application {
15 |
16 | private static WeCodeApp sInstance;
17 | private DaoSession daoSession;
18 |
19 | @Override
20 | public void onCreate() {
21 | super.onCreate();
22 | sInstance = this;
23 |
24 | initGreenDao();
25 | }
26 |
27 | private void initGreenDao() {
28 | DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "repos-db");
29 | Database db = helper.getWritableDb();
30 | daoSession = new DaoMaster(db).newSession();
31 | }
32 |
33 | public static WeCodeApp get() {
34 | return sInstance;
35 | }
36 |
37 | public DaoSession getDaoSession() {
38 | return daoSession;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_lang.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/MD5Utils.java:
--------------------------------------------------------------------------------
1 |
2 | package net.angrycode.wehub.utils;
3 |
4 | import java.security.MessageDigest;
5 |
6 | public class MD5Utils {
7 | private static final String TAG = "MD5Utils";
8 |
9 | public final static String MD5(String s) {
10 | if (s == null)
11 | return null;
12 | char hexDigits[] = {
13 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
14 | };
15 | try {
16 | byte[] strTemp = s.getBytes();
17 | MessageDigest mdTemp = MessageDigest.getInstance("MD5");
18 | mdTemp.update(strTemp);
19 | byte[] md = mdTemp.digest();
20 | int j = md.length;
21 | char str[] = new char[j * 2];
22 | int k = 0;
23 | for (int i = 0; i < j; i++) {
24 | byte byte0 = md[i];
25 | str[k++] = hexDigits[byte0 >>> 4 & 0xf];
26 | str[k++] = hexDigits[byte0 & 0xf];
27 | }
28 | return new String(str);
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | return null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/LangApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import android.content.Context;
4 |
5 | import net.angrycode.wehub.bean.GsonBuilder;
6 | import net.angrycode.wehub.bean.Langs;
7 | import net.angrycode.wehub.utils.LangsUtils;
8 |
9 | import rx.Observable;
10 |
11 | /**
12 | * Created by lancelot on 2016/12/17.
13 | */
14 |
15 | public class LangApi {
16 | private LangApi() {
17 | }
18 |
19 | public static Observable getLangs(Context context) {
20 | Observable observable = Observable.create(subscriber -> {
21 | String json = LangsUtils.readLangJson(context);
22 | Langs langs = GsonBuilder.parseJson(json, Langs.class);
23 | subscriber.onNext(langs);
24 | subscriber.onCompleted();
25 | });
26 | return observable;
27 | }
28 |
29 | public static Observable setLangs(Context context, Langs langs) {
30 | Observable observable = Observable.create(subscriber -> {
31 | LangsUtils.write2sdcard(context, langs);
32 | subscriber.onNext(langs);
33 | subscriber.onCompleted();
34 | });
35 | return observable;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/customtabs/src/main/java/org/chromium/customtabsclient/shared/KeepAliveService.java:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package org.chromium.customtabsclient.shared;
16 |
17 | import android.app.Service;
18 | import android.content.Intent;
19 | import android.os.Binder;
20 | import android.os.IBinder;
21 |
22 | /**
23 | * Empty service used by the custom tab to bind to, raising the application's importance.
24 | */
25 | public class KeepAliveService extends Service {
26 | private static final Binder sBinder = new Binder();
27 |
28 | @Override
29 | public IBinder onBind(Intent intent) {
30 | return sBinder;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/Lang.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonSyntaxException;
5 |
6 | import net.angrycode.wehub.utils.LogUtils;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by lancelot on 2016/12/17.
13 | */
14 |
15 | public class Lang {
16 | private String key;
17 | private String name;
18 |
19 | public Lang() {
20 | }
21 |
22 | public Lang(String key, String name) {
23 | this.key = key;
24 | this.name = name;
25 | }
26 |
27 | public String getKey() {
28 | return key;
29 | }
30 |
31 | public void setKey(String key) {
32 | this.key = key;
33 | }
34 |
35 | public String getName() {
36 | return name;
37 | }
38 |
39 | public void setName(String name) {
40 | this.name = name;
41 | }
42 |
43 | public static List buildLangs(String json) {
44 | List list = new ArrayList<>();
45 | try {
46 | Gson gson = new Gson();
47 | gson.fromJson(json, Lang.class);
48 | } catch (JsonSyntaxException e) {
49 | LogUtils.log(e.getMessage());
50 | }
51 | return list;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/FavorApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import net.angrycode.wehub.bean.GsonBuilder;
7 | import net.angrycode.wehub.bean.Repos;
8 | import net.angrycode.wehub.bean.TrendingRepo;
9 | import net.angrycode.wehub.utils.FileUtils;
10 |
11 | import java.io.File;
12 | import java.util.List;
13 |
14 | import rx.Observable;
15 |
16 | /**
17 | * Created by lancelot on 2016/12/17.
18 | */
19 |
20 | public class FavorApi {
21 | public static final String FILE_NAME = "favor";
22 |
23 | private FavorApi() {
24 | }
25 |
26 | public Observable> getFavorList(Context context) {
27 | Observable> observable = Observable.create(subscriber -> {
28 | String json = read(context);
29 | List list = GsonBuilder.buildArray(json, TrendingRepo.class);
30 | subscriber.onNext(list);
31 | subscriber.onCompleted();
32 | });
33 | return observable;
34 | }
35 |
36 |
37 | private static String read(Context context) {
38 | File file = new File(context.getExternalCacheDir(), FILE_NAME);
39 | return FileUtils.readFile(file.getAbsolutePath());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/customtabs/src/main/java/org/chromium/customtabsclient/shared/ServiceConnectionCallback.java:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package org.chromium.customtabsclient.shared;
16 |
17 | import android.support.customtabs.CustomTabsClient;
18 |
19 | /**
20 | * Callback for events when connecting and disconnecting from Custom Tabs Service.
21 | */
22 | public interface ServiceConnectionCallback {
23 | /**
24 | * Called when the service is connected.
25 | * @param client a CustomTabsClient
26 | */
27 | void onServiceConnected(CustomTabsClient client);
28 |
29 | /**
30 | * Called when the service is disconnected.
31 | */
32 | void onServiceDisconnected();
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/WebViewFallback.java:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | package net.angrycode.wehub.ui.component;
15 |
16 | import android.app.Activity;
17 | import android.content.Intent;
18 | import android.net.Uri;
19 |
20 | /**
21 | * A Fallback that opens a Webview when Custom Tabs is not available
22 | */
23 | public class WebViewFallback implements CustomTabActivityHelper.CustomTabFallback {
24 | @Override
25 | public void openUri(Activity activity, Uri uri) {
26 | Intent intent = new Intent(activity, WebViewActivity.class);
27 | intent.putExtra(WebViewActivity.EXTRA_URL, uri.toString());
28 | activity.startActivity(intent);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/AboutActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.FloatingActionButton;
5 | import android.support.design.widget.Snackbar;
6 | import android.view.View;
7 | import android.widget.TextView;
8 |
9 | import net.angrycode.wehub.BuildConfig;
10 | import net.angrycode.wehub.R;
11 |
12 | import butterknife.BindView;
13 |
14 | public class AboutActivity extends BaseActivity {
15 |
16 | @BindView(R.id.tv_version)
17 | TextView tvVersion;
18 | @BindView(R.id.fab)
19 | FloatingActionButton fab;
20 |
21 | @Override
22 | protected int getLayoutResource() {
23 | return R.layout.activity_about;
24 | }
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | init();
30 | fab.setOnClickListener(new View.OnClickListener() {
31 | @Override
32 | public void onClick(View view) {
33 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
34 | .setAction("Action", null).show();
35 | }
36 | });
37 | }
38 |
39 | private void init() {
40 | tvVersion.setText("V"+ BuildConfig.VERSION_NAME);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/MainPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentStatePagerAdapter;
6 |
7 | import net.angrycode.wehub.bean.Lang;
8 | import net.angrycode.wehub.ui.fragment.TrendsFragment;
9 |
10 | import java.util.List;
11 |
12 |
13 | /**
14 | * Created by lancelot on 2016/11/15.
15 | */
16 |
17 | public class MainPagerAdapter extends FragmentStatePagerAdapter {
18 | // private String[] mTabTitles = new String[]{"All", "C", "C++", "HTML", "Java", "JavaScript", "Kotlin", "Python", "PHP", "Swift"};
19 | private List mLangs;
20 |
21 | public MainPagerAdapter(FragmentManager fm, List list) {
22 | super(fm);
23 | mLangs = list;
24 | }
25 | public void setLangs(List langs){
26 | mLangs = langs;
27 | notifyDataSetChanged();
28 | }
29 | @Override
30 | public Fragment getItem(int position) {
31 | return TrendsFragment.newInstance(mLangs.get(position).getKey());
32 | }
33 |
34 | @Override
35 | public int getCount() {
36 | return mLangs.size();
37 | }
38 |
39 | @Override
40 | public CharSequence getPageTitle(int position) {
41 | return mLangs.get(position).getName();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/JsonCacheUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.text.TextUtils;
6 |
7 | import java.io.File;
8 |
9 | /**
10 | * 接口缓存数据
11 | * Created by lancelot on 2016/12/3.
12 | */
13 |
14 | public class JsonCacheUtils {
15 |
16 | /**
17 | * 读取
18 | *
19 | * @param lang
20 | * @return
21 | */
22 | public static String readFile(Context context,String lang) {
23 | if (TextUtils.isEmpty(lang)) {
24 | return null;
25 | }
26 | String fileName = getFileName(context, lang);
27 | String txt = FileUtils.readFile(fileName);
28 | return txt;
29 | }
30 |
31 | /**
32 | * @param json
33 | * @param lang
34 | */
35 | public static void write2File(Context context,String json, String lang) {
36 | String fileName = getFileName(context, lang);
37 | FileUtils.write2File(json, fileName);
38 |
39 | }
40 |
41 | /**
42 | * @param context
43 | * @param lang
44 | * @return
45 | */
46 | public static String getFileName(@NonNull Context context, @NonNull String lang) {
47 | File cacheDir = context.getCacheDir();
48 | String fileName = MD5Utils.MD5(lang);
49 | return new File(cacheDir, fileName).getAbsolutePath();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/assets/langs:
--------------------------------------------------------------------------------
1 | {
2 | "sel":[
3 | {
4 | "name":"All",
5 | "key":"all"
6 | },
7 | {
8 | "name":"Java",
9 | "key":"java"
10 | },
11 | {
12 | "name":"C",
13 | "key":"c"
14 | },
15 | {
16 | "name":"HTML",
17 | "key":"html"
18 | },
19 | {
20 | "name":"JavaScript",
21 | "key":"javascript"
22 | },
23 | {
24 | "name":"Python",
25 | "key":"python"
26 | },
27 | {
28 | "name":"PHP",
29 | "key":"php"
30 | },
31 | {
32 | "name":"Swift",
33 | "key":"swift"
34 | }
35 | ],
36 | "unsel":[
37 | {
38 | "name":"CSS",
39 | "key":"css"
40 | },
41 | {
42 | "name":"Kotlin",
43 | "key":"kotlin"
44 | },
45 | {
46 | "name":"Go",
47 | "key":"go"
48 | },
49 | {
50 | "name":"C++",
51 | "key":"cpp"
52 | },
53 | {
54 | "name":"Object-C",
55 | "key":"objective-c"
56 | }
57 |
58 | ]
59 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/fragment/TagCacheFragment.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 |
6 | import net.angrycode.wehub.R;
7 | import net.angrycode.wehub.utils.TagPrefUtils;
8 |
9 | import butterknife.BindView;
10 | import me.gujun.android.taggroup.TagGroup;
11 |
12 | /**
13 | * 搜索页面的历史记录
14 | * Created by lancelot on 2016/12/4.
15 | */
16 |
17 | public class TagCacheFragment extends BaseFragment {
18 | @BindView(R.id.tag_group)
19 | TagGroup mTagGroup;
20 | OnTagClickListener mOnTagClickListener;
21 |
22 | @Override
23 | public int getLayoutResource() {
24 | return R.layout.fragment_tag_cache;
25 | }
26 |
27 | @Override
28 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
29 | super.onActivityCreated(savedInstanceState);
30 | String[] tags = TagPrefUtils.getTags(getActivity());
31 | if (tags.length > 0) {
32 | mTagGroup.setTags(tags);
33 | }
34 | mTagGroup.setOnTagClickListener(tag -> {
35 | if (mOnTagClickListener != null) {
36 | mOnTagClickListener.onTagClick(tag);
37 | }
38 | });
39 | }
40 |
41 | public void setOnTagClickListener(OnTagClickListener listener) {
42 | mOnTagClickListener = listener;
43 | }
44 |
45 | public interface OnTagClickListener {
46 | void onTagClick(String tag);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/AppUpdate.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | /**
4 | * Created by lancelot on 2016/12/21.
5 | */
6 |
7 | public class AppUpdate {
8 | private int code;
9 | private String msg;
10 | private Data data;
11 |
12 | public AppUpdate() {
13 | }
14 |
15 | public Data getData() {
16 | return data;
17 | }
18 |
19 | public void setData(Data data) {
20 | this.data = data;
21 | }
22 |
23 | public int getCode() {
24 | return code;
25 | }
26 |
27 | public void setCode(int code) {
28 | this.code = code;
29 | }
30 |
31 | public String getMsg() {
32 | return msg;
33 | }
34 |
35 | public void setMsg(String msg) {
36 | this.msg = msg;
37 | }
38 |
39 | public static class Data {
40 | private String url;
41 | private int has_new;
42 | private String version;
43 |
44 | public String getUrl() {
45 | return url;
46 | }
47 |
48 | public void setUrl(String url) {
49 | this.url = url;
50 | }
51 |
52 | public int getHas_new() {
53 | return has_new;
54 | }
55 |
56 | public void setHas_new(int has_new) {
57 | this.has_new = has_new;
58 | }
59 |
60 | public String getVersion() {
61 | return version;
62 | }
63 |
64 | public void setVersion(String version) {
65 | this.version = version;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
23 |
24 |
30 |
31 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/ReposAdapter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.widget.ImageView;
4 | import android.widget.TextView;
5 |
6 | import net.angrycode.wehub.R;
7 | import net.angrycode.wehub.bean.Repository;
8 | import net.angrycode.wehub.utils.GlideUtils;
9 |
10 | /**
11 | * Created by lancelot on 2016/11/13.
12 | */
13 |
14 | public class ReposAdapter extends BaseSimpleRecycleAdapter {
15 |
16 | public ReposAdapter() {
17 |
18 | }
19 |
20 | @Override
21 | public int getItemLayout(int viewType) {
22 | return R.layout.item_repo;
23 | }
24 |
25 | @Override
26 | public void onRender(ViewHolder holder, int position) {
27 | Repository repository = getItem(position);
28 |
29 | ImageView imageView = holder.getView(R.id.iv_user_face);
30 | TextView titleTv = holder.getView(R.id.tv_repo_name);
31 | TextView descTv = holder.getView(R.id.tv_repo_desc);
32 | // TextView watcherCountTv = holder.getView(R.id.tv_watcher_count);
33 | TextView starCountTv = holder.getView(R.id.tv_star_count);
34 | TextView forkCountTv = holder.getView(R.id.tv_fork_count);
35 |
36 | titleTv.setText(repository.getFull_name());
37 | descTv.setText(repository.getDescription());
38 | // watcherCountTv.setText(String.valueOf(repository.getWatchers()));
39 | starCountTv.setText(String.valueOf(repository.getWatchers_count()));
40 | forkCountTv.setText(String.valueOf(repository.getForks()));
41 | GlideUtils.loadImage(imageView.getContext(), repository.getOwner().getAvatar_url(), imageView);
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/TagPrefUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.text.TextUtils;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Created by lancelot on 2016/12/4.
12 | */
13 |
14 | public class TagPrefUtils {
15 | private static final String FILE_NAME = "tag_file_name";
16 | private static final String KEY_TAG = "tag_key";
17 |
18 | /**
19 | * 获取Tags
20 | *
21 | * @param context
22 | * @return
23 | */
24 | public static String[] getTags(Context context) {
25 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
26 | String tags = sp.getString(KEY_TAG, "");
27 | String[] list = new String[]{};
28 | if (!TextUtils.isEmpty(tags)) {
29 | list = tags.split(",");
30 | }
31 | return list;
32 | }
33 |
34 | public static void appendTags(Context context, String query) {
35 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
36 | String[] tags = sp.getString(KEY_TAG, "").split(",");
37 | List tagList = new ArrayList<>();
38 | for (int i = 0; i < tags.length; ++i) {
39 | if (!TextUtils.isEmpty(tags[i])) {
40 | tagList.add(tags[i]);
41 | }
42 | }
43 | if (!tagList.contains(query)) {
44 | tagList.add(query);
45 | }
46 | String tagStr = TextUtils.join(",", tagList);
47 | sp.edit().putString(KEY_TAG, tagStr).apply();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/GsonBuilder.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.JsonArray;
5 | import com.google.gson.JsonElement;
6 | import com.google.gson.JsonParser;
7 |
8 | import net.angrycode.wehub.utils.LogUtils;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * Created by lancelot on 2016/12/3.
15 | */
16 |
17 | public class GsonBuilder {
18 |
19 | /**
20 | * 解析数组[{}]
21 | *
22 | * @param jsonString
23 | * @param cls
24 | * @param
25 | * @return
26 | */
27 | public static List buildArray(String jsonString, Class cls) {
28 | List list = new ArrayList<>();
29 | try {
30 | Gson gson = new Gson();
31 | JsonArray array = new JsonParser().parse(jsonString).getAsJsonArray();
32 | for (JsonElement jsonElement : array) {
33 | list.add(gson.fromJson(jsonElement, cls));
34 | }
35 | } catch (Exception e) {
36 | LogUtils.e(e);
37 | }
38 | return list;
39 | }
40 |
41 | /**
42 | * 对象转换成json字符串
43 | *
44 | * @param obj
45 | * @return
46 | */
47 | public static String toJson(Object obj) {
48 | Gson gson = new Gson();
49 | return gson.toJson(obj);
50 | }
51 |
52 | /**
53 | * 将Json数据解析成相应的映射对象
54 | *
55 | * @param json
56 | * @param type
57 | * @param
58 | * @return
59 | */
60 | public static T parseJson(String json, Class type) {
61 | Gson gson = new Gson();
62 | T result = gson.fromJson(json, type);
63 | return result;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/Presenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.StringRes;
5 |
6 | import net.angrycode.wehub.R;
7 | import net.angrycode.wehub.mvp.view.BaseView;
8 |
9 | import rx.subscriptions.CompositeSubscription;
10 |
11 | public abstract class Presenter {
12 | T view;
13 |
14 | interface ErrorCode {
15 | int ERROR_REQUEST_DATA = 400;
16 | int ERROR_OBSERVER_DATA = 404;
17 | int ERROR_SAVE_DATA = 405;
18 | int ERROR_GET_DATA = 406;
19 | int ERROR_DEL_DATA = 407;
20 | }
21 |
22 | protected CompositeSubscription compositeSubscription = new CompositeSubscription();
23 |
24 | public Presenter(T view) {
25 | this.view = view;
26 | }
27 |
28 | public Activity getActivity() {
29 | return view.getActivity();
30 | }
31 |
32 | public void resume() {
33 | }
34 |
35 | public void pause() {
36 |
37 | }
38 |
39 | public String getString(@StringRes int res) {
40 | return view.getActivity().getString(res);
41 | }
42 |
43 | public String getRequestErrorMsg() {
44 | return view.getActivity().getString(R.string.error_request_data);
45 | }
46 |
47 | public boolean isViewAttached() {
48 | if (compositeSubscription.isUnsubscribed()) {
49 | return false;
50 | }
51 | if (view == null || view.getActivity() == null || view.getActivity().isFinishing()) {
52 | return false;
53 | }
54 | return true;
55 | }
56 |
57 | public void destroy() {
58 | compositeSubscription.unsubscribe();
59 | view = null;
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
23 |
24 |
30 |
31 |
32 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/TrendsAdapter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.widget.ImageView;
4 | import android.widget.TextView;
5 |
6 | import net.angrycode.wehub.R;
7 | import net.angrycode.wehub.bean.TrendingRepo;
8 | import net.angrycode.wehub.utils.GlideUtils;
9 |
10 | /**
11 | * Created by lancelot on 2016/12/2.
12 | */
13 |
14 | public class TrendsAdapter extends BaseSimpleRecycleAdapter {
15 | public TrendsAdapter() {
16 | }
17 |
18 | @Override
19 | public int getItemLayout(int viewType) {
20 | return R.layout.item_trends;
21 | }
22 |
23 | @Override
24 | public void onRender(ViewHolder holder, int position) {
25 | TrendingRepo repo = getItem(position);
26 |
27 | ImageView imageView = holder.getView(R.id.iv_user_face);
28 | TextView titleTv = holder.getView(R.id.tv_repo_name);
29 | TextView descTv = holder.getView(R.id.tv_repo_desc);
30 | TextView starCountTv = holder.getView(R.id.tv_stars_today);
31 |
32 | titleTv.setText(repo.getOwner() + "/" + repo.getName());
33 | descTv.setText(repo.getDesc());
34 | starCountTv.setText(String.valueOf(repo.getStars_today()) + " stars today");
35 | GlideUtils.loadImage(imageView.getContext(), repo.getAvatar(), imageView);
36 | }
37 |
38 | public void remove(TrendingRepo repo) {
39 | int len = mList.size();
40 | for (int i = 0; i < len; ++i) {
41 | TrendingRepo trend = mList.get(i);
42 | if (trend.equals(repo)) {
43 | mList.remove(i);
44 | notifyItemRemoved(i);
45 | notifyItemRangeChanged(0,mList.size());
46 | break;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_selected_langs_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_webview.xml:
--------------------------------------------------------------------------------
1 |
15 |
21 |
22 |
23 |
24 |
27 |
28 |
32 |
33 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/BaseTrendApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import java.io.File;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import okhttp3.Cache;
7 | import okhttp3.OkHttpClient;
8 | import retrofit2.Retrofit;
9 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
10 | import retrofit2.converter.gson.GsonConverterFactory;
11 |
12 | /**
13 | * Created by lancelot on 2016/12/2.
14 | */
15 |
16 | public class BaseTrendApi {
17 | public final static String API_SERVER = "https://angrycode.leanapp.cn/api/github/";
18 | private final static OkHttpClient mOkHttpClient;
19 | private static Retrofit sRetrofit;
20 |
21 | public BaseTrendApi() {
22 |
23 |
24 | }
25 |
26 | static {
27 | Cache cache = new Cache(new File("/data/data/net.angrycode.wehub/cache"), 1024 * 1024);
28 | mOkHttpClient = new OkHttpClient.Builder()
29 | .cache(cache)
30 | .addNetworkInterceptor(new CacheInterceptor())
31 | .connectTimeout(30, TimeUnit.SECONDS)
32 | .readTimeout(20, TimeUnit.SECONDS)
33 | .build();
34 | }
35 |
36 | protected static Retrofit getRetrofit() {
37 | if (sRetrofit == null) {
38 | //构建Retrofit
39 | sRetrofit = new Retrofit.Builder()
40 | //配置服务器路径
41 | .baseUrl(API_SERVER)
42 | //设置日期解析格式,这样可以直接解析Date类型
43 | // .setDateFormat("yyyy-MM-dd HH:mm:ss")
44 | //配置转化库,默认是Gson
45 | .addConverterFactory(GsonConverterFactory.create())
46 | //配置回调库,采用RxJava
47 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
48 | //设置OKHttpClient为网络客户端
49 | .client(mOkHttpClient)
50 | .build();
51 | }
52 | return sRetrofit;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/AppUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.app.DownloadManager;
4 | import android.content.Context;
5 | import android.net.Uri;
6 | import android.os.Environment;
7 | import android.util.DisplayMetrics;
8 | import android.webkit.MimeTypeMap;
9 |
10 | import net.angrycode.wehub.R;
11 |
12 | import java.io.File;
13 |
14 | /**
15 | * Created by lancelot on 2016/12/17.
16 | */
17 |
18 | public class AppUtils {
19 | public static int getScreenWidth(Context context) {
20 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
21 | return dm.widthPixels;
22 | }
23 | public static void downLoad(Context context,String url) {
24 | DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
25 | DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
26 | //设置状态栏中显示Notification
27 | request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
28 | request.setTitle(context.getResources().getString(R.string.app_name));
29 |
30 | request.setDescription("正在下载中...");
31 | //设置可用的网络类型
32 | request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
33 | //不显示下载界面
34 | request.setVisibleInDownloadsUi(true);
35 |
36 | //创建文件的下载路径
37 | File folder = Environment.getDownloadCacheDirectory();
38 |
39 | //指定下载的路径为和上面创建的路径相同
40 | request.setDestinationInExternalPublicDir(folder.getAbsolutePath(), "WeCode_xyz.apk");
41 |
42 | //设置文件类型
43 | MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
44 | String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
45 | request.setMimeType(mimeString);
46 | //将请求加入请求队列会 downLoadManager会自动调用对应的服务执行者个请求
47 | downloadManager.enqueue(request);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 这是一款可以查阅Github上的热门趋势的APP
2 |
3 | 随时查阅当前Github上的热门趋势。使用Material Design设计风格,和流行的MVP+Retrofit+RxJava框架。数据抓取自[https://github.com/trending](https://github.com/trending)
4 |
5 | ### Features
6 |
7 | - Material Design设计风格
8 | - MVP结构
9 | - 使用Retrofit网络请求
10 | - 多种内置皮肤可以切换
11 | - 可订阅常见编程语言
12 | - 支持搜索
13 |
14 | ### Demo
15 |
16 | 
17 |
18 | 
19 |
20 | 
21 |
22 | 
23 |
24 | ### Thanks
25 |
26 | 感谢以下开源协议
27 |
28 | ```groovy
29 |
30 | compile 'com.android.support:appcompat-v7:25.0.1'
31 | compile 'com.android.support:recyclerview-v7:25.0.1'
32 | compile 'com.android.support:cardview-v7:25.0.1'
33 | //design
34 | compile 'com.android.support:design:25.0.1'
35 | //custom tabs
36 | compile 'com.android.support:customtabs:25.0.1'
37 | compile project(':customtabs')
38 |
39 | //编译RxJava
40 | compile 'io.reactivex:rxjava:1.1.6'
41 | //编译RxAndroid
42 | compile 'io.reactivex:rxandroid:1.2.1'
43 | //编译Retrofit及其相关库,包括Gson
44 | compile 'com.squareup.okhttp3:okhttp:3.3.1'
45 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
46 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
47 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
48 | compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
49 | //view injector
50 | compile 'com.jakewharton:butterknife:8.4.0'
51 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
52 | //glide
53 | compile 'com.github.bumptech.glide:glide:3.7.0'
54 | //TagGroup
55 | compile 'me.gujun.android.taggroup:library:1.4@aar'
56 | //EventBus
57 | compile 'org.greenrobot:eventbus:3.0.0'
58 | compile 'org.greenrobot:greendao:3.2.0'
59 | ```
60 | ### App下载
61 | [App](http://sj.qq.com/myapp/detail.htm?apkName=net.angrycode.wehub)
62 |
--------------------------------------------------------------------------------
/customtabs/src/main/java/org/chromium/customtabsclient/shared/ServiceConnection.java:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package org.chromium.customtabsclient.shared;
16 |
17 | import android.content.ComponentName;
18 | import android.support.customtabs.CustomTabsClient;
19 | import android.support.customtabs.CustomTabsServiceConnection;
20 |
21 | import java.lang.ref.WeakReference;
22 |
23 | /**
24 | * Implementation for the CustomTabsServiceConnection that avoids leaking the
25 | * ServiceConnectionCallback
26 | */
27 | public class ServiceConnection extends CustomTabsServiceConnection {
28 | // A weak reference to the ServiceConnectionCallback to avoid leaking it.
29 | private WeakReference mConnectionCallback;
30 |
31 | public ServiceConnection(ServiceConnectionCallback connectionCallback) {
32 | mConnectionCallback = new WeakReference<>(connectionCallback);
33 | }
34 |
35 | @Override
36 | public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
37 | ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
38 | if (connectionCallback != null) connectionCallback.onServiceConnected(client);
39 | }
40 |
41 | @Override
42 | public void onServiceDisconnected(ComponentName name) {
43 | ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
44 | if (connectionCallback != null) connectionCallback.onServiceDisconnected();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/TrendingApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import android.content.Context;
4 |
5 | import net.angrycode.wehub.bean.AppUpdate;
6 | import net.angrycode.wehub.bean.GsonBuilder;
7 | import net.angrycode.wehub.bean.TrendingRepo;
8 | import net.angrycode.wehub.utils.JsonCacheUtils;
9 |
10 | import java.util.List;
11 |
12 | import retrofit2.http.FormUrlEncoded;
13 | import retrofit2.http.GET;
14 | import retrofit2.http.Path;
15 | import rx.Observable;
16 |
17 | /**
18 | * Created by lancelot on 2016/12/1.
19 | */
20 |
21 | public class TrendingApi extends BaseTrendApi {
22 | public TrendingApi() {
23 | }
24 |
25 | private interface TrendingService {
26 | @GET("trending/{lang}")
27 | Observable> getTrendingRepos(@Path("lang") String lang);
28 |
29 | @GET("check_update/{version}")
30 | Observable checkAppUpdate(@Path("version") String version);
31 | }
32 |
33 | protected static final TrendingService service = getRetrofit().create(TrendingService.class);
34 |
35 | public static Observable> getRepos(String lang) {
36 | Observable> observable = service.getTrendingRepos(lang);
37 | return observable;
38 | }
39 |
40 | /**
41 | * 获取缓存数据
42 | *
43 | * @param lang
44 | * @returnxxxx
45 | */
46 | public static Observable> getReposFromeCache(Context context, String lang) {
47 | Observable> observable = Observable.create(subscriber -> {
48 | String json = JsonCacheUtils.readFile(context, lang);
49 | List list = GsonBuilder.buildArray(json, TrendingRepo.class);
50 | subscriber.onNext(list);
51 | subscriber.onCompleted();
52 | });
53 | return observable;
54 | }
55 |
56 | /**
57 | * 检查更新
58 | * @param version
59 | * @return
60 | */
61 | public static Observable checkAppUpdate(String version) {
62 | Observable observable = service.checkAppUpdate(version);
63 | return observable;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/LangsUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import net.angrycode.wehub.bean.GsonBuilder;
7 | import net.angrycode.wehub.bean.Langs;
8 |
9 | import java.io.File;
10 | import java.io.FileOutputStream;
11 | import java.io.IOException;
12 | import java.io.InputStream;
13 |
14 | /**
15 | * Created by lancelot on 2016/12/17.
16 | */
17 |
18 | public class LangsUtils {
19 | private static final String FILE_NAME = "langs";
20 |
21 | public static boolean write2sdcard(Context context, String langsJson) {
22 | File file = new File(context.getExternalCacheDir(), FILE_NAME);
23 | FileOutputStream out = null;
24 | try {
25 | out = new FileOutputStream(file);
26 | out.write(langsJson.getBytes());
27 | out.flush();
28 | } catch (IOException e) {
29 | LogUtils.e(e);
30 | return false;
31 | } finally {
32 | try {
33 | if (out != null) {
34 | out.close();
35 | }
36 | } catch (IOException e) {
37 | LogUtils.e(e);
38 | }
39 | }
40 | return true;
41 | }
42 |
43 | public static boolean write2sdcard(Context context, Langs langs) {
44 | String json = GsonBuilder.toJson(langs);
45 | return write2sdcard(context, json);
46 | }
47 |
48 | public static String readFromAssets(Context context) {
49 | String retVal = null;
50 | try {
51 | InputStream in = context.getAssets().open(FILE_NAME);
52 | retVal = FileUtils.read(in);
53 | } catch (IOException e) {
54 | LogUtils.e(e);
55 | }
56 | return retVal;
57 | }
58 |
59 | public static String readLangJson(Context context) {
60 | File file = new File(context.getExternalCacheDir(), FILE_NAME);
61 | String json = FileUtils.readFile(file.getAbsolutePath());
62 | if (TextUtils.isEmpty(json)) {
63 | json = readFromAssets(context);
64 | write2sdcard(context, json);
65 | }
66 | return json;
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
22 |
23 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/custom_progress_drawable.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
27 |
28 |
29 |
30 | -
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 | -
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
35 |
39 |
43 |
47 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/Keyboard.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.os.Build;
4 | import android.view.View;
5 | import android.view.ViewTreeObserver;
6 | import android.view.inputmethod.InputMethodManager;
7 |
8 | import static android.content.Context.INPUT_METHOD_SERVICE;
9 |
10 |
11 | /**
12 | * Keyboard utilities
13 | */
14 | public class Keyboard {
15 |
16 | public static void show(final View view, long delay) {
17 | if (view == null) {
18 | return;
19 | }
20 | InputMethodManager manager = (InputMethodManager) view.getContext().getSystemService(INPUT_METHOD_SERVICE);
21 | if (view.getWidth() == 0 && view.getHeight() == 0) {
22 | view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
23 | @Override
24 | public void onGlobalLayout() {
25 | removeViewTreeObserver(view, this);
26 | if (delay <= 0) {
27 | manager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
28 | } else {
29 | view.postDelayed(() -> manager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT), delay);
30 | }
31 | }
32 | });
33 | } else {
34 | if (delay <= 0) {
35 | manager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
36 | } else {
37 | view.postDelayed(() -> manager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT), delay);
38 | }
39 | }
40 |
41 | }
42 |
43 | public static void hide(final View view) {
44 | if (view == null) {
45 | return;
46 | }
47 | InputMethodManager manager = (InputMethodManager) view.getContext().getSystemService(INPUT_METHOD_SERVICE);
48 | if (manager != null) {
49 | manager.hideSoftInputFromWindow(view.getWindowToken(), 0);
50 | }
51 | }
52 |
53 | /**
54 | * 移除View上的ViewTreeObserver,兼容方法
55 | */
56 | public static void removeViewTreeObserver(View view, ViewTreeObserver.OnGlobalLayoutListener l) {
57 | if (view != null && view.getViewTreeObserver() != null) {
58 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
59 | view.getViewTreeObserver().removeOnGlobalLayoutListener(l);
60 | } else {
61 | view.getViewTreeObserver().removeGlobalOnLayoutListener(l);
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/ThemeActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 | import android.view.View;
9 |
10 | import net.angrycode.wehub.R;
11 | import net.angrycode.wehub.event.ThemeChangeEvent;
12 | import net.angrycode.wehub.ui.adapter.ThemeAdapter;
13 | import net.angrycode.wehub.ui.adapter.TrendsAdapter;
14 | import net.angrycode.wehub.ui.component.DividerItemDecoration;
15 | import net.angrycode.wehub.utils.ThemeUtils;
16 |
17 | import org.greenrobot.eventbus.EventBus;
18 |
19 | import butterknife.BindView;
20 |
21 | /**
22 | * Created by lancelot on 2016/12/9.
23 | */
24 |
25 | public class ThemeActivity extends BaseActivity {
26 | @BindView(R.id.recycler_view)
27 | RecyclerView mRecyclerView;
28 |
29 | ThemeAdapter mAdapter;
30 | int mCurrTheme;
31 |
32 | @Override
33 | protected int getLayoutResource() {
34 | return R.layout.activity_theme;
35 | }
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 |
41 | init();
42 |
43 | }
44 |
45 | private void init() {
46 | mCurrTheme = getCurrTheme();
47 |
48 | mAdapter = new ThemeAdapter();
49 | mAdapter.setCurrTheme(mCurrTheme);
50 |
51 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
52 | mRecyclerView.setLayoutManager(linearLayoutManager);
53 |
54 | mRecyclerView.setAdapter(mAdapter);
55 | mAdapter.setOnItemClickListener(this::onItemClick);
56 | }
57 |
58 | private void onItemClick(View itemView, int position) {
59 | ThemeAdapter.Theme theme = mAdapter.getItem(position);
60 | mToolbar.setBackgroundResource(theme.getColor());
61 | mCurrTheme = theme.getStyle();
62 | mAdapter.setCurrTheme(mCurrTheme);
63 | }
64 |
65 | @Override
66 | public boolean onCreateOptionsMenu(Menu menu) {
67 | getMenuInflater().inflate(R.menu.activity_theme_menu, menu);
68 | return true;
69 | }
70 |
71 | @Override
72 | public boolean onOptionsItemSelected(MenuItem item) {
73 | switch (item.getItemId()) {
74 | case R.id.action_finish:
75 | if (mCurrTheme != getCurrTheme()) {
76 | ThemeUtils.putTheme(this, mCurrTheme);
77 | EventBus.getDefault().post(new ThemeChangeEvent());
78 | }
79 | finish();
80 | return true;
81 | }
82 | return super.onOptionsItemSelected(item);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/LanguagesActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.support.v7.widget.helper.ItemTouchHelper;
7 |
8 | import net.angrycode.wehub.R;
9 | import net.angrycode.wehub.bean.Langs;
10 | import net.angrycode.wehub.mvp.presenter.LangPresenter;
11 | import net.angrycode.wehub.mvp.view.LangView;
12 | import net.angrycode.wehub.ui.adapter.LangAdapter;
13 | import net.angrycode.wehub.ui.component.LangGridLayoutManager;
14 | import net.angrycode.wehub.ui.component.drag.ItemDragHelperCallback;
15 | import net.angrycode.wehub.utils.ToastUtils;
16 |
17 | import butterknife.BindView;
18 |
19 | /**
20 | * 设置语言
21 | * Created by lancelot on 2016/12/9.
22 | */
23 |
24 | public class LanguagesActivity extends BaseActivity implements LangView {
25 | @BindView(R.id.recycle_view)
26 | RecyclerView mRecycleView;
27 | LangAdapter mAdapter;
28 | LangPresenter mPresenter;
29 |
30 | @Override
31 | protected int getLayoutResource() {
32 | return R.layout.activity_languages;
33 | }
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | init();
39 | mPresenter = new LangPresenter(this);
40 | mPresenter.getLangs();
41 | }
42 |
43 | private void init() {
44 |
45 | ItemDragHelperCallback callback = new ItemDragHelperCallback();
46 | final ItemTouchHelper helper = new ItemTouchHelper(callback);
47 | helper.attachToRecyclerView(mRecycleView);
48 |
49 | mAdapter = new LangAdapter(helper);
50 |
51 | LangGridLayoutManager manager = new LangGridLayoutManager(this, mAdapter, 4);
52 |
53 | mRecycleView.setLayoutManager(manager);
54 | mRecycleView.setAdapter(mAdapter);
55 |
56 | }
57 |
58 | @Override
59 | public void onGetLangsFinished(Langs langs) {
60 | mAdapter.setLangs(langs);
61 | }
62 |
63 | @Override
64 | public void onSetLangsFinished(Langs langs) {
65 |
66 | }
67 |
68 | @Override
69 | protected void onPause() {
70 | if (mAdapter.isEdited()) {
71 | mPresenter.setLangs(mAdapter.getLangs());
72 | }
73 | super.onPause();
74 | }
75 |
76 | @Override
77 | public Activity getActivity() {
78 | return this;
79 | }
80 |
81 | @Override
82 | public void onRequestLoading() {
83 | showLoading();
84 | }
85 |
86 | @Override
87 | public void onRequestFinished() {
88 | dismissLoading();
89 | }
90 |
91 | @Override
92 | public void onRequestError(int code, String message) {
93 | ToastUtils.show(getApplicationContext(), message);
94 | dismissLoading();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_trends.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
18 |
19 |
26 |
27 |
36 |
37 |
48 |
49 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/FavorPresenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import net.angrycode.wehub.R;
4 | import net.angrycode.wehub.WeCodeApp;
5 | import net.angrycode.wehub.bean.DaoSession;
6 | import net.angrycode.wehub.bean.TrendingRepo;
7 | import net.angrycode.wehub.bean.TrendingRepoDao;
8 | import net.angrycode.wehub.mvp.view.FavorView;
9 | import net.angrycode.wehub.utils.LogUtils;
10 |
11 | import org.greenrobot.greendao.rx.RxDao;
12 | import org.greenrobot.greendao.rx.RxQuery;
13 |
14 | import rx.android.schedulers.AndroidSchedulers;
15 |
16 | /**
17 | * Created by lancelot on 2016/12/18.
18 | */
19 |
20 | public class FavorPresenter extends Presenter {
21 | RxDao mRxDao;
22 | private RxQuery mRxQuery;
23 |
24 | public FavorPresenter(FavorView view) {
25 | super(view);
26 | DaoSession session = WeCodeApp.get().getDaoSession();
27 | mRxDao = session.getTrendingRepoDao().rx();
28 | }
29 |
30 | public void deleteFavor(TrendingRepo repo) {
31 | mRxDao.delete(repo)
32 | .observeOn(AndroidSchedulers.mainThread())
33 | .subscribe(aVoid -> {
34 | if (!isViewAttached()) {
35 | return;
36 | }
37 | view.onDelFavorFinish(repo);
38 | },
39 | throwable -> {
40 | LogUtils.e(throwable);
41 | view.onRequestError(ErrorCode.ERROR_DEL_DATA, throwable.getMessage());
42 | },
43 | () -> view.onRequestFinished());
44 | }
45 |
46 | public void getFavorList() {
47 | if (mRxQuery == null) {
48 | DaoSession session = WeCodeApp.get().getDaoSession();
49 | mRxQuery = session.getTrendingRepoDao().queryBuilder().orderAsc(TrendingRepoDao.Properties.Date).rx();
50 | }
51 | mRxQuery.list()
52 | .observeOn(AndroidSchedulers.mainThread())
53 | .subscribe(repoList -> {
54 | if (!isViewAttached()) {
55 | return;
56 | }
57 | if (repoList != null) {
58 | view.onGetFavorListFinish(repoList);
59 | } else {
60 | view.onRequestError(ErrorCode.ERROR_GET_DATA, getActivity().getResources().getString(R.string.error_request_data));
61 | }
62 | },
63 | throwable -> {
64 | LogUtils.e(throwable);
65 | view.onRequestError(ErrorCode.ERROR_GET_DATA, throwable.getMessage());
66 | },
67 | () -> view.onRequestFinished());
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/MainPresenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import net.angrycode.wehub.BuildConfig;
4 | import net.angrycode.wehub.R;
5 | import net.angrycode.wehub.api.LangApi;
6 | import net.angrycode.wehub.api.TrendingApi;
7 | import net.angrycode.wehub.bean.AppUpdate;
8 | import net.angrycode.wehub.bean.Langs;
9 | import net.angrycode.wehub.mvp.view.MainView;
10 | import net.angrycode.wehub.utils.LogUtils;
11 |
12 | import rx.Observable;
13 | import rx.android.schedulers.AndroidSchedulers;
14 | import rx.schedulers.Schedulers;
15 |
16 | /**
17 | * Created by lancelot on 2016/12/17.
18 | */
19 |
20 | public class MainPresenter extends Presenter {
21 | public MainPresenter(MainView view) {
22 | super(view);
23 | }
24 |
25 | public void getLangs() {
26 | Observable observable = LangApi.getLangs(getActivity());
27 | compositeSubscription.add(
28 | observable.observeOn(AndroidSchedulers.mainThread())
29 | .subscribeOn(Schedulers.io())
30 | .subscribe(langs -> {
31 | if (!isViewAttached()) {
32 | return;
33 | }
34 | if (langs != null) {
35 | view.onGetLangsFinished(langs);
36 | } else {
37 | view.onRequestError(ErrorCode.ERROR_REQUEST_DATA, view.getActivity().getString(R.string.error_request_data));
38 |
39 | }
40 | }, error -> {
41 | LogUtils.e(error);
42 | view.onRequestError(ErrorCode.ERROR_OBSERVER_DATA, error.getMessage());
43 | }, () -> view.onRequestFinished())
44 | );
45 |
46 | }
47 |
48 | public void checkAppUpdate() {
49 | Observable observable = TrendingApi.checkAppUpdate(BuildConfig.VERSION_NAME);
50 | compositeSubscription.add(
51 | observable.observeOn(AndroidSchedulers.mainThread())
52 | .subscribeOn(Schedulers.io())
53 | .subscribe(appUpdate -> {
54 | if (!isViewAttached()) {
55 | return;
56 | }
57 | if (appUpdate.getCode() > 0) {
58 | view.onCheckAppUpdateFinish(appUpdate);
59 | }
60 | }, throwable -> {
61 | LogUtils.e(throwable);
62 | view.onRequestError(ErrorCode.ERROR_SAVE_DATA, view.getActivity().getString(R.string.error_request_data));
63 | },
64 | () -> view.onRequestFinished())
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/Owner.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | public class Owner {
4 | /**
5 | * login : parallella
6 | * id : 2919930
7 | * avatar_url : https://avatars.githubusercontent.com/u/2919930?v=3
8 | * gravatar_id :
9 | * url : https://api.github.com/users/parallella
10 | * html_url : https://github.com/parallella
11 | * followers_url : https://api.github.com/users/parallella/followers
12 | * following_url : https://api.github.com/users/parallella/following{/other_user}
13 | * gists_url : https://api.github.com/users/parallella/gists{/gist_id}
14 | * starred_url : https://api.github.com/users/parallella/starred{/owner}{/repo}
15 | * subscriptions_url : https://api.github.com/users/parallella/subscriptions
16 | * organizations_url : https://api.github.com/users/parallella/orgs
17 | * repos_url : https://api.github.com/users/parallella/repos
18 | * events_url : https://api.github.com/users/parallella/events{/privacy}
19 | * received_events_url : https://api.github.com/users/parallella/received_events
20 | * type : Organization
21 | * site_admin : false
22 | */
23 |
24 | private String login;
25 | private int id;
26 | private String avatar_url;
27 | private String gravatar_id;
28 | private String url;
29 | private String html_url;
30 | private String type;
31 | private boolean site_admin;
32 |
33 | public String getLogin() {
34 | return login;
35 | }
36 |
37 | public void setLogin(String login) {
38 | this.login = login;
39 | }
40 |
41 | public int getId() {
42 | return id;
43 | }
44 |
45 | public void setId(int id) {
46 | this.id = id;
47 | }
48 |
49 | public String getAvatar_url() {
50 | return avatar_url;
51 | }
52 |
53 | public void setAvatar_url(String avatar_url) {
54 | this.avatar_url = avatar_url;
55 | }
56 |
57 | public String getGravatar_id() {
58 | return gravatar_id;
59 | }
60 |
61 | public void setGravatar_id(String gravatar_id) {
62 | this.gravatar_id = gravatar_id;
63 | }
64 |
65 | public String getUrl() {
66 | return url;
67 | }
68 |
69 | public void setUrl(String url) {
70 | this.url = url;
71 | }
72 |
73 | public String getHtml_url() {
74 | return html_url;
75 | }
76 |
77 | public void setHtml_url(String html_url) {
78 | this.html_url = html_url;
79 | }
80 |
81 | public String getType() {
82 | return type;
83 | }
84 |
85 | public void setType(String type) {
86 | this.type = type;
87 | }
88 |
89 | public boolean isSite_admin() {
90 | return site_admin;
91 | }
92 |
93 | public void setSite_admin(boolean site_admin) {
94 | this.site_admin = site_admin;
95 | }
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/ReposPresenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import android.util.Log;
4 |
5 | import net.angrycode.wehub.R;
6 | import net.angrycode.wehub.WeCodeApp;
7 | import net.angrycode.wehub.api.ReposApi;
8 | import net.angrycode.wehub.bean.DaoSession;
9 | import net.angrycode.wehub.bean.Repos;
10 | import net.angrycode.wehub.bean.TrendingRepo;
11 | import net.angrycode.wehub.mvp.view.RepositoriesView;
12 |
13 | import org.greenrobot.greendao.rx.RxDao;
14 |
15 | import java.util.Date;
16 |
17 | import rx.Observable;
18 | import rx.android.schedulers.AndroidSchedulers;
19 | import rx.schedulers.Schedulers;
20 |
21 | /**
22 | * Created by lancelot on 2016/11/13.
23 | */
24 |
25 | public class ReposPresenter extends Presenter {
26 | RxDao mRxDao;
27 |
28 | public ReposPresenter(RepositoriesView view) {
29 | super(view);
30 | }
31 |
32 | public void getRepositories(String query) {
33 | view.onRequestLoading();
34 | Observable observable = ReposApi.getRepositories(query);
35 | compositeSubscription.add(
36 | observable.observeOn(AndroidSchedulers.mainThread())
37 | .subscribeOn(Schedulers.io())
38 | .subscribe(repositories -> {
39 | if (!isViewAttached()) {
40 | return;
41 | }
42 | if (repositories != null && !repositories.isIncomplete_results()) {
43 | view.onGetRepositories(repositories);
44 | } else {
45 | view.onRequestError(ErrorCode.ERROR_REQUEST_DATA, getRequestErrorMsg());
46 | }
47 | }, error -> {
48 | Log.d("repositories", error.getMessage());
49 | view.onRequestError(ErrorCode.ERROR_OBSERVER_DATA, getRequestErrorMsg());
50 | }, () -> view.onRequestFinished())
51 | );
52 | }
53 |
54 | public void addFavor(TrendingRepo repo) {
55 | if (repo == null) {
56 | return;
57 | }
58 | if (mRxDao == null) {
59 | DaoSession session = WeCodeApp.get().getDaoSession();
60 | mRxDao = session.getTrendingRepoDao().rx();
61 | }
62 | repo.setDate(new Date());
63 | mRxDao.insertOrReplace(repo)
64 | .observeOn(AndroidSchedulers.mainThread())
65 | .subscribe(trendingRepo -> {
66 | if (!isViewAttached()) {
67 | return;
68 | }
69 | if (trendingRepo.getId() > 0) {
70 | view.onAddFavorFinish(trendingRepo);
71 | }
72 | }, throwable -> {
73 | view.onRequestError(ErrorCode.ERROR_SAVE_DATA, throwable.getMessage());
74 | },
75 | () -> view.onRequestFinished());
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/LangPresenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import net.angrycode.wehub.R;
4 | import net.angrycode.wehub.api.LangApi;
5 | import net.angrycode.wehub.bean.Langs;
6 | import net.angrycode.wehub.event.LangEditedEvent;
7 | import net.angrycode.wehub.mvp.view.LangView;
8 | import net.angrycode.wehub.utils.LogUtils;
9 |
10 | import org.greenrobot.eventbus.EventBus;
11 |
12 | import rx.Observable;
13 | import rx.android.schedulers.AndroidSchedulers;
14 | import rx.schedulers.Schedulers;
15 |
16 | /**
17 | * Created by lancelot on 2016/12/17.
18 | */
19 |
20 | public class LangPresenter extends Presenter {
21 | public LangPresenter(LangView view) {
22 | super(view);
23 | }
24 |
25 | public void getLangs() {
26 | view.onRequestLoading();
27 | Observable observable = LangApi.getLangs(getActivity());
28 | compositeSubscription.add(
29 | observable.observeOn(AndroidSchedulers.mainThread())
30 | .subscribeOn(Schedulers.io())
31 | .subscribe(langs -> {
32 | if (!isViewAttached()) {
33 | return;
34 | }
35 | if (langs != null) {
36 | view.onGetLangsFinished(langs);
37 | } else {
38 | view.onRequestError(ErrorCode.ERROR_REQUEST_DATA, view.getActivity().getString(R.string.error_request_data));
39 |
40 | }
41 | }, error -> {
42 | LogUtils.e(error);
43 | view.onRequestError(ErrorCode.ERROR_OBSERVER_DATA, error.getMessage());
44 | }, () -> view.onRequestFinished())
45 | );
46 |
47 | }
48 |
49 | public void setLangs(final Langs langs) {
50 | // view.onRequestLoading();
51 | Observable observable = LangApi.setLangs(getActivity(), langs);
52 | compositeSubscription.add(
53 | observable.observeOn(AndroidSchedulers.mainThread())
54 | .subscribeOn(Schedulers.io())
55 | .subscribe(result -> {
56 | EventBus.getDefault().post(new LangEditedEvent(langs));
57 | if (!isViewAttached()) {
58 | return;
59 | }
60 | if (result != null) {
61 | view.onGetLangsFinished(result);
62 | } else {
63 | // view.onRequestError(ErrorCode.ERROR_REQUEST_DATA, view.getActivity().getString(R.string.error_request_data));
64 | LogUtils.log("Set langs error");
65 | }
66 | }, error -> {
67 | LogUtils.e(error);
68 | // view.onRequestError(ErrorCode.ERROR_OBSERVER_DATA, error.getMessage());
69 | }, () -> view.onRequestFinished())
70 | );
71 |
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/mvp/presenter/TrendsPresenter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.mvp.presenter;
2 |
3 | import android.util.Log;
4 |
5 | import net.angrycode.wehub.R;
6 | import net.angrycode.wehub.WeCodeApp;
7 | import net.angrycode.wehub.api.TrendingApi;
8 | import net.angrycode.wehub.bean.DaoSession;
9 | import net.angrycode.wehub.bean.TrendingRepo;
10 | import net.angrycode.wehub.mvp.view.TrendingView;
11 | import net.angrycode.wehub.utils.LogUtils;
12 |
13 | import org.greenrobot.greendao.rx.RxDao;
14 |
15 | import java.util.Date;
16 | import java.util.List;
17 |
18 | import rx.Observable;
19 | import rx.android.schedulers.AndroidSchedulers;
20 | import rx.schedulers.Schedulers;
21 |
22 | /**
23 | * Created by lancelot on 2016/12/3.
24 | */
25 |
26 | public class TrendsPresenter extends Presenter {
27 | RxDao mRxDao;
28 |
29 | public TrendsPresenter(TrendingView view) {
30 | super(view);
31 | DaoSession session = WeCodeApp.get().getDaoSession();
32 | mRxDao = session.getTrendingRepoDao().rx();
33 | }
34 |
35 | /**
36 | * @param lang
37 | */
38 | public void getTrending(String lang, boolean cache) {
39 | view.onRequestLoading();
40 | Observable> observable = TrendingApi.getRepos(lang);
41 | compositeSubscription.add(
42 | observable.observeOn(AndroidSchedulers.mainThread())
43 | .subscribeOn(Schedulers.io())
44 | .subscribe(repositories -> {
45 | if (!isViewAttached()) {
46 | return;
47 | }
48 | if (repositories != null && repositories.size() > 0) {
49 | view.onGetTrendingsFinish(repositories);
50 | } else {
51 | view.onRequestError(ErrorCode.ERROR_REQUEST_DATA, view.getActivity().getString(R.string.error_request_data));
52 | }
53 | }, error -> {
54 | LogUtils.e(error);
55 | view.onRequestError(ErrorCode.ERROR_OBSERVER_DATA, getRequestErrorMsg());
56 | }, () -> view.onRequestFinished())
57 | );
58 | }
59 |
60 | public void addFavor(TrendingRepo repo) {
61 | if (repo == null) {
62 | return;
63 | }
64 | repo.setDate(new Date());
65 | mRxDao.insertOrReplace(repo)
66 | .observeOn(AndroidSchedulers.mainThread())
67 | .subscribe(trendingRepo -> {
68 | if (!isViewAttached()) {
69 | return;
70 | }
71 | if (trendingRepo.getId() > 0) {
72 | view.onAddFavorFinish(trendingRepo);
73 | }
74 | }, throwable -> {
75 | view.onRequestError(ErrorCode.ERROR_SAVE_DATA, throwable.getMessage());
76 | },
77 | () -> view.onRequestFinished());
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
30 |
31 |
38 |
39 |
47 |
48 |
49 |
50 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/utils/FileUtils.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.utils;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 |
12 | /**
13 | * Created by lancelot on 2016/12/3.
14 | */
15 |
16 | public class FileUtils {
17 |
18 | /**
19 | * 读取
20 | *
21 | * @param fileName
22 | * @return
23 | */
24 | public static String readFile(String fileName) {
25 | String txt = null;
26 | ByteArrayOutputStream out = new ByteArrayOutputStream();
27 | FileInputStream in = null;
28 | try {
29 | in = new FileInputStream(fileName);
30 | byte[] data = new byte[1024];
31 | int len = -1;
32 | while ((len = in.read(data)) != -1) {
33 | out.write(data, 0, len);
34 | out.flush();
35 | }
36 | txt = out.toString();
37 | } catch (IOException e) {
38 | LogUtils.log(e.getMessage());
39 | } finally {
40 | try {
41 | if (in != null) {
42 | in.close();
43 | }
44 | out.close();
45 | } catch (IOException e) {
46 | LogUtils.log(e.getMessage());
47 | }
48 |
49 | }
50 | return txt;
51 | }
52 |
53 | public static void write2File(String json, String fileName) {
54 |
55 | FileOutputStream out = null;
56 | try {
57 | out = new FileOutputStream(fileName);
58 | byte[] data = json.getBytes();
59 | out.write(data, 0, data.length);
60 | out.flush();
61 | } catch (IOException e) {
62 | LogUtils.log(e.getMessage());
63 | } finally {
64 | try {
65 | out.close();
66 | } catch (IOException e) {
67 | LogUtils.log(e.getMessage());
68 | }
69 | }
70 | }
71 |
72 | public static String getFileName(Context context, String lang) {
73 | File cacheDir = context.getCacheDir();
74 | String fileName = MD5Utils.MD5(lang);
75 | return new File(cacheDir, fileName).getAbsolutePath();
76 | }
77 |
78 | /**
79 | * 读取
80 | * @param in
81 | * @return
82 | */
83 | public static String read(InputStream in) {
84 | String retVal = null;
85 | if (in == null) {
86 | return retVal;
87 | }
88 | ByteArrayOutputStream out = new ByteArrayOutputStream();
89 | int len = -1;
90 | byte[] data = new byte[1024];
91 | try {
92 | while ((len = in.read(data)) != -1) {
93 | out.write(data, 0, len);
94 | }
95 | out.flush();
96 |
97 | } catch (IOException e) {
98 | LogUtils.log(e.getMessage());
99 | } finally {
100 | try {
101 | out.close();
102 | } catch (IOException e) {
103 | LogUtils.log(e.getMessage());
104 | }
105 | }
106 | return out.toString();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/SearchActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.view.MenuItemCompat;
8 | import android.support.v7.widget.SearchView;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 |
12 | import net.angrycode.wehub.R;
13 | import net.angrycode.wehub.ui.fragment.ReposFragment;
14 | import net.angrycode.wehub.ui.fragment.TagCacheFragment;
15 | import net.angrycode.wehub.utils.Keyboard;
16 | import net.angrycode.wehub.utils.TagPrefUtils;
17 |
18 | /**
19 | * Search
20 | * Created by lancelot on 2016/12/3.
21 | */
22 |
23 | public class SearchActivity extends BaseActivity {
24 | SearchView mSearchView;
25 | ReposFragment mFragment;
26 |
27 | @Override
28 | protected int getLayoutResource() {
29 | return R.layout.activity_search;
30 | }
31 |
32 | @Override
33 | protected void onCreate(@Nullable Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 |
36 | initTagFrag();
37 | }
38 |
39 | void initTagFrag() {
40 | TagCacheFragment fragment = new TagCacheFragment();
41 | fragment.setOnTagClickListener(tag -> mSearchView.setQuery(tag, true));
42 | getSupportFragmentManager().beginTransaction()
43 | .replace(R.id.search_container, fragment)
44 | .commitAllowingStateLoss();
45 | }
46 |
47 | void initSearch() {
48 | mSearchView.setIconified(false);
49 | mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
50 | @Override
51 | public boolean onQueryTextSubmit(String query) {
52 | query = query.trim();
53 | if (mFragment == null) {
54 | mFragment = ReposFragment.newInstance(query);
55 | getSupportFragmentManager().beginTransaction()
56 | .replace(R.id.search_container, mFragment)
57 | .commitAllowingStateLoss();
58 | } else {
59 | mFragment.search(query);
60 | }
61 |
62 | TagPrefUtils.appendTags(getApplicationContext(), query);
63 | return true;
64 | }
65 |
66 | @Override
67 | public boolean onQueryTextChange(String newText) {
68 | return false;
69 | }
70 | });
71 | }
72 |
73 | public void clearFocus() {
74 | if (mSearchView != null) {
75 | mSearchView.clearFocus();
76 | }
77 | }
78 |
79 | @Override
80 | public boolean onCreateOptionsMenu(Menu menu) {
81 | getMenuInflater().inflate(R.menu.activity_search_menu, menu);
82 | MenuItem menuItem = menu.findItem(R.id.action_search_view);
83 | mSearchView = (SearchView) MenuItemCompat.getActionView(menuItem);
84 |
85 | initSearch();
86 | MenuItemCompat.collapseActionView(menuItem);
87 | MenuItemCompat.expandActionView(menuItem);
88 | return true;
89 | }
90 |
91 | public static void launch(Context context) {
92 | Intent intent = new Intent(context, SearchActivity.class);
93 | context.startActivity(intent);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WeCode
3 |
4 | 加载中...
5 |
6 | 请求数据失败
7 | Observer出错
8 |
9 | WebView
10 |
11 | Open navigation drawer
12 | Close navigation drawer
13 |
14 | 搜索
15 | WeCode %s
16 | 语言
17 | 收藏
18 | 主题
19 | 关于
20 |
21 | 长按编辑,拖拽排序
22 | 已选语言
23 | 其他语言
24 | 编辑
25 | 完成
26 |
27 | 已收藏
28 |
29 | 天空蓝
30 | 健康绿
31 | 闷骚红
32 | 高贵紫
33 | 暗夜灰
34 |
35 |
36 | - 收藏
37 | - 复制链接
38 |
39 |
40 |
41 | - 取消收藏
42 | - 复制链接
43 |
44 |
45 | "开发者自白\n\n"
46 |
47 | "WeCode的产生源于我的一个小需求:就是查找当前GitHub上最热门的项目。WeCode帮助我随时关注当前的热门趋势。"
48 | "它的数据抓取自https://github.com/trending,就像GitHub上说的一样:See what the GitHub community is most excited about today."
49 | "每天都从这里开始,这已成为我工作的一部分。如果你喜欢,可以请我喝杯咖啡,万分感谢。\n\n"
50 |
51 | "支付宝打赏我:elgoog1900@gmail.com(长按可复制)\n\n"
52 |
53 | "开源协议\n\n"
54 |
55 | "WeCode崇尚开源,我的代码也是建立在前人的成果之上的,WeCode也会完全开源,可以关注我的GitHub:https://github.com/wecodexyz。感谢以下开源库的组织以及作者。\n\n"
56 |
57 | "com.android.support:design:25.0.1\n"
58 | "com.android.support:appcompat-v7:25.0.1\n"
59 | "com.android.support:recyclerview-v7:25.0.1\n"
60 | "com.android.support:cardview-v7:25.0.1\n"
61 | "com.android.support:customtabs:25.0.1\n"
62 | "io.reactivex:rxjava:1.1.6\n"
63 | "io.reactivex:rxandroid:1.2.1\n"
64 | "com.squareup.okhttp3:okhttp:3.3.1\n"
65 | "com.squareup.retrofit2:retrofit:2.1.0\n"
66 | "com.squareup.retrofit2:converter-gson:2.1.0\n"
67 | "com.squareup.retrofit2:adapter-rxjava:2.1.0\n"
68 | "com.squareup.okhttp3:logging-interceptor:3.3.1\n"
69 | "com.jakewharton:butterknife:8.4.0\n"
70 | "com.github.bumptech.glide:glide:3.7.0\n"
71 | "me.gujun.android.taggroup:library:1.4@aar\n"
72 | "org.greenrobot:eventbus:3.0.0\n"
73 | "org.greenrobot:greendao:3.2.0\n\n"
74 |
75 |
76 | Settings
77 | 更新
78 | 已复制到粘贴板
79 | 已有新版了哦
80 | 更新WeCode%s
81 | 马上下载
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/drag/ItemDragHelperCallback.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.component.drag;
2 |
3 | import android.support.v7.widget.GridLayoutManager;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.support.v7.widget.StaggeredGridLayoutManager;
6 | import android.support.v7.widget.helper.ItemTouchHelper;
7 |
8 | import net.angrycode.wehub.ui.adapter.LangAdapter;
9 |
10 | public class ItemDragHelperCallback extends ItemTouchHelper.Callback {
11 |
12 | @Override
13 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
14 | int dragFlags;
15 | RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
16 | if (manager instanceof GridLayoutManager || manager instanceof StaggeredGridLayoutManager) {
17 | dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
18 | } else {
19 | dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
20 | }
21 | // 如果想支持滑动(删除)操作, swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END
22 | int swipeFlags = 0;
23 | return makeMovementFlags(dragFlags, swipeFlags);
24 | }
25 |
26 | @Override
27 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
28 | // 不同Type之间不可移动
29 | if (viewHolder.getItemViewType() != target.getItemViewType()) {
30 | return false;
31 | }
32 | if (viewHolder.getItemViewType() != LangAdapter.VIEW_TYPE_LANG_SEL_ITEM) {
33 | return false;
34 | }
35 | if (target.getAdapterPosition() == 1 || viewHolder.getAdapterPosition() == 1) {
36 | return false;
37 | }
38 |
39 | if (recyclerView.getAdapter() instanceof OnItemMoveListener) {
40 | OnItemMoveListener listener = ((OnItemMoveListener) recyclerView.getAdapter());
41 | return listener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
42 | }
43 | return false;
44 | }
45 |
46 | @Override
47 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
48 |
49 | }
50 |
51 | @Override
52 | public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
53 | // 不在闲置状态
54 | if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
55 | if (viewHolder instanceof OnDragVHListener) {
56 | OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
57 | itemViewHolder.onItemSelected();
58 | }
59 | }
60 | super.onSelectedChanged(viewHolder, actionState);
61 | }
62 |
63 | @Override
64 | public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
65 | if (viewHolder instanceof OnDragVHListener) {
66 | OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
67 | itemViewHolder.onItemFinish();
68 | }
69 | super.clearView(recyclerView, viewHolder);
70 | }
71 |
72 | @Override
73 | public boolean isLongPressDragEnabled() {
74 | return false;
75 | }
76 |
77 | @Override
78 | public boolean isItemViewSwipeEnabled() {
79 | // 不支持滑动功能
80 | return false;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/BaseSimpleRecycleAdapter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * 封装了RecycleAdapter
13 | * Created by lancelot on 2016/12/3.
14 | */
15 |
16 | public abstract class BaseSimpleRecycleAdapter extends RecyclerView.Adapter {
17 | private OnItemClickListener mOnItemClickListener;
18 | private OnItemLongClickListener mOnItemLongClickListener;
19 | protected List mList = new ArrayList<>();
20 | // private int mCount;
21 |
22 | public void setData(List list) {
23 | mList = list;
24 | // if (list != null) {
25 | // mCount = list.size();
26 | // }
27 | notifyDataSetChanged();
28 | }
29 |
30 | public void addData(T t) {
31 | if (mList != null) {
32 | mList = new ArrayList<>();
33 | }
34 | mList.add(t);
35 | notifyItemInserted(mList.size() - 1);
36 | }
37 |
38 | public List getData() {
39 | return mList;
40 | }
41 |
42 | @Override
43 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
44 | int layoutRes = getItemLayout(viewType);
45 | if (layoutRes > 0) {
46 | View itemView = LayoutInflater.from(parent.getContext()).inflate(getItemLayout(viewType), parent, false);
47 | return new ViewHolder(itemView);
48 | }
49 | return null;
50 | }
51 |
52 | /**
53 | * 获取item layout
54 | *
55 | * @return
56 | */
57 | public abstract int getItemLayout(int viewType);
58 |
59 | /**
60 | * 渲染item
61 | *
62 | * @param holder
63 | * @param position
64 | */
65 | public abstract void onRender(ViewHolder holder, int position);
66 |
67 | @Override
68 | public void onBindViewHolder(ViewHolder holder, int position) {
69 | if (mOnItemClickListener != null) {
70 | holder.itemView.setOnClickListener(view -> mOnItemClickListener.onItemClick(holder.itemView, position));
71 | }
72 | if (mOnItemLongClickListener != null) {
73 | holder.itemView.setOnLongClickListener(view -> {
74 | mOnItemLongClickListener.onItemLongClick(holder.itemView, position);
75 | return true;
76 | });
77 | }
78 | onRender(holder, holder.getAdapterPosition());
79 | }
80 |
81 | @Override
82 | public int getItemCount() {
83 | return mList.size();
84 | }
85 |
86 | public T getItem(int position) {
87 | return mList.get(position);
88 | }
89 |
90 | public void setOnItemClickListener(OnItemClickListener listener) {
91 | mOnItemClickListener = listener;
92 | }
93 |
94 | public void setOnItemLongClickListener(OnItemLongClickListener listener) {
95 | mOnItemLongClickListener = listener;
96 | }
97 |
98 | public interface OnItemClickListener {
99 | void onItemClick(View itemView, int position);
100 | }
101 |
102 | public interface OnItemLongClickListener {
103 | void onItemLongClick(View itemView, int position);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/adapter/ThemeAdapter.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.adapter;
2 |
3 | import android.view.View;
4 | import android.widget.TextView;
5 |
6 | import net.angrycode.wehub.R;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by lancelot on 2016/12/18.
13 | */
14 |
15 | public class ThemeAdapter extends BaseSimpleRecycleAdapter {
16 | int mCurrTheme;
17 |
18 | public ThemeAdapter() {
19 | List list = new ArrayList<>();
20 | list.add(new Theme(R.style.AppTheme));
21 | list.add(new Theme(R.style.AppTheme_Green));
22 | list.add(new Theme(R.style.AppTheme_Red));
23 | list.add(new Theme(R.style.AppTheme_Purple));
24 | list.add(new Theme(R.style.AppTheme_Gray));
25 | setData(list);
26 | }
27 |
28 | @Override
29 | public int getItemLayout(int viewType) {
30 | return R.layout.item_theme;
31 | }
32 |
33 | public void setCurrTheme(int currTheme) {
34 | mCurrTheme = currTheme;
35 | notifyDataSetChanged();
36 | }
37 |
38 | @Override
39 | public void onRender(ViewHolder holder, int position) {
40 | Theme theme = getItem(position);
41 | holder.getView(R.id.iv_theme).setBackgroundResource(theme.color);
42 | holder.getView(R.id.iv_check).setVisibility(mCurrTheme == theme.getStyle() ? View.VISIBLE : View.INVISIBLE);
43 | TextView textView = holder.getView(R.id.tv_theme_name);
44 | textView.setText(theme.name);
45 | }
46 |
47 |
48 | public static class Theme {
49 | private int color;
50 | private int name;
51 | private int style;
52 |
53 | public Theme(int style) {
54 | this.style = style;
55 | switch (style) {
56 | case R.style.AppTheme:
57 | color = R.color.primary;
58 | name = R.string.theme_default;
59 | break;
60 | case R.style.AppTheme_Gray:
61 | color = R.color.primary_gray;
62 | name = R.string.theme_gray;
63 | break;
64 | case R.style.AppTheme_Green:
65 | color = R.color.primary_green;
66 | name = R.string.theme_green;
67 | break;
68 | case R.style.AppTheme_Purple:
69 | color = R.color.primary_purple;
70 | name = R.string.theme_purple;
71 | break;
72 | case R.style.AppTheme_Red:
73 | color = R.color.primary_red;
74 | name = R.string.theme_red;
75 | break;
76 | }
77 | }
78 |
79 | public int getColor() {
80 | return color;
81 | }
82 |
83 | public void setColor(int color) {
84 | this.color = color;
85 | }
86 |
87 | public int getName() {
88 | return name;
89 | }
90 |
91 | public void setName(int name) {
92 | this.name = name;
93 | }
94 |
95 | public int getStyle() {
96 | return style;
97 | }
98 |
99 | public void setStyle(int style) {
100 | this.style = style;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/api/BaseApi.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.api;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.io.IOException;
6 |
7 | import okhttp3.Interceptor;
8 | import okhttp3.OkHttpClient;
9 | import okhttp3.Request;
10 | import okhttp3.Response;
11 | import retrofit2.Retrofit;
12 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
13 | import retrofit2.converter.gson.GsonConverterFactory;
14 |
15 | public abstract class BaseApi {
16 | public static String API_SERVER = "https://api.github.com";
17 | private static final OkHttpClient sOkHttpClient = new OkHttpClient();
18 | private static Retrofit sRetrofit;
19 |
20 | static {
21 | sOkHttpClient.newBuilder()
22 | .addInterceptor(new Interceptor() {
23 | @Override
24 | public Response intercept(Chain chain) throws IOException {
25 | Request original = chain.request();
26 |
27 | String[] headers = {
28 | "application/vnd.github.html+json",
29 | "application/vnd.github.raw+json"
30 | };
31 |
32 | // String token = TokenStore.getInstance(context).getToken();
33 | String token = null;
34 | Request.Builder requestBuilder = original.newBuilder()
35 | .header("Authorization", "Token " + token)
36 | .method(original.method(), original.body());
37 |
38 | if (original.header("Accept") == null) {
39 | requestBuilder.addHeader("Accept", TextUtils.join(",", headers));
40 | }
41 |
42 | Request request = requestBuilder.build();
43 | return chain.proceed(request);
44 | }
45 | }).build();
46 | }
47 |
48 | protected static Retrofit getRetrofit() {
49 | if (sRetrofit == null) {
50 | // Context context = TrendingApplication.get();
51 | //设定30秒超时
52 | // mOkHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
53 | //设置拦截器,以用于自定义Cookies的设置
54 | // mOkHttpClient.networkInterceptors()
55 | // .add(new CookiesInterceptor(context));
56 | //设置缓存目录
57 | // File cacheDirectory = new File(context.getCacheDir()
58 | // .getAbsolutePath(), "HttpCache");
59 | // Cache cache = new Cache(cacheDirectory, 20 * 1024 * 1024);
60 | // mOkHttpClient.setCache(cache);
61 | //构建Retrofit
62 | sRetrofit = new Retrofit.Builder()
63 | //配置服务器路径
64 | .baseUrl(getBaseUrl() + "/")
65 | //设置日期解析格式,这样可以直接解析Date类型
66 | // .setDateFormat("yyyy-MM-dd HH:mm:ss")
67 | //配置转化库,默认是Gson
68 | .addConverterFactory(GsonConverterFactory.create())
69 | //配置回调库,采用RxJava
70 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
71 | //设置OKHttpClient为网络客户端
72 | .client(sOkHttpClient)
73 | .build();
74 | }
75 | return sRetrofit;
76 | }
77 |
78 | protected static String getBaseUrl() {
79 | return API_SERVER;
80 | }
81 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'me.tatarka.retrolambda'
3 | apply plugin: 'com.jakewharton.butterknife'
4 | apply plugin: 'org.greenrobot.greendao'
5 |
6 | android {
7 | compileSdkVersion 25
8 | buildToolsVersion "25.0.0"
9 | signingConfigs {
10 | config {
11 | storeFile file(KEY_STORE)
12 | keyAlias KEY_ALIATS
13 | keyPassword KEY_PASSWORD
14 | storePassword KEY_PASSWORD
15 | }
16 | debug {
17 | storeFile file(KEY_STORE)
18 | keyAlias KEY_ALIATS
19 | keyPassword KEY_PASSWORD
20 | storePassword KEY_PASSWORD
21 | }
22 | }
23 | defaultConfig {
24 | applicationId "net.angrycode.wehub"
25 | minSdkVersion 15
26 | targetSdkVersion 25
27 | versionCode 2
28 | versionName "1.0.2"
29 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
30 | }
31 | buildTypes {
32 | release {
33 | minifyEnabled true
34 | shrinkResources true
35 | zipAlignEnabled true
36 | signingConfig signingConfigs.config
37 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
38 | }
39 | }
40 | compileOptions {
41 | sourceCompatibility JavaVersion.VERSION_1_8
42 | targetCompatibility JavaVersion.VERSION_1_8
43 | }
44 | applicationVariants.all { variant ->
45 | variant.outputs.each { output ->
46 | def outputFile = output.outputFile
47 | if (variant.buildType.name.equals('release')) {
48 | //定义打包名称
49 | def fileName = "app_v${defaultConfig.versionName}_${buildTime()}.apk"
50 | output.outputFile = new File(outputFile.parent, fileName)
51 | }
52 |
53 |
54 | }
55 | }
56 |
57 | }
58 |
59 | def buildTime() {
60 | Date date = new Date();
61 | return date.format("yyyyMMdd_HHmm", TimeZone.getDefault())
62 | }
63 |
64 | greendao {
65 | schemaVersion 2
66 | }
67 |
68 | dependencies {
69 | compile fileTree(include: ['*.jar'], dir: 'libs')
70 | compile 'com.android.support:appcompat-v7:25.0.1'
71 | compile 'com.android.support:recyclerview-v7:25.0.1'
72 | compile 'com.android.support:cardview-v7:25.0.1'
73 | //design
74 | compile 'com.android.support:design:25.0.1'
75 | //custom tabs
76 | compile 'com.android.support:customtabs:25.0.1'
77 | compile project(':customtabs')
78 |
79 | //编译RxJava
80 | compile 'io.reactivex:rxjava:1.1.6'
81 | //编译RxAndroid
82 | compile 'io.reactivex:rxandroid:1.2.1'
83 | //编译Retrofit及其相关库,包括Gson
84 | compile 'com.squareup.okhttp3:okhttp:3.3.1'
85 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
86 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
87 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
88 | compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
89 | //view injector
90 | compile 'com.jakewharton:butterknife:8.4.0'
91 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
92 | //glide
93 | compile 'com.github.bumptech.glide:glide:3.7.0'
94 | //TagGroup
95 | compile 'me.gujun.android.taggroup:library:1.4@aar'
96 | //EventBus
97 | compile 'org.greenrobot:eventbus:3.0.0'
98 | compile 'org.greenrobot:greendao:3.2.0'
99 | //test stuff
100 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
101 | exclude group: 'com.android.support', module: 'support-annotations'
102 | })
103 | testCompile 'junit:junit:4.12'
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_repo.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
17 |
18 |
25 |
26 |
35 |
36 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
71 |
72 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/WebViewActivity.java:
--------------------------------------------------------------------------------
1 | // Copyright 2015 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net.angrycode.wehub.ui.component;
16 |
17 | import android.graphics.Bitmap;
18 | import android.os.Build;
19 | import android.os.Bundle;
20 | import android.view.MenuItem;
21 | import android.view.View;
22 | import android.webkit.WebChromeClient;
23 | import android.webkit.WebSettings;
24 | import android.webkit.WebView;
25 | import android.webkit.WebViewClient;
26 | import android.widget.ProgressBar;
27 |
28 | import net.angrycode.wehub.R;
29 | import net.angrycode.wehub.ui.activity.BaseActivity;
30 |
31 | import butterknife.BindView;
32 |
33 | /**
34 | * This Activity is used as a fallback when there is no browser installed that supports
35 | * Chrome Custom Tabs
36 | */
37 | public class WebViewActivity extends BaseActivity {
38 |
39 | public static final String EXTRA_URL = "extra.url";
40 |
41 | @BindView(R.id.progress)
42 | ProgressBar mProgressBar;
43 | @BindView(R.id.webview)
44 | WebView mWebView;
45 |
46 |
47 | @Override
48 | protected int getLayoutResource() {
49 | return R.layout.activity_webview;
50 | }
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | String url = getIntent().getStringExtra(EXTRA_URL);
56 |
57 | WebSettings webSettings = mWebView.getSettings();
58 | webSettings.setJavaScriptEnabled(true);
59 | mWebView.setWebViewClient(new WebViewClient() {
60 | @Override
61 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
62 | super.onPageStarted(view, url, favicon);
63 | if (isFinishing()) {
64 | return;
65 | }
66 | mProgressBar.setVisibility(View.VISIBLE);
67 | }
68 |
69 | @Override
70 | public void onPageFinished(WebView view, String url) {
71 | super.onPageFinished(view, url);
72 | if (isFinishing()) {
73 | return;
74 | }
75 | mProgressBar.setVisibility(View.GONE);
76 | }
77 | });
78 | mWebView.setWebChromeClient(new WebChromeClient() {
79 | @Override
80 | public void onProgressChanged(WebView view, int newProgress) {
81 | super.onProgressChanged(view, newProgress);
82 | if (isFinishing()) {
83 | return;
84 | }
85 | if (Build.VERSION.SDK_INT >= 24) {
86 | mProgressBar.setProgress(newProgress, true);
87 | } else {
88 | mProgressBar.setProgress(newProgress);
89 | }
90 | }
91 | });
92 | setTitle(url);
93 | mWebView.loadUrl(url);
94 | }
95 |
96 | @Override
97 | public boolean onOptionsItemSelected(MenuItem item) {
98 | switch (item.getItemId()) {
99 | // Respond to the action bar's Up/Home button
100 | case android.R.id.home:
101 | finish();
102 | return true;
103 | }
104 | return super.onOptionsItemSelected(item);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/bean/TrendingRepo.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.bean;
2 |
3 | import org.greenrobot.greendao.annotation.Entity;
4 | import org.greenrobot.greendao.annotation.Id;
5 | import org.greenrobot.greendao.annotation.Index;
6 | import org.greenrobot.greendao.annotation.NotNull;
7 | import org.json.JSONObject;
8 | import org.greenrobot.greendao.annotation.Generated;
9 |
10 | import java.util.Date;
11 |
12 | /**
13 | * https://github.com/trending
14 | * Created by lancelot on 2016/12/2.
15 | */
16 | @Entity(indexes = {
17 | @Index(value = "name, date DESC", unique = true)
18 | })
19 | public class TrendingRepo {
20 | @Id
21 | private Long id;
22 |
23 | private String avatar;
24 | private String desc;
25 | @NotNull
26 | private String url;
27 | @NotNull
28 | private String name;
29 | private String owner;
30 | private String stars_today;
31 |
32 | private Date date;
33 |
34 | public TrendingRepo(JSONObject jsonObject) {
35 | avatar = jsonObject.optString("avatar");
36 | desc = jsonObject.optString("desc");
37 | url = jsonObject.optString("url");
38 | name = jsonObject.optString("name");
39 | owner = jsonObject.optString("owner");
40 | stars_today = jsonObject.optString("stars_today");
41 | }
42 |
43 |
44 |
45 | @Generated(hash = 34610607)
46 | public TrendingRepo(Long id, String avatar, String desc, @NotNull String url,
47 | @NotNull String name, String owner, String stars_today, Date date) {
48 | this.id = id;
49 | this.avatar = avatar;
50 | this.desc = desc;
51 | this.url = url;
52 | this.name = name;
53 | this.owner = owner;
54 | this.stars_today = stars_today;
55 | this.date = date;
56 | }
57 |
58 |
59 |
60 | @Generated(hash = 843815768)
61 | public TrendingRepo() {
62 | }
63 |
64 |
65 |
66 | public String getAvatar() {
67 | return avatar;
68 | }
69 |
70 | public String getDesc() {
71 | return desc;
72 | }
73 |
74 |
75 | public String getStars_today() {
76 | return stars_today;
77 | }
78 |
79 | public String getOwner() {
80 | return owner;
81 | }
82 |
83 |
84 | public Long getId() {
85 | return id;
86 | }
87 |
88 | public void setId(Long id) {
89 | this.id = id;
90 | }
91 |
92 | public void setAvatar(String avatar) {
93 | this.avatar = avatar;
94 | }
95 |
96 | public void setDesc(String desc) {
97 | this.desc = desc;
98 | }
99 |
100 | public String getUrl() {
101 | return url;
102 | }
103 |
104 | public void setUrl(String url) {
105 | this.url = url;
106 | }
107 |
108 | public String getName() {
109 | return name;
110 | }
111 |
112 | public void setName(String name) {
113 | this.name = name;
114 | }
115 |
116 | public void setOwner(String owner) {
117 | this.owner = owner;
118 | }
119 |
120 | public void setStars_today(String stars_today) {
121 | this.stars_today = stars_today;
122 | }
123 |
124 | public Date getDate() {
125 | return date;
126 | }
127 |
128 | public void setDate(Date date) {
129 | this.date = date;
130 | }
131 |
132 | @Override
133 | public boolean equals(Object o) {
134 | if (this == o) return true;
135 | if (o == null || getClass() != o.getClass()) return false;
136 |
137 | TrendingRepo repo1 = (TrendingRepo) o;
138 |
139 | if (!url.equals(repo1.url)) return false;
140 | if (!name.equals(repo1.name)) return false;
141 | return owner != null ? owner.equals(repo1.owner) : repo1.owner == null;
142 |
143 | }
144 |
145 | @Override
146 | public int hashCode() {
147 | int result = url != null ? url.hashCode() : 0;
148 | result = 31 * result + name.hashCode();
149 | result = 31 * result + (owner != null ? owner.hashCode() : 0);
150 | return result;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/lancelot/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | -keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | public *;
17 | }
18 | -dontwarn net.angrycode.wehub.**
19 | -keep class net.angrycode.wehub.event.** {*;}
20 | #gson相关的bean类不要混淆
21 | -keep class net.angrycode.wehub.bean.** {*;}
22 | #Glide
23 | -keep public class * implements com.bumptech.glide.module.GlideModule
24 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
25 | **[] $VALUES;
26 | public *;
27 | }
28 | # for DexGuard only
29 | #-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
30 |
31 | #okhttp
32 | -dontwarn okhttp3.**
33 | -keep class okhttp3.** { *;}
34 | -dontwarn okio.**
35 |
36 | # Retrofit
37 | -dontwarn retrofit2.**
38 | -dontwarn org.codehaus.mojo.**
39 | -keep class retrofit2.** { *; }
40 | -keepattributes Exceptions
41 | -keepclasseswithmembers class * {
42 | @retrofit.* ;
43 | }
44 |
45 | -keepclasseswithmembers interface * {
46 | @retrofit.* ;
47 | }
48 | #rx
49 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
50 | long producerIndex;
51 | long consumerIndex;
52 | }
53 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
54 | rx.internal.util.atomic.LinkedQueueNode producerNode;
55 | }
56 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
57 | rx.internal.util.atomic.LinkedQueueNode consumerNode;
58 | }
59 | -dontwarn sun.misc.Unsafe
60 | -keep class rx.schedulers.Schedulers {
61 | public static ;
62 | }
63 | -keep class rx.schedulers.ImmediateScheduler {
64 | public ;
65 | }
66 | -keep class rx.schedulers.TestScheduler {
67 | public ;
68 | }
69 | -keep class rx.schedulers.Schedulers {
70 | public static ** test();
71 | }
72 | ##---------------Begin: proguard configuration for Gson ----------
73 | # Gson uses generic type information stored in a class file when working with fields. Proguard
74 | # removes such information by default, so configure it to keep all of it.
75 | -keepattributes Signature
76 |
77 | # For using GSON @Expose annotation
78 | -keepattributes *Annotation*
79 |
80 | # Gson specific classes
81 | -keep class sun.misc.Unsafe { *; }
82 | #-keep class com.google.gson.stream.** { *; }
83 |
84 | # Application classes that will be serialized/deserialized over Gson
85 | -keep class com.google.gson.examples.android.model.** { *; }
86 |
87 | # Prevent proguard from stripping interface information from TypeAdapterFactory,
88 | # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
89 | -keep class * implements com.google.gson.TypeAdapterFactory
90 | -keep class * implements com.google.gson.JsonSerializer
91 | -keep class * implements com.google.gson.JsonDeserializer
92 |
93 | ##---------------End: proguard configuration for Gson ----------
94 | #greenDao
95 | -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
96 | public static java.lang.String TABLENAME;
97 | }
98 | -keep class **$Properties
99 |
100 | # If you do not use SQLCipher:
101 | -dontwarn org.greenrobot.greendao.database.**
102 |
103 | # eventbus
104 | -keepattributes *Annotation*
105 | -keepclassmembers class ** {
106 | @org.greenrobot.eventbus.Subscribe ;
107 | }
108 | -keep enum org.greenrobot.eventbus.ThreadMode { *; }
109 | # Only required if you use AsyncExecutor
110 | -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
111 | (java.lang.Throwable);
112 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/component/DividerItemDecoration.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.component;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.support.annotation.ColorRes;
8 | import android.support.v4.content.ContextCompat;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.support.v7.widget.RecyclerView;
11 | import android.util.TypedValue;
12 | import android.view.View;
13 |
14 |
15 | public class DividerItemDecoration extends RecyclerView.ItemDecoration {
16 |
17 | /*
18 | * RecyclerView的布局方向,默认先赋值
19 | * 为纵向布局
20 | * RecyclerView 布局可横向,也可纵向
21 | * 横向和纵向对应的分割想画法不一样
22 | * */
23 | private int mOrientation = LinearLayoutManager.VERTICAL;
24 |
25 | /**
26 | * 分割线的高度,默认为1
27 | */
28 | private int mDividerHeight = 1;
29 |
30 | /**
31 | * 分割线的画笔,和设置其属性
32 | * 来绘制个性分割线
33 | */
34 | private Paint mPaint;
35 |
36 | /**
37 | * 构造方法传入布局方向,不可不传
38 | *
39 | * @param context
40 | * @param orientation
41 | */
42 | public DividerItemDecoration(Context context, int orientation, @ColorRes int color) {
43 | this.mOrientation = orientation;
44 | if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
45 | throw new IllegalArgumentException("请传入正确的参数");
46 | }
47 | mDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources().getDisplayMetrics());
48 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
49 | mPaint.setColor(ContextCompat.getColor(context, color));
50 | /*设置填充*/
51 | mPaint.setStyle(Paint.Style.FILL);
52 | }
53 |
54 | @Override
55 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
56 | if (mOrientation == LinearLayoutManager.VERTICAL) {
57 | drawVertical(c, parent);
58 | } else {
59 | drawHorizontal(c, parent);
60 | }
61 | }
62 |
63 | /**
64 | * 绘制纵向 item 分割线
65 | *
66 | * @param canvas
67 | * @param parent
68 | */
69 | private void drawVertical(Canvas canvas, RecyclerView parent) {
70 | final int left = parent.getPaddingLeft();
71 | final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
72 | final int childSize = parent.getChildCount();
73 | for (int i = 0; i < childSize; i++) {
74 | final View child = parent.getChildAt(i);
75 | RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
76 | final int top = child.getBottom() + layoutParams.bottomMargin;
77 | final int bottom = top + mDividerHeight;
78 | canvas.drawRect(left, top, right, bottom, mPaint);
79 | }
80 | }
81 |
82 | /**
83 | * 绘制横向 item 分割线
84 | *
85 | * @param canvas
86 | * @param parent
87 | */
88 | private void drawHorizontal(Canvas canvas, RecyclerView parent) {
89 | final int top = parent.getPaddingTop();
90 | final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
91 | final int childSize = parent.getChildCount();
92 | for (int i = 0; i < childSize; i++) {
93 | final View child = parent.getChildAt(i);
94 | RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
95 | final int left = child.getRight() + layoutParams.rightMargin;
96 | final int right = left + mDividerHeight;
97 | canvas.drawRect(left, top, right, bottom, mPaint);
98 | }
99 | }
100 |
101 | /**
102 | * 设置item分割线的size
103 | *
104 | * @param outRect
105 | * @param view
106 | * @param parent
107 | * @param state
108 | */
109 | @Override
110 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
111 | if (mOrientation == LinearLayoutManager.VERTICAL) {
112 | outRect.set(0, 0, 0, mDividerHeight);
113 | } else {
114 | outRect.set(0, 0, mDividerHeight, 0);
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/FavoritesActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.app.Activity;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.customtabs.CustomTabsIntent;
8 | import android.support.v4.content.ContextCompat;
9 | import android.support.v7.app.AlertDialog;
10 | import android.support.v7.widget.LinearLayoutManager;
11 | import android.support.v7.widget.RecyclerView;
12 | import android.view.View;
13 |
14 | import net.angrycode.wehub.R;
15 | import net.angrycode.wehub.bean.TrendingRepo;
16 | import net.angrycode.wehub.mvp.presenter.FavorPresenter;
17 | import net.angrycode.wehub.mvp.view.FavorView;
18 | import net.angrycode.wehub.ui.adapter.TrendsAdapter;
19 | import net.angrycode.wehub.ui.component.CustomTabActivityHelper;
20 | import net.angrycode.wehub.ui.component.DividerItemDecoration;
21 | import net.angrycode.wehub.ui.component.WebViewFallback;
22 | import net.angrycode.wehub.utils.ToastUtils;
23 | import net.angrycode.wehub.utils.Utils;
24 |
25 | import java.util.List;
26 |
27 | import butterknife.BindView;
28 |
29 | /**
30 | * Created by lancelot on 2016/12/9.
31 | */
32 |
33 | public class FavoritesActivity extends BaseActivity implements FavorView {
34 | @BindView(R.id.recycle_view)
35 | RecyclerView mRecycleView;
36 |
37 | FavorPresenter mPresenter;
38 | TrendsAdapter mAdapter;
39 |
40 | @Override
41 | protected void onCreate(@Nullable Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 |
44 | init();
45 |
46 | mPresenter = new FavorPresenter(this);
47 | mPresenter.getFavorList();
48 | }
49 |
50 | private void init() {
51 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
52 | mRecycleView.setLayoutManager(linearLayoutManager);
53 | mRecycleView.addItemDecoration(new DividerItemDecoration(getActivity().getApplicationContext(), LinearLayoutManager.VERTICAL, R.color.divider));
54 | mAdapter = new TrendsAdapter();
55 | mRecycleView.setAdapter(mAdapter);
56 | mAdapter.setOnItemClickListener(this::onItemClick);
57 | mAdapter.setOnItemLongClickListener(this::onItemLongClick);
58 | }
59 |
60 | private void onItemClick(View itemView, int position) {
61 | String url = mAdapter.getItem(position).getUrl();
62 | CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
63 | builder.setToolbarColor(ContextCompat.getColor(getActivity(), R.color.primary));
64 | CustomTabsIntent customTabsIntent = builder.build();
65 | CustomTabActivityHelper.openCustomTab(
66 | getActivity(), customTabsIntent, Uri.parse(url), new WebViewFallback());
67 | }
68 |
69 | private void onItemLongClick(View itemView, int position) {
70 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
71 | builder.setItems(R.array.more_opt_favor, (dialog, which) -> {
72 | TrendingRepo repo = mAdapter.getItem(position);
73 | switch (which) {
74 | case 0:
75 | mPresenter.deleteFavor(repo);
76 | break;
77 | case 1:
78 | Utils.copyText(getActivity(), repo.getUrl());
79 | break;
80 | }
81 | });
82 | AlertDialog dialog = builder.create();
83 | dialog.show();
84 | }
85 |
86 | @Override
87 | protected int getLayoutResource() {
88 | return R.layout.activity_favorites;
89 | }
90 |
91 | @Override
92 | public void onGetFavorListFinish(List repoList) {
93 | mAdapter.setData(repoList);
94 | }
95 |
96 | @Override
97 | public void onDelFavorFinish(TrendingRepo repo) {
98 | mAdapter.remove(repo);
99 | }
100 |
101 | @Override
102 | public Activity getActivity() {
103 | return this;
104 | }
105 |
106 | @Override
107 | public void onRequestLoading() {
108 | showLoading();
109 | }
110 |
111 | @Override
112 | public void onRequestFinished() {
113 | dismissLoading();
114 | }
115 |
116 | @Override
117 | public void onRequestError(int code, String message) {
118 | ToastUtils.show(this, message);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.activity;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.res.Resources;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 | import android.util.Log;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.view.View;
13 | import android.widget.TextView;
14 |
15 | import net.angrycode.wehub.R;
16 | import net.angrycode.wehub.utils.ThemeUtils;
17 |
18 | import butterknife.ButterKnife;
19 |
20 | /**
21 | * Created by lancelot on 2016/5/30.
22 | */
23 | public abstract class BaseActivity extends AppCompatActivity {
24 |
25 | protected abstract int getLayoutResource();
26 |
27 | protected Toolbar mToolbar;
28 | protected TextView mToolbarTitle;
29 |
30 | protected ProgressDialog mLoading;
31 |
32 | @Override
33 | protected void onCreate(@Nullable Bundle savedInstanceState) {
34 | setTheme(getCurrTheme());
35 | super.onCreate(savedInstanceState);
36 | int layoutResId = getLayoutResource();
37 | try {
38 | if (layoutResId != 0) {
39 | setContentView(layoutResId);
40 | }
41 | } catch (Exception e) {
42 | Log.d("base activity", e.getMessage());
43 | }
44 | // AppManager.getAppManager().addActivity(this);
45 | }
46 |
47 | @Override
48 | public void setContentView(int layoutResID) {
49 | super.setContentView(layoutResID);
50 | ButterKnife.bind(this);
51 | View v = findViewById(R.id.toolbar);
52 | if (v != null) {
53 | mToolbar = (Toolbar) v;
54 | setSupportActionBar(mToolbar);
55 | mToolbarTitle = (TextView) v.findViewById(R.id.toolbar_title);
56 | if (mToolbarTitle != null && getSupportActionBar() != null) {
57 | getSupportActionBar().setDisplayShowTitleEnabled(false);
58 | }
59 | if (getSupportActionBar() != null && isShowHomeAsUpIndicator()) {
60 | // getSupportActionBar().setIcon(R.drawable.app_divider_drawable);
61 | // getSupportActionBar().setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
62 |
63 | getSupportActionBar().setHomeButtonEnabled(true);
64 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
65 |
66 | }
67 |
68 | mToolbar.setNavigationOnClickListener(view -> onBackPressed());
69 |
70 | }
71 | }
72 |
73 | public void showLoading() {
74 | if (isFinishing()) {
75 | return;
76 | }
77 | if (mLoading == null) {
78 | mLoading = new ProgressDialog(this);
79 | }
80 | mLoading.setCancelable(false);
81 | mLoading.setCanceledOnTouchOutside(false);
82 | mLoading.setMessage(getResources().getString(R.string.loading));
83 | mLoading.show();
84 | }
85 |
86 | public void showLoading(int msg) {
87 |
88 | if (mLoading == null) {
89 | mLoading = new ProgressDialog(this);
90 | }
91 | mLoading.setMessage(getResources().getString(msg));
92 | mLoading.show();
93 | }
94 |
95 | public void dismissLoading() {
96 | if (mLoading != null) {
97 | mLoading.dismiss();
98 | }
99 | }
100 |
101 | // @Override
102 | // public boolean onOptionsItemSelected(MenuItem item) {
103 | // if (item.getItemId() == android.R.id.home) {
104 | // onBackPressed();
105 | // return true;
106 | // }
107 | // return super.onOptionsItemSelected(item);
108 | // }
109 |
110 | @Override
111 | public boolean onPrepareOptionsMenu(Menu menu) {
112 | int size = menu.size();
113 | for (int i = 0; i < size; i++) {
114 | MenuItem item = menu.getItem(i);
115 | }
116 | return super.onPrepareOptionsMenu(menu);
117 | }
118 |
119 | protected boolean isShowHomeAsUpIndicator() {
120 | return true;
121 | }
122 |
123 | @Override
124 | public void onBackPressed() {
125 | super.onBackPressed();
126 | }
127 |
128 | @Override
129 | protected void onDestroy() {
130 | if (mLoading != null && mLoading.isShowing()) {
131 | mLoading.dismiss();
132 | }
133 | // AppManager.getAppManager().removeActivity(this);
134 | super.onDestroy();
135 | }
136 |
137 | protected int getCurrTheme() {
138 | return ThemeUtils.getTheme(this);
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_logo_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/net/angrycode/wehub/ui/fragment/ReposFragment.java:
--------------------------------------------------------------------------------
1 | package net.angrycode.wehub.ui.fragment;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.widget.SwipeRefreshLayout;
6 | import android.support.v7.app.AlertDialog;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.util.Log;
10 | import android.view.View;
11 |
12 | import net.angrycode.wehub.R;
13 | import net.angrycode.wehub.bean.Repos;
14 | import net.angrycode.wehub.bean.Repository;
15 | import net.angrycode.wehub.bean.TrendingRepo;
16 | import net.angrycode.wehub.mvp.presenter.ReposPresenter;
17 | import net.angrycode.wehub.mvp.view.RepositoriesView;
18 | import net.angrycode.wehub.ui.activity.SearchActivity;
19 | import net.angrycode.wehub.ui.adapter.ReposAdapter;
20 | import net.angrycode.wehub.ui.component.DividerItemDecoration;
21 | import net.angrycode.wehub.utils.Keyboard;
22 | import net.angrycode.wehub.utils.ToastUtils;
23 | import net.angrycode.wehub.utils.Utils;
24 | import net.angrycode.wehub.utils.WebUtils;
25 |
26 | import butterknife.BindView;
27 |
28 | /**
29 | * Created by lancelot on 2016/11/15.
30 | */
31 |
32 | public class ReposFragment extends BaseFragment implements RepositoriesView {
33 | ReposPresenter mPresenter;
34 | @BindView(R.id.recycle_view)
35 | RecyclerView mRecycleView;
36 | @BindView(R.id.refresh_layout)
37 | SwipeRefreshLayout mRefreshLayout;
38 |
39 | ReposAdapter mAdapter;
40 |
41 | // String mLanguage;
42 |
43 | String mQuery;
44 |
45 | public static ReposFragment newInstance(String query) {
46 | ReposFragment fragment = new ReposFragment();
47 | fragment.mQuery = query;
48 | return fragment;
49 | }
50 |
51 | @Override
52 | public int getLayoutResource() {
53 | return R.layout.fragment_repos;
54 | }
55 |
56 | @Override
57 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
58 | super.onActivityCreated(savedInstanceState);
59 | // mQuery = mLanguage+"&language:" + mLanguage;
60 | mPresenter = new ReposPresenter(this);
61 | search(mQuery);
62 | mRefreshLayout.setOnRefreshListener(() -> {
63 | mPresenter.getRepositories(mQuery);
64 | });
65 |
66 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
67 | mRecycleView.setLayoutManager(linearLayoutManager);
68 | mRecycleView.addItemDecoration(new DividerItemDecoration(getActivity().getApplicationContext(), LinearLayoutManager.VERTICAL, R.color.divider));
69 | mAdapter = new ReposAdapter();
70 | mRecycleView.setAdapter(mAdapter);
71 | mAdapter.setOnItemClickListener((itemView, position) -> {
72 | String url = mAdapter.getItem(position).getHtml_url();
73 | WebUtils.openUrl(getActivity(), url);
74 | });
75 | mAdapter.setOnItemLongClickListener(this::onItemLongClick);
76 | }
77 |
78 | private void onItemLongClick(View itemView, int position) {
79 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
80 | builder.setItems(R.array.more_opt, (dialog, which) -> {
81 | Repository repository = mAdapter.getItem(position);
82 | switch (which) {
83 | case 0:
84 | mPresenter.addFavor(repository.convert2TrendingRepo());
85 | break;
86 | case 1:
87 | Utils.copyText(getActivity(), repository.getHtml_url());
88 | break;
89 | }
90 | if (getActivity() instanceof SearchActivity) {
91 | ((SearchActivity) getActivity()).clearFocus();
92 | }
93 | });
94 | AlertDialog dialog = builder.create();
95 | dialog.show();
96 | }
97 |
98 | public void search(String query) {
99 | mPresenter.getRepositories(query);
100 | mRefreshLayout.setRefreshing(true);
101 | }
102 |
103 | @Override
104 | public void onGetRepositories(Repos repos) {
105 | Log.d("main", repos.toString());
106 | mAdapter.setData(repos.getItems());
107 | Keyboard.hide(getActivity().getCurrentFocus());
108 | }
109 |
110 | @Override
111 | public void onAddFavorFinish(TrendingRepo repo) {
112 | ToastUtils.show(getActivity(), R.string.add_favor_finish);
113 | }
114 |
115 | @Override
116 | public void onRequestLoading() {
117 | mRefreshLayout.setRefreshing(true);
118 | }
119 |
120 | @Override
121 | public void onRequestFinished() {
122 | mRefreshLayout.setRefreshing(false);
123 | }
124 |
125 | @Override
126 | public void onRequestError(int code, String message) {
127 | ToastUtils.show(getContext(), message);
128 | mRefreshLayout.setRefreshing(false);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------