diff --git a/app/build.gradle b/app/build.gradle
index cc1244b..441c0a9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -110,4 +110,6 @@ dependencies {
implementation 'com.squareup.okhttp3:mockwebserver:3.9.0'
androidTestImplementation 'com.squareup.spoon:spoon-client:1.4.0'
+
+ compile 'org.apache.commons:commons-lang3:3.4'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cd78ef6..8f6bdb5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -22,7 +22,7 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/app/injection/AppComponent.java b/app/src/main/java/com/tline/android/app/injection/AppComponent.java
index c1ea98d..4b8d331 100644
--- a/app/src/main/java/com/tline/android/app/injection/AppComponent.java
+++ b/app/src/main/java/com/tline/android/app/injection/AppComponent.java
@@ -3,6 +3,7 @@
import android.content.Context;
import com.tline.android.app.TLineApp;
+import com.tline.android.utils.LocaleHelper;
import com.tline.android.utils.PreferencesUtils;
import javax.inject.Singleton;
@@ -19,4 +20,6 @@ public interface AppComponent {
PreferencesUtils exposePreferencesUtils();
+ LocaleHelper exposeLocaleHelper();
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/app/view/BaseFragmentView.java b/app/src/main/java/com/tline/android/app/view/BaseFragmentView.java
new file mode 100644
index 0000000..12ee022
--- /dev/null
+++ b/app/src/main/java/com/tline/android/app/view/BaseFragmentView.java
@@ -0,0 +1,14 @@
+package com.tline.android.app.view;
+
+/**
+ * Created by Naeem(naeemark@gmail.com)
+ * On 30/11/2017.
+ * For TLine
+ */
+
+public interface BaseFragmentView {
+
+ void showLoading();
+
+ void hideLoading();
+}
diff --git a/app/src/main/java/com/tline/android/app/view/impl/BaseFragment.java b/app/src/main/java/com/tline/android/app/view/impl/BaseFragment.java
index 53323e2..738d4bd 100644
--- a/app/src/main/java/com/tline/android/app/view/impl/BaseFragment.java
+++ b/app/src/main/java/com/tline/android/app/view/impl/BaseFragment.java
@@ -12,10 +12,11 @@
import com.tline.android.app.presenter.loader.PresenterLoader;
import com.tline.android.app.injection.AppComponent;
import com.tline.android.app.presenter.BasePresenter;
+import com.tline.android.app.view.BaseFragmentView;
import java.util.concurrent.atomic.AtomicBoolean;
-public abstract class BaseFragment
, V> extends Fragment implements LoaderManager.LoaderCallbacks
{
+public abstract class BaseFragment
, V> extends Fragment implements LoaderManager.LoaderCallbacks
, BaseFragmentView {
/**
* Do we need to call {@link #doStart()} from the {@link #onLoadFinished(Loader, BasePresenter)} method.
* Will be true if presenter wasn't loaded when {@link #onStart()} is reached
@@ -120,4 +121,14 @@ public final void onLoaderReset(Loader
loader) {
* @param appComponent the app component
*/
protected abstract void setupComponent(@NonNull AppComponent appComponent);
+
+ @Override
+ public void showLoading() {
+ ((BaseActivity)getActivity()).showLoading();
+ }
+
+ @Override
+ public void hideLoading() {
+ ((BaseActivity)getActivity()).hideLoading();
+ }
}
diff --git a/app/src/main/java/com/tline/android/constants/AppConstants.java b/app/src/main/java/com/tline/android/constants/AppConstants.java
index e30aba6..a79a166 100644
--- a/app/src/main/java/com/tline/android/constants/AppConstants.java
+++ b/app/src/main/java/com/tline/android/constants/AppConstants.java
@@ -6,4 +6,8 @@ public class AppConstants {
public static final String SHARED_PREFS_NAME = "This_app_Prefs";
+ public static final String TWITTER_HANDLE_OLX_EGYPT = "@OLXEgypt";
+
+ public static final String TWITTER_HANDLE_ANDROID_DEV = "@AndroidDev";
+
}
diff --git a/app/src/main/java/com/tline/android/features/login/view/impl/LoginActivity.java b/app/src/main/java/com/tline/android/features/login/view/impl/LoginActivity.java
index 6d712c6..5d0dd21 100644
--- a/app/src/main/java/com/tline/android/features/login/view/impl/LoginActivity.java
+++ b/app/src/main/java/com/tline/android/features/login/view/impl/LoginActivity.java
@@ -4,8 +4,6 @@
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@@ -18,7 +16,7 @@
import com.tline.android.features.login.injection.LoginViewModule;
import com.tline.android.features.login.presenter.LoginPresenter;
import com.tline.android.features.login.view.LoginView;
-import com.tline.android.features.timeline.view.impl.TimelineActivity;
+import com.tline.android.features.timeline.activity.view.impl.TimelineActivity;
import com.twitter.sdk.android.core.Callback;
import com.twitter.sdk.android.core.Result;
import com.twitter.sdk.android.core.TwitterApiClient;
@@ -77,33 +75,13 @@ protected int getContentView() {
protected void onViewReady(Bundle savedInstanceState, Intent intent) {
super.onViewReady(savedInstanceState, intent);
-// setActionBarIcon(R.mipmap.ic_launcher);
setButtonListeners();
- test();
-
if (TwitterCore.getInstance().getSessionManager().getActiveSession() != null) {
launchHomeActivity();
}
}
- private void test() {
- TwitterApiClient twitterApiClient = TwitterCore.getInstance().getApiClient();
- StatusesService statusesService = twitterApiClient.getStatusesService();
- Call call = statusesService.show(524971209851543553L, null, null, null);
- call.enqueue(new Callback() {
- @Override
- public void success(Result result) {
- //Do something with result
- Timber.i("R: " + result.data.text);
- }
-
- public void failure(TwitterException exception) {
- Timber.i("E: " + exception.getMessage());
- }
- });
- }
-
private void setButtonListeners() {
mLoginButton.setCallback(new Callback() {
@@ -145,7 +123,7 @@ public void updateUi() {
if (activeSession == null) {
mLogoutButton.setVisibility(View.GONE);
mLoginButton.setVisibility(View.VISIBLE);
- mUserDpImageView.setBackgroundResource(R.drawable.ic_launcher_round_web);
+ mUserDpImageView.setImageResource(R.drawable.ic_launcher_round_web);
} else {
mUserNameTextView.setText(getString(R.string.prompt_welcome_prefix, activeSession.getUserName()));
diff --git a/app/src/main/java/com/tline/android/features/splash/SplashPresenterImpl.java b/app/src/main/java/com/tline/android/features/splash/SplashPresenterImpl.java
index 7e51dd3..67bc8e8 100644
--- a/app/src/main/java/com/tline/android/features/splash/SplashPresenterImpl.java
+++ b/app/src/main/java/com/tline/android/features/splash/SplashPresenterImpl.java
@@ -25,7 +25,7 @@ public void onStart(boolean viewCreated) {
if (viewCreated) {
startLoading();
- if (mInteractor.isSplashDone()) {
+ if (!mInteractor.isSplashDone()) {
doSplash();
} else {
launchNextActivity();
diff --git a/app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewComponent.java b/app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewComponent.java
similarity index 68%
rename from app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewComponent.java
rename to app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewComponent.java
index 4debb47..606d29c 100644
--- a/app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewComponent.java
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewComponent.java
@@ -1,8 +1,8 @@
-package com.tline.android.features.timeline.injection;
+package com.tline.android.features.timeline.activity.injection;
import com.tline.android.app.injection.ActivityScope;
import com.tline.android.app.injection.AppComponent;
-import com.tline.android.features.timeline.view.impl.TimelineActivity;
+import com.tline.android.features.timeline.activity.view.impl.TimelineActivity;
import dagger.Component;
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewModule.java b/app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewModule.java
new file mode 100644
index 0000000..8131700
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/injection/TimelineViewModule.java
@@ -0,0 +1,33 @@
+package com.tline.android.features.timeline.activity.injection;
+
+import android.support.annotation.NonNull;
+
+import com.tline.android.app.presenter.loader.PresenterFactory;
+import com.tline.android.features.timeline.activity.interactor.TimelineInteractor;
+import com.tline.android.features.timeline.activity.interactor.impl.TimelineInteractorImpl;
+import com.tline.android.features.timeline.activity.presenter.TimelinePresenter;
+import com.tline.android.features.timeline.activity.presenter.impl.TimelinePresenterImpl;
+import com.tline.android.utils.LocaleHelper;
+import com.tline.android.utils.PreferencesUtils;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public final class TimelineViewModule {
+ @Provides
+ public TimelineInteractor provideInteractor(PreferencesUtils preferencesUtils, LocaleHelper localeHelper) {
+ return new TimelineInteractorImpl(preferencesUtils, localeHelper);
+ }
+
+ @Provides
+ public PresenterFactory providePresenterFactory(@NonNull final TimelineInteractor interactor) {
+ return new PresenterFactory() {
+ @NonNull
+ @Override
+ public TimelinePresenter create() {
+ return new TimelinePresenterImpl(interactor);
+ }
+ };
+ }
+}
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/interactor/TimelineInteractor.java b/app/src/main/java/com/tline/android/features/timeline/activity/interactor/TimelineInteractor.java
new file mode 100644
index 0000000..4fe119b
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/interactor/TimelineInteractor.java
@@ -0,0 +1,16 @@
+package com.tline.android.features.timeline.activity.interactor;
+
+import android.app.Activity;
+
+import com.tline.android.app.interactor.BaseInteractor;
+
+public interface TimelineInteractor extends BaseInteractor {
+
+ void saveSelectedTabIndex(int selectedTabIndex);
+
+ int retrieveSelectedTabIndex();
+
+ void switchAppLocale(Activity activity);
+
+ void invalidatePreference();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/interactor/impl/TimelineInteractorImpl.java b/app/src/main/java/com/tline/android/features/timeline/activity/interactor/impl/TimelineInteractorImpl.java
new file mode 100644
index 0000000..04ad2b2
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/interactor/impl/TimelineInteractorImpl.java
@@ -0,0 +1,45 @@
+package com.tline.android.features.timeline.activity.interactor.impl;
+
+import android.app.Activity;
+
+import javax.inject.Inject;
+
+import com.tline.android.app.interactor.impl.BaseInteractorImpl;
+import com.tline.android.features.timeline.activity.interactor.TimelineInteractor;
+import com.tline.android.utils.LocaleHelper;
+import com.tline.android.utils.PreferencesUtils;
+
+public final class TimelineInteractorImpl extends BaseInteractorImpl implements TimelineInteractor {
+
+ private final PreferencesUtils mPreferencesUtils;
+ private final LocaleHelper mLocaleHelper;
+
+ @Inject
+ public TimelineInteractorImpl(PreferencesUtils preferencesUtils, LocaleHelper localeHelper) {
+
+ mPreferencesUtils = preferencesUtils;
+ mLocaleHelper = localeHelper;
+ }
+
+ @Override
+ public void saveSelectedTabIndex(int selectedTabIndex) {
+
+ mPreferencesUtils.putInt(PreferencesUtils.PrefKeys.SELECTED_TAB_INDEX.name(), selectedTabIndex);
+ }
+
+ @Override
+ public int retrieveSelectedTabIndex() {
+ int index = mPreferencesUtils.getInt(PreferencesUtils.PrefKeys.SELECTED_TAB_INDEX.name());
+ return (index <= 0) ? 0 : index;
+ }
+
+ @Override
+ public void switchAppLocale(Activity activity) {
+ mLocaleHelper.switchLocale(activity);
+ }
+
+ @Override
+ public void invalidatePreference() {
+ mPreferencesUtils.clear();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/presenter/TimelinePresenter.java b/app/src/main/java/com/tline/android/features/timeline/activity/presenter/TimelinePresenter.java
new file mode 100644
index 0000000..dc99aab
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/presenter/TimelinePresenter.java
@@ -0,0 +1,15 @@
+package com.tline.android.features.timeline.activity.presenter;
+
+import android.app.Activity;
+
+import com.tline.android.app.presenter.BasePresenter;
+import com.tline.android.features.timeline.activity.view.TimelineView;
+
+public interface TimelinePresenter extends BasePresenter {
+
+ void saveSelectedTabIndex(int selectedTabIndex);
+
+ void logout();
+
+ void switchAppLocale(Activity activity);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/presenter/impl/TimelinePresenterImpl.java b/app/src/main/java/com/tline/android/features/timeline/activity/presenter/impl/TimelinePresenterImpl.java
new file mode 100644
index 0000000..2328431
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/presenter/impl/TimelinePresenterImpl.java
@@ -0,0 +1,63 @@
+package com.tline.android.features.timeline.activity.presenter.impl;
+
+import android.app.Activity;
+import android.support.annotation.NonNull;
+
+import com.tline.android.app.presenter.impl.BasePresenterImpl;
+import com.tline.android.features.timeline.activity.presenter.TimelinePresenter;
+import com.tline.android.features.timeline.activity.view.TimelineView;
+import com.tline.android.features.timeline.activity.interactor.TimelineInteractor;
+
+import javax.inject.Inject;
+
+public final class TimelinePresenterImpl extends BasePresenterImpl implements TimelinePresenter {
+ /**
+ * The interactor
+ */
+ @NonNull
+ private final TimelineInteractor mInteractor;
+
+ // The view is available using the mView variable
+
+ @Inject
+ public TimelinePresenterImpl(@NonNull TimelineInteractor interactor) {
+ mInteractor = interactor;
+ }
+
+ @Override
+ public void onStart(boolean viewCreated) {
+ super.onStart(viewCreated);
+
+ if (viewCreated) {
+ initTimeline();
+ }
+ }
+
+ @Override
+ public void saveSelectedTabIndex(int selectedTabIndex) {
+ mInteractor.saveSelectedTabIndex(selectedTabIndex);
+ }
+
+ @Override
+ public void logout() {
+ assert mView != null;
+ mView.logoutTwitter();
+ mInteractor.invalidatePreference();
+ mView.launchLoginActivity();
+ }
+
+ @Override
+ public void switchAppLocale(Activity activity) {
+ mInteractor.switchAppLocale(activity);
+ }
+
+
+ private void initTimeline() {
+
+ assert mView != null;
+ mView.setSelectedNavItemId(mInteractor.retrieveSelectedTabIndex());
+ mView.showInitialFragment();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/view/TimelineView.java b/app/src/main/java/com/tline/android/features/timeline/activity/view/TimelineView.java
new file mode 100644
index 0000000..e0279db
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/view/TimelineView.java
@@ -0,0 +1,15 @@
+package com.tline.android.features.timeline.activity.view;
+
+import android.support.annotation.UiThread;
+
+import com.tline.android.app.view.BaseView;
+
+@UiThread
+public interface TimelineView extends BaseView {
+
+ void showInitialFragment();
+
+ void setSelectedNavItemId(int mSelectedNavItemId);
+
+ void launchLoginActivity();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/activity/view/impl/TimelineActivity.java b/app/src/main/java/com/tline/android/features/timeline/activity/view/impl/TimelineActivity.java
new file mode 100644
index 0000000..7d338b8
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/activity/view/impl/TimelineActivity.java
@@ -0,0 +1,169 @@
+package com.tline.android.features.timeline.activity.view.impl;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.BottomNavigationView;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.tline.android.R;
+import com.tline.android.app.injection.AppComponent;
+import com.tline.android.app.presenter.loader.PresenterFactory;
+import com.tline.android.app.view.impl.BaseActivity;
+import com.tline.android.features.login.view.impl.LoginActivity;
+import com.tline.android.features.timeline.activity.injection.DaggerTimelineViewComponent;
+import com.tline.android.features.timeline.activity.injection.TimelineViewModule;
+import com.tline.android.features.timeline.activity.presenter.TimelinePresenter;
+import com.tline.android.features.timeline.fragment.view.impl.TweetsFragment;
+import com.tline.android.features.timeline.activity.view.TimelineView;
+import com.twitter.sdk.android.core.TwitterCore;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+
+import static com.tline.android.constants.AppConstants.TWITTER_HANDLE_ANDROID_DEV;
+import static com.tline.android.constants.AppConstants.TWITTER_HANDLE_OLX_EGYPT;
+
+public final class TimelineActivity extends BaseActivity implements TimelineView {
+
+ private static final String SELECTED_NAV_ITEM = "SELECTED_NAV_ITEM";
+ @Inject
+ PresenterFactory mPresenterFactory;
+
+ @BindView(R.id.bottom_navigation)
+ BottomNavigationView mBottomNavigationView;
+
+ private int mSelectedNavItemId;
+
+
+ Fragment mUserTimeline = TweetsFragment.newInstance(TwitterCore.getInstance().getSessionManager().getActiveSession().getUserName());
+ Fragment mOlxEgypt = TweetsFragment.newInstance(TWITTER_HANDLE_OLX_EGYPT);
+ Fragment mAndroidDev = TweetsFragment.newInstance(TWITTER_HANDLE_ANDROID_DEV);
+
+ @Override
+ protected void setupComponent(@NonNull AppComponent parentComponent) {
+ DaggerTimelineViewComponent.builder()
+ .appComponent(parentComponent)
+ .timelineViewModule(new TimelineViewModule())
+ .build()
+ .inject(this);
+ }
+
+ @NonNull
+ @Override
+ protected PresenterFactory getPresenterFactory() {
+ return mPresenterFactory;
+ }
+
+ @Override
+ protected int getContentView() {
+ return R.layout.activity_timeline;
+ }
+
+ @Override
+ protected void onViewReady(Bundle savedInstanceState, Intent intent) {
+ super.onViewReady(savedInstanceState, intent);
+
+ prepareBottomNavigation();
+
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+// mPresenter.start();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+
+ assert mPresenter != null;
+ switch (id) {
+ case R.id.action_logout:
+ mPresenter.logout();
+ return true;
+ case R.id.action_language:
+ mPresenter.switchAppLocale(this);
+ return true;
+ }
+
+
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ private void prepareBottomNavigation() {
+
+ mBottomNavigationView.setOnNavigationItemSelectedListener(
+ new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ selectFragment(item);
+ return true;
+ }
+ }
+ );
+ }
+
+ private void selectFragment(MenuItem item) {
+
+ assert mPresenter != null;
+ switch (item.getItemId()) {
+ case R.id.action_timeline:
+ mPresenter.saveSelectedTabIndex(0);
+ replaceFragment(mUserTimeline);
+ break;
+ case R.id.action_olx_egypt:
+ mPresenter.saveSelectedTabIndex(1);
+ replaceFragment(mOlxEgypt);
+ break;
+ case R.id.action_android_dev:
+ mPresenter.saveSelectedTabIndex(2);
+ replaceFragment(mAndroidDev);
+ break;
+ }
+
+ }
+
+ @Override
+ public void showInitialFragment() {
+
+ MenuItem selectedItem = mBottomNavigationView.getMenu().getItem(mSelectedNavItemId);
+ if (selectedItem == null) {
+ selectedItem = mBottomNavigationView.getMenu().getItem(0);
+ }
+ selectedItem.setChecked(true);
+ selectFragment(selectedItem);
+ }
+
+ @Override
+ public void setSelectedNavItemId(int mSelectedNavItemId) {
+
+ this.mSelectedNavItemId = mSelectedNavItemId;
+ }
+
+ @Override
+ public void launchLoginActivity() {
+
+ Intent intent = new Intent(this, LoginActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void replaceFragment(Fragment targetFragment) {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.replace(R.id.container, targetFragment, targetFragment.getTag());
+ ft.commit();
+ }
+}
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewComponent.java b/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewComponent.java
new file mode 100644
index 0000000..689a077
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewComponent.java
@@ -0,0 +1,13 @@
+package com.tline.android.features.timeline.fragment.injection;
+
+import com.tline.android.app.injection.AppComponent;
+import com.tline.android.app.injection.FragmentScope;
+import com.tline.android.features.timeline.fragment.view.impl.TweetsFragment;
+
+import dagger.Component;
+
+@FragmentScope
+@Component(dependencies = AppComponent.class, modules = TweetsViewModule.class)
+public interface TweetsViewComponent {
+ void inject(TweetsFragment fragment);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewModule.java b/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewModule.java
new file mode 100644
index 0000000..4c3fc5c
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/injection/TweetsViewModule.java
@@ -0,0 +1,64 @@
+package com.tline.android.features.timeline.fragment.injection;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import com.tline.android.app.presenter.loader.PresenterFactory;
+import com.tline.android.features.timeline.fragment.interactor.TweetsInteractor;
+import com.tline.android.features.timeline.fragment.interactor.impl.TweetsInteractorImpl;
+import com.tline.android.features.timeline.fragment.presenter.TweetsPresenter;
+import com.tline.android.features.timeline.fragment.presenter.impl.TweetsPresenterImpl;
+import com.twitter.sdk.android.core.TwitterApiClient;
+import com.twitter.sdk.android.core.TwitterCore;
+import com.twitter.sdk.android.core.TwitterSession;
+
+import dagger.Module;
+import dagger.Provides;
+import okhttp3.OkHttpClient;
+import okhttp3.logging.HttpLoggingInterceptor;
+
+@Module
+public final class TweetsViewModule {
+ @Provides
+ public TweetsInteractor provideInteractor(Context context, TwitterApiClient twitterApiClient) {
+ return new TweetsInteractorImpl(context, twitterApiClient);
+ }
+
+ @Provides
+ public PresenterFactory providePresenterFactory(@NonNull final TweetsInteractor interactor) {
+ return new PresenterFactory() {
+ @NonNull
+ @Override
+ public TweetsPresenter create() {
+ return new TweetsPresenterImpl(interactor);
+ }
+ };
+ }
+
+
+ /**
+ * Providing custom API Client for logging and intercepting the requests
+ * @return
+ */
+ @Provides
+ public TwitterApiClient provideTwitterApiClient() {
+ final TwitterSession activeSession = TwitterCore.getInstance().getSessionManager().getActiveSession();
+
+ // example of custom OkHttpClient with logging HttpLoggingInterceptor
+ final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
+ loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
+ final OkHttpClient customClient = new OkHttpClient.Builder().addInterceptor(loggingInterceptor).build();
+
+ // pass custom OkHttpClient into TwitterApiClient and add to TwitterCore
+ final TwitterApiClient customApiClient;
+ if (activeSession != null) {
+ customApiClient = new TwitterApiClient(activeSession, customClient);
+ TwitterCore.getInstance().addApiClient(activeSession, customApiClient);
+ } else {
+ customApiClient = new TwitterApiClient(customClient);
+ TwitterCore.getInstance().addGuestApiClient(customApiClient);
+ }
+
+ return customApiClient;
+ }
+}
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/TweetsInteractor.java b/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/TweetsInteractor.java
new file mode 100644
index 0000000..1389e2f
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/TweetsInteractor.java
@@ -0,0 +1,14 @@
+package com.tline.android.features.timeline.fragment.interactor;
+
+import com.tline.android.app.interactor.BaseInteractor;
+import com.tline.android.features.timeline.fragment.presenter.TweetsPresenter;
+import com.tline.android.features.timeline.fragment.presenter.impl.TweetsPresenterImpl;
+
+public interface TweetsInteractor extends BaseInteractor {
+
+ boolean isNetworkConnected();
+
+ void fetchTweets(String twitterHandle, TweetsPresenter.OnFetchDataListener listener);
+
+ String getErrorString();
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/impl/TweetsInteractorImpl.java b/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/impl/TweetsInteractorImpl.java
new file mode 100644
index 0000000..682ed31
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/interactor/impl/TweetsInteractorImpl.java
@@ -0,0 +1,65 @@
+package com.tline.android.features.timeline.fragment.interactor.impl;
+
+import android.content.Context;
+
+import com.tline.android.R;
+import com.tline.android.app.interactor.impl.BaseInteractorImpl;
+import com.tline.android.features.timeline.fragment.interactor.TweetsInteractor;
+import com.tline.android.features.timeline.fragment.presenter.TweetsPresenter;
+import com.tline.android.utils.NetworkUtils;
+import com.twitter.sdk.android.core.Callback;
+import com.twitter.sdk.android.core.Result;
+import com.twitter.sdk.android.core.TwitterApiClient;
+import com.twitter.sdk.android.core.TwitterException;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import retrofit2.Call;
+
+public final class TweetsInteractorImpl extends BaseInteractorImpl implements TweetsInteractor {
+
+ private final Context mContext;
+ private final TwitterApiClient mTwitterApiClient;
+
+ @Inject
+ public TweetsInteractorImpl(Context context, TwitterApiClient twitterApiClient) {
+
+ mContext = context;
+ mTwitterApiClient = twitterApiClient;
+ }
+
+
+ @Override
+ public boolean isNetworkConnected() {
+ return NetworkUtils.isNetAvailable(mContext);
+ }
+
+ @Override
+ public void fetchTweets(String twitterHandle, final TweetsPresenter.OnFetchDataListener listener) {
+
+ listener.onStart();
+
+ final Call> listCall = mTwitterApiClient.getStatusesService().userTimeline(null, twitterHandle, 50, null, null, false, true, false, true);
+ listCall.enqueue(new Callback>() {
+ @Override
+ public void success(Result> result) {
+ listener.onSuccess(result.data);
+ listener.onComplete();
+ }
+
+ @Override
+ public void failure(TwitterException exception) {
+ listener.onFailure(exception.getMessage());
+ listener.onComplete();
+ }
+ });
+ }
+
+ @Override
+ public String getErrorString() {
+ return mContext.getString(R.string.error_no_network);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/TweetsPresenter.java b/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/TweetsPresenter.java
new file mode 100644
index 0000000..d26e9ec
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/TweetsPresenter.java
@@ -0,0 +1,23 @@
+package com.tline.android.features.timeline.fragment.presenter;
+
+import com.tline.android.app.presenter.BasePresenter;
+import com.tline.android.features.timeline.fragment.view.TweetsView;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.List;
+
+public interface TweetsPresenter extends BasePresenter {
+
+ interface OnFetchDataListener {
+
+ void onStart();
+
+ void onSuccess(List tweets);
+
+ void onFailure(String message);
+
+ void onComplete();
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/impl/TweetsPresenterImpl.java b/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/impl/TweetsPresenterImpl.java
new file mode 100644
index 0000000..54ff203
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/presenter/impl/TweetsPresenterImpl.java
@@ -0,0 +1,91 @@
+package com.tline.android.features.timeline.fragment.presenter.impl;
+
+import android.support.annotation.NonNull;
+
+import com.tline.android.app.presenter.impl.BasePresenterImpl;
+import com.tline.android.features.timeline.fragment.presenter.TweetsPresenter;
+import com.tline.android.features.timeline.fragment.view.TweetsView;
+import com.tline.android.features.timeline.fragment.interactor.TweetsInteractor;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import timber.log.Timber;
+
+public final class TweetsPresenterImpl extends BasePresenterImpl implements TweetsPresenter, TweetsPresenter.OnFetchDataListener {
+ /**
+ * The interactor
+ */
+ @NonNull
+ private final TweetsInteractor mInteractor;
+
+ @Inject
+ public TweetsPresenterImpl(@NonNull TweetsInteractor interactor) {
+ mInteractor = interactor;
+ }
+
+ @Override
+ public void onStart(boolean viewCreated) {
+ super.onStart(viewCreated);
+
+ if (viewCreated) {
+ initLoading();
+ }
+ }
+
+ @Override
+ public void onStop() {
+
+ mInteractor.cancelOnGoingHttpRequest();
+ Timber.e("onPresenterStopped()");
+ super.onStop();
+ }
+
+
+ private void initLoading() {
+ if(mInteractor.isNetworkConnected()) {
+ loadData();
+ }else{
+ if (mView != null) {
+ mView.showErrorMessage(mInteractor.getErrorString());
+ }
+ }
+ }
+
+ private void loadData() {
+ assert mView != null;
+ String twitterHandle = mView.getTwitterHandle();
+ mInteractor.fetchTweets(twitterHandle, this);
+ }
+
+ @Override
+ public void onStart() {
+ assert mView != null;
+ mView.showLoading();
+ }
+
+ @Override
+ public void onSuccess(List tweets) {
+
+ if (mView != null) {
+ mView.loadData(tweets);
+ }
+ }
+
+ @Override
+ public void onFailure(String message) {
+ if (mView != null) {
+ mView.showErrorMessage(message);
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ if (mView != null) {
+ mView.hideLoading();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/view/TweetsView.java b/app/src/main/java/com/tline/android/features/timeline/fragment/view/TweetsView.java
new file mode 100644
index 0000000..3e48755
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/view/TweetsView.java
@@ -0,0 +1,19 @@
+package com.tline.android.features.timeline.fragment.view;
+
+import android.support.annotation.UiThread;
+
+import com.tline.android.app.view.BaseFragmentView;
+import com.tline.android.app.view.BaseView;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.List;
+
+@UiThread
+public interface TweetsView extends BaseFragmentView {
+
+ String getTwitterHandle();
+
+ void loadData(List tweets);
+
+ void showErrorMessage(String message);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/view/adapter/RecyclerViewAdapter.java b/app/src/main/java/com/tline/android/features/timeline/fragment/view/adapter/RecyclerViewAdapter.java
new file mode 100644
index 0000000..0840b47
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/view/adapter/RecyclerViewAdapter.java
@@ -0,0 +1,56 @@
+package com.tline.android.features.timeline.fragment.view.adapter;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.tline.android.R;
+import com.tline.android.features.timeline.fragment.view.holder.RecyclerViewItemHolder;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RecyclerViewAdapter extends RecyclerView.Adapter {
+
+ private List mList;
+ private Context mContext;
+
+ public RecyclerViewAdapter(Context context) {
+ this.mList = new ArrayList<>();
+ mContext = context;
+ }
+
+ @Override
+ public int getItemCount() {
+ return this.mList.size();
+ }
+
+ @Override
+ public RecyclerViewItemHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+
+ LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
+ View view = inflater.inflate(R.layout.item_list, viewGroup, false);
+ return new RecyclerViewItemHolder(mContext, view);
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerViewItemHolder viewHolder, int position) {
+
+ viewHolder.setTweet(mList.get(position));
+ }
+
+ public void clear() {
+
+ mList.clear();
+ notifyDataSetChanged();
+ }
+
+ public void addAll(List list) {
+
+ mList.addAll(list);
+ notifyDataSetChanged();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/view/holder/RecyclerViewItemHolder.java b/app/src/main/java/com/tline/android/features/timeline/fragment/view/holder/RecyclerViewItemHolder.java
new file mode 100644
index 0000000..d0e4fce
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/view/holder/RecyclerViewItemHolder.java
@@ -0,0 +1,74 @@
+package com.tline.android.features.timeline.fragment.view.holder;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.tline.android.R;
+import com.tline.android.utils.DateTimeUtils;
+import com.tline.android.utils.DialogUtils;
+import com.tline.android.utils.ImageUtils;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+
+public class RecyclerViewItemHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+ @BindView(R.id.ivTweetImage)
+ ImageView mImageViewTweetImage;
+ @BindView(R.id.tvTweetText)
+ TextView mTextViewTweetWithImage;
+ @BindView(R.id.tvUserName)
+ TextView mTextViewUserName;
+ @BindView(R.id.tvTwitterHandle)
+ TextView mTextViewTwitterHandle;
+ @BindView(R.id.tvTimeSend)
+ TextView mTextViewTimeSend;
+ @BindView(R.id.imageView_dp)
+ ImageView mImageViewProfileImage;
+
+ Context mContext;
+ private Tweet mTweet;
+
+ public RecyclerViewItemHolder(Context context, View view) {
+ super(view);
+
+ this.mContext = context;
+
+ ButterKnife.bind(this, view);
+
+ view.setOnClickListener(this);
+ }
+
+ public void setTweet(Tweet mTweet) {
+ this.mTweet = mTweet;
+ bind();
+ }
+
+ @Override
+ public void onClick(View view) {
+
+ DialogUtils.showTweetDialog(mContext, mTweet);
+ }
+
+ private void bind() {
+
+ this.mTextViewUserName.setText(mTweet.user.name);
+ this.mTextViewTimeSend.setText(DateTimeUtils.getRelativeTimeAgo(mTweet.createdAt));
+ this.mTextViewTwitterHandle.setText(mContext.getString(R.string.prefix_screenname, mTweet.user.screenName));
+
+ this.mTextViewTweetWithImage.setText(mTweet.text);
+
+ ImageUtils.loadImage(mContext, this.mImageViewProfileImage, mTweet.user.profileImageUrl);
+
+ if (!mTweet.entities.media.isEmpty() && mTweet.entities.media.get(0).type.equals("photo")) {
+ ImageUtils.loadImage(mContext, this.mImageViewTweetImage, mTweet.entities.media.get(0).mediaUrl);
+ mImageViewTweetImage.setVisibility(View.VISIBLE);
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/tline/android/features/timeline/fragment/view/impl/TweetsFragment.java b/app/src/main/java/com/tline/android/features/timeline/fragment/view/impl/TweetsFragment.java
new file mode 100644
index 0000000..c47e46a
--- /dev/null
+++ b/app/src/main/java/com/tline/android/features/timeline/fragment/view/impl/TweetsFragment.java
@@ -0,0 +1,140 @@
+package com.tline.android.features.timeline.fragment.view.impl;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.tline.android.R;
+import com.tline.android.app.injection.AppComponent;
+import com.tline.android.app.presenter.loader.PresenterFactory;
+import com.tline.android.app.view.impl.BaseActivity;
+import com.tline.android.app.view.impl.BaseFragment;
+import com.tline.android.features.timeline.fragment.injection.DaggerTweetsViewComponent;
+import com.tline.android.features.timeline.fragment.injection.TweetsViewModule;
+import com.tline.android.features.timeline.fragment.presenter.TweetsPresenter;
+import com.tline.android.features.timeline.fragment.view.TweetsView;
+import com.tline.android.features.timeline.activity.view.impl.TimelineActivity;
+import com.tline.android.features.timeline.fragment.view.adapter.RecyclerViewAdapter;
+import com.twitter.sdk.android.core.Callback;
+import com.twitter.sdk.android.core.Result;
+import com.twitter.sdk.android.core.TwitterApiClient;
+import com.twitter.sdk.android.core.TwitterCore;
+import com.twitter.sdk.android.core.TwitterException;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import retrofit2.Call;
+import timber.log.Timber;
+
+public final class TweetsFragment extends BaseFragment implements TweetsView {
+ private static final String TARGET_HANDLE = "TARGET_HANDLE";
+ @Inject
+ PresenterFactory mPresenterFactory;
+
+ protected RecyclerView mRecyclerView;
+
+ private RecyclerViewAdapter mRecyclerViewAdapter;
+
+ private String mTwitterHandle;
+
+ public TweetsFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_tweets, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // retrieve text and color from bundle or savedInstanceState
+ if (savedInstanceState == null) {
+ Bundle args = getArguments();
+ mTwitterHandle = args.getString(TARGET_HANDLE);
+ } else {
+ mTwitterHandle = savedInstanceState.getString(TARGET_HANDLE);
+ }
+
+ mRecyclerView = view.findViewById(R.id.recyclerView);
+
+ init();
+
+ }
+
+ @Override
+ protected void setupComponent(@NonNull AppComponent parentComponent) {
+ DaggerTweetsViewComponent.builder()
+ .appComponent(parentComponent)
+ .tweetsViewModule(new TweetsViewModule())
+ .build()
+ .inject(this);
+ }
+
+ @NonNull
+ @Override
+ protected PresenterFactory getPresenterFactory() {
+ return mPresenterFactory;
+ }
+
+ public static Fragment newInstance(String twitterHandle) {
+ Fragment fragment = new TweetsFragment();
+ Bundle args = new Bundle();
+ args.putString(TARGET_HANDLE, twitterHandle);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putString(TARGET_HANDLE, mTwitterHandle);
+ super.onSaveInstanceState(outState);
+ }
+
+ private void init() {
+
+ mRecyclerViewAdapter = new RecyclerViewAdapter(getActivity());
+ mRecyclerView.setAdapter(mRecyclerViewAdapter);
+
+ RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL);
+ mRecyclerView.addItemDecoration(itemDecoration);
+
+ LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ layoutManager.scrollToPosition(0);
+
+ mRecyclerView.setLayoutManager(layoutManager);
+
+ }
+
+ @Override
+ public String getTwitterHandle() {
+ return mTwitterHandle;
+ }
+
+ @Override
+ public void loadData(List tweets) {
+ mRecyclerViewAdapter.clear();
+ mRecyclerViewAdapter.addAll(tweets);
+ }
+
+ @Override
+ public void showErrorMessage(String message) {
+ ((BaseActivity)getActivity()).showErrorWithMessage(message);
+ }
+
+
+}
diff --git a/app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewModule.java b/app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewModule.java
deleted file mode 100644
index d70d072..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/injection/TimelineViewModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.tline.android.features.timeline.injection;
-
-import android.support.annotation.NonNull;
-
-import com.tline.android.app.presenter.loader.PresenterFactory;
-import com.tline.android.features.timeline.interactor.TimelineInteractor;
-import com.tline.android.features.timeline.interactor.impl.TimelineInteractorImpl;
-import com.tline.android.features.timeline.presenter.TimelinePresenter;
-import com.tline.android.features.timeline.presenter.impl.TimelinePresenterImpl;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public final class TimelineViewModule {
- @Provides
- public TimelineInteractor provideInteractor() {
- return new TimelineInteractorImpl();
- }
-
- @Provides
- public PresenterFactory providePresenterFactory(@NonNull final TimelineInteractor interactor) {
- return new PresenterFactory() {
- @NonNull
- @Override
- public TimelinePresenter create() {
- return new TimelinePresenterImpl(interactor);
- }
- };
- }
-}
diff --git a/app/src/main/java/com/tline/android/features/timeline/interactor/TimelineInteractor.java b/app/src/main/java/com/tline/android/features/timeline/interactor/TimelineInteractor.java
deleted file mode 100644
index 447c4e9..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/interactor/TimelineInteractor.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.tline.android.features.timeline.interactor;
-
-import com.tline.android.app.interactor.BaseInteractor;
-
-public interface TimelineInteractor extends BaseInteractor {
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/interactor/impl/TimelineInteractorImpl.java b/app/src/main/java/com/tline/android/features/timeline/interactor/impl/TimelineInteractorImpl.java
deleted file mode 100644
index d57a39c..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/interactor/impl/TimelineInteractorImpl.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.tline.android.features.timeline.interactor.impl;
-
-import javax.inject.Inject;
-
-import com.tline.android.app.interactor.impl.BaseInteractorImpl;
-import com.tline.android.features.timeline.interactor.TimelineInteractor;
-
-public final class TimelineInteractorImpl extends BaseInteractorImpl implements TimelineInteractor {
- @Inject
- public TimelineInteractorImpl() {
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/presenter/TimelinePresenter.java b/app/src/main/java/com/tline/android/features/timeline/presenter/TimelinePresenter.java
deleted file mode 100644
index 8fd03a0..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/presenter/TimelinePresenter.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.tline.android.features.timeline.presenter;
-
-import com.tline.android.app.presenter.BasePresenter;
-import com.tline.android.features.timeline.view.TimelineView;
-
-public interface TimelinePresenter extends BasePresenter {
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/presenter/impl/TimelinePresenterImpl.java b/app/src/main/java/com/tline/android/features/timeline/presenter/impl/TimelinePresenterImpl.java
deleted file mode 100644
index 2c042ca..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/presenter/impl/TimelinePresenterImpl.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.tline.android.features.timeline.presenter.impl;
-
-import android.support.annotation.NonNull;
-
-import com.tline.android.app.presenter.impl.BasePresenterImpl;
-import com.tline.android.features.timeline.presenter.TimelinePresenter;
-import com.tline.android.features.timeline.view.TimelineView;
-import com.tline.android.features.timeline.interactor.TimelineInteractor;
-
-import javax.inject.Inject;
-
-public final class TimelinePresenterImpl extends BasePresenterImpl implements TimelinePresenter {
- /**
- * The interactor
- */
- @NonNull
- private final TimelineInteractor mInteractor;
-
- // The view is available using the mView variable
-
- @Inject
- public TimelinePresenterImpl(@NonNull TimelineInteractor interactor) {
- mInteractor = interactor;
- }
-
- @Override
- public void onStart(boolean viewCreated) {
- super.onStart(viewCreated);
-
- // Your code here. Your view is available using mView and will not be null until next onStop()
- }
-
- @Override
- public void onStop() {
- // Your code here, mView will be null after this method until next onStart()
-
- super.onStop();
- }
-
- @Override
- public void onPresenterDestroyed() {
- /*
- * Your code here. After this method, your presenter (and view) will be completely destroyed
- * so make sure to cancel any HTTP call or database connection
- */
-
- super.onPresenterDestroyed();
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/view/TimelineView.java b/app/src/main/java/com/tline/android/features/timeline/view/TimelineView.java
deleted file mode 100644
index 853c931..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/view/TimelineView.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.tline.android.features.timeline.view;
-
-import android.support.annotation.UiThread;
-
-import com.tline.android.app.view.BaseView;
-
-@UiThread
-public interface TimelineView extends BaseView {
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tline/android/features/timeline/view/impl/TimelineActivity.java b/app/src/main/java/com/tline/android/features/timeline/view/impl/TimelineActivity.java
deleted file mode 100644
index 876ae39..0000000
--- a/app/src/main/java/com/tline/android/features/timeline/view/impl/TimelineActivity.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.tline.android.features.timeline.view.impl;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.BottomNavigationView;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-
-import com.tline.android.R;
-import com.tline.android.app.injection.AppComponent;
-import com.tline.android.app.presenter.loader.PresenterFactory;
-import com.tline.android.app.view.impl.BaseActivity;
-import com.tline.android.features.login.view.impl.LoginActivity;
-import com.tline.android.features.timeline.injection.DaggerTimelineViewComponent;
-import com.tline.android.features.timeline.injection.TimelineViewModule;
-import com.tline.android.features.timeline.presenter.TimelinePresenter;
-import com.tline.android.features.timeline.view.TimelineView;
-import com.tline.android.utils.LocaleHelper;
-
-import java.util.Locale;
-
-import javax.inject.Inject;
-
-import butterknife.BindView;
-import timber.log.Timber;
-
-public final class TimelineActivity extends BaseActivity implements TimelineView {
-
- @Inject
- PresenterFactory mPresenterFactory;
-
- @BindView(R.id.bottom_navigation)
- BottomNavigationView mBottomNavigationView;
-
- @Override
- protected void setupComponent(@NonNull AppComponent parentComponent) {
- DaggerTimelineViewComponent.builder()
- .appComponent(parentComponent)
- .timelineViewModule(new TimelineViewModule())
- .build()
- .inject(this);
- }
-
- @NonNull
- @Override
- protected PresenterFactory getPresenterFactory() {
- return mPresenterFactory;
- }
-
- @Override
- protected int getContentView() {
- return R.layout.activity_timeline;
- }
-
- @Override
- protected void onViewReady(Bundle savedInstanceState, Intent intent) {
- super.onViewReady(savedInstanceState, intent);
-
-
- prepareBottomNavigation();
- }
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
-
- switch (id) {
- case R.id.action_logout:
- Timber.e("R.id.action_logout");
- logoutTwitter();
- startLoginActivity();
- return true;
- case R.id.action_language:
- LocaleHelper.switchLocale(this);
- return true;
- }
-
-
- return super.onOptionsItemSelected(item);
- }
-
- private void prepareBottomNavigation() {
- mBottomNavigationView.setOnNavigationItemSelectedListener(
- new BottomNavigationView.OnNavigationItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_timeline:
- Timber.e("R.id.action_timeline");
- break;
- case R.id.action_olx_egypt:
- Timber.e("R.id.action_olx_egypt");
- break;
- case R.id.action_android_dev:
- Timber.e("R.id.action_android_dev");
- break;
- }
- return true;
- }
- }
- );
- }
-
-
- private void startLoginActivity() {
- Intent intent = new Intent(this, LoginActivity.class);
- startActivity(intent);
- finish();
- }
-}
diff --git a/app/src/main/java/com/tline/android/utils/DateTimeUtils.java b/app/src/main/java/com/tline/android/utils/DateTimeUtils.java
new file mode 100644
index 0000000..be6df2d
--- /dev/null
+++ b/app/src/main/java/com/tline/android/utils/DateTimeUtils.java
@@ -0,0 +1,46 @@
+package com.tline.android.utils;
+
+import android.text.format.DateUtils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Created by Naeem(naeemark@gmail.com)
+ * On 29/11/2017.
+ * For TLine
+ */
+
+public class DateTimeUtils {
+
+ static final String TWITTER_FORMAT = "EEE MMM dd HH:mm:ss ZZZZZ yyyy";
+
+ public static String getRelativeTimeAgo(String rawJsonDate) {
+ SimpleDateFormat sf = new SimpleDateFormat(TWITTER_FORMAT, Locale.ENGLISH);
+ sf.setLenient(true);
+
+ String relativeDate = "";
+ try {
+ long dateMillis = sf.parse(rawJsonDate).getTime();
+ relativeDate = DateUtils.getRelativeTimeSpanString(dateMillis, System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ return (relativeDate.equals("")) ? rawJsonDate : relativeDate;
+ }
+
+ public static String formatTimeForFooter(String createdAt) {
+ SimpleDateFormat sf = new SimpleDateFormat(TWITTER_FORMAT, Locale.ENGLISH);
+//1:00 AM - 29 Nov 2017
+ try {
+ long dateMillis = sf.parse(createdAt).getTime();
+ return new SimpleDateFormat("dd MMM yy", Locale.ENGLISH).format(dateMillis);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+}
diff --git a/app/src/main/java/com/tline/android/utils/DialogUtils.java b/app/src/main/java/com/tline/android/utils/DialogUtils.java
new file mode 100644
index 0000000..960cff3
--- /dev/null
+++ b/app/src/main/java/com/tline/android/utils/DialogUtils.java
@@ -0,0 +1,77 @@
+package com.tline.android.utils;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.tline.android.R;
+import com.twitter.sdk.android.core.models.Tweet;
+
+import timber.log.Timber;
+
+/**
+ * Created by Naeem(naeemark@gmail.com)
+ * On 29/11/2017.
+ * For TLine
+ */
+
+public class DialogUtils {
+
+ public static void showTweetDialog(Context context, Tweet tweet) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(tweet.user.name)
+ .setCancelable(true)
+ .setIcon(R.drawable.ic_launcher_round_web)
+ ;
+
+ builder.setPositiveButton(context.getString(R.string.lbl_button_close), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+
+ final FrameLayout frameView = new FrameLayout(context);
+ builder.setView(frameView);
+
+ final AlertDialog alertDialog = builder.create();
+ LayoutInflater inflater = alertDialog.getLayoutInflater();
+ View view = inflater.inflate(R.layout.item_list, frameView);
+
+ view.findViewById(R.id.tvUserName).setVisibility(View.GONE);
+ view.findViewById(R.id.wrapper_footer).setVisibility(View.VISIBLE);
+
+ TextView screenName = view.findViewById(R.id.tvTwitterHandle);
+ TextView tweetText = view.findViewById(R.id.tvTweetText);
+ TextView timeSent = view.findViewById(R.id.tvTimeSend);
+ ImageView dpImage = view.findViewById(R.id.imageView_dp);
+ ImageView tweetImage = view.findViewById(R.id.ivTweetImage);
+ TextView time = view.findViewById(R.id.replyCount);
+ TextView retweets = view.findViewById(R.id.reTweetCount);
+ TextView likes = view.findViewById(R.id.likeCount);
+
+ timeSent.setText(DateTimeUtils.getRelativeTimeAgo(tweet.createdAt));
+ screenName.setText(context.getString(R.string.prefix_screenname, tweet.user.screenName));
+
+ tweetText.setText(tweet.text);
+
+ time.setText(DateTimeUtils.formatTimeForFooter(tweet.createdAt));
+ retweets.setText(String.valueOf(tweet.retweetCount));
+ likes.setText(String.valueOf(tweet.favoriteCount));
+
+ ImageUtils.loadImage(context, dpImage, tweet.user.profileImageUrl);
+
+ if (!tweet.entities.media.isEmpty() && tweet.entities.media.get(0).type.equals("photo")) {
+ ImageUtils.loadImage(context, tweetImage, tweet.entities.media.get(0).mediaUrl);
+ tweetImage.setVisibility(View.VISIBLE);
+ }
+ Timber.e(tweet.toString());
+
+ alertDialog.show();
+ }
+}
diff --git a/app/src/main/java/com/tline/android/utils/ImageUtils.java b/app/src/main/java/com/tline/android/utils/ImageUtils.java
new file mode 100644
index 0000000..ae6a936
--- /dev/null
+++ b/app/src/main/java/com/tline/android/utils/ImageUtils.java
@@ -0,0 +1,41 @@
+package com.tline.android.utils;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.tline.android.R;
+
+/**
+ * Created by Naeem(naeemark@gmail.com)
+ * On 29/11/2017.
+ * For TLine
+ */
+
+public class ImageUtils {
+ /**
+ * Generifies the image loading through the application
+ *
+ * @param context
+ * @param target
+ * @param url
+ */
+ public static void loadImage(Context context, final ImageView target, String url) {
+
+ Glide.with(context)
+ .load((url == null || url.isEmpty()) ? R.drawable.ic_launcher_round_web : url)
+ .asBitmap()
+ .placeholder(R.drawable.ic_launcher_round_web)
+ .diskCacheStrategy(DiskCacheStrategy.RESULT)
+ .into(new SimpleTarget() {
+ @Override
+ public void onResourceReady(Bitmap arg0, GlideAnimation super Bitmap> arg1) {
+ target.setImageBitmap(arg0);
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/com/tline/android/utils/LocaleHelper.java b/app/src/main/java/com/tline/android/utils/LocaleHelper.java
index 8d17979..5a17921 100644
--- a/app/src/main/java/com/tline/android/utils/LocaleHelper.java
+++ b/app/src/main/java/com/tline/android/utils/LocaleHelper.java
@@ -6,30 +6,39 @@
import android.content.res.Configuration;
import android.content.res.Resources;
-import com.tline.android.features.splash.SplashActivity;
-import com.tline.android.features.timeline.view.impl.TimelineActivity;
+import com.tline.android.features.timeline.activity.view.impl.TimelineActivity;
import java.util.Locale;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Created by Naeem(naeemark@gmail.com)
* On 28/11/2017.
* For TLine
*/
+@Singleton
public class LocaleHelper {
- public static void switchLocale(Activity activity) {
+
+ @Inject
+ public LocaleHelper() {
+
+ }
+
+ public void switchLocale(Activity activity) {
Resources resources = activity.getResources();
Configuration config = resources.getConfiguration();
Locale locale = getNewLocale(config);
config.locale = locale;
config.setLayoutDirection(locale);
resources.updateConfiguration(config, resources.getDisplayMetrics());
- relaunch(activity);
+ relaunchTimelineActivity(activity);
}
- private static Locale getNewLocale(Configuration configuration) {
+ private Locale getNewLocale(Configuration configuration) {
if (configuration.locale.getLanguage().equals("ar")) {
return new Locale("en");
} else {
@@ -37,11 +46,11 @@ private static Locale getNewLocale(Configuration configuration) {
}
}
- private static void relaunch(Context context) {
- Intent i = new Intent(context, TimelineActivity.class);
+ private void relaunchTimelineActivity(Activity activity) {
+ Intent i = new Intent(activity, TimelineActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
+ activity.startActivity(i);
}
}
diff --git a/app/src/main/java/com/tline/android/utils/PreferencesUtils.java b/app/src/main/java/com/tline/android/utils/PreferencesUtils.java
index 1b35049..57c5a96 100644
--- a/app/src/main/java/com/tline/android/utils/PreferencesUtils.java
+++ b/app/src/main/java/com/tline/android/utils/PreferencesUtils.java
@@ -17,6 +17,7 @@ public class PreferencesUtils {
public enum PrefKeys {
IS_SPLASH_DONE,
+ SELECTED_TAB_INDEX,
}
private final SharedPreferences mPref;
@@ -30,7 +31,11 @@ public void clear() {
mPref.edit().clear().apply();
}
- public void putData(String key, String data) {
+ public void putInt(String key, int digit) {
+ mPref.edit().putInt(key, digit).apply();
+ }
+
+ public void putString(String key, String data) {
mPref.edit().putString(key, data).apply();
}
@@ -38,7 +43,11 @@ public void putBoolean(String key, boolean flag) {
mPref.edit().putBoolean(key, flag).apply();
}
- public String getData(String key) {
+ public int getInt(String key) {
+ return mPref.getInt(key, -1);
+ }
+
+ public String getString(String key) {
return mPref.getString(key, null);
}
diff --git a/app/src/main/res/drawable/bg_row.xml b/app/src/main/res/drawable/bg_row.xml
new file mode 100644
index 0000000..ba62fa8
--- /dev/null
+++ b/app/src/main/res/drawable/bg_row.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_like.xml b/app/src/main/res/drawable/ic_like.xml
new file mode 100644
index 0000000..ae469d9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_like.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_reply.xml b/app/src/main/res/drawable/ic_reply.xml
new file mode 100644
index 0000000..7ac4c89
--- /dev/null
+++ b/app/src/main/res/drawable/ic_reply.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_retweet.xml b/app/src/main/res/drawable/ic_retweet.xml
new file mode 100644
index 0000000..23f8680
--- /dev/null
+++ b/app/src/main/res/drawable/ic_retweet.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-land/activity_login.xml b/app/src/main/res/layout-land/activity_login.xml
new file mode 100644
index 0000000..aba6c5a
--- /dev/null
+++ b/app/src/main/res/layout-land/activity_login.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 52b2ce2..d29934e 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,56 +1,55 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="40dp"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_timeline.xml b/app/src/main/res/layout/activity_timeline.xml
index 564d75c..079bf4c 100644
--- a/app/src/main/res/layout/activity_timeline.xml
+++ b/app/src/main/res/layout/activity_timeline.xml
@@ -1,24 +1,25 @@
-
+ android:orientation="vertical"
+ tools:context=".features.timeline.activity.view.impl.TimelineActivity">
-
+
-
+ android:layout_gravity="start"
+ android:background="@color/colorPrimary"
+ design:itemIconTint="@drawable/selector_action_nav"
+ design:itemTextColor="@drawable/selector_action_nav"
+ design:menu="@menu/bottom_navigation" />
+
diff --git a/app/src/main/res/layout/fragment_tweets.xml b/app/src/main/res/layout/fragment_tweets.xml
new file mode 100644
index 0000000..272dcc7
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tweets.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/item_list.xml b/app/src/main/res/layout/item_list.xml
new file mode 100644
index 0000000..ec70eb2
--- /dev/null
+++ b/app/src/main/res/layout/item_list.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/view_tweet_footer.xml b/app/src/main/res/layout/view_tweet_footer.xml
new file mode 100644
index 0000000..6fb345f
--- /dev/null
+++ b/app/src/main/res/layout/view_tweet_footer.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_tweet_header.xml b/app/src/main/res/layout/view_tweet_header.xml
new file mode 100644
index 0000000..40275e2
--- /dev/null
+++ b/app/src/main/res/layout/view_tweet_header.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/botton_navigation.xml b/app/src/main/res/menu/bottom_navigation.xml
similarity index 100%
rename from app/src/main/res/menu/botton_navigation.xml
rename to app/src/main/res/menu/bottom_navigation.xml
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index ad7c6db..9ee0f89 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -4,4 +4,5 @@
عربى
مرحبا بكم في النظام
EN
+ أغلق
\ 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 27181b2..bcd89bc 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -15,4 +15,9 @@
@color/colorPrimary
@color/colorPrimaryDark
+
+ @android:color/white
+ @android:color/black
+ #939393
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 0156295..0c35cd1 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -15,17 +15,19 @@
36dp
1dp
2dp
- 3dp
+ 4dp
15sp
16sp
- 256dp
+ 180dp
144dp
96dp
+ 64dp
+ 2dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 09aa1a4..615a8ee 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -21,5 +21,8 @@
Timeline
\@OLXEgypt
\@AndroidDev
+ \@%s
+
+ CLOSE
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index dde1b17..2715d5f 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -5,6 +5,7 @@
- @color/colorPrimary
- @color/colorPrimaryDark
- @color/colorAccent
+ - @android:color/black
+
+
+