From 242e2bc9d204e9bbcdbde1b48597d546b8580adf Mon Sep 17 00:00:00 2001 From: Sabari Date: Tue, 26 Mar 2019 09:49:57 +0530 Subject: [PATCH 01/18] base mvp presenter and implementation class added. --- .../stack/data/api/RetrofitClient.java | 2 +- .../stack/data/api/StackExchangeApi.java | 1 + .../stack/data/model/MyAdapterFactory.java | 15 +++++ .../nathansdev/stack/data/model/Owner.java | 35 ++++++++++ .../nathansdev/stack/data/model/Question.java | 61 +++++++++++++++++ .../stack/data/model/QuestionsResponse.java | 26 +++++++ .../com/nathansdev/stack/di/ApiModule.java | 67 +++++++++++++++++++ .../com/nathansdev/stack/di/AppComponent.java | 5 +- .../com/nathansdev/stack/di/AppModule.java | 2 + .../stack/home/HomeActivityModule.java | 10 +++ .../{ => home}/adapter/QuestionsAdapter.java | 10 ++- .../stack/home/feed/FeaturedFeedFragment.java | 22 ++++++ .../stack/home/feed/FeedFragment.java | 35 +++++++++- .../nathansdev/stack/home/feed/FeedView.java | 7 ++ .../stack/home/feed/FeedViewPresenter.java | 8 +++ .../home/feed/FeedViewPresenterImpl.java | 17 +++++ .../home/feed/InterestingFeedFragment.java | 17 +++++ app/src/main/res/layout/fragment_feed.xml | 2 +- 18 files changed, 337 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/nathansdev/stack/data/model/MyAdapterFactory.java create mode 100644 app/src/main/java/com/nathansdev/stack/di/ApiModule.java rename app/src/main/java/com/nathansdev/stack/{ => home}/adapter/QuestionsAdapter.java (70%) create mode 100644 app/src/main/java/com/nathansdev/stack/home/feed/FeedView.java create mode 100644 app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java create mode 100644 app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java diff --git a/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java b/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java index 9090703..f1a9196 100644 --- a/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java +++ b/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java @@ -11,7 +11,7 @@ public class RetrofitClient { private static Retrofit retrofit; private static final String BASE_URL = "https://api.stackexchange.com"; - public static Retrofit getRetrofitInstance() { + public static Retrofit getRetrofit() { OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); if (BuildConfig.DEBUG) { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); diff --git a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java index 8b7b67c..0e5a65f 100644 --- a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java +++ b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java @@ -1,6 +1,7 @@ package com.nathansdev.stack.data.api; public interface StackExchangeApi { + String BASE_URL = "/api/v1/events/search.json"; String API_V1_QUESTIONS_JSON = "/api/v1/events/search.json"; String SORT = "sort"; String ORDER = "order"; diff --git a/app/src/main/java/com/nathansdev/stack/data/model/MyAdapterFactory.java b/app/src/main/java/com/nathansdev/stack/data/model/MyAdapterFactory.java new file mode 100644 index 0000000..35c2fa2 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/data/model/MyAdapterFactory.java @@ -0,0 +1,15 @@ +package com.nathansdev.stack.data.model; + +import com.ryanharter.auto.value.moshi.MoshiAdapterFactory; +import com.squareup.moshi.JsonAdapter; + +/** + * MyAdapterFactory common adapter factory for all models. + */ +@MoshiAdapterFactory +public abstract class MyAdapterFactory implements JsonAdapter.Factory { + + public static JsonAdapter.Factory create() { + return new AutoValueMoshi_MyAdapterFactory(); + } +} diff --git a/app/src/main/java/com/nathansdev/stack/data/model/Owner.java b/app/src/main/java/com/nathansdev/stack/data/model/Owner.java index 17273ef..c753d3c 100644 --- a/app/src/main/java/com/nathansdev/stack/data/model/Owner.java +++ b/app/src/main/java/com/nathansdev/stack/data/model/Owner.java @@ -1,9 +1,44 @@ package com.nathansdev.stack.data.model; import android.os.Parcelable; +import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; +import com.squareup.moshi.Json; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; @AutoValue public abstract class Owner implements Parcelable { + @Nullable + @Json(name = "reputation") + public abstract Long reputation(); + + @Nullable + @Json(name = "user_id") + public abstract Long id(); + + @Nullable + @Json(name = "user_type") + public abstract String type(); + + @Nullable + @Json(name = "accept_rate") + public abstract Long rate(); + + @Nullable + @Json(name = "profile_image") + public abstract String image(); + + @Nullable + @Json(name = "display_name") + public abstract String name(); + + @Nullable + @Json(name = "link") + public abstract String link(); + + public static JsonAdapter ownerJsonAdapter(Moshi moshi) { + return new AutoValue_Owner.MoshiJsonAdapter(moshi); + } } diff --git a/app/src/main/java/com/nathansdev/stack/data/model/Question.java b/app/src/main/java/com/nathansdev/stack/data/model/Question.java index d7595c6..9fd3919 100644 --- a/app/src/main/java/com/nathansdev/stack/data/model/Question.java +++ b/app/src/main/java/com/nathansdev/stack/data/model/Question.java @@ -1,9 +1,70 @@ package com.nathansdev.stack.data.model; import android.os.Parcelable; +import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; +import com.squareup.moshi.Json; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; + +import java.util.List; @AutoValue public abstract class Question implements Parcelable { + @Nullable + @Json(name = "question_id") + public abstract Long id(); + + @Nullable + @Json(name = "title") + public abstract String title(); + + @Nullable + @Json(name = "Owner") + public abstract Owner owner(); + + @Nullable + @Json(name = "is_answered") + public abstract Boolean isAnswered(); + + @Nullable + @Json(name = "view_count") + public abstract Integer viewCount(); + + @Nullable + @Json(name = "answer_count") + public abstract Integer answerCount(); + + @Nullable + @Json(name = "score") + public abstract Integer score(); + + @Nullable + @Json(name = "display_name") + public abstract String name(); + + @Nullable + @Json(name = "link") + public abstract String link(); + + @Nullable + @Json(name = "last_activity_date") + public abstract String updatedAt(); + + @Nullable + @Json(name = "creation_date") + public abstract String createdAt(); + + @Nullable + @Json(name = "last_edit_date") + public abstract String editedAt(); + + @Nullable + @Json(name = "tags") + public abstract List tags(); + + public static JsonAdapter questionJsonAdapter(Moshi moshi) { + return new AutoValue_Question.MoshiJsonAdapter(moshi); + } } diff --git a/app/src/main/java/com/nathansdev/stack/data/model/QuestionsResponse.java b/app/src/main/java/com/nathansdev/stack/data/model/QuestionsResponse.java index f6ffbf3..201ce8b 100644 --- a/app/src/main/java/com/nathansdev/stack/data/model/QuestionsResponse.java +++ b/app/src/main/java/com/nathansdev/stack/data/model/QuestionsResponse.java @@ -1,9 +1,35 @@ package com.nathansdev.stack.data.model; import android.os.Parcelable; +import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; +import com.squareup.moshi.Json; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; + +import java.util.List; @AutoValue public abstract class QuestionsResponse implements Parcelable { + + @Nullable + @Json(name = "items") + public abstract List questions(); + + @Nullable + @Json(name = "has_more") + public abstract Boolean hasMore(); + + @Nullable + @Json(name = "quota_max") + public abstract Long max(); + + @Nullable + @Json(name = "quota_remaining") + public abstract Long remaining(); + + public static JsonAdapter questionsResponseJsonAdapter(Moshi moshi) { + return new AutoValue_QuestionsResponse.MoshiJsonAdapter(moshi); + } } diff --git a/app/src/main/java/com/nathansdev/stack/di/ApiModule.java b/app/src/main/java/com/nathansdev/stack/di/ApiModule.java new file mode 100644 index 0000000..57225be --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/di/ApiModule.java @@ -0,0 +1,67 @@ +package com.nathansdev.stack.di; + +import com.nathansdev.stack.data.api.StackExchangeApi; +import com.nathansdev.stack.data.model.MyAdapterFactory; +import com.squareup.moshi.Moshi; + +import java.io.IOException; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.moshi.MoshiConverterFactory; + +@Module +public class ApiModule { + private static final String BASE_URL = "https://api.stackexchange.com"; + + @Provides + @Singleton + Moshi provideMoshi() { + return new Moshi.Builder().add(MyAdapterFactory.create()).build(); + } + + @Provides + @Singleton + Retrofit provideCall() { + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .addInterceptor(new Interceptor() { + @Override + public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException { + Request original = chain.request(); + + // Customize the request + Request request = original.newBuilder() + .header("Content-Type", "application/json") + .build(); + okhttp3.Response response = chain.proceed(request); + response.cacheResponse(); + // Customize or return the response + return response; + } + }) + .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) + .build(); + + return new Retrofit.Builder() + .client(okHttpClient) + .baseUrl(BASE_URL) + .addConverterFactory(MoshiConverterFactory.create()) + .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .build(); + } + + @Provides + @Singleton + public StackExchangeApi providesApi( + Retrofit retrofit) { + return retrofit.create(StackExchangeApi.class); + } +} diff --git a/app/src/main/java/com/nathansdev/stack/di/AppComponent.java b/app/src/main/java/com/nathansdev/stack/di/AppComponent.java index 3e51200..c36acb7 100644 --- a/app/src/main/java/com/nathansdev/stack/di/AppComponent.java +++ b/app/src/main/java/com/nathansdev/stack/di/AppComponent.java @@ -3,6 +3,7 @@ import android.app.Application; import com.nathansdev.stack.StackQueryApp; +import com.squareup.moshi.Moshi; import javax.inject.Singleton; @@ -15,7 +16,7 @@ */ @Singleton -@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, ActivityBuilderModule.class}) +@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, ApiModule.class, ActivityBuilderModule.class}) public interface AppComponent { /** @@ -31,4 +32,6 @@ interface Builder { } void inject(StackQueryApp app); + + Moshi moshi(); } diff --git a/app/src/main/java/com/nathansdev/stack/di/AppModule.java b/app/src/main/java/com/nathansdev/stack/di/AppModule.java index 8c9dd7a..623a68a 100755 --- a/app/src/main/java/com/nathansdev/stack/di/AppModule.java +++ b/app/src/main/java/com/nathansdev/stack/di/AppModule.java @@ -4,7 +4,9 @@ import android.content.Context; import com.nathansdev.stack.AppPreferences; +import com.nathansdev.stack.data.model.MyAdapterFactory; import com.nathansdev.stack.rxevent.RxEventBus; +import com.squareup.moshi.Moshi; import javax.inject.Singleton; diff --git a/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java b/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java index e370c98..12f9437 100755 --- a/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java +++ b/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java @@ -1,14 +1,19 @@ package com.nathansdev.stack.home; +import com.nathansdev.stack.di.PerActivity; import com.nathansdev.stack.di.PerFragment; import com.nathansdev.stack.home.feed.FeaturedFeedFragment; +import com.nathansdev.stack.home.feed.FeedView; +import com.nathansdev.stack.home.feed.FeedViewPresenter; +import com.nathansdev.stack.home.feed.FeedViewPresenterImpl; import com.nathansdev.stack.home.feed.HotFeedFragment; import com.nathansdev.stack.home.feed.InterestingFeedFragment; import com.nathansdev.stack.home.feed.MonthLyFeedFragment; import com.nathansdev.stack.home.feed.SelfFragment; import com.nathansdev.stack.home.feed.WeekLyFeedFragment; +import dagger.Binds; import dagger.Module; import dagger.android.ContributesAndroidInjector; @@ -40,4 +45,9 @@ public abstract class HomeActivityModule { @PerFragment @ContributesAndroidInjector() abstract SelfFragment providePSelfFragmentFactory(); + + @PerActivity + @Binds + abstract FeedViewPresenter provideFeedViewPresenter(FeedViewPresenterImpl + feedViewPresenterImpl); } diff --git a/app/src/main/java/com/nathansdev/stack/adapter/QuestionsAdapter.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java similarity index 70% rename from app/src/main/java/com/nathansdev/stack/adapter/QuestionsAdapter.java rename to app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java index 6a91677..eff5fe4 100644 --- a/app/src/main/java/com/nathansdev/stack/adapter/QuestionsAdapter.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java @@ -1,10 +1,14 @@ -package com.nathansdev.stack.adapter; +package com.nathansdev.stack.home.adapter; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; +import com.nathansdev.stack.rxevent.RxEventBus; + public class QuestionsAdapter extends RecyclerView.Adapter { + private RxEventBus eventBus; + @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { @@ -20,4 +24,8 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) public int getItemCount() { return 0; } + + public void setEventBus(RxEventBus eventBus) { + this.eventBus = eventBus; + } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java index 3cddff9..61f46fc 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java @@ -7,6 +7,8 @@ import android.view.View; import android.view.ViewGroup; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; + import javax.inject.Inject; public class FeaturedFeedFragment extends FeedFragment { @@ -31,4 +33,24 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c protected void setUpView(View view) { } + + @Override + protected void attachPresenter() { + + } + + @Override + protected QuestionsAdapter getAdapter() { + return new QuestionsAdapter(); + } + + @Override + protected void setRefreshLayout(boolean refresh) { + + } + + @Override + public void onQuestionsLoaded() { + + } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java index 7d62969..b5ef0df 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java @@ -3,6 +3,9 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -10,14 +13,28 @@ import com.nathansdev.stack.R; import com.nathansdev.stack.base.BaseFragment; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; +import com.nathansdev.stack.rxevent.RxEventBus; + +import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; -public abstract class FeedFragment extends BaseFragment { +public abstract class FeedFragment extends BaseFragment implements FeedView { @BindView(R.id.feeds_recycler) RecyclerView recyclerView; + @BindView(R.id.feeds_refresh_layout) + SwipeRefreshLayout refreshLayout; + + @Inject + RxEventBus eventBus; + @Inject + FeedViewPresenter presenter; + + private LinearLayoutManager layoutManager; + private QuestionsAdapter adapter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -29,6 +46,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_feed, container, false); setViewUnbinder(ButterKnife.bind(this, rootView)); + presenter.onAttach(this); attachPresenter(); return rootView; } @@ -40,9 +58,24 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { @Override protected void setUpView(View view) { + adapter = new QuestionsAdapter(); + layoutManager = new LinearLayoutManager(getActivity()); + layoutManager.setOrientation(LinearLayoutManager.VERTICAL); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setItemAnimator(new DefaultItemAnimator()); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }); + adapter.setEventBus(eventBus); } protected abstract void attachPresenter(); + protected abstract QuestionsAdapter getAdapter(); + + protected abstract void setRefreshLayout(boolean refresh); } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedView.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedView.java new file mode 100644 index 0000000..0a65152 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedView.java @@ -0,0 +1,7 @@ +package com.nathansdev.stack.home.feed; + +import com.nathansdev.stack.base.MvpView; + +public interface FeedView extends MvpView { + void onQuestionsLoaded(); +} diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java new file mode 100644 index 0000000..fc2bbe6 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java @@ -0,0 +1,8 @@ +package com.nathansdev.stack.home.feed; + +import com.nathansdev.stack.base.MvpPresenter; +import com.nathansdev.stack.base.MvpView; + +public interface FeedViewPresenter extends MvpPresenter { + void loadQuestions(); +} diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java new file mode 100644 index 0000000..8c58a63 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java @@ -0,0 +1,17 @@ +package com.nathansdev.stack.home.feed; + +import com.nathansdev.stack.base.BasePresenter; + +import javax.inject.Inject; + +public class FeedViewPresenterImpl extends BasePresenter implements FeedViewPresenter { + @Inject + FeedViewPresenterImpl() { + + } + + @Override + public void loadQuestions() { + + } +} diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java index b3a9576..1c1bfd3 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java @@ -2,6 +2,8 @@ import android.view.View; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; + import javax.inject.Inject; public class InterestingFeedFragment extends FeedFragment { @@ -25,4 +27,19 @@ protected void setUpView(View view) { protected void attachPresenter() { } + + @Override + protected QuestionsAdapter getAdapter() { + return new QuestionsAdapter(); + } + + @Override + protected void setRefreshLayout(boolean refresh) { + + } + + @Override + public void onQuestionsLoaded() { + + } } diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index 9b68f5d..f6b198d 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -1,6 +1,6 @@ From 37bcc109d4879c958daaa25d156fbca97c1dc617 Mon Sep 17 00:00:00 2001 From: Sabari Date: Tue, 26 Mar 2019 13:40:26 +0530 Subject: [PATCH 02/18] questions base type api call implemented --- .../com/nathansdev/stack/AppConstants.java | 13 ++++ ...RetrofitClient.java => NetworkClient.java} | 6 +- .../stack/data/api/StackExchangeApi.java | 30 +++++++--- .../com/nathansdev/stack/di/ApiModule.java | 12 ++-- .../DisposableObserverCallbackWrapper.java | 41 +++++++++++++ .../DisposableSubscriberCallbackWrapper.java | 38 ++++++++++++ .../error/SingleObserverCallbackWrapper.java | 31 ++++++++++ .../stack/home/feed/FeaturedFeedFragment.java | 2 +- .../stack/home/feed/FeedFragment.java | 2 + .../stack/home/feed/FeedViewPresenter.java | 6 ++ .../home/feed/FeedViewPresenterImpl.java | 59 ++++++++++++++++++- 11 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/nathansdev/stack/AppConstants.java rename app/src/main/java/com/nathansdev/stack/data/api/{RetrofitClient.java => NetworkClient.java} (86%) create mode 100644 app/src/main/java/com/nathansdev/stack/error/DisposableObserverCallbackWrapper.java create mode 100644 app/src/main/java/com/nathansdev/stack/error/DisposableSubscriberCallbackWrapper.java create mode 100644 app/src/main/java/com/nathansdev/stack/error/SingleObserverCallbackWrapper.java diff --git a/app/src/main/java/com/nathansdev/stack/AppConstants.java b/app/src/main/java/com/nathansdev/stack/AppConstants.java new file mode 100644 index 0000000..a172796 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/AppConstants.java @@ -0,0 +1,13 @@ +package com.nathansdev.stack; + +/** + * App Constants data. + */ +public final class AppConstants { + String VOTES = "votes"; + String ACTIVITY = "activity"; + String HOT = "hot"; + String WEEK = "week"; + String MONTH = "month"; + String SITE = "stackoverflow"; +} diff --git a/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java b/app/src/main/java/com/nathansdev/stack/data/api/NetworkClient.java similarity index 86% rename from app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java rename to app/src/main/java/com/nathansdev/stack/data/api/NetworkClient.java index f1a9196..9271d49 100644 --- a/app/src/main/java/com/nathansdev/stack/data/api/RetrofitClient.java +++ b/app/src/main/java/com/nathansdev/stack/data/api/NetworkClient.java @@ -5,9 +5,10 @@ import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.moshi.MoshiConverterFactory; -public class RetrofitClient { +public class NetworkClient { private static Retrofit retrofit; private static final String BASE_URL = "https://api.stackexchange.com"; @@ -21,8 +22,9 @@ public static Retrofit getRetrofit() { if (retrofit == null) { retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) - .addConverterFactory(MoshiConverterFactory.create()) .client(httpClient.build()) + .addConverterFactory(MoshiConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } return retrofit; diff --git a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java index 0e5a65f..eeedf6d 100644 --- a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java +++ b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java @@ -1,13 +1,29 @@ package com.nathansdev.stack.data.api; + +import com.nathansdev.stack.data.model.QuestionsResponse; + +import io.reactivex.Flowable; +import io.reactivex.Observable; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + public interface StackExchangeApi { - String BASE_URL = "/api/v1/events/search.json"; - String API_V1_QUESTIONS_JSON = "/api/v1/events/search.json"; + String API_V1_QUESTIONS_JSON = "/2.2/questions?"; String SORT = "sort"; + String SITE = "site"; String ORDER = "order"; - String VOTES = "votes"; - String ACTIVITY = "activity"; - String HOT = "hot"; - String WEEK = "week"; - String MONTH = "month"; + + @GET(API_V1_QUESTIONS_JSON) + Observable getQuestionsRx(@Query(SORT) String sort, @Query(SITE) String site, + @Query(ORDER) String order); + + @GET(API_V1_QUESTIONS_JSON) + Flowable getQuestionsFlowable(@Query(SORT) String sort, @Query(SITE) String site, + @Query(ORDER) String order); + + @GET(API_V1_QUESTIONS_JSON) + Call getQuestions(@Query(SORT) String sort, @Query(SITE) String site, + @Query(ORDER) String order); } diff --git a/app/src/main/java/com/nathansdev/stack/di/ApiModule.java b/app/src/main/java/com/nathansdev/stack/di/ApiModule.java index 57225be..0e68e0c 100644 --- a/app/src/main/java/com/nathansdev/stack/di/ApiModule.java +++ b/app/src/main/java/com/nathansdev/stack/di/ApiModule.java @@ -5,6 +5,7 @@ import com.squareup.moshi.Moshi; import java.io.IOException; +import java.util.concurrent.TimeUnit; import javax.inject.Singleton; @@ -15,7 +16,7 @@ import okhttp3.Request; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; -import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.moshi.MoshiConverterFactory; @Module @@ -30,7 +31,7 @@ Moshi provideMoshi() { @Provides @Singleton - Retrofit provideCall() { + Retrofit provideCall(Moshi moshi) { OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override @@ -47,14 +48,17 @@ public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException { return response; } }) + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); return new Retrofit.Builder() .client(okHttpClient) .baseUrl(BASE_URL) - .addConverterFactory(MoshiConverterFactory.create()) - .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } diff --git a/app/src/main/java/com/nathansdev/stack/error/DisposableObserverCallbackWrapper.java b/app/src/main/java/com/nathansdev/stack/error/DisposableObserverCallbackWrapper.java new file mode 100644 index 0000000..76da9ff --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/error/DisposableObserverCallbackWrapper.java @@ -0,0 +1,41 @@ +package com.nathansdev.stack.error; + +import com.nathansdev.stack.base.MvpView; + +import java.lang.ref.WeakReference; + +import io.reactivex.observers.DisposableObserver; + +/** + * DisposableObserver wrapper for handling errors on a single place + */ +public abstract class DisposableObserverCallbackWrapper extends DisposableObserver { + + //MvpView is just a reference of a View in MVP + private WeakReference weakReference; + + public DisposableObserverCallbackWrapper(MvpView view) { + this.weakReference = new WeakReference<>(view); + } + + protected abstract void onSuccess(T t); + + protected abstract void onNextAction(T t); + + protected abstract void onCompleted(); + + @Override + public void onNext(T t) { + onNextAction(t); + } + + @Override + public void onError(Throwable e) { + MvpView view = weakReference.get(); + } + + @Override + public void onComplete() { + onCompleted(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nathansdev/stack/error/DisposableSubscriberCallbackWrapper.java b/app/src/main/java/com/nathansdev/stack/error/DisposableSubscriberCallbackWrapper.java new file mode 100644 index 0000000..f22e434 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/error/DisposableSubscriberCallbackWrapper.java @@ -0,0 +1,38 @@ +package com.nathansdev.stack.error; + +import com.nathansdev.stack.base.MvpView; + +import java.lang.ref.WeakReference; + +import io.reactivex.subscribers.DisposableSubscriber; +import timber.log.Timber; + +public abstract class DisposableSubscriberCallbackWrapper extends DisposableSubscriber { + + //MvpView is just a reference of a View in MVP + private WeakReference weakReference; + + public DisposableSubscriberCallbackWrapper(MvpView view) { + this.weakReference = new WeakReference<>(view); + } + + protected abstract void onNextAction(T t); + + protected abstract void onCompleted(); + + @Override + public void onNext(T t) { + onNextAction(t); + } + + @Override + public void onError(Throwable t) { + Timber.e(t); + MvpView view = weakReference.get(); + } + + @Override + public void onComplete() { + onCompleted(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nathansdev/stack/error/SingleObserverCallbackWrapper.java b/app/src/main/java/com/nathansdev/stack/error/SingleObserverCallbackWrapper.java new file mode 100644 index 0000000..13336a7 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/error/SingleObserverCallbackWrapper.java @@ -0,0 +1,31 @@ +package com.nathansdev.stack.error; + +import com.nathansdev.stack.base.MvpView; + +import java.lang.ref.WeakReference; + +import io.reactivex.observers.DisposableSingleObserver; + +public abstract class SingleObserverCallbackWrapper extends DisposableSingleObserver { + + //MvpView is just a reference of a View in MVP + private WeakReference weakReference; + + public SingleObserverCallbackWrapper(MvpView view) { + this.weakReference = new WeakReference<>(view); + } + + @Override + public void onSuccess(T t) { + duringSuccess(t); + } + + @Override + public void onError(Throwable e) { + MvpView view = weakReference.get(); + } + + protected abstract void duringSuccess(T t); + + protected abstract void duringFailure(String message); +} \ No newline at end of file diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java index 61f46fc..943953d 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java @@ -31,7 +31,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c @Override protected void setUpView(View view) { - + super.setUpView(view); } @Override diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java index b5ef0df..e8aea65 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java @@ -71,6 +71,8 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } }); adapter.setEventBus(eventBus); + presenter.init(); + presenter.loadQuestions(); } protected abstract void attachPresenter(); diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java index fc2bbe6..fb68059 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java @@ -4,5 +4,11 @@ import com.nathansdev.stack.base.MvpView; public interface FeedViewPresenter extends MvpPresenter { + void init(); + void loadQuestions(); + + void loadNextPage(); + + void cleanUp(); } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java index 8c58a63..952fa89 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java @@ -1,17 +1,74 @@ package com.nathansdev.stack.home.feed; import com.nathansdev.stack.base.BasePresenter; +import com.nathansdev.stack.data.api.StackExchangeApi; +import com.nathansdev.stack.data.model.QuestionsResponse; +import com.nathansdev.stack.error.DisposableSubscriberCallbackWrapper; + +import org.reactivestreams.Publisher; import javax.inject.Inject; +import io.reactivex.Flowable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Function; +import io.reactivex.processors.PublishProcessor; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + public class FeedViewPresenterImpl extends BasePresenter implements FeedViewPresenter { + + private StackExchangeApi api; + private PublishProcessor questionsSubject = PublishProcessor.create(); + private CompositeDisposable disposables = new CompositeDisposable(); + @Inject - FeedViewPresenterImpl() { + FeedViewPresenterImpl(StackExchangeApi api) { + this.api = api; + } + + @Override + public void init() { + Disposable disposable = questionsSubject + .onBackpressureDrop() + .concatMap((Function>) page -> + getObservable()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(new DisposableSubscriberCallbackWrapper(getMvpView()) { + + @Override + protected void onNextAction(QuestionsResponse alertsAdapterRows) { + Timber.d("alert rows %s", alertsAdapterRows); + } + @Override + protected void onCompleted() { + + } + }); + disposables.add(disposable); } @Override public void loadQuestions() { + Timber.d("loadQuestions"); + questionsSubject.onNext(0L); + } + + @Override + public void loadNextPage() { + Timber.d("loadNextPage"); + } + + @Override + public void cleanUp() { + disposables.clear(); + } + private Flowable getObservable() { + return api.getQuestionsFlowable("activity", "stackoverflow", "desc") + .subscribeOn(Schedulers.io()); } } From 68dde056f1ae48105d2158511ed5719b732a0417 Mon Sep 17 00:00:00 2001 From: Sabari Date: Tue, 26 Mar 2019 16:24:52 +0530 Subject: [PATCH 03/18] listing questions in recyclerview impelemented --- app/build.gradle | 6 + .../stack/StackQueryAppGlideModule.java | 21 ++ .../nathansdev/stack/data/model/Question.java | 8 +- .../nathansdev/stack/home/HomeActivity.java | 1 + .../stack/home/adapter/QuestionsAdapter.java | 190 +++++++++++++++++- .../home/adapter/QuestionsAdapterRow.java | 142 +++++++++++++ .../adapter/QuestionsAdapterRowDataSet.java | 134 ++++++++++++ .../stack/home/feed/FeedFragment.java | 11 +- .../stack/home/feed/FeedViewPresenter.java | 3 +- .../home/feed/FeedViewPresenterImpl.java | 25 ++- .../stack/home/feed/HotFeedFragment.java | 24 ++- .../home/feed/InterestingFeedFragment.java | 4 +- .../stack/home/feed/MonthLyFeedFragment.java | 24 ++- .../stack/home/feed/WeekLyFeedFragment.java | 24 ++- .../com/nathansdev/stack/utils/Utils.java | 37 ++++ .../drawable/ic_account_circle_black_24dp.xml | 7 +- app/src/main/res/layout/activity_home.xml | 11 +- .../res/layout/adapter_item_load_more.xml | 24 +++ .../main/res/layout/adapter_item_question.xml | 54 +++++ app/src/main/res/values/colors.xml | 10 +- app/src/main/res/values/dimens.xml | 9 + app/src/main/res/values/strings.xml | 3 + 22 files changed, 743 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/com/nathansdev/stack/StackQueryAppGlideModule.java create mode 100644 app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java create mode 100644 app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java create mode 100644 app/src/main/java/com/nathansdev/stack/utils/Utils.java create mode 100644 app/src/main/res/layout/adapter_item_load_more.xml create mode 100644 app/src/main/res/layout/adapter_item_question.xml diff --git a/app/build.gradle b/app/build.gradle index 6cce5f7..b04fa61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,6 +84,12 @@ dependencies { annotationProcessor 'com.ryanharter.auto.value:auto-value-moshi:0.4.4' compileOnly 'com.ryanharter.auto.value:auto-value-moshi-annotations:0.4.4' + implementation "com.github.marlonlom:timeago:${"4.0.1"}" + + // do not change glide library order + implementation 'com.github.bumptech.glide:glide:4.8.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' + testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/java/com/nathansdev/stack/StackQueryAppGlideModule.java b/app/src/main/java/com/nathansdev/stack/StackQueryAppGlideModule.java new file mode 100644 index 0000000..4102d48 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/StackQueryAppGlideModule.java @@ -0,0 +1,21 @@ +package com.nathansdev.stack; + +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.module.AppGlideModule; + +/** + * Glide module for vibe app. + */ +@GlideModule +public class StackQueryAppGlideModule extends AppGlideModule { + + /** + * Disabling manifest parsing to avoid adding same module twice. + * + * @return true or false. + */ + @Override + public boolean isManifestParsingEnabled() { + return false; + } +} diff --git a/app/src/main/java/com/nathansdev/stack/data/model/Question.java b/app/src/main/java/com/nathansdev/stack/data/model/Question.java index 9fd3919..152d12d 100644 --- a/app/src/main/java/com/nathansdev/stack/data/model/Question.java +++ b/app/src/main/java/com/nathansdev/stack/data/model/Question.java @@ -21,7 +21,7 @@ public abstract class Question implements Parcelable { public abstract String title(); @Nullable - @Json(name = "Owner") + @Json(name = "owner") public abstract Owner owner(); @Nullable @@ -50,15 +50,15 @@ public abstract class Question implements Parcelable { @Nullable @Json(name = "last_activity_date") - public abstract String updatedAt(); + public abstract Long updatedAt(); @Nullable @Json(name = "creation_date") - public abstract String createdAt(); + public abstract Long createdAt(); @Nullable @Json(name = "last_edit_date") - public abstract String editedAt(); + public abstract Long editedAt(); @Nullable @Json(name = "tags") diff --git a/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java b/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java index f638717..3a0b071 100644 --- a/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java +++ b/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java @@ -123,6 +123,7 @@ public void onPageScrollStateChanged(int i) { }); tableLayout.setupWithViewPager(viewPager); + tableLayout.setTabMode(TabLayout.MODE_SCROLLABLE); viewPager.setCurrentItem(0); } diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java index eff5fe4..f68fe71 100644 --- a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java @@ -2,30 +2,208 @@ import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.nathansdev.stack.R; import com.nathansdev.stack.rxevent.RxEventBus; +import com.nathansdev.stack.utils.Utils; + +import java.util.ArrayList; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class QuestionsAdapter extends RecyclerView.Adapter { + private static final int VIEW_TYPE_QUESTION = 0x01; + private static final int VIEW_TYPE_LOADING = 0x02; + private static final int VIEW_TYPE_LOADMORE = 0x03; -public class QuestionsAdapter extends RecyclerView.Adapter { private RxEventBus eventBus; + private ArrayList viewHolders = new ArrayList<>(); + private QuestionsAdapterRowDataSet dataSet; @NonNull @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { - return null; + public QuestionsAdapterVH onCreateViewHolder(ViewGroup parent, int viewType) { + QuestionsAdapterVH holder = null; + switch (viewType) { + case VIEW_TYPE_QUESTION: + holder = QuestionsAdapterVH.ofQuestion(parent); + break; + case VIEW_TYPE_LOADMORE: + holder = QuestionsAdapterVH.ofLoadMore(parent); + break; + default: + break; + } + if (holder != null) { + viewHolders.add(holder); + } + return holder; } @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { - + public void onBindViewHolder(QuestionsAdapterVH holder, int position) { + holder.bind(dataSet.get(position), eventBus); } @Override public int getItemCount() { - return 0; + return dataSet.size(); + } + + @Override + public int getItemViewType(int position) { + QuestionsAdapterRow row = dataSet.get(position); + if (row.isTypeQuestion()) { + return VIEW_TYPE_QUESTION; + } else if (row.isTypeLoadMore()) { + return VIEW_TYPE_LOADMORE; + } + return RecyclerView.NO_POSITION; + } + + /** + * Set data to the list. + * + * @param adapterRowDataSet group adapter item/row. + */ + public void setData(QuestionsAdapterRowDataSet adapterRowDataSet) { + this.dataSet = adapterRowDataSet; } public void setEventBus(RxEventBus eventBus) { this.eventBus = eventBus; } + + /** + * Releasing resources. + */ + public void handleDestroy() { + dataSet.clearDataSet(); + for (QuestionsAdapterVH holder : viewHolders) { + holder.destroy(); + } + viewHolders.clear(); + } + + /** + * abstract class for feeds related view holder. + */ + abstract static class QuestionsAdapterVH extends RecyclerView.ViewHolder { + + /** + * Initialize constructor with item view. + * + * @param itemView item view / row view. + */ + QuestionsAdapterVH(View itemView) { + super(itemView); + } + + static QuestionsAdapterVH ofQuestion(ViewGroup parent) { + return QuestionVH.create(parent); + } + + static QuestionsAdapterVH ofLoadMore(ViewGroup parent) { + return LoadMoreVH.create(parent); + } + + abstract void bind(QuestionsAdapterRow row, RxEventBus eventBus); + + abstract void destroy(); + } + + + /** + * Binds groups name and avatar in the list. + */ + public static class QuestionVH extends QuestionsAdapterVH { + + @BindView(R.id.tv_owner_name) + TextView ownerName; + @BindView(R.id.tv_question_posted_at) + TextView timeStamp; + @BindView(R.id.tv_question_title) + TextView title; + @BindView(R.id.iv_owner_profile) + ImageView avatar; + + /** + * Initialize constructor with item view. + * + * @param itemView item view / row view. + */ + QuestionVH(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + /** + * Inflate the user skillLayout to view. + * + * @param parent view group. + * @return users view holder. + */ + public static QuestionVH create(ViewGroup parent) { + View rootView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.adapter_item_question, parent, false); + return new QuestionVH(rootView); + } + + @Override + void bind(final QuestionsAdapterRow row, final RxEventBus eventBus) { + Utils.loadRoundImage(itemView.getContext(), row.imageUrl(), avatar); + ownerName.setText(row.name()); + title.setText(row.title()); + timeStamp.setText(String.valueOf(row.timeStamp())); + } + + @Override + void destroy() { + + } + } + + /** + * Binds groups name and avatar in the list. + */ + public static class LoadMoreVH extends QuestionsAdapterVH { + + /** + * Initialize constructor with item view. + * + * @param itemView item view / row view. + */ + LoadMoreVH(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + /** + * Inflate the user skillLayout to view. + * + * @param parent view group. + * @return users view holder. + */ + public static LoadMoreVH create(ViewGroup parent) { + View rootView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.adapter_item_load_more, parent, false); + return new LoadMoreVH(rootView); + } + + @Override + void bind(QuestionsAdapterRow row, final RxEventBus eventBus) { + + } + + @Override + void destroy() { + + } + } } diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java new file mode 100644 index 0000000..90d57a2 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java @@ -0,0 +1,142 @@ +package com.nathansdev.stack.home.adapter; + +import android.os.Parcelable; +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; +import com.nathansdev.stack.data.model.Question; + +@AutoValue +public abstract class QuestionsAdapterRow implements Parcelable { + private static final int TYPE_QUESTION = 0x01; + private static final int TYPE_LOAD_MORE = 0x02; + private static final int TYPE_LOADING = 0x03; + + /** + * Builds along with already built row. + * + * @return update row/model. + */ + public abstract Builder toBuilder(); + + /** + * Returns to builder. + * + * @return builder class. + */ + public Builder withBuild() { + return toBuilder(); + } + + public boolean isTypeQuestion() { + return type() == TYPE_QUESTION; + } + + public boolean isTypeLoadMore() { + return type() == TYPE_LOAD_MORE; + } + + public boolean isTypeLoading() { + return type() == TYPE_LOADING; + } + + public static Builder builder() { + return new AutoValue_QuestionsAdapterRow.Builder(); + } + + public static QuestionsAdapterRow ofQuestion(Question question) { + return builder().type(TYPE_QUESTION).imageUrl(question.owner().image()).question(question) + .name(question.owner().name()).timeStamp(question.updatedAt()) + .title(question.title()).votes(question.score()).build(); + } + + /** + * Compare the new item with existing item. + * + * @param r2 row. + * @return -1 if new item should be top of existing item, 0 if items are same and + * 1 if new item should below the existing item. + */ + public int compare(QuestionsAdapterRow r2) { + int comparedValue = 1; + if (this.isTypeLoadMore() && r2.isTypeLoadMore()) { + return 0; + } else if (this.isTypeQuestion() && r2.isTypeQuestion()) { + return 1; + } else if (this.isTypeQuestion() && r2.isTypeLoadMore()) { + return 1; + } else if (this.isTypeLoadMore() && r2.isTypeQuestion()) { + return -1; + } + return comparedValue; + } + + /** + * Compare two item with hashcode. + * + * @param newItem new item. + * @return true if items are same else false. + */ + public boolean areContentsTheSame(QuestionsAdapterRow newItem) { + if (this.isTypeQuestion() && newItem.isTypeQuestion()) { + return this.question().equals(newItem.question()); + } else if (isTypeLoadMore() && newItem.isTypeLoadMore()) { + return this.equals(newItem); + } + return false; + } + + /** + * Compare two items for the same item with particular attribute value. + * + * @param newItem new item. + * @return true if items are same else false. + */ + public boolean areItemsTheSame(QuestionsAdapterRow newItem) { + if (isTypeQuestion() && newItem.isTypeQuestion()) { + return this.question().id().longValue() == newItem.question().id().longValue(); + } else if (isTypeLoadMore() && newItem.isTypeLoadMore()) { + return true; + } + return false; + } + + @Nullable + public abstract Question question(); + + @Nullable + public abstract String title(); + + @Nullable + public abstract Long timeStamp(); + + @Nullable + public abstract String name(); + + @Nullable + public abstract String imageUrl(); + + @Nullable + public abstract Integer votes(); + + public abstract int type(); + + @AutoValue.Builder + public abstract static class Builder { + public abstract QuestionsAdapterRow build(); + + public abstract Builder type(int t); + + public abstract Builder question(Question t); + + public abstract Builder title(String t); + + public abstract Builder timeStamp(Long t); + + public abstract Builder name(String t); + + public abstract Builder imageUrl(String t); + + public abstract Builder votes(Integer t); + } +} diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java new file mode 100644 index 0000000..d0435f3 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java @@ -0,0 +1,134 @@ +package com.nathansdev.stack.home.adapter; + +import android.support.v7.util.SortedList; +import android.support.v7.widget.util.SortedListAdapterCallback; + +import java.util.ArrayList; +import java.util.List; + +public class QuestionsAdapterRowDataSet extends SortedListAdapterCallback { + + private SortedList sortedList; + + /** + * Creates a {@link SortedList.Callback} that will forward data change events to the provided + * Adapter. + * + * @param adapter The Adapter instance which should receive events from the SortedList. + */ + public QuestionsAdapterRowDataSet(QuestionsAdapter adapter, ArrayList data) { + super(adapter); + sortedList = new SortedList<>(QuestionsAdapterRow.class, this); + if (data != null && !data.isEmpty()) { + sortedList.addAll(data); + } + } + + public static QuestionsAdapterRowDataSet createWithInitialData(QuestionsAdapter adapter, + ArrayList data) { + return new QuestionsAdapterRowDataSet(adapter, data); + } + + public static QuestionsAdapterRowDataSet createWithEmptyData(QuestionsAdapter adapter) { + return new QuestionsAdapterRowDataSet(adapter, null); + } + + @Override + public int compare(QuestionsAdapterRow o1, QuestionsAdapterRow o2) { + return o1.compare(o2); + } + + @Override + public boolean areContentsTheSame(QuestionsAdapterRow oldItem, QuestionsAdapterRow newItem) { + return oldItem.areContentsTheSame(newItem); + } + + @Override + public boolean areItemsTheSame(QuestionsAdapterRow item1, QuestionsAdapterRow item2) { + return item1.areItemsTheSame(item2); + } + + /** + * Add all item to the sorted list. + * + * @param rows adapter row. + */ + public void addAllRows(List rows) { + for (QuestionsAdapterRow row : rows) { + sortedList.add(row); + } + } + + /** + * Add single item to the sorted list. + * + * @param row adapter row. + */ + public void addRow(QuestionsAdapterRow row) { + sortedList.add(row); + } + + /** + * clear data for the sorted list. + */ + public void clearDataSet() { + sortedList.clear(); + } + + /** + * return the size of sorted list. + * + * @return size. + */ + public int size() { + return sortedList.size(); + } + + /** + * verify whether sorted list is empty. + * + * @return boolean. + */ + public boolean isEmpty() { + return sortedList.size() == 0; + } + + /** + * provide the particular item from the list based on pos. + * + * @param pos position in the list. + * @return item. + */ + public QuestionsAdapterRow get(int pos) { + return sortedList.get(pos); + } + + /** + * Check for load more row type available in sorted list. + * + * @return true if present else false. + */ + public boolean containsLoadMore() { + for (int i = 0; i < sortedList.size(); i++) { + if (sortedList.get(0).isTypeLoadMore()) { + return true; + } + } + return false; + } + + /** + * Remove load more row from sorted list. + */ + public void removeLoadMore() { + int index = -1; + for (int i = 0; i < sortedList.size(); i++) { + if (sortedList.get(i).isTypeLoadMore()) { + index = i; + } + } + if (index > -1) { + sortedList.removeItemAt(index); + } + } +} diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java index e8aea65..404e023 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java @@ -14,12 +14,14 @@ import com.nathansdev.stack.R; import com.nathansdev.stack.base.BaseFragment; import com.nathansdev.stack.home.adapter.QuestionsAdapter; +import com.nathansdev.stack.home.adapter.QuestionsAdapterRowDataSet; import com.nathansdev.stack.rxevent.RxEventBus; import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; +import timber.log.Timber; public abstract class FeedFragment extends BaseFragment implements FeedView { @@ -35,6 +37,7 @@ public abstract class FeedFragment extends BaseFragment implements FeedView { private LinearLayoutManager layoutManager; private QuestionsAdapter adapter; + private QuestionsAdapterRowDataSet dataset; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -71,7 +74,13 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } }); adapter.setEventBus(eventBus); - presenter.init(); + if (dataset == null) { + Timber.d("creating new data set"); + dataset = QuestionsAdapterRowDataSet.createWithEmptyData(adapter); + } + adapter.setData(dataset); + recyclerView.setAdapter(adapter); + presenter.init(dataset); presenter.loadQuestions(); } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java index fb68059..d3ca62d 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java @@ -2,9 +2,10 @@ import com.nathansdev.stack.base.MvpPresenter; import com.nathansdev.stack.base.MvpView; +import com.nathansdev.stack.home.adapter.QuestionsAdapterRowDataSet; public interface FeedViewPresenter extends MvpPresenter { - void init(); + void init(QuestionsAdapterRowDataSet dataset); void loadQuestions(); diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java index 952fa89..81f1ed3 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java @@ -2,11 +2,17 @@ import com.nathansdev.stack.base.BasePresenter; import com.nathansdev.stack.data.api.StackExchangeApi; +import com.nathansdev.stack.data.model.Question; import com.nathansdev.stack.data.model.QuestionsResponse; import com.nathansdev.stack.error.DisposableSubscriberCallbackWrapper; +import com.nathansdev.stack.home.adapter.QuestionsAdapterRow; +import com.nathansdev.stack.home.adapter.QuestionsAdapterRowDataSet; import org.reactivestreams.Publisher; +import java.util.ArrayList; +import java.util.List; + import javax.inject.Inject; import io.reactivex.Flowable; @@ -23,6 +29,7 @@ public class FeedViewPresenterImpl extends BasePresenter private StackExchangeApi api; private PublishProcessor questionsSubject = PublishProcessor.create(); private CompositeDisposable disposables = new CompositeDisposable(); + private QuestionsAdapterRowDataSet rowDataSet; @Inject FeedViewPresenterImpl(StackExchangeApi api) { @@ -30,7 +37,8 @@ public class FeedViewPresenterImpl extends BasePresenter } @Override - public void init() { + public void init(QuestionsAdapterRowDataSet dataset) { + this.rowDataSet = dataset; Disposable disposable = questionsSubject .onBackpressureDrop() .concatMap((Function>) page -> @@ -39,8 +47,9 @@ public void init() { .subscribeWith(new DisposableSubscriberCallbackWrapper(getMvpView()) { @Override - protected void onNextAction(QuestionsResponse alertsAdapterRows) { - Timber.d("alert rows %s", alertsAdapterRows); + protected void onNextAction(QuestionsResponse response) { + Timber.d("questions %s", response); + handleQuestionResponse(response); } @Override @@ -51,6 +60,16 @@ protected void onCompleted() { disposables.add(disposable); } + private void handleQuestionResponse(QuestionsResponse response) { + List rows = new ArrayList<>(); + if (response != null && response.questions() != null && !response.questions().isEmpty()) { + for (Question question : response.questions()) { + rows.add(QuestionsAdapterRow.ofQuestion(question)); + } + } + rowDataSet.addAllRows(rows); + } + @Override public void loadQuestions() { Timber.d("loadQuestions"); diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java index 752c83b..4037005 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java @@ -2,11 +2,11 @@ import android.view.View; -import com.nathansdev.stack.base.BaseFragment; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; import javax.inject.Inject; -public class HotFeedFragment extends BaseFragment { +public class HotFeedFragment extends FeedFragment { @Inject public HotFeedFragment() { @@ -20,6 +20,26 @@ public static HotFeedFragment newInstance() { @Override protected void setUpView(View view) { + super.setUpView(view); + } + + @Override + protected void attachPresenter() { + + } + + @Override + protected QuestionsAdapter getAdapter() { + return null; + } + + @Override + protected void setRefreshLayout(boolean refresh) { + + } + + @Override + public void onQuestionsLoaded() { } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java index 1c1bfd3..4805b2a 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java @@ -20,7 +20,7 @@ public static InterestingFeedFragment newInstance() { @Override protected void setUpView(View view) { - + super.setUpView(view); } @Override @@ -30,7 +30,7 @@ protected void attachPresenter() { @Override protected QuestionsAdapter getAdapter() { - return new QuestionsAdapter(); + return null; } @Override diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java index d9efe79..374b1ce 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java @@ -2,11 +2,11 @@ import android.view.View; -import com.nathansdev.stack.base.BaseFragment; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; import javax.inject.Inject; -public class MonthLyFeedFragment extends BaseFragment { +public class MonthLyFeedFragment extends FeedFragment { @Inject public MonthLyFeedFragment() { @@ -20,6 +20,26 @@ public static MonthLyFeedFragment newInstance() { @Override protected void setUpView(View view) { + super.setUpView(view); + } + + @Override + protected void attachPresenter() { + + } + + @Override + protected QuestionsAdapter getAdapter() { + return null; + } + + @Override + protected void setRefreshLayout(boolean refresh) { + + } + + @Override + public void onQuestionsLoaded() { } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java index 3cc23e5..6acecad 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java @@ -2,11 +2,11 @@ import android.view.View; -import com.nathansdev.stack.base.BaseFragment; +import com.nathansdev.stack.home.adapter.QuestionsAdapter; import javax.inject.Inject; -public class WeekLyFeedFragment extends BaseFragment { +public class WeekLyFeedFragment extends FeedFragment { @Inject public WeekLyFeedFragment() { @@ -20,6 +20,26 @@ public static WeekLyFeedFragment newInstance() { @Override protected void setUpView(View view) { + super.setUpView(view); + } + + @Override + protected void attachPresenter() { + + } + + @Override + protected QuestionsAdapter getAdapter() { + return null; + } + + @Override + protected void setRefreshLayout(boolean refresh) { + + } + + @Override + public void onQuestionsLoaded() { } } diff --git a/app/src/main/java/com/nathansdev/stack/utils/Utils.java b/app/src/main/java/com/nathansdev/stack/utils/Utils.java new file mode 100644 index 0000000..3d1c8cc --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/utils/Utils.java @@ -0,0 +1,37 @@ +package com.nathansdev.stack.utils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; +import android.widget.ImageView; + +import com.bumptech.glide.request.target.BitmapImageViewTarget; +import com.nathansdev.stack.GlideApp; +import com.nathansdev.stack.R; + +public class Utils { + /** + * Load round image into imageview. + * + * @param context ui context. + * @param url image url. + * @param iv image view. + */ + public static final void loadRoundImage(final Context context, String url, final ImageView iv) { + GlideApp.with(context).asBitmap() + .load(url) + .placeholder(R.drawable.ic_account_circle_black_24dp) + .error(R.drawable.ic_account_circle_black_24dp) + .centerCrop() + .into(new BitmapImageViewTarget(iv) { + @Override + protected void setResource(Bitmap resource) { + RoundedBitmapDrawable circularBitmapDrawable = + RoundedBitmapDrawableFactory.create(context.getResources(), resource); + circularBitmapDrawable.setCircular(true); + iv.setImageDrawable(circularBitmapDrawable); + } + }); + } +} diff --git a/app/src/main/res/drawable/ic_account_circle_black_24dp.xml b/app/src/main/res/drawable/ic_account_circle_black_24dp.xml index 38d58a8..0d6d80c 100644 --- a/app/src/main/res/drawable/ic_account_circle_black_24dp.xml +++ b/app/src/main/res/drawable/ic_account_circle_black_24dp.xml @@ -1,5 +1,8 @@ - - + diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 27ab6af..68da3bc 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -1,12 +1,13 @@ - @@ -14,6 +15,9 @@ diff --git a/app/src/main/res/layout/adapter_item_load_more.xml b/app/src/main/res/layout/adapter_item_load_more.xml new file mode 100644 index 0000000..10ad469 --- /dev/null +++ b/app/src/main/res/layout/adapter_item_load_more.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_item_question.xml b/app/src/main/res/layout/adapter_item_question.xml new file mode 100644 index 0000000..7466218 --- /dev/null +++ b/app/src/main/res/layout/adapter_item_question.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 85b272f..847568f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,11 @@ - #008577 - #00574B - #D81B60 + #fafafa + #f5f5f5 + #ff9100 + #000000 #FFFFFF + #fafafa + #f5f5f5 + #ff9100 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index cef3abc..3f1fe40 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -4,4 +4,13 @@ 16dp 16dp 8dp + //text size + 12sp + 10.5sp + 14sp + 16sp + 18sp + 22sp + 28sp + 36sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 146adfd..5dc0c53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,9 @@ Tab 3 Settings Hello World from section: %1$d + All Questions + loading… + Image Interesting From 71851e8c451e3abf52c4becdf453049f6a4fedc5 Mon Sep 17 00:00:00 2001 From: Sabari Date: Tue, 26 Mar 2019 20:03:57 +0530 Subject: [PATCH 04/18] Questions page * loadmore implemented. * showing initial loading added. * error layout added in adapter. --- .../com/nathansdev/stack/AppConstants.java | 14 +-- .../stack/data/api/StackExchangeApi.java | 11 +- .../nathansdev/stack/home/HomeActivity.java | 19 +++- .../stack/home/HomeActivityModule.java | 4 +- .../stack/home/HomePagerAdapter.java | 6 +- .../stack/home/adapter/QuestionsAdapter.java | 100 +++++++++++++++++- .../home/adapter/QuestionsAdapterRow.java | 29 +++++ .../adapter/QuestionsAdapterRowDataSet.java | 30 ++++++ ...ragment.java => ActivityFeedFragment.java} | 17 +-- .../stack/home/feed/FeaturedFeedFragment.java | 12 +-- .../stack/home/feed/FeedFragment.java | 66 +++++++++++- .../stack/home/feed/FeedViewPresenter.java | 2 +- .../home/feed/FeedViewPresenterImpl.java | 15 ++- .../stack/home/feed/HotFeedFragment.java | 12 +-- .../stack/home/feed/MonthLyFeedFragment.java | 12 +-- .../stack/home/feed/WeekLyFeedFragment.java | 12 +-- app/src/main/res/layout/activity_home.xml | 6 +- .../main/res/layout/adapter_item_error.xml | 16 +++ .../main/res/layout/adapter_item_loading.xml | 16 +++ app/src/main/res/values/colors.xml | 11 +- app/src/main/res/values/strings.xml | 3 +- app/src/main/res/values/styles.xml | 3 + 22 files changed, 327 insertions(+), 89 deletions(-) rename app/src/main/java/com/nathansdev/stack/home/feed/{InterestingFeedFragment.java => ActivityFeedFragment.java} (55%) create mode 100644 app/src/main/res/layout/adapter_item_error.xml create mode 100644 app/src/main/res/layout/adapter_item_loading.xml diff --git a/app/src/main/java/com/nathansdev/stack/AppConstants.java b/app/src/main/java/com/nathansdev/stack/AppConstants.java index a172796..fe1b877 100644 --- a/app/src/main/java/com/nathansdev/stack/AppConstants.java +++ b/app/src/main/java/com/nathansdev/stack/AppConstants.java @@ -4,10 +4,12 @@ * App Constants data. */ public final class AppConstants { - String VOTES = "votes"; - String ACTIVITY = "activity"; - String HOT = "hot"; - String WEEK = "week"; - String MONTH = "month"; - String SITE = "stackoverflow"; + public static final String VOTES = "votes"; + public static final String ACTIVITY = "activity"; + public static final String HOT = "hot"; + public static final String WEEK = "week"; + public static final String MONTH = "month"; + public static final String DESC = "desc"; + public static final String SITE = "stackoverflow"; + public static final String ARG_FILTER_TYPE = "filterType"; } diff --git a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java index eeedf6d..a27b7d3 100644 --- a/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java +++ b/app/src/main/java/com/nathansdev/stack/data/api/StackExchangeApi.java @@ -14,16 +14,21 @@ public interface StackExchangeApi { String SORT = "sort"; String SITE = "site"; String ORDER = "order"; + String PAGE = "page"; + String PAGE_SIZE = "pagesize"; @GET(API_V1_QUESTIONS_JSON) Observable getQuestionsRx(@Query(SORT) String sort, @Query(SITE) String site, - @Query(ORDER) String order); + @Query(ORDER) String order, @Query(PAGE) String page, + @Query(PAGE_SIZE) String size); @GET(API_V1_QUESTIONS_JSON) Flowable getQuestionsFlowable(@Query(SORT) String sort, @Query(SITE) String site, - @Query(ORDER) String order); + @Query(ORDER) String order, @Query(PAGE) long page, + @Query(PAGE_SIZE) long size); @GET(API_V1_QUESTIONS_JSON) Call getQuestions(@Query(SORT) String sort, @Query(SITE) String site, - @Query(ORDER) String order); + @Query(ORDER) String order, @Query(PAGE) String page, + @Query(PAGE_SIZE) String size); } diff --git a/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java b/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java index 3a0b071..aaf5f26 100644 --- a/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java +++ b/app/src/main/java/com/nathansdev/stack/home/HomeActivity.java @@ -8,11 +8,12 @@ import android.support.v7.widget.Toolbar; import android.util.Pair; +import com.nathansdev.stack.AppConstants; import com.nathansdev.stack.R; import com.nathansdev.stack.base.BaseActivity; +import com.nathansdev.stack.home.feed.ActivityFeedFragment; import com.nathansdev.stack.home.feed.FeaturedFeedFragment; import com.nathansdev.stack.home.feed.HotFeedFragment; -import com.nathansdev.stack.home.feed.InterestingFeedFragment; import com.nathansdev.stack.home.feed.MonthLyFeedFragment; import com.nathansdev.stack.home.feed.SelfFragment; import com.nathansdev.stack.home.feed.WeekLyFeedFragment; @@ -47,7 +48,7 @@ public class HomeActivity extends BaseActivity { @Inject HotFeedFragment hotFeedFragment; @Inject - InterestingFeedFragment interestingFeedFragment; + ActivityFeedFragment activityFeedFragment; @Inject MonthLyFeedFragment monthLyFeedFragment; @Inject @@ -101,7 +102,12 @@ private void addFragmentsToContainer() { * Initializing view pager. */ private void setUpViewPager() { - homePagerAdapter = new HomePagerAdapter(getSupportFragmentManager(), interestingFeedFragment, + activityFeedFragment.setArguments(getFilterArgBundle(AppConstants.ACTIVITY)); + featuredFeedFragment.setArguments(getFilterArgBundle(AppConstants.VOTES)); + hotFeedFragment.setArguments(getFilterArgBundle(AppConstants.HOT)); + monthLyFeedFragment.setArguments(getFilterArgBundle(AppConstants.MONTH)); + weekLyFeedFragment.setArguments(getFilterArgBundle(AppConstants.WEEK)); + homePagerAdapter = new HomePagerAdapter(getSupportFragmentManager(), activityFeedFragment, featuredFeedFragment, hotFeedFragment, monthLyFeedFragment, weekLyFeedFragment, selfFragment, getResources().getStringArray(R.array.home_tabs)); viewPager.setAdapter(homePagerAdapter); @@ -138,6 +144,13 @@ private void setUpViews() { } private void handleProfileMenuClicked() { + + } + + private Bundle getFilterArgBundle(String filterType) { + Bundle bundle = new Bundle(); + bundle.putString(AppConstants.ARG_FILTER_TYPE, filterType); + return bundle; } @Override diff --git a/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java b/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java index 12f9437..64ea3f8 100755 --- a/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java +++ b/app/src/main/java/com/nathansdev/stack/home/HomeActivityModule.java @@ -3,12 +3,12 @@ import com.nathansdev.stack.di.PerActivity; import com.nathansdev.stack.di.PerFragment; +import com.nathansdev.stack.home.feed.ActivityFeedFragment; import com.nathansdev.stack.home.feed.FeaturedFeedFragment; import com.nathansdev.stack.home.feed.FeedView; import com.nathansdev.stack.home.feed.FeedViewPresenter; import com.nathansdev.stack.home.feed.FeedViewPresenterImpl; import com.nathansdev.stack.home.feed.HotFeedFragment; -import com.nathansdev.stack.home.feed.InterestingFeedFragment; import com.nathansdev.stack.home.feed.MonthLyFeedFragment; import com.nathansdev.stack.home.feed.SelfFragment; import com.nathansdev.stack.home.feed.WeekLyFeedFragment; @@ -24,7 +24,7 @@ public abstract class HomeActivityModule { @PerFragment @ContributesAndroidInjector - abstract InterestingFeedFragment provideInterestingFeedFragmentFactory(); + abstract ActivityFeedFragment provideInterestingFeedFragmentFactory(); @PerFragment @ContributesAndroidInjector diff --git a/app/src/main/java/com/nathansdev/stack/home/HomePagerAdapter.java b/app/src/main/java/com/nathansdev/stack/home/HomePagerAdapter.java index 457920c..668cba9 100644 --- a/app/src/main/java/com/nathansdev/stack/home/HomePagerAdapter.java +++ b/app/src/main/java/com/nathansdev/stack/home/HomePagerAdapter.java @@ -4,9 +4,9 @@ import android.support.v4.app.FragmentManager; import com.nathansdev.stack.TaggedFragmentStatePagerAdapter; +import com.nathansdev.stack.home.feed.ActivityFeedFragment; import com.nathansdev.stack.home.feed.FeaturedFeedFragment; import com.nathansdev.stack.home.feed.HotFeedFragment; -import com.nathansdev.stack.home.feed.InterestingFeedFragment; import com.nathansdev.stack.home.feed.MonthLyFeedFragment; import com.nathansdev.stack.home.feed.SelfFragment; import com.nathansdev.stack.home.feed.WeekLyFeedFragment; @@ -19,7 +19,7 @@ public class HomePagerAdapter extends TaggedFragmentStatePagerAdapter { private static final String TAG = HomePagerAdapter.class.getSimpleName(); private final FeaturedFeedFragment featuredFeedFragment; private final HotFeedFragment hotFeedFragment; - private final InterestingFeedFragment interestingFeedFragment; + private final ActivityFeedFragment interestingFeedFragment; private final MonthLyFeedFragment monthLyFeedFragment; private final WeekLyFeedFragment weekLyFeedFragment; private final SelfFragment selfFragment; @@ -36,7 +36,7 @@ public class HomePagerAdapter extends TaggedFragmentStatePagerAdapter { * @param weekLyFeedFragment feed type weekly instance. * @param selfFragment feed type self instance. */ - HomePagerAdapter(FragmentManager fm, InterestingFeedFragment interestingFeedFragment, + HomePagerAdapter(FragmentManager fm, ActivityFeedFragment interestingFeedFragment, FeaturedFeedFragment featuredFeedFragment, HotFeedFragment hotFeedFragment, MonthLyFeedFragment monthLyFeedFragment, WeekLyFeedFragment weekLyFeedFragment, SelfFragment selfFragment, String[] names) { diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java index f68fe71..ec46c77 100644 --- a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java @@ -19,8 +19,9 @@ public class QuestionsAdapter extends RecyclerView.Adapter { private static final int VIEW_TYPE_QUESTION = 0x01; - private static final int VIEW_TYPE_LOADING = 0x02; - private static final int VIEW_TYPE_LOADMORE = 0x03; + private static final int VIEW_TYPE_LOADMORE = 0x02; + private static final int VIEW_TYPE_LOADING = 0x03; + private static final int VIEW_TYPE_ERROR = 0x04; private RxEventBus eventBus; private ArrayList viewHolders = new ArrayList<>(); @@ -37,6 +38,12 @@ public QuestionsAdapterVH onCreateViewHolder(ViewGroup parent, int viewType) { case VIEW_TYPE_LOADMORE: holder = QuestionsAdapterVH.ofLoadMore(parent); break; + case VIEW_TYPE_LOADING: + holder = QuestionsAdapterVH.ofLoading(parent); + break; + case VIEW_TYPE_ERROR: + holder = QuestionsAdapterVH.ofError(parent); + break; default: break; } @@ -63,6 +70,10 @@ public int getItemViewType(int position) { return VIEW_TYPE_QUESTION; } else if (row.isTypeLoadMore()) { return VIEW_TYPE_LOADMORE; + } else if (row.isTypeLoading()) { + return VIEW_TYPE_LOADING; + } else if (row.isTypeError()) { + return VIEW_TYPE_ERROR; } return RecyclerView.NO_POSITION; } @@ -113,6 +124,13 @@ static QuestionsAdapterVH ofLoadMore(ViewGroup parent) { return LoadMoreVH.create(parent); } + static QuestionsAdapterVH ofError(ViewGroup parent) { + return ErrorVH.create(parent); + } + + static QuestionsAdapterVH ofLoading(ViewGroup parent) { + return LoadingVH.create(parent); + } abstract void bind(QuestionsAdapterRow row, RxEventBus eventBus); abstract void destroy(); @@ -206,4 +224,82 @@ void destroy() { } } + + + /** + * Binds groups name and avatar in the list. + */ + public static class LoadingVH extends QuestionsAdapterVH { + + /** + * Initialize constructor with item view. + * + * @param itemView item view / row view. + */ + LoadingVH(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + /** + * Inflate the user skillLayout to view. + * + * @param parent view group. + * @return users view holder. + */ + public static LoadingVH create(ViewGroup parent) { + View rootView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.adapter_item_loading, parent, false); + return new LoadingVH(rootView); + } + + @Override + void bind(QuestionsAdapterRow row, final RxEventBus eventBus) { + + } + + @Override + void destroy() { + + } + } + + + /** + * Binds groups name and avatar in the list. + */ + public static class ErrorVH extends QuestionsAdapterVH { + + /** + * Initialize constructor with item view. + * + * @param itemView item view / row view. + */ + ErrorVH(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + /** + * Inflate the user skillLayout to view. + * + * @param parent view group. + * @return users view holder. + */ + public static ErrorVH create(ViewGroup parent) { + View rootView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.adapter_item_error, parent, false); + return new ErrorVH(rootView); + } + + @Override + void bind(QuestionsAdapterRow row, final RxEventBus eventBus) { + + } + + @Override + void destroy() { + + } + } } diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java index 90d57a2..0898c3d 100644 --- a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRow.java @@ -11,6 +11,7 @@ public abstract class QuestionsAdapterRow implements Parcelable { private static final int TYPE_QUESTION = 0x01; private static final int TYPE_LOAD_MORE = 0x02; private static final int TYPE_LOADING = 0x03; + private static final int TYPE_ERROR = 0x04; /** * Builds along with already built row. @@ -40,6 +41,10 @@ public boolean isTypeLoading() { return type() == TYPE_LOADING; } + public boolean isTypeError() { + return type() == TYPE_ERROR; + } + public static Builder builder() { return new AutoValue_QuestionsAdapterRow.Builder(); } @@ -50,6 +55,18 @@ public static QuestionsAdapterRow ofQuestion(Question question) { .title(question.title()).votes(question.score()).build(); } + public static QuestionsAdapterRow ofLoading() { + return builder().type(TYPE_LOADING).build(); + } + + public static QuestionsAdapterRow ofLoadMore() { + return builder().type(TYPE_LOAD_MORE).build(); + } + + public static QuestionsAdapterRow ofError() { + return builder().type(TYPE_ERROR).build(); + } + /** * Compare the new item with existing item. * @@ -61,6 +78,10 @@ public int compare(QuestionsAdapterRow r2) { int comparedValue = 1; if (this.isTypeLoadMore() && r2.isTypeLoadMore()) { return 0; + } else if (this.isTypeLoading() && r2.isTypeLoading()) { + return 0; + } else if (this.isTypeError() && r2.isTypeError()) { + return 0; } else if (this.isTypeQuestion() && r2.isTypeQuestion()) { return 1; } else if (this.isTypeQuestion() && r2.isTypeLoadMore()) { @@ -82,6 +103,10 @@ public boolean areContentsTheSame(QuestionsAdapterRow newItem) { return this.question().equals(newItem.question()); } else if (isTypeLoadMore() && newItem.isTypeLoadMore()) { return this.equals(newItem); + } else if (isTypeLoading() && newItem.isTypeLoading()) { + return this.equals(newItem); + } else if (isTypeError() && newItem.isTypeError()) { + return this.equals(newItem); } return false; } @@ -97,6 +122,10 @@ public boolean areItemsTheSame(QuestionsAdapterRow newItem) { return this.question().id().longValue() == newItem.question().id().longValue(); } else if (isTypeLoadMore() && newItem.isTypeLoadMore()) { return true; + } else if (isTypeLoading() && newItem.isTypeLoading()) { + return true; + } else if (isTypeError() && newItem.isTypeError()) { + return true; } return false; } diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java index d0435f3..615bfe7 100644 --- a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapterRowDataSet.java @@ -131,4 +131,34 @@ public void removeLoadMore() { sortedList.removeItemAt(index); } } + + /** + * Remove load more row from sorted list. + */ + public void removeLoading() { + int index = -1; + for (int i = 0; i < sortedList.size(); i++) { + if (sortedList.get(i).isTypeLoading()) { + index = i; + } + } + if (index > -1) { + sortedList.removeItemAt(index); + } + } + + /** + * Remove load more row from sorted list. + */ + public void removeError() { + int index = -1; + for (int i = 0; i < sortedList.size(); i++) { + if (sortedList.get(i).isTypeError()) { + index = i; + } + } + if (index > -1) { + sortedList.removeItemAt(index); + } + } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java similarity index 55% rename from app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java rename to app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java index 4805b2a..3aa3762 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/InterestingFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java @@ -6,15 +6,15 @@ import javax.inject.Inject; -public class InterestingFeedFragment extends FeedFragment { +public class ActivityFeedFragment extends FeedFragment { @Inject - public InterestingFeedFragment() { + public ActivityFeedFragment() { } - public static InterestingFeedFragment newInstance() { - InterestingFeedFragment fragment = new InterestingFeedFragment(); + public static ActivityFeedFragment newInstance() { + ActivityFeedFragment fragment = new ActivityFeedFragment(); return fragment; } @@ -33,13 +33,4 @@ protected QuestionsAdapter getAdapter() { return null; } - @Override - protected void setRefreshLayout(boolean refresh) { - - } - - @Override - public void onQuestionsLoaded() { - - } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java index 943953d..f3cb0aa 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeaturedFeedFragment.java @@ -31,7 +31,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c @Override protected void setUpView(View view) { - super.setUpView(view); +// super.setUpView(view); } @Override @@ -43,14 +43,4 @@ protected void attachPresenter() { protected QuestionsAdapter getAdapter() { return new QuestionsAdapter(); } - - @Override - protected void setRefreshLayout(boolean refresh) { - - } - - @Override - public void onQuestionsLoaded() { - - } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java index 404e023..99cfc4d 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedFragment.java @@ -11,9 +11,11 @@ import android.view.View; import android.view.ViewGroup; +import com.nathansdev.stack.AppConstants; import com.nathansdev.stack.R; import com.nathansdev.stack.base.BaseFragment; import com.nathansdev.stack.home.adapter.QuestionsAdapter; +import com.nathansdev.stack.home.adapter.QuestionsAdapterRow; import com.nathansdev.stack.home.adapter.QuestionsAdapterRowDataSet; import com.nathansdev.stack.rxevent.RxEventBus; @@ -23,7 +25,8 @@ import butterknife.ButterKnife; import timber.log.Timber; -public abstract class FeedFragment extends BaseFragment implements FeedView { +public abstract class FeedFragment extends BaseFragment implements FeedView, + SwipeRefreshLayout.OnRefreshListener { @BindView(R.id.feeds_recycler) RecyclerView recyclerView; @@ -38,10 +41,15 @@ public abstract class FeedFragment extends BaseFragment implements FeedView { private LinearLayoutManager layoutManager; private QuestionsAdapter adapter; private QuestionsAdapterRowDataSet dataset; - + private int lastVisibleItem; + private boolean loadingMore = false; + private String filterType = "activity"; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (getArguments() != null) { + filterType = getArguments().getString(AppConstants.ARG_FILTER_TYPE); + } } @Nullable @@ -71,6 +79,14 @@ protected void setUpView(View view) { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); + lastVisibleItem = layoutManager.findLastVisibleItemPosition(); + if (lastVisibleItem > -1) { + QuestionsAdapterRow row = dataset.get(lastVisibleItem); + if (!loadingMore && row.isTypeLoadMore()) { + loadingMore = true; + loadNextPage(); + } + } } }); adapter.setEventBus(eventBus); @@ -80,13 +96,55 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } adapter.setData(dataset); recyclerView.setAdapter(adapter); - presenter.init(dataset); + refreshLayout.setOnRefreshListener(this); + presenter.init(dataset, filterType); + presenter.loadQuestions(); + } + + @Override + public void showLoading() { + dataset.addRow(QuestionsAdapterRow.ofLoading()); + setRefreshLayout(false); + } + + @Override + public void hideLoading() { + loadingMore = false; + dataset.removeLoading(); + setRefreshLayout(true); + } + + @Override + public void onRefresh() { presenter.loadQuestions(); } + @Override + public void onDestroyView() { + refreshLayout.setOnRefreshListener(null); + recyclerView.setOnScrollListener(null); + super.onDestroyView(); + } + + private void setRefreshLayout(boolean refresh) { + if (refreshLayout != null) { + refreshLayout.setRefreshing(refresh); + } + } + + private void loadNextPage() { + Timber.d("loading next page"); + presenter.loadNextPage(); + } + + + @Override + public void onQuestionsLoaded() { + loadingMore = false; + } + protected abstract void attachPresenter(); protected abstract QuestionsAdapter getAdapter(); - protected abstract void setRefreshLayout(boolean refresh); } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java index d3ca62d..f72d7e3 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenter.java @@ -5,7 +5,7 @@ import com.nathansdev.stack.home.adapter.QuestionsAdapterRowDataSet; public interface FeedViewPresenter extends MvpPresenter { - void init(QuestionsAdapterRowDataSet dataset); + void init(QuestionsAdapterRowDataSet dataset, String filterType); void loadQuestions(); diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java index 81f1ed3..b2eb389 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/FeedViewPresenterImpl.java @@ -1,5 +1,6 @@ package com.nathansdev.stack.home.feed; +import com.nathansdev.stack.AppConstants; import com.nathansdev.stack.base.BasePresenter; import com.nathansdev.stack.data.api.StackExchangeApi; import com.nathansdev.stack.data.model.Question; @@ -30,6 +31,8 @@ public class FeedViewPresenterImpl extends BasePresenter private PublishProcessor questionsSubject = PublishProcessor.create(); private CompositeDisposable disposables = new CompositeDisposable(); private QuestionsAdapterRowDataSet rowDataSet; + private String type; + private long page = 1; @Inject FeedViewPresenterImpl(StackExchangeApi api) { @@ -37,8 +40,10 @@ public class FeedViewPresenterImpl extends BasePresenter } @Override - public void init(QuestionsAdapterRowDataSet dataset) { + public void init(QuestionsAdapterRowDataSet dataset, String filterType) { this.rowDataSet = dataset; + this.type = filterType; + getMvpView().showLoading(); Disposable disposable = questionsSubject .onBackpressureDrop() .concatMap((Function>) page -> @@ -49,6 +54,7 @@ public void init(QuestionsAdapterRowDataSet dataset) { @Override protected void onNextAction(QuestionsResponse response) { Timber.d("questions %s", response); + getMvpView().hideLoading(); handleQuestionResponse(response); } @@ -66,6 +72,9 @@ private void handleQuestionResponse(QuestionsResponse response) { for (Question question : response.questions()) { rows.add(QuestionsAdapterRow.ofQuestion(question)); } + if (response.hasMore() != null && response.hasMore()) { + rows.add(QuestionsAdapterRow.ofLoadMore()); + } } rowDataSet.addAllRows(rows); } @@ -79,6 +88,8 @@ public void loadQuestions() { @Override public void loadNextPage() { Timber.d("loadNextPage"); + page = page + 1; + questionsSubject.onNext(page); } @Override @@ -87,7 +98,7 @@ public void cleanUp() { } private Flowable getObservable() { - return api.getQuestionsFlowable("activity", "stackoverflow", "desc") + return api.getQuestionsFlowable(type, AppConstants.SITE, AppConstants.DESC, page, 10) .subscribeOn(Schedulers.io()); } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java index 4037005..6c33f1c 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/HotFeedFragment.java @@ -20,7 +20,7 @@ public static HotFeedFragment newInstance() { @Override protected void setUpView(View view) { - super.setUpView(view); +// super.setUpView(view); } @Override @@ -32,14 +32,4 @@ protected void attachPresenter() { protected QuestionsAdapter getAdapter() { return null; } - - @Override - protected void setRefreshLayout(boolean refresh) { - - } - - @Override - public void onQuestionsLoaded() { - - } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java index 374b1ce..df3f5c1 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/MonthLyFeedFragment.java @@ -20,7 +20,7 @@ public static MonthLyFeedFragment newInstance() { @Override protected void setUpView(View view) { - super.setUpView(view); +// super.setUpView(view); } @Override @@ -32,14 +32,4 @@ protected void attachPresenter() { protected QuestionsAdapter getAdapter() { return null; } - - @Override - protected void setRefreshLayout(boolean refresh) { - - } - - @Override - public void onQuestionsLoaded() { - - } } diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java index 6acecad..b14934c 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/WeekLyFeedFragment.java @@ -20,7 +20,7 @@ public static WeekLyFeedFragment newInstance() { @Override protected void setUpView(View view) { - super.setUpView(view); +// super.setUpView(view); } @Override @@ -32,14 +32,4 @@ protected void attachPresenter() { protected QuestionsAdapter getAdapter() { return null; } - - @Override - protected void setRefreshLayout(boolean refresh) { - - } - - @Override - public void onQuestionsLoaded() { - - } } diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 68da3bc..bb0cffa 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -18,6 +18,7 @@ android:fitsSystemWindows="true" app:title="@string/all_questions" app:titleTextColor="@color/black" + app:titleMarginStart="@dimen/super_large_text" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" @@ -28,12 +29,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" app:tabGravity="fill" + android:minHeight="?actionBarSize" app:tabIndicatorColor="@color/orange" app:tabIndicatorHeight="4dp" - app:tabSelectedTextColor="@color/black" + app:tabMode="scrollable" app:tabTextAppearance="?android:attr/textAppearanceSmall" app:tabTextColor="@color/black" - app:tabMode="fixed" /> + app:tabSelectedTextColor="@color/orange" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_item_loading.xml b/app/src/main/res/layout/adapter_item_loading.xml new file mode 100644 index 0000000..cf0db6c --- /dev/null +++ b/app/src/main/res/layout/adapter_item_loading.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 847568f..e6c7242 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,9 +3,14 @@ #fafafa #f5f5f5 #ff9100 - #000000 - #FFFFFF - #fafafa #f5f5f5 #ff9100 + #CDCDCD + #A9A9A9 + #808080 + #ffffff + #B3ffffff + #66ffffff + #4Dffffff + #000000 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5dc0c53..cf165f9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,9 +6,10 @@ Tab 3 Settings Hello World from section: %1$d - All Questions + ALL QUESTIONS loading… Image + No Results Interesting diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c73ccaa..ffa4834 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -22,4 +22,7 @@ From f925bc592abba3cbeb06bb7ad4978af8b3df40ef Mon Sep 17 00:00:00 2001 From: Sabari Date: Tue, 26 Mar 2019 21:09:11 +0530 Subject: [PATCH 05/18] added flow layout for tags --- app/src/main/AndroidManifest.xml | 2 +- .../stack/home/adapter/QuestionsAdapter.java | 20 ++++ .../stack/home/feed/ActivityFeedFragment.java | 1 - .../nathansdev/stack/rxevent/AppEvents.java | 1 + .../nathansdev/stack/view/FlowLayoutTags.java | 108 ++++++++++++++++++ .../com/nathansdev/stack/view/TagView.java | 39 +++++++ app/src/main/res/layout/activity_home.xml | 27 +++-- .../main/res/layout/adapter_item_question.xml | 6 + app/src/main/res/layout/layout_tag_view.xml | 7 ++ app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/styles.xml | 9 +- 11 files changed, 202 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/com/nathansdev/stack/view/FlowLayoutTags.java create mode 100644 app/src/main/java/com/nathansdev/stack/view/TagView.java create mode 100644 app/src/main/res/layout/layout_tag_view.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 143554c..f0f0ecf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ android:theme="@style/AppTheme"> + android:theme="@style/AppTheme"> diff --git a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java index ec46c77..d6eb6f2 100644 --- a/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java +++ b/app/src/main/java/com/nathansdev/stack/home/adapter/QuestionsAdapter.java @@ -2,6 +2,7 @@ import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -9,8 +10,10 @@ import android.widget.TextView; import com.nathansdev.stack.R; +import com.nathansdev.stack.rxevent.AppEvents; import com.nathansdev.stack.rxevent.RxEventBus; import com.nathansdev.stack.utils.Utils; +import com.nathansdev.stack.view.TagView; import java.util.ArrayList; @@ -150,6 +153,8 @@ public static class QuestionVH extends QuestionsAdapterVH { TextView title; @BindView(R.id.iv_owner_profile) ImageView avatar; + @BindView(R.id.flow_layout_tags) + ViewGroup tagsLayout; /** * Initialize constructor with item view. @@ -175,10 +180,25 @@ public static QuestionVH create(ViewGroup parent) { @Override void bind(final QuestionsAdapterRow row, final RxEventBus eventBus) { + if (tagsLayout != null && tagsLayout.getChildCount() != 0) { + tagsLayout.removeAllViews(); + } Utils.loadRoundImage(itemView.getContext(), row.imageUrl(), avatar); ownerName.setText(row.name()); title.setText(row.title()); timeStamp.setText(String.valueOf(row.timeStamp())); + if (row.question().tags() != null && !row.question().tags().isEmpty()) { + for (String tag : row.question().tags()) { + TagView tagView = TagView.formView(tagsLayout, tag); + tagView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + eventBus.send(new Pair<>(AppEvents.QUESTION_TAG_CLICKED, tag)); + } + }); + tagsLayout.addView(tagView); + } + } } @Override diff --git a/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java b/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java index 3aa3762..77c42c9 100644 --- a/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java +++ b/app/src/main/java/com/nathansdev/stack/home/feed/ActivityFeedFragment.java @@ -32,5 +32,4 @@ protected void attachPresenter() { protected QuestionsAdapter getAdapter() { return null; } - } diff --git a/app/src/main/java/com/nathansdev/stack/rxevent/AppEvents.java b/app/src/main/java/com/nathansdev/stack/rxevent/AppEvents.java index f0d689d..fe3c59f 100755 --- a/app/src/main/java/com/nathansdev/stack/rxevent/AppEvents.java +++ b/app/src/main/java/com/nathansdev/stack/rxevent/AppEvents.java @@ -7,4 +7,5 @@ public final class AppEvents { // Splash Activity Events. public static final String PROFILE_MENU_CLICKED = "profileMenuClicked"; + public static final String QUESTION_TAG_CLICKED = "questionTagClicked"; } diff --git a/app/src/main/java/com/nathansdev/stack/view/FlowLayoutTags.java b/app/src/main/java/com/nathansdev/stack/view/FlowLayoutTags.java new file mode 100644 index 0000000..7e23740 --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/view/FlowLayoutTags.java @@ -0,0 +1,108 @@ +package com.nathansdev.stack.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + + +/** + * A custom layout that takes care of overflowing items and positions them on the next line. + */ +public class FlowLayoutTags extends ViewGroup { + private int mLineHeight; + private int mVerticalSpacing; + private int mHorizontalSpacing; + + /** + * Constructor with no attributes. + * + * @param context Context + */ + public FlowLayoutTags(Context context) { + super(context); + init(context, null); + } + + /** + * Constructor with attribute set. + * + * @param context Context + * @param attrs attribute set + */ + public FlowLayoutTags(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + mVerticalSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()); + mHorizontalSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); + final int count = getChildCount(); + int lineHeight = 0; + + int xpos = getPaddingLeft(); + int ypos = getPaddingTop(); + + int childHeightMeasureSpec; + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec); + final int childMeasuredWidth = child.getMeasuredWidth() + child.getPaddingLeft() + child.getPaddingRight(); + lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + mVerticalSpacing); + + if (xpos + childMeasuredWidth > width) { + xpos = getPaddingLeft(); + ypos += lineHeight; + } + + xpos += childMeasuredWidth + mHorizontalSpacing; + } + } + this.mLineHeight = lineHeight; + + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { + height = ypos + lineHeight; + + } else if ((MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) && (ypos + lineHeight < height)) { + height = ypos + lineHeight; + } + setMeasuredDimension(width, height); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int count = getChildCount(); + final int width = r - l; + int xpos = getPaddingLeft(); + int ypos = getPaddingTop(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + final int childMeasuredWidth = child.getMeasuredWidth(); + final int childMeasuredHeight = child.getMeasuredHeight(); + if (xpos + childMeasuredWidth > width) { + xpos = getPaddingLeft(); + ypos += mLineHeight; + } + child.layout(xpos, ypos, xpos + childMeasuredWidth, ypos + childMeasuredHeight); + xpos += childMeasuredWidth + mHorizontalSpacing; + } + } + } +} diff --git a/app/src/main/java/com/nathansdev/stack/view/TagView.java b/app/src/main/java/com/nathansdev/stack/view/TagView.java new file mode 100644 index 0000000..cf5945d --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/view/TagView.java @@ -0,0 +1,39 @@ +package com.nathansdev.stack.view; + +import android.content.Context; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.AppCompatTextView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import com.nathansdev.stack.R; + +public class TagView extends AppCompatTextView { + public TagView(Context context) { + super(context); + } + + public TagView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public TagView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + /** + * Returns single SkillsInterestItem. + * + * @param viewGroup item's view group. + * @param text text should be set. + * @return SkillsInterest item. + */ + public static TagView formView(ViewGroup viewGroup, String text) { + @LayoutRes int layoutId = R.layout.layout_tag_view; + TagView itemView = (TagView) LayoutInflater.from(viewGroup.getContext()) + .inflate(layoutId, viewGroup, false); + itemView.setText(text); + return itemView; + } +} diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index bb0cffa..8be69dd 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -2,27 +2,36 @@ + app:popupTheme="@style/ThemeOverlay.AppCompat.Light"> + + + + app:tabSelectedTextColor="@color/black" + app:tabTextColor="@color/grey" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_tag_view.xml b/app/src/main/res/layout/layout_tag_view.xml new file mode 100644 index 0000000..6f23716 --- /dev/null +++ b/app/src/main/res/layout/layout_tag_view.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index e6c7242..b16bdef 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,7 @@ #fafafa - #f5f5f5 + #cccccc #ff9100 #f5f5f5 #ff9100 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index ffa4834..b8e364d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,21 +1,14 @@ - - - From 5f58b3b906177fe9292a3292249253b5fc848d81 Mon Sep 17 00:00:00 2001 From: Sabari Date: Wed, 27 Mar 2019 03:53:13 +0530 Subject: [PATCH 06/18] implementing oauth with stackexchange api --- app/src/main/AndroidManifest.xml | 21 ++++- .../com/nathansdev/stack/AppConstants.java | 6 ++ .../com/nathansdev/stack/AppPreferences.java | 18 +++++ .../nathansdev/stack/auth/LoginActivity.kt | 80 +++++++++++++++++++ .../stack/di/ActivityBuilderModule.java | 5 ++ .../com/nathansdev/stack/di/AppModule.java | 2 - .../nathansdev/stack/splash/SplashActivity.kt | 21 ++++- app/src/main/res/layout/activity_login.xml | 21 +++++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles.xml | 1 + 10 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/nathansdev/stack/auth/LoginActivity.kt create mode 100644 app/src/main/res/layout/activity_login.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f0f0ecf..0540639 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + + + + android:theme="@style/AppTheme" /> @@ -24,6 +28,19 @@ - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/nathansdev/stack/AppConstants.java b/app/src/main/java/com/nathansdev/stack/AppConstants.java index fe1b877..8c79efb 100644 --- a/app/src/main/java/com/nathansdev/stack/AppConstants.java +++ b/app/src/main/java/com/nathansdev/stack/AppConstants.java @@ -4,6 +4,7 @@ * App Constants data. */ public final class AppConstants { + public static final String AUTH_URL = "https://stackoverflow.com/oauth/dialog"; public static final String VOTES = "votes"; public static final String ACTIVITY = "activity"; public static final String HOT = "hot"; @@ -12,4 +13,9 @@ public final class AppConstants { public static final String DESC = "desc"; public static final String SITE = "stackoverflow"; public static final String ARG_FILTER_TYPE = "filterType"; + public static final String CLIENT_ID = "client_id"; + public static final String REDIRECT_URI = "redirect_uri"; + public static final String ACCESS_TOKEN = "access_token"; + public static final String EXPIRES = "expires"; + public static final String ERROR = "error"; } diff --git a/app/src/main/java/com/nathansdev/stack/AppPreferences.java b/app/src/main/java/com/nathansdev/stack/AppPreferences.java index 19658bd..4eb5de2 100644 --- a/app/src/main/java/com/nathansdev/stack/AppPreferences.java +++ b/app/src/main/java/com/nathansdev/stack/AppPreferences.java @@ -9,9 +9,27 @@ */ public class AppPreferences { private static final String PREFS_NAME = "app-prefs"; + private static final String IS_LOGGEDIN = "isLoggedIn"; + private static final String ACCESS_TOKEN = "accessToken"; private final SharedPreferences prefs; public AppPreferences(Application app) { this.prefs = app.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); } + + public boolean isLoggedIn() { + return prefs.getBoolean(IS_LOGGEDIN, Boolean.FALSE); + } + + public void setIsLoggedin(boolean isLoggedin) { + prefs.edit().putBoolean(IS_LOGGEDIN, isLoggedin).apply(); + } + + public String getAccessToken() { + return prefs.getString(ACCESS_TOKEN, ""); + } + + public void setAccessToken(String token) { + prefs.edit().putString(ACCESS_TOKEN, token).apply(); + } } diff --git a/app/src/main/java/com/nathansdev/stack/auth/LoginActivity.kt b/app/src/main/java/com/nathansdev/stack/auth/LoginActivity.kt new file mode 100644 index 0000000..164d50b --- /dev/null +++ b/app/src/main/java/com/nathansdev/stack/auth/LoginActivity.kt @@ -0,0 +1,80 @@ +package com.nathansdev.stack.auth + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.widget.Button +import android.widget.ProgressBar +import com.nathansdev.stack.AppConstants +import com.nathansdev.stack.AppPreferences +import com.nathansdev.stack.base.BaseActivity +import timber.log.Timber +import javax.inject.Inject + + +/** + * A login screen that offers login via email/password. + */ +class LoginActivity : BaseActivity() { + + @Inject + lateinit var appPreferences: AppPreferences + + private val clientId = "14730" + private val redirectUri = "https://stack-query.herokuapp.com" + private var progressBar: ProgressBar? = null + private var button: Button? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(com.nathansdev.stack.R.layout.activity_login) + button = findViewById(com.nathansdev.stack.R.id.button_auth) + progressBar = findViewById(com.nathansdev.stack.R.id.progress_loading) + progressBar?.visibility = View.GONE + button?.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW, + Uri.parse(AppConstants.AUTH_URL + "?" + AppConstants.CLIENT_ID + "=" + clientId + + "&" + AppConstants.REDIRECT_URI + "=" + redirectUri)) + startActivity(intent) + progressBar?.visibility = View.VISIBLE + button?.visibility = View.GONE + } + Timber.d("onCreate") + } + + override fun onResume() { + super.onResume() + Timber.d("onResume %s", intent) + handleIntent() + } + + private fun handleIntent() { + progressBar?.visibility = View.GONE + button?.visibility = View.VISIBLE + // the intent filter defined in AndroidManifest will handle the return from ACTION_VIEW intent + val uri = intent.data + Timber.d("uri %s", uri) + if (uri != null && uri.toString().startsWith(redirectUri)) { + val extra = uri.fragment + Timber.d("extra %s", extra) + val accessToken = extra?.split("&")?.get(0)?.split("=")?.get(1) + Timber.d("token %s", accessToken) + if (accessToken != null) { + appPreferences.setIsLoggedin(true) + appPreferences.accessToken = accessToken + // get access token + // we'll do that in a minute + } else if (uri.getQueryParameter(AppConstants.ERROR) != null) { + val error = uri.getQueryParameter(AppConstants.ERROR) + Timber.d(error) + // show an error message here + } + } + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + Timber.d("onNewIntent %s", intent) + } +} diff --git a/app/src/main/java/com/nathansdev/stack/di/ActivityBuilderModule.java b/app/src/main/java/com/nathansdev/stack/di/ActivityBuilderModule.java index e845837..ef459ae 100644 --- a/app/src/main/java/com/nathansdev/stack/di/ActivityBuilderModule.java +++ b/app/src/main/java/com/nathansdev/stack/di/ActivityBuilderModule.java @@ -1,5 +1,6 @@ package com.nathansdev.stack.di; +import com.nathansdev.stack.auth.LoginActivity; import com.nathansdev.stack.home.HomeActivity; import com.nathansdev.stack.home.HomeActivityModule; import com.nathansdev.stack.splash.SplashActivity; @@ -19,4 +20,8 @@ abstract class ActivityBuilderModule { @PerActivity @ContributesAndroidInjector abstract SplashActivity bindSplashActivity(); + + @PerActivity + @ContributesAndroidInjector + abstract LoginActivity bindLoginActivity(); } diff --git a/app/src/main/java/com/nathansdev/stack/di/AppModule.java b/app/src/main/java/com/nathansdev/stack/di/AppModule.java index 623a68a..8c9dd7a 100755 --- a/app/src/main/java/com/nathansdev/stack/di/AppModule.java +++ b/app/src/main/java/com/nathansdev/stack/di/AppModule.java @@ -4,9 +4,7 @@ import android.content.Context; import com.nathansdev.stack.AppPreferences; -import com.nathansdev.stack.data.model.MyAdapterFactory; import com.nathansdev.stack.rxevent.RxEventBus; -import com.squareup.moshi.Moshi; import javax.inject.Singleton; diff --git a/app/src/main/java/com/nathansdev/stack/splash/SplashActivity.kt b/app/src/main/java/com/nathansdev/stack/splash/SplashActivity.kt index 07eeff6..d6435df 100644 --- a/app/src/main/java/com/nathansdev/stack/splash/SplashActivity.kt +++ b/app/src/main/java/com/nathansdev/stack/splash/SplashActivity.kt @@ -3,12 +3,18 @@ package com.nathansdev.stack.splash import android.content.Intent import android.os.Bundle import android.os.Handler +import com.nathansdev.stack.AppPreferences import com.nathansdev.stack.R +import com.nathansdev.stack.auth.LoginActivity import com.nathansdev.stack.base.BaseActivity import com.nathansdev.stack.home.HomeActivity +import javax.inject.Inject class SplashActivity : BaseActivity() { + @Inject + lateinit var appPreferences: AppPreferences + override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.AppTheme_Launcher) super.onCreate(savedInstanceState) @@ -16,13 +22,20 @@ class SplashActivity : BaseActivity() { override fun onResume() { super.onResume() - routeToHome() + routeTo() } - private fun routeToHome() { + private fun routeTo() { Handler().postDelayed({ - val intent = Intent(this, HomeActivity::class.java) - startActivity(intent) + if (appPreferences.isLoggedIn) { + val intent = Intent(this, HomeActivity::class.java) + startActivity(intent) + finish() + } else { + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + finish() + } }, 1000) } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..2c637b5 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,21 @@ + + + +