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 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 @@ + + + + + + + + + + + + + + +