├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_profile.xml
│ │ │ │ ├── ic_login.xml
│ │ │ │ ├── ic_register.xml
│ │ │ │ ├── ic_logout.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── menu
│ │ │ │ └── navigation.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── ir
│ │ │ │ └── oveissi
│ │ │ │ └── androidmodularization
│ │ │ │ ├── MainActivityComponent.java
│ │ │ │ ├── BaseApplication.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── androidmodularization
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── ir
│ │ └── oveissi
│ │ └── androidmodularization
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── core
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── colors.xml
│ │ │ └── layout
│ │ │ │ └── toolbar.xml
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── core
│ │ │ ├── navigation
│ │ │ ├── HasNavigatorManager.java
│ │ │ ├── SearchEntryPoint.java
│ │ │ ├── MainEntryPoint.java
│ │ │ ├── EntryPointHolder.java
│ │ │ ├── AuthEntryPoint.java
│ │ │ ├── NavigationManager.java
│ │ │ └── Navigator.java
│ │ │ ├── utils
│ │ │ └── Constants.java
│ │ │ ├── bases
│ │ │ ├── AppComponentHolder.java
│ │ │ ├── BaseFragment.java
│ │ │ └── BaseActivity.java
│ │ │ ├── pojo
│ │ │ ├── Pagination.java
│ │ │ ├── RegisterBody.java
│ │ │ ├── Token.java
│ │ │ ├── Metadata.java
│ │ │ ├── Register.java
│ │ │ ├── UserInfo.java
│ │ │ └── Movie.java
│ │ │ ├── di
│ │ │ ├── AcitvityScope.java
│ │ │ ├── FragmentScope.java
│ │ │ ├── ActivityContext.java
│ │ │ ├── UserManagerProvider.java
│ │ │ ├── AppComponent.java
│ │ │ ├── AndroidModule.java
│ │ │ └── NavigationProvider.java
│ │ │ ├── BaseApplication.java
│ │ │ ├── user
│ │ │ ├── User.java
│ │ │ └── UserManager.java
│ │ │ ├── network
│ │ │ ├── TokenInterceptor.java
│ │ │ ├── ApiInterface.java
│ │ │ ├── ApiModule.java
│ │ │ ├── ClientModule.java
│ │ │ └── TokenAuthenticator.java
│ │ │ └── local
│ │ │ └── SettingsManager.java
│ ├── test
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── core
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── ir
│ │ └── oveissi
│ │ └── core
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── main
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ ├── drawable
│ │ │ │ └── ic_search.xml
│ │ │ └── layout
│ │ │ │ ├── fragment_movies.xml
│ │ │ │ ├── fragment_movie_detail.xml
│ │ │ │ └── row_movie.xml
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── main
│ │ │ ├── MainEntryPointImpl.java
│ │ │ ├── MoviesComponent.java
│ │ │ ├── MovieDetailComponent.java
│ │ │ ├── MoviesAdapter.java
│ │ │ ├── EndlessLinearLayoutRecyclerview.java
│ │ │ ├── MovieDetailFragment.java
│ │ │ └── MoviesFragment.java
│ ├── test
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── main
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── ir
│ │ └── oveissi
│ │ └── main
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── search
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ ├── drawable
│ │ │ │ └── ic_search.xml
│ │ │ └── layout
│ │ │ │ ├── row_suggestion.xml
│ │ │ │ └── fragment_movie_search.xml
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── search
│ │ │ ├── SearchEntryPointImpl.java
│ │ │ ├── db
│ │ │ ├── AppDatabase.java
│ │ │ ├── AppDatabaseSingleton.java
│ │ │ ├── SuggestionDAO.java
│ │ │ └── Suggestion.java
│ │ │ ├── MovieSearchComponent.java
│ │ │ ├── MovieSearchAdapter.java
│ │ │ ├── EndlessLinearLayoutRecyclerview.java
│ │ │ └── MovieSearchFragment.java
│ ├── test
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── search
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── ir
│ │ └── oveissi
│ │ └── search
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── authentication
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ ├── activity_auth_main.xml
│ │ │ │ ├── fragment_auth_register.xml
│ │ │ │ └── fragment_auth_login.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── authentication
│ │ │ ├── di
│ │ │ ├── FragmentSubcomponent.java
│ │ │ └── AuthComponent.java
│ │ │ ├── AuthNavigator.java
│ │ │ ├── AuthMainActivity.java
│ │ │ ├── RegisterFragment.java
│ │ │ └── LoginFragment.java
│ ├── test
│ │ └── java
│ │ │ └── ir
│ │ │ └── oveissi
│ │ │ └── authentication
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── ir
│ │ └── oveissi
│ │ └── authentication
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── gradle.properties
├── gradlew.bat
├── libraries.gradle
├── gradlew
└── .gitignore
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/main/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/search/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/authentication/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/main/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/search/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':core', ':main', ':search', ':authentication'
2 |
--------------------------------------------------------------------------------
/main/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | main
3 |
4 |
--------------------------------------------------------------------------------
/search/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | search
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android Modularization
3 |
4 |
--------------------------------------------------------------------------------
/authentication/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | authentication
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | core
3 | core
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abbas-oveissi/AndroidModularization/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/HasNavigatorManager.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | public interface HasNavigatorManager {
4 | Navigator provideNavigator();
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/utils/Constants.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.utils;
2 |
3 | public class Constants {
4 | public static final String BASE_URL = "http://moviesapi.ir";
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/SearchEntryPoint.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | import android.support.v4.app.Fragment;
4 |
5 | public interface SearchEntryPoint {
6 | Fragment openMain();
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/bases/AppComponentHolder.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.bases;
2 |
3 |
4 | import ir.oveissi.core.di.AppComponent;
5 |
6 | public interface AppComponentHolder {
7 | AppComponent getAppComponent();
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/MainEntryPoint.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | import android.support.v4.app.Fragment;
4 |
5 | public interface MainEntryPoint {
6 | Fragment openMain(String query);
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/EntryPointHolder.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | public interface EntryPointHolder {
4 | SearchEntryPoint getSearchEntryPoint();
5 |
6 | MainEntryPoint getMainEntryPoint();
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/Pagination.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class Pagination {
7 | public List data = new ArrayList<>();
8 | public Metadata metadata;
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/RegisterBody.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by Abbas on 25/05/2016.
5 | */
6 | public class RegisterBody {
7 | public String email;
8 | public String password;
9 | public String name;
10 | }
11 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 10 16:38:32 IRDT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Modularizing Android application - WIP
2 |
3 | This project is an example of modular app. It is an ongoing project and it's gonna modified by time.
4 |
5 | you can find out more information in persian in the links below:
6 |
7 | [link 1](http://abbas.oveissi.ir/2018/08/22/android-modularization/)
8 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/bases/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.bases;
2 |
3 | import android.support.v4.app.Fragment;
4 |
5 |
6 | public class BaseFragment extends Fragment {
7 |
8 | @Override
9 | public void onStart() {
10 | super.onStart();
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/AuthEntryPoint.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | import android.content.Intent;
4 | import android.support.v4.app.Fragment;
5 |
6 | public interface AuthEntryPoint {
7 |
8 | Intent openMain();
9 |
10 | Fragment openFrag3();
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/AcitvityScope.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Scope
10 | public @interface AcitvityScope {
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/FragmentScope.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | @Retention(RetentionPolicy.RUNTIME)
9 | @Scope
10 | public @interface FragmentScope {
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/Token.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by Abbas on 25/05/2016.
5 | */
6 | public class Token {
7 | public String token_type;
8 | public String access_token;
9 | public String refresh_token;
10 | public Integer expires_in;
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/Metadata.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by abbas on 3/12/17.
5 | */
6 |
7 | public class Metadata {
8 | public Integer current_page;
9 | public Integer per_page;
10 | public Integer page_count;
11 | public Integer total_count;
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/Register.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by Abbas on 25/05/2016.
5 | */
6 | public class Register {
7 | public String email;
8 | public String updated_at;
9 | public String created_at;
10 | public Integer id;
11 | public String name;
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/UserInfo.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by Abbas on 25/05/2016.
5 | */
6 | public class UserInfo {
7 | public String name;
8 | public String email;
9 | public String updated_at;
10 | public String created_at;
11 | public Integer id;
12 | }
13 |
--------------------------------------------------------------------------------
/authentication/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/ActivityContext.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Qualifier;
7 |
8 | @Qualifier
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface ActivityContext {
11 | String value();
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/bases/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.bases;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | public class BaseActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | }
12 | }
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/SearchEntryPointImpl.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import android.support.v4.app.Fragment;
4 |
5 | import ir.oveissi.core.navigation.SearchEntryPoint;
6 |
7 | public class SearchEntryPointImpl implements SearchEntryPoint {
8 | @Override
9 | public Fragment openMain() {
10 | return MovieSearchFragment.newInstance();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MainEntryPointImpl.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 |
4 | import android.support.v4.app.Fragment;
5 |
6 | import ir.oveissi.core.navigation.MainEntryPoint;
7 |
8 | public class MainEntryPointImpl implements MainEntryPoint {
9 | @Override
10 | public Fragment openMain(String query) {
11 | return MoviesFragment.newInstance(query);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/db/AppDatabase.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search.db;
2 |
3 | import android.arch.persistence.room.Database;
4 | import android.arch.persistence.room.RoomDatabase;
5 |
6 | @Database(entities = {Suggestion.class}, version = 3, exportSchema = false)
7 | public abstract class AppDatabase extends RoomDatabase {
8 | public abstract SuggestionDAO suggestionDAO();
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_profile.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MoviesComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import dagger.Component;
4 | import ir.oveissi.core.di.AppComponent;
5 | import ir.oveissi.core.di.FragmentScope;
6 | import ir.oveissi.core.di.NavigationProvider;
7 |
8 | @FragmentScope
9 | @Component(dependencies = {
10 | AppComponent.class
11 | }, modules = {
12 | NavigationProvider.class
13 | })
14 | public interface MoviesComponent {
15 |
16 | void inject(MoviesFragment __);
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/test/java/ir/oveissi/core/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/main/src/test/java/ir/oveissi/main/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_login.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_register.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MovieDetailComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import dagger.Component;
4 | import ir.oveissi.core.di.AppComponent;
5 | import ir.oveissi.core.di.FragmentScope;
6 | import ir.oveissi.core.di.NavigationProvider;
7 |
8 | @FragmentScope
9 | @Component(dependencies = {
10 | AppComponent.class
11 | }, modules = {
12 | NavigationProvider.class
13 | })
14 | public interface MovieDetailComponent {
15 |
16 | void inject(MovieDetailFragment __);
17 | }
18 |
--------------------------------------------------------------------------------
/search/src/test/java/ir/oveissi/search/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/MovieSearchComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import dagger.Component;
4 | import ir.oveissi.core.di.AppComponent;
5 | import ir.oveissi.core.di.FragmentScope;
6 | import ir.oveissi.core.di.NavigationProvider;
7 |
8 | @FragmentScope
9 | @Component(dependencies = {
10 | AppComponent.class
11 | }, modules = {
12 | NavigationProvider.class
13 | })
14 | public interface MovieSearchComponent {
15 |
16 | void inject(MovieSearchFragment __);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logout.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/authentication/src/test/java/ir/oveissi/authentication/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/test/java/ir/oveissi/androidmodularization/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.androidmodularization;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/ir/oveissi/androidmodularization/MainActivityComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.androidmodularization;
2 |
3 |
4 | import dagger.Component;
5 | import ir.oveissi.core.di.AcitvityScope;
6 | import ir.oveissi.core.di.AppComponent;
7 | import ir.oveissi.core.di.NavigationProvider;
8 |
9 | @AcitvityScope
10 | @Component(dependencies = {
11 | AppComponent.class
12 | }, modules = {
13 | NavigationProvider.class,
14 | })
15 | public interface MainActivityComponent {
16 | void inject(MainActivity __);
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/UserManagerProvider.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 |
4 | import javax.inject.Singleton;
5 |
6 | import dagger.Module;
7 | import dagger.Provides;
8 | import ir.oveissi.core.local.SettingsManager;
9 | import ir.oveissi.core.user.UserManager;
10 |
11 | @Module
12 | public class UserManagerProvider {
13 |
14 | @Singleton
15 | @Provides
16 | public UserManager provideUserManager(SettingsManager settingsManager) {
17 | return new UserManager(settingsManager);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/di/FragmentSubcomponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication.di;
2 |
3 | import dagger.Module;
4 | import dagger.android.ContributesAndroidInjector;
5 | import ir.oveissi.authentication.LoginFragment;
6 | import ir.oveissi.authentication.RegisterFragment;
7 |
8 | @Module
9 | public abstract class FragmentSubcomponent {
10 |
11 | @ContributesAndroidInjector
12 | abstract LoginFragment provideSub1();
13 |
14 | @ContributesAndroidInjector
15 | abstract RegisterFragment provideSub2();
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
--------------------------------------------------------------------------------
/main/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/search/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/search/src/main/res/layout/row_suggestion.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/main/src/main/res/layout/fragment_movies.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/AuthNavigator.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.support.v4.app.Fragment;
6 |
7 | import ir.oveissi.core.navigation.AuthEntryPoint;
8 |
9 |
10 | public class AuthNavigator implements AuthEntryPoint {
11 |
12 |
13 | @Override
14 | public Intent openMain() {
15 | return new Intent(Intent.ACTION_VIEW, Uri.parse("dsvs://dsfood/main"));
16 | }
17 |
18 |
19 | @Override
20 | public Fragment openFrag3() {
21 | return RegisterFragment.newInstance();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/db/AppDatabaseSingleton.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search.db;
2 |
3 | import android.arch.persistence.room.Room;
4 | import android.content.Context;
5 |
6 | public class AppDatabaseSingleton {
7 |
8 | private static AppDatabase instance;
9 |
10 | public static AppDatabase getIntance(Context appContext) {
11 | if (instance == null) {
12 | instance = Room.databaseBuilder(appContext,
13 | AppDatabase.class, "suggestiondb")
14 | .fallbackToDestructiveMigration()
15 | .build();
16 | }
17 | return instance;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/AppComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 |
4 | import javax.inject.Singleton;
5 |
6 | import dagger.Component;
7 | import ir.oveissi.core.network.ApiInterface;
8 | import ir.oveissi.core.network.ApiModule;
9 | import ir.oveissi.core.network.ClientModule;
10 | import ir.oveissi.core.user.UserManager;
11 |
12 | @Singleton
13 | @Component(modules = {
14 | ApiModule.class,
15 | AndroidModule.class,
16 | ClientModule.class,
17 | UserManagerProvider.class
18 | })
19 | public interface AppComponent {
20 |
21 | UserManager getUserManager();
22 |
23 | ApiInterface wqegetApiInterface();
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/authentication/src/main/res/layout/activity_auth_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/di/AuthComponent.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication.di;
2 |
3 | import dagger.Component;
4 | import dagger.android.support.AndroidSupportInjectionModule;
5 | import ir.oveissi.authentication.AuthMainActivity;
6 | import ir.oveissi.core.di.AcitvityScope;
7 | import ir.oveissi.core.di.AppComponent;
8 | import ir.oveissi.core.di.NavigationProvider;
9 |
10 | @AcitvityScope
11 | @Component(dependencies = {
12 | AppComponent.class
13 | }, modules = {
14 | AndroidSupportInjectionModule.class,
15 | FragmentSubcomponent.class,
16 | NavigationProvider.class
17 | })
18 | public interface AuthComponent {
19 |
20 | void inject(AuthMainActivity __);
21 | }
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/pojo/Movie.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.pojo;
2 |
3 | /**
4 | * Created by Abbas on 25/05/2016.
5 | */
6 | public class Movie {
7 | public Integer id;
8 | public String title;
9 | public String poster;
10 | public String year;
11 | public String country;
12 | public String imdb_rating;
13 | public String[] genres;
14 | public String[] images;
15 | public String rated;
16 | public String released;
17 | public String runtime;
18 | public String director;
19 | public String writer;
20 | public String actors;
21 | public String plot;
22 | public String awards;
23 | public String metascore;
24 | public String imdb_votes;
25 | public String imdb_id;
26 | public String type;
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/BaseApplication.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core;
2 |
3 | import android.app.Application;
4 |
5 | import ir.oveissi.core.bases.AppComponentHolder;
6 | import ir.oveissi.core.di.AndroidModule;
7 | import ir.oveissi.core.di.AppComponent;
8 | import ir.oveissi.core.di.DaggerAppComponent;
9 |
10 | public class BaseApplication extends Application implements AppComponentHolder {
11 |
12 |
13 | private AppComponent appCommponent;
14 |
15 | @Override
16 | public void onCreate() {
17 | super.onCreate();
18 |
19 | appCommponent = DaggerAppComponent.builder()
20 | .androidModule(new AndroidModule(this))
21 | .build();
22 | }
23 |
24 |
25 | @Override
26 | public AppComponent getAppComponent() {
27 | return appCommponent;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/main/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/search/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/user/User.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.user;
2 |
3 | public class User {
4 | private String username;
5 | private String name;
6 | private String accessToken;
7 | private String refreshToken;
8 |
9 | public User(String name, String username, String accessToken, String refreshToken) {
10 | this.name = name;
11 | this.username = username;
12 | this.accessToken = accessToken;
13 | this.refreshToken = refreshToken;
14 | }
15 |
16 | public String getUsername() {
17 | return username;
18 | }
19 |
20 | public String getName() {
21 | return name;
22 | }
23 |
24 | public String getAccessToken() {
25 | return accessToken;
26 | }
27 |
28 | public String getRefreshToken() {
29 | return refreshToken;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/authentication/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/java/ir/oveissi/androidmodularization/BaseApplication.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.androidmodularization;
2 |
3 |
4 | import ir.oveissi.core.navigation.EntryPointHolder;
5 | import ir.oveissi.core.navigation.MainEntryPoint;
6 | import ir.oveissi.core.navigation.SearchEntryPoint;
7 | import ir.oveissi.main.MainEntryPointImpl;
8 | import ir.oveissi.search.SearchEntryPointImpl;
9 |
10 | public class BaseApplication extends ir.oveissi.core.BaseApplication implements EntryPointHolder {
11 |
12 |
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | }
17 |
18 | @Override
19 | public SearchEntryPoint getSearchEntryPoint() {
20 | return new SearchEntryPointImpl();
21 | }
22 |
23 | @Override
24 | public MainEntryPoint getMainEntryPoint() {
25 | return new MainEntryPointImpl();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/androidTest/java/ir/oveissi/core/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.core.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/main/src/androidTest/java/ir/oveissi/main/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.main.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/search/src/androidTest/java/ir/oveissi/search/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.search.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/db/SuggestionDAO.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search.db;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Delete;
5 | import android.arch.persistence.room.Insert;
6 | import android.arch.persistence.room.Query;
7 | import android.arch.persistence.room.Update;
8 |
9 | import java.util.List;
10 |
11 | import io.reactivex.Flowable;
12 |
13 | @Dao
14 | public interface SuggestionDAO {
15 | @Query("SELECT * FROM suggestion")
16 | Flowable> getAll();
17 |
18 | @Query("SELECT * FROM suggestion WHERE title LIKE :title Order by count")
19 | Flowable> getAllOrderByCount(String title);
20 |
21 | @Insert
22 | void insertAll(Suggestion... suggestions);
23 |
24 | @Delete
25 | void delete(Suggestion suggestion);
26 |
27 | @Update
28 | void update(Suggestion suggestion);
29 | }
--------------------------------------------------------------------------------
/authentication/src/androidTest/java/ir/oveissi/authentication/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.authentication.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/ir/oveissi/androidmodularization/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.androidmodularization;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.abbas.androidmodularize", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/db/Suggestion.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search.db;
2 |
3 | import android.arch.persistence.room.ColumnInfo;
4 | import android.arch.persistence.room.Entity;
5 | import android.arch.persistence.room.PrimaryKey;
6 |
7 | @Entity
8 | public class Suggestion {
9 | @PrimaryKey(autoGenerate = true)
10 | private int uid;
11 |
12 | @ColumnInfo(name = "title")
13 | private String title;
14 |
15 | @ColumnInfo(name = "count")
16 | private Integer count;
17 |
18 | public int getUid() {
19 | return uid;
20 | }
21 |
22 | public void setUid(int uid) {
23 | this.uid = uid;
24 | }
25 |
26 | public String getTitle() {
27 | return title;
28 | }
29 |
30 | public void setTitle(String title) {
31 | this.title = title;
32 | }
33 |
34 | public Integer getCount() {
35 | return count;
36 | }
37 |
38 | public void setCount(Integer count) {
39 | this.count = count;
40 | }
41 | }
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/AndroidModule.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.preference.PreferenceManager;
7 |
8 | import javax.inject.Singleton;
9 |
10 | import dagger.Module;
11 | import dagger.Provides;
12 | import ir.oveissi.core.BaseApplication;
13 |
14 | /**
15 | * Created by Abbas on 5/17/16.
16 | */
17 | @Module
18 | public class AndroidModule {
19 |
20 |
21 | private BaseApplication baseApplication;
22 |
23 | public AndroidModule(BaseApplication baseApplication) {
24 | this.baseApplication = baseApplication;
25 | }
26 |
27 | @Provides
28 | @Singleton
29 | public Context provideAppContext() {
30 | return baseApplication;
31 | }
32 |
33 | @Provides
34 | @Singleton
35 | public SharedPreferences provideSharedPreferences() {
36 | return PreferenceManager.getDefaultSharedPreferences(provideAppContext());
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/network/TokenInterceptor.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.network;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.inject.Inject;
6 |
7 | import ir.oveissi.core.local.SettingsManager;
8 | import okhttp3.Interceptor;
9 | import okhttp3.Request;
10 | import okhttp3.Response;
11 |
12 |
13 | public class TokenInterceptor implements Interceptor {
14 |
15 | private SettingsManager uspm;
16 |
17 | @Inject
18 | public TokenInterceptor(SettingsManager uspm) {
19 | this.uspm = uspm;
20 | }
21 |
22 |
23 | @Override
24 | public Response intercept(Interceptor.Chain chain) throws IOException {
25 | Request request = chain.request();
26 |
27 | if (uspm.getAccessToken().length() > 0) {
28 | request = request.newBuilder()
29 | .addHeader("Authorization", "Bearer " + uspm.getAccessToken())
30 | .build();
31 | }
32 | Response response = chain.proceed(request);
33 | return response;
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/main/src/main/res/layout/fragment_movie_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/network/ApiInterface.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.network;
2 |
3 |
4 | import java.util.Map;
5 |
6 | import io.reactivex.Flowable;
7 | import ir.oveissi.core.pojo.Movie;
8 | import ir.oveissi.core.pojo.Pagination;
9 | import ir.oveissi.core.pojo.Register;
10 | import ir.oveissi.core.pojo.RegisterBody;
11 | import ir.oveissi.core.pojo.Token;
12 | import ir.oveissi.core.pojo.UserInfo;
13 | import retrofit2.http.Body;
14 | import retrofit2.http.FieldMap;
15 | import retrofit2.http.FormUrlEncoded;
16 | import retrofit2.http.GET;
17 | import retrofit2.http.POST;
18 | import retrofit2.http.Path;
19 | import retrofit2.http.Query;
20 |
21 | /**
22 | * Created by Abbas on 24/05/2016.
23 | */
24 | public interface ApiInterface {
25 |
26 | @GET("/api/v1/movies")
27 | Flowable> getMoviesByTitle(@Query("q") String query, @Query("page") Integer page);
28 |
29 | @GET("/api/v1/movies/{id}")
30 | Flowable getMovieById(@Path("id") String id);
31 |
32 | @FormUrlEncoded
33 | @POST("/oauth/token")
34 | Flowable getToken(@FieldMap Map params);
35 |
36 | @POST("/api/v1/register")
37 | Flowable register(@Body RegisterBody registerBody);
38 |
39 | @GET("/api/user")
40 | Flowable getUserInfo();
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/di/NavigationProvider.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.di;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import dagger.Module;
7 | import dagger.Provides;
8 | import ir.oveissi.core.navigation.EntryPointHolder;
9 | import ir.oveissi.core.navigation.MainEntryPoint;
10 | import ir.oveissi.core.navigation.NavigationManager;
11 | import ir.oveissi.core.navigation.SearchEntryPoint;
12 |
13 | @Module
14 | public class NavigationProvider {
15 |
16 |
17 | private AppCompatActivity activity;
18 |
19 | public NavigationProvider(AppCompatActivity activity) {
20 | this.activity = activity;
21 | }
22 |
23 | public NavigationProvider(Fragment fragment) {
24 | this.activity = (AppCompatActivity) fragment.getActivity();
25 | }
26 |
27 | @Provides
28 | public NavigationManager provideManager() {
29 | return new NavigationManager(activity);
30 | }
31 |
32 |
33 | @Provides
34 | public MainEntryPoint provideMainEntryPoint() {
35 | return ((EntryPointHolder) activity.getApplicationContext()).getMainEntryPoint();
36 | }
37 |
38 | @Provides
39 | public SearchEntryPoint provideSearchEntryPoint() {
40 | return ((EntryPointHolder) activity.getApplicationContext()).getSearchEntryPoint();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/authentication/src/main/res/layout/fragment_auth_register.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
20 |
21 |
24 |
25 |
29 |
30 |
31 |
32 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/authentication/src/main/res/layout/fragment_auth_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
19 |
20 |
26 |
27 |
32 |
33 |
42 |
43 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/user/UserManager.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.user;
2 |
3 |
4 | import ir.oveissi.core.local.SettingsManager;
5 |
6 | public class UserManager {
7 |
8 | private User currentUser;
9 | private SettingsManager settingsManager;
10 |
11 | public UserManager(SettingsManager settingsManager) {
12 | this.settingsManager = settingsManager;
13 | }
14 |
15 | public boolean isUserLogged() {
16 | if (currentUser != null)
17 | return true;
18 | return tryLogin();
19 | }
20 |
21 | public boolean tryLogin() {
22 |
23 | if (!settingsManager.getAccessToken().isEmpty()) {
24 | User user = new User(settingsManager.getName(),
25 | settingsManager.getUsername(),
26 | settingsManager.getAccessToken(),
27 | settingsManager.getRefreshToken());
28 | this.currentUser = user;
29 | return true;
30 | }
31 | return false;
32 | }
33 |
34 | public void login(User user) {
35 | settingsManager.setName(user.getName());
36 | settingsManager.setAccessToken(user.getAccessToken());
37 | settingsManager.setRefreshToken(user.getRefreshToken());
38 | settingsManager.setUsername(user.getUsername());
39 | this.currentUser = user;
40 | }
41 |
42 | public void logout() {
43 | settingsManager.setName("");
44 | settingsManager.setAccessToken("");
45 | settingsManager.setRefreshToken("");
46 | settingsManager.setUsername("");
47 | this.currentUser = null;
48 | }
49 |
50 | public User getCurrentUser() {
51 | return currentUser;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/local/SettingsManager.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.local;
2 |
3 | import android.content.SharedPreferences;
4 |
5 | import javax.inject.Inject;
6 | import javax.inject.Singleton;
7 |
8 | @Singleton
9 | public class SettingsManager {
10 | private static final String NAME = "name";
11 | private static final String ACCESS_TOKEN = "accesstoken";
12 | private static final String REFRESH_TOKEN = "refreshtoken";
13 | private static final String USERNAME = "username";
14 | SharedPreferences pref;
15 | SharedPreferences.Editor editor;
16 |
17 | @Inject
18 | public SettingsManager(SharedPreferences sharedPreferences) {
19 | pref = sharedPreferences;
20 | editor = pref.edit();
21 | }
22 |
23 | public String getName() {
24 | return pref.getString(NAME, "");
25 | }
26 |
27 | public void setName(String name) {
28 | editor.putString(NAME, name);
29 | editor.commit();
30 | }
31 |
32 | public String getAccessToken() {
33 | return pref.getString(ACCESS_TOKEN, "");
34 | }
35 |
36 | public void setAccessToken(String accessToken) {
37 | editor.putString(ACCESS_TOKEN, accessToken);
38 | editor.commit();
39 | }
40 |
41 | public String getRefreshToken() {
42 | return pref.getString(REFRESH_TOKEN, "");
43 | }
44 |
45 | public void setRefreshToken(String refreshToken) {
46 | editor.putString(REFRESH_TOKEN, refreshToken);
47 | editor.commit();
48 | }
49 |
50 | public String getUsername() {
51 | return pref.getString(USERNAME, "");
52 | }
53 |
54 | public void setUsername(String username) {
55 | editor.putString(USERNAME, username);
56 | editor.commit();
57 | }
58 |
59 |
60 | }
--------------------------------------------------------------------------------
/core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode rootProject.ext.versionMajor * 10000 + rootProject.ext.versionMinor * 100 + rootProject.ext.versionPatch
12 | versionName "${rootProject.ext.versionMajor}.${rootProject.ext.versionMinor}.${rootProject.ext.versionPatch}"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | dataBinding {
26 | enabled = true
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | implementation rootProject.ext.libraries.appCompat
35 |
36 | testImplementation rootProject.ext.libraries.junit
37 |
38 | implementation rootProject.ext.libraries.dagger
39 | annotationProcessor rootProject.ext.libraries.daggerCompiler
40 | implementation rootProject.ext.libraries.daggerAndroid
41 | implementation rootProject.ext.libraries.daggerSupportAndroid
42 | annotationProcessor rootProject.ext.libraries.daggerAndroidCompiler
43 |
44 | implementation rootProject.ext.libraries.okhttp3
45 | implementation rootProject.ext.libraries.okhttpLogging
46 | implementation rootProject.ext.libraries.retrofit
47 | implementation rootProject.ext.libraries.retrofitConverter
48 | implementation rootProject.ext.libraries.retrofitAdapter
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/network/ApiModule.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.network;
2 |
3 |
4 | import com.google.gson.Gson;
5 |
6 | import javax.inject.Singleton;
7 |
8 | import dagger.Module;
9 | import dagger.Provides;
10 | import ir.oveissi.core.utils.Constants;
11 | import okhttp3.OkHttpClient;
12 | import retrofit2.CallAdapter;
13 | import retrofit2.Converter;
14 | import retrofit2.Retrofit;
15 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
16 | import retrofit2.converter.gson.GsonConverterFactory;
17 |
18 | /**
19 | * Created by Abbas on 5/17/16.
20 | */
21 | @Module
22 | public class ApiModule {
23 |
24 | @Provides
25 | @Singleton
26 | public static ApiInterface provideSearchMoviesApiService(Retrofit retrofit) {
27 | return retrofit.create(ApiInterface.class);
28 | }
29 |
30 | @Provides
31 | @Singleton
32 | public static Retrofit provideRetrofit(Converter.Factory converterFactory, CallAdapter.Factory callAdapterFactory, OkHttpClient okHttpClient) {
33 | return new Retrofit.Builder()
34 | .baseUrl(Constants.BASE_URL)
35 | .addConverterFactory(converterFactory)
36 | .addCallAdapterFactory(callAdapterFactory)
37 | .client(okHttpClient)
38 | .build();
39 | }
40 |
41 | @Singleton
42 | @Provides
43 | public static Gson provideGson() {
44 | return new Gson();
45 | }
46 |
47 |
48 | @Provides
49 | @Singleton
50 | public static CallAdapter.Factory provideRxJavaCallAdapterFactory() {
51 | return RxJava2CallAdapterFactory.create();
52 | }
53 |
54 | @Provides
55 | @Singleton
56 | public static Converter.Factory provideGsonConverterFactory(Gson gson) {
57 | return GsonConverterFactory.create(gson);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/main/src/main/res/layout/row_movie.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
26 |
27 |
36 |
37 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/search/src/main/res/layout/fragment_movie_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
28 |
29 |
36 |
37 |
38 |
39 |
43 |
44 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/main/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode rootProject.ext.versionMajor * 10000 + rootProject.ext.versionMinor * 100 + rootProject.ext.versionPatch
12 | versionName "${rootProject.ext.versionMajor}.${rootProject.ext.versionMinor}.${rootProject.ext.versionPatch}"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | dataBinding {
26 | enabled = true
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(include: ['*.jar'], dir: 'libs')
33 | compile rootProject.ext.libraries.recyclerView
34 | implementation rootProject.ext.libraries.cardView
35 | implementation rootProject.ext.libraries.appCompat
36 |
37 | implementation rootProject.ext.libraries.dagger
38 | annotationProcessor rootProject.ext.libraries.daggerCompiler
39 | implementation rootProject.ext.libraries.daggerAndroid
40 | implementation rootProject.ext.libraries.daggerSupportAndroid
41 | annotationProcessor rootProject.ext.libraries.daggerAndroidCompiler
42 |
43 | implementation rootProject.ext.libraries.picasso
44 |
45 | implementation rootProject.ext.libraries.rxjava
46 | implementation rootProject.ext.libraries.rxandroid
47 | implementation rootProject.ext.libraries.rxbinding
48 |
49 | testImplementation rootProject.ext.libraries.junit
50 |
51 | implementation project(':core')
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/authentication/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode rootProject.ext.versionMajor * 10000 + rootProject.ext.versionMinor * 100 + rootProject.ext.versionPatch
12 | versionName "${rootProject.ext.versionMajor}.${rootProject.ext.versionMinor}.${rootProject.ext.versionPatch}"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 |
26 | dataBinding {
27 | enabled = true
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(include: ['*.jar'], dir: 'libs')
33 | implementation rootProject.ext.libraries.appCompat
34 | implementation rootProject.ext.libraries.constraintLayout
35 | implementation rootProject.ext.libraries.recyclerView
36 |
37 | testImplementation rootProject.ext.libraries.junit
38 |
39 | implementation rootProject.ext.libraries.dagger
40 | annotationProcessor rootProject.ext.libraries.daggerCompiler
41 | implementation rootProject.ext.libraries.daggerAndroid
42 | implementation rootProject.ext.libraries.daggerSupportAndroid
43 | annotationProcessor rootProject.ext.libraries.daggerAndroidCompiler
44 |
45 | implementation rootProject.ext.libraries.picasso
46 |
47 | implementation rootProject.ext.libraries.rxjava
48 | implementation rootProject.ext.libraries.rxandroid
49 | implementation rootProject.ext.libraries.rxbinding
50 |
51 | implementation project(':core')
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 |
7 | defaultConfig {
8 | applicationId "ir.oveissi.androidmodularization"
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode rootProject.ext.versionMajor * 10000 + rootProject.ext.versionMinor * 100 + rootProject.ext.versionPatch
12 | versionName "${rootProject.ext.versionMajor}.${rootProject.ext.versionMinor}.${rootProject.ext.versionPatch}"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | dataBinding {
23 | enabled = true
24 | }
25 | }
26 |
27 | dependencies {
28 |
29 | implementation fileTree(include: ['*.jar'], dir: 'libs')
30 | implementation rootProject.ext.libraries.appCompat
31 | implementation rootProject.ext.libraries.constraintLayout
32 | implementation rootProject.ext.libraries.design
33 |
34 | testImplementation rootProject.ext.libraries.junit
35 |
36 | implementation rootProject.ext.libraries.dagger
37 | annotationProcessor rootProject.ext.libraries.daggerCompiler
38 | implementation rootProject.ext.libraries.daggerAndroid
39 | implementation rootProject.ext.libraries.daggerSupportAndroid
40 | annotationProcessor rootProject.ext.libraries.daggerAndroidCompiler
41 |
42 |
43 | implementation rootProject.ext.libraries.rxjava
44 | implementation rootProject.ext.libraries.rxandroid
45 | implementation rootProject.ext.libraries.rxbinding
46 |
47 | implementation project(':core')
48 | implementation project(':main')
49 | implementation project(':search')
50 | implementation project(':authentication')
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/search/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.targetSdkVersion
10 | versionCode rootProject.ext.versionMajor * 10000 + rootProject.ext.versionMinor * 100 + rootProject.ext.versionPatch
11 | versionName "${rootProject.ext.versionMajor}.${rootProject.ext.versionMinor}.${rootProject.ext.versionPatch}"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | dataBinding {
25 | enabled = true
26 | }
27 |
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 |
33 | compile rootProject.ext.libraries.recyclerView
34 | implementation rootProject.ext.libraries.appCompat
35 |
36 | testImplementation rootProject.ext.libraries.junit
37 |
38 | implementation rootProject.ext.libraries.dagger
39 | annotationProcessor rootProject.ext.libraries.daggerCompiler
40 | implementation rootProject.ext.libraries.daggerAndroid
41 | implementation rootProject.ext.libraries.daggerSupportAndroid
42 | annotationProcessor rootProject.ext.libraries.daggerAndroidCompiler
43 |
44 | implementation rootProject.ext.libraries.picasso
45 |
46 | implementation rootProject.ext.libraries.rxjava
47 | implementation rootProject.ext.libraries.rxandroid
48 | implementation rootProject.ext.libraries.rxbinding
49 |
50 | implementation rootProject.ext.libraries.roomRuntime
51 | annotationProcessor rootProject.ext.libraries.roomCompiler
52 | implementation rootProject.ext.libraries.roomRxjava
53 |
54 | implementation project(':core')
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/AuthMainActivity.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 |
6 | import javax.inject.Inject;
7 |
8 | import dagger.android.AndroidInjector;
9 | import dagger.android.DispatchingAndroidInjector;
10 | import dagger.android.support.HasSupportFragmentInjector;
11 | import ir.oveissi.authentication.di.AuthComponent;
12 | import ir.oveissi.authentication.di.DaggerAuthComponent;
13 | import ir.oveissi.core.bases.AppComponentHolder;
14 | import ir.oveissi.core.bases.BaseActivity;
15 | import ir.oveissi.core.di.NavigationProvider;
16 | import ir.oveissi.core.navigation.HasNavigatorManager;
17 | import ir.oveissi.core.navigation.NavigationManager;
18 | import ir.oveissi.core.navigation.Navigator;
19 |
20 | public class AuthMainActivity extends BaseActivity implements HasNavigatorManager, HasSupportFragmentInjector {
21 |
22 | @Inject
23 | DispatchingAndroidInjector dispatchingAndroidInjector;
24 |
25 | @Inject
26 | NavigationManager navigationManager;
27 | Navigator navigator;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_auth_main);
33 |
34 |
35 | AuthComponent authComponent =
36 | DaggerAuthComponent.builder()
37 | .appComponent(((AppComponentHolder) getApplicationContext()).getAppComponent())
38 | .navigationProvider(new NavigationProvider(this))
39 | .build();
40 | authComponent.inject(this);
41 |
42 | navigator = navigationManager.init(this, getSupportFragmentManager(), R.id.container);
43 |
44 | if (savedInstanceState == null)
45 | navigator.openAsRoot(LoginFragment.newInstance());
46 | }
47 |
48 |
49 | @Override
50 | public Navigator provideNavigator() {
51 | return navigator;
52 | }
53 |
54 | @Override
55 | public AndroidInjector supportFragmentInjector() {
56 | return dispatchingAndroidInjector;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/NavigationManager.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | import android.content.Intent;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v7.app.AppCompatActivity;
7 |
8 | public class NavigationManager {
9 |
10 |
11 | private AppCompatActivity context;
12 |
13 | public NavigationManager(AppCompatActivity Context) {
14 | context = Context;
15 | }
16 |
17 | public NavigationManager(Fragment fragment) {
18 | context = (AppCompatActivity) fragment.getActivity();
19 | }
20 |
21 | void checkImplementInterface(Object object) throws IllegalStateException {
22 | if (!(object instanceof HasNavigatorManager))
23 | throw new IllegalStateException("implement HasNavigationManager");
24 | }
25 |
26 | public Navigator init(Fragment fragment, FragmentManager mFragmentManager, int container) {
27 | if (!(fragment instanceof HasNavigatorManager))
28 | throw new IllegalStateException("implement HasNavigationManager");
29 | return init((Object) fragment, mFragmentManager, container);
30 | }
31 |
32 | Navigator init(Object obj, FragmentManager mFragmentManager, int container) {
33 | Navigator navigator = new Navigator(context, mFragmentManager, container);
34 | return navigator;
35 | }
36 |
37 | public Navigator init(AppCompatActivity activity, FragmentManager mFragmentManager, int container) {
38 | if (!(activity instanceof HasNavigatorManager))
39 | throw new IllegalStateException("implement HasNavigationManager");
40 | return init((Object) activity, mFragmentManager, container);
41 | }
42 |
43 | public Navigator getNavigator(Fragment fragment) {
44 | Fragment parentFrag = fragment;
45 | while (true) {
46 | parentFrag = parentFrag.getParentFragment();
47 | if (parentFrag == null)
48 | break;
49 | if (parentFrag instanceof HasNavigatorManager) {
50 | return ((HasNavigatorManager) parentFrag).provideNavigator();
51 | }
52 | }
53 | checkImplementInterface(fragment.getActivity());
54 | return ((HasNavigatorManager) context).provideNavigator();
55 | }
56 |
57 | public void openActivity(Intent intent) {
58 | context.startActivity(intent);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/network/ClientModule.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.network;
2 |
3 |
4 | import java.io.IOException;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | import javax.inject.Named;
8 | import javax.inject.Singleton;
9 |
10 | import dagger.Module;
11 | import dagger.Provides;
12 | import ir.oveissi.core.BuildConfig;
13 | import okhttp3.Authenticator;
14 | import okhttp3.Interceptor;
15 | import okhttp3.OkHttpClient;
16 | import okhttp3.Request;
17 | import okhttp3.Response;
18 | import okhttp3.logging.HttpLoggingInterceptor;
19 |
20 | /**
21 | * Created by Abbas on 5/17/16.
22 | */
23 | @Module
24 | public class ClientModule {
25 | @Singleton
26 | @Provides
27 | public static OkHttpClient provideOkHttpClient(
28 | HttpLoggingInterceptor loggingInterceptor,
29 | Authenticator authenticator,
30 | @Named("tokenInterceptor") Interceptor tokenInterceptor) {
31 |
32 | OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder()
33 | .readTimeout(30, TimeUnit.SECONDS)
34 | .addInterceptor(tokenInterceptor)
35 | .addInterceptor(new Interceptor() {
36 | @Override
37 | public Response intercept(Chain chain) throws IOException {
38 | Request request = chain.request().newBuilder().addHeader("Accept", "application/json").build();
39 | return chain.proceed(request);
40 | }
41 | })
42 | .connectTimeout(30, TimeUnit.SECONDS);
43 |
44 | okHttpClient.authenticator(authenticator);
45 |
46 |
47 | if (BuildConfig.DEBUG)
48 | okHttpClient.addInterceptor(loggingInterceptor);
49 |
50 | return okHttpClient.build();
51 | }
52 |
53 | @Singleton
54 | @Provides
55 | public static HttpLoggingInterceptor provideHttpLoggingInterceptor() {
56 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
57 | logging.setLevel(HttpLoggingInterceptor.Level.BODY);
58 | return logging;
59 |
60 | }
61 |
62 |
63 | @Singleton
64 | @Provides
65 | @Named("tokenInterceptor")
66 | public Interceptor provideTokenInterceptor(TokenInterceptor tokenInterceptor) {
67 | return tokenInterceptor;
68 |
69 | }
70 |
71 | @Singleton
72 | @Provides
73 | public Authenticator provideTokenAuthenticator(TokenAuthenticator tokenInterceptor) {
74 | return tokenInterceptor;
75 |
76 | }
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/libraries.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | minSdkVersion = 17
3 | targetSdkVersion = 27
4 | compileSdkVersion = 27
5 |
6 | androidSupportVersion = "27.1.1"
7 | room_version = "1.0.0"
8 |
9 | versionMajor = 0
10 | versionMinor = 9
11 | versionPatch = 7
12 |
13 | libraries = [
14 | junit : "junit:junit:4.12",
15 | androidSupport4 : "com.android.support:support-v4:${androidSupportVersion}",
16 | appCompat : "com.android.support:appcompat-v7:${androidSupportVersion}",
17 | designSupport : "com.android.support:design:${androidSupportVersion}",
18 | cardView : "com.android.support:cardview-v7:${androidSupportVersion}",
19 | recyclerView : "com.android.support:recyclerview-v7:${androidSupportVersion}",
20 | design : "com.android.support:design:${androidSupportVersion}",
21 |
22 | roomRuntime : "android.arch.persistence.room:runtime:${room_version}",
23 | roomCompiler : "android.arch.persistence.room:compiler:${room_version}",
24 | roomRxjava : "android.arch.persistence.room:rxjava2:${room_version}",
25 |
26 | constraintLayout : "com.android.support.constraint:constraint-layout:1.1.2",
27 |
28 | gson : "com.google.code.gson:gson:2.6.2",
29 | picasso : "com.squareup.picasso:picasso:2.5.2",
30 |
31 | dagger : "com.google.dagger:dagger:2.17",
32 | daggerCompiler : "com.google.dagger:dagger-compiler:2.17",
33 | daggerAndroid : "com.google.dagger:dagger-android:2.17",
34 | daggerSupportAndroid : "com.google.dagger:dagger-android-support:2.17",
35 | daggerAndroidCompiler: "com.google.dagger:dagger-android-processor:2.17",
36 |
37 | rxjava : "io.reactivex.rxjava2:rxjava:2.1.0",
38 | rxandroid : "io.reactivex.rxjava2:rxandroid:2.1.0",
39 | rxbinding : "com.jakewharton.rxbinding2:rxbinding:2.0.0",
40 |
41 | okhttp3 : "com.squareup.okhttp3:okhttp:3.5.0",
42 | okhttpLogging : "com.squareup.okhttp3:logging-interceptor:3.5.0",
43 | retrofit : "com.squareup.retrofit2:retrofit:2.3.0",
44 | retrofitConverter : "com.squareup.retrofit2:converter-gson:2.3.0",
45 | retrofitAdapter : "com.squareup.retrofit2:adapter-rxjava2:2.3.0",
46 |
47 | ]
48 |
49 | }
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/MovieSearchAdapter.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import java.util.List;
11 |
12 | import ir.oveissi.search.db.Suggestion;
13 |
14 | public class MovieSearchAdapter extends RecyclerView.Adapter {
15 | ItemClickListener itemClickListener;
16 | private Context mContext;
17 | private List itemsData;
18 |
19 | MovieSearchAdapter(Context mContext, List itemsData) {
20 | this.mContext = mContext;
21 | this.itemsData = itemsData;
22 | }
23 |
24 | void setItemClickListener(ItemClickListener itemClickListener) {
25 | this.itemClickListener = itemClickListener;
26 | }
27 |
28 | void clear() {
29 | this.itemsData.clear();
30 | notifyDataSetChanged();
31 | }
32 |
33 | void addItem(Suggestion post) {
34 | this.itemsData.add(post);
35 | notifyItemInserted(this.itemsData.size() - 1);
36 | }
37 |
38 | @Override
39 | public MovieSearchAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
40 | View itemLayoutView = LayoutInflater.from(parent.getContext())
41 | .inflate(R.layout.row_suggestion, parent, false);
42 | return new ViewHolder(itemLayoutView);
43 | }
44 |
45 | @Override
46 | public void onBindViewHolder(ViewHolder viewHolder, int position) {
47 | Suggestion tempItem = itemsData.get(position);
48 | viewHolder.bind(tempItem);
49 | }
50 |
51 | @Override
52 | public int getItemCount() {
53 | return itemsData.size();
54 | }
55 |
56 | public interface ItemClickListener {
57 | void ItemClicked(int position, Suggestion item);
58 | }
59 |
60 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
61 | TextView tvMovieTitle;
62 |
63 | ViewHolder(View itemLayoutView) {
64 | super(itemLayoutView);
65 | tvMovieTitle = itemLayoutView.findViewById(R.id.tvMovieTitle);
66 | itemLayoutView.setOnClickListener(this);
67 |
68 | }
69 |
70 | void bind(Suggestion item) {
71 | tvMovieTitle.setText(item.getTitle());
72 | }
73 |
74 | @Override
75 | public void onClick(View v) {
76 | if (itemClickListener != null) {
77 | itemClickListener.ItemClicked(getAdapterPosition(),
78 | itemsData.get(getAdapterPosition()));
79 | }
80 |
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/navigation/Navigator.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.navigation;
2 |
3 | import android.content.Context;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v4.app.FragmentTransaction;
7 |
8 | public class Navigator {
9 |
10 | private Context appcompat;
11 | private FragmentManager mFragmentManager;
12 | private int container;
13 | private NavigationListener navigationListener;
14 |
15 |
16 | public Navigator(Context appcompat, FragmentManager mFragmentManager, int container) {
17 | this.appcompat = appcompat;
18 |
19 | this.mFragmentManager = mFragmentManager;
20 | this.container = container;
21 |
22 | mFragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
23 | @Override
24 | public void onBackStackChanged() {
25 | if (navigationListener != null)
26 | navigationListener.change();
27 | }
28 | });
29 | }
30 |
31 | public void setNavigationListener(NavigationListener navigationListener) {
32 | this.navigationListener = navigationListener;
33 | }
34 |
35 | public boolean isRootFragmentVisible() {
36 | return mFragmentManager.getBackStackEntryCount() <= 1;
37 | }
38 |
39 | public void open(Fragment fragment) {
40 | FragmentTransaction fragTransaction = mFragmentManager.beginTransaction();
41 | fragTransaction.replace(container, fragment);
42 | fragTransaction.addToBackStack(fragment.toString());
43 | fragTransaction.commit();
44 | }
45 |
46 | public void openFragment(Fragment fragment, boolean addToBackStack) {
47 | FragmentTransaction fragTransaction = mFragmentManager.beginTransaction();
48 | fragTransaction.replace(container, fragment);
49 | if (addToBackStack)
50 | fragTransaction.addToBackStack(fragment.toString());
51 | fragTransaction.commit();
52 | }
53 |
54 | public void openAsRoot(Fragment fragment) {
55 | popEveryFragment();
56 | openFragment(fragment, false);
57 | }
58 |
59 | private void popEveryFragment() {
60 | int backStackCount = mFragmentManager.getBackStackEntryCount();
61 | for (int i = 0; i < backStackCount; i++) {
62 | int backStackId = mFragmentManager.getBackStackEntryAt(i).getId();
63 | mFragmentManager.popBackStack(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
64 | }
65 | }
66 |
67 | public boolean navigateBack() {
68 | if (mFragmentManager.getBackStackEntryCount() == 0) {
69 | return false;
70 | } else {
71 | mFragmentManager.popBackStackImmediate();
72 | return true;
73 | }
74 | }
75 |
76 |
77 | public interface NavigationListener {
78 | public void change();
79 | }
80 |
81 | }
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MoviesAdapter.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.squareup.picasso.Picasso;
12 |
13 | import java.util.List;
14 |
15 | import ir.oveissi.core.pojo.Movie;
16 |
17 | public class MoviesAdapter extends RecyclerView.Adapter {
18 | ItemClickListener itemClickListener;
19 | private Context mContext;
20 | private List itemsData;
21 |
22 | MoviesAdapter(Context mContext, List itemsData) {
23 | this.mContext = mContext;
24 | this.itemsData = itemsData;
25 | }
26 |
27 | void setItemClickListener(ItemClickListener itemClickListener) {
28 | this.itemClickListener = itemClickListener;
29 | }
30 |
31 | void clear() {
32 | this.itemsData.clear();
33 | notifyDataSetChanged();
34 | }
35 |
36 | void addItem(Movie post) {
37 | this.itemsData.add(post);
38 | notifyItemInserted(this.itemsData.size() - 1);
39 | }
40 |
41 | @Override
42 | public MoviesAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
43 | View itemLayoutView = LayoutInflater.from(parent.getContext())
44 | .inflate(R.layout.row_movie, parent, false);
45 | return new ViewHolder(itemLayoutView);
46 | }
47 |
48 | @Override
49 | public void onBindViewHolder(ViewHolder viewHolder, int position) {
50 | Movie tempItem = itemsData.get(position);
51 | viewHolder.bind(tempItem);
52 | }
53 |
54 | @Override
55 | public int getItemCount() {
56 | return itemsData.size();
57 | }
58 |
59 | public interface ItemClickListener {
60 | void ItemClicked(int position, Movie item);
61 | }
62 |
63 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
64 | TextView tvMovieTitle;
65 | ImageView imPoster;
66 |
67 | ViewHolder(View itemLayoutView) {
68 | super(itemLayoutView);
69 | tvMovieTitle = itemLayoutView.findViewById(R.id.tvMovieTitle);
70 | imPoster = itemLayoutView.findViewById(R.id.imPoster);
71 | itemLayoutView.setOnClickListener(this);
72 |
73 | }
74 |
75 | void bind(Movie item) {
76 | tvMovieTitle.setText(item.title);
77 | Picasso.with(mContext)
78 | .load(item.poster)
79 | .into(imPoster);
80 | }
81 |
82 |
83 | @Override
84 | public void onClick(View v) {
85 | if (itemClickListener != null) {
86 | itemClickListener.ItemClicked(getAdapterPosition(),
87 | itemsData.get(getAdapterPosition()));
88 | }
89 |
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/core/src/main/java/ir/oveissi/core/network/TokenAuthenticator.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.core.network;
2 |
3 |
4 | import com.google.gson.Gson;
5 |
6 | import java.io.IOException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import javax.inject.Inject;
11 |
12 | import ir.oveissi.core.pojo.Token;
13 | import ir.oveissi.core.user.User;
14 | import ir.oveissi.core.user.UserManager;
15 | import ir.oveissi.core.utils.Constants;
16 | import okhttp3.Authenticator;
17 | import okhttp3.MediaType;
18 | import okhttp3.MultipartBody;
19 | import okhttp3.OkHttpClient;
20 | import okhttp3.Request;
21 | import okhttp3.RequestBody;
22 | import okhttp3.Response;
23 | import okhttp3.Route;
24 | import okhttp3.logging.HttpLoggingInterceptor;
25 |
26 |
27 | public class TokenAuthenticator implements Authenticator {
28 | static final MediaType JSON
29 | = MediaType.parse("application/json; charset=utf-8");
30 | private UserManager uspm;
31 |
32 | @Inject
33 | public TokenAuthenticator(UserManager uspm) {
34 | this.uspm = uspm;
35 | }
36 |
37 |
38 | @Override
39 | public Request authenticate(Route route, Response response) throws IOException {
40 |
41 | if (response.request().url().toString().equals(Constants.BASE_URL + "/oauth/token"))
42 | return null;
43 |
44 | Map map = new HashMap<>();
45 |
46 |
47 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
48 | logging.setLevel(HttpLoggingInterceptor.Level.BODY);
49 |
50 | OkHttpClient client = new OkHttpClient.Builder()
51 | .addInterceptor(logging)
52 | .build();
53 |
54 |
55 | RequestBody requestBody = new MultipartBody.Builder()
56 | .setType(MultipartBody.FORM)
57 | .addFormDataPart("grant_type", "refresh_token")
58 | .addFormDataPart("refresh_token", uspm.getCurrentUser().getRefreshToken())
59 | .build();
60 |
61 | Request request = new Request.Builder()
62 | .url(Constants.BASE_URL + "/oauth/token")
63 | .post(requestBody)
64 | .addHeader("Accept", "application/json;")
65 | .addHeader("Authorization", "Bearer " + uspm.getCurrentUser().getAccessToken())
66 | .build();
67 |
68 | Response r = client.newCall(request).execute();
69 | if (r.code() == 401) {
70 | uspm.logout();
71 | return null;
72 | }
73 |
74 |
75 | String jsonResponse = r.body().string();
76 | Gson gson = new Gson();
77 | Token token = gson.fromJson(jsonResponse, Token.class);
78 |
79 | String email = uspm.getCurrentUser().getUsername();
80 | String name = uspm.getCurrentUser().getName();
81 |
82 | User user = new User(name,
83 | email,
84 | token.access_token,
85 | token.refresh_token);
86 | uspm.login(user);
87 |
88 | return response.request().newBuilder().header("Authorization", "Bearer " + token.access_token).build();
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/EndlessLinearLayoutRecyclerview.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 |
9 | /**
10 | * Created by abbas on 6/14/17.
11 | */
12 |
13 | public class EndlessLinearLayoutRecyclerview extends RecyclerView {
14 | AdvancedEndlessRecyclerOnScrollListener aeros;
15 | private onLoadMoreListener onLoadMoreListener;
16 |
17 | public EndlessLinearLayoutRecyclerview(Context context) {
18 | super(context);
19 | }
20 |
21 |
22 | public EndlessLinearLayoutRecyclerview(Context context, @Nullable AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public EndlessLinearLayoutRecyclerview(Context context, @Nullable AttributeSet attrs, int defStyle) {
27 | super(context, attrs, defStyle);
28 | }
29 |
30 | @Override
31 | public void setLayoutManager(LayoutManager layout) {
32 |
33 | if (!(layout instanceof LinearLayoutManager)) {
34 | throw new RuntimeException();
35 | }
36 |
37 | aeros = new AdvancedEndlessRecyclerOnScrollListener(layout) {
38 | @Override
39 | public void onLoadMore() {
40 | if (onLoadMoreListener != null)
41 | onLoadMoreListener.onLoadMore();
42 | }
43 | };
44 |
45 | addOnScrollListener(aeros);
46 | super.setLayoutManager(layout);
47 | }
48 |
49 | public void setOnLoadMoreListener(EndlessLinearLayoutRecyclerview.onLoadMoreListener onLoadMoreListener) {
50 | this.onLoadMoreListener = onLoadMoreListener;
51 | }
52 |
53 | public void setLoading(boolean enable) {
54 | this.aeros.setLoading(enable);
55 | }
56 |
57 |
58 | public interface onLoadMoreListener {
59 | void onLoadMore();
60 | }
61 |
62 | public abstract class AdvancedEndlessRecyclerOnScrollListener extends OnScrollListener {
63 | private int visibleThreshold = 3;
64 | private int lastVisibleItem, totalItemCount;
65 | private boolean loading = true;
66 | private LayoutManager linearLayoutManager;
67 |
68 | public AdvancedEndlessRecyclerOnScrollListener(LayoutManager linearLayoutManager) {
69 | this.linearLayoutManager = linearLayoutManager;
70 | }
71 |
72 | @Override
73 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
74 | super.onScrolled(recyclerView, dx, dy);
75 |
76 | totalItemCount = linearLayoutManager.getItemCount();
77 | lastVisibleItem = ((LinearLayoutManager) linearLayoutManager).findLastVisibleItemPosition();
78 | if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
79 | onLoadMore();
80 | loading = true;
81 | }
82 | }
83 |
84 | public void setLoading(boolean enable) {
85 | this.loading = enable;
86 | }
87 |
88 | public abstract void onLoadMore();
89 |
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/EndlessLinearLayoutRecyclerview.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 |
9 | /**
10 | * Created by abbas on 6/14/17.
11 | */
12 |
13 | public class EndlessLinearLayoutRecyclerview extends RecyclerView {
14 | AdvancedEndlessRecyclerOnScrollListener aeros;
15 | private onLoadMoreListener onLoadMoreListener;
16 |
17 | public EndlessLinearLayoutRecyclerview(Context context) {
18 | super(context);
19 | }
20 |
21 |
22 | public EndlessLinearLayoutRecyclerview(Context context, @Nullable AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public EndlessLinearLayoutRecyclerview(Context context, @Nullable AttributeSet attrs, int defStyle) {
27 | super(context, attrs, defStyle);
28 | }
29 |
30 | @Override
31 | public void setLayoutManager(LayoutManager layout) {
32 |
33 | if (!(layout instanceof LinearLayoutManager)) {
34 | throw new RuntimeException();
35 | }
36 |
37 | aeros = new AdvancedEndlessRecyclerOnScrollListener(layout) {
38 | @Override
39 | public void onLoadMore() {
40 | if (onLoadMoreListener != null)
41 | onLoadMoreListener.onLoadMore();
42 | }
43 | };
44 |
45 | addOnScrollListener(aeros);
46 | super.setLayoutManager(layout);
47 | }
48 |
49 | public void setOnLoadMoreListener(EndlessLinearLayoutRecyclerview.onLoadMoreListener onLoadMoreListener) {
50 | this.onLoadMoreListener = onLoadMoreListener;
51 | }
52 |
53 | public void setLoading(boolean enable) {
54 | this.aeros.setLoading(enable);
55 | }
56 |
57 |
58 | public interface onLoadMoreListener {
59 | void onLoadMore();
60 | }
61 |
62 | public abstract class AdvancedEndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
63 | private int visibleThreshold = 3;
64 | private int lastVisibleItem, totalItemCount;
65 | private boolean loading = true;
66 | private RecyclerView.LayoutManager linearLayoutManager;
67 |
68 | public AdvancedEndlessRecyclerOnScrollListener(RecyclerView.LayoutManager linearLayoutManager) {
69 | this.linearLayoutManager = linearLayoutManager;
70 | }
71 |
72 | @Override
73 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
74 | super.onScrolled(recyclerView, dx, dy);
75 |
76 | totalItemCount = linearLayoutManager.getItemCount();
77 | lastVisibleItem = ((LinearLayoutManager) linearLayoutManager).findLastVisibleItemPosition();
78 | if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
79 | onLoadMore();
80 | loading = true;
81 | }
82 | }
83 |
84 | public void setLoading(boolean enable) {
85 | this.loading = enable;
86 | }
87 |
88 | public abstract void onLoadMore();
89 |
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MovieDetailFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import android.content.Context;
4 | import android.databinding.DataBindingUtil;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.Toast;
13 |
14 | import com.squareup.picasso.NetworkPolicy;
15 | import com.squareup.picasso.Picasso;
16 |
17 | import javax.inject.Inject;
18 |
19 | import io.reactivex.android.schedulers.AndroidSchedulers;
20 | import io.reactivex.schedulers.Schedulers;
21 | import io.reactivex.subscribers.DisposableSubscriber;
22 | import ir.oveissi.core.bases.AppComponentHolder;
23 | import ir.oveissi.core.bases.BaseFragment;
24 | import ir.oveissi.core.network.ApiInterface;
25 | import ir.oveissi.core.pojo.Movie;
26 | import ir.oveissi.main.databinding.FragmentMovieDetailBinding;
27 |
28 | public class MovieDetailFragment extends BaseFragment {
29 |
30 |
31 | @Inject
32 | ApiInterface apiInterface;
33 | FragmentMovieDetailBinding binding;
34 | private String movie_id;
35 |
36 | public MovieDetailFragment() {
37 | // Required empty public constructor
38 | }
39 |
40 | public static MovieDetailFragment newInstance(String movie_id) {
41 | MovieDetailFragment fragment = new MovieDetailFragment();
42 | Bundle args = new Bundle();
43 | args.putString("movie_id", movie_id);
44 | fragment.setArguments(args);
45 | return fragment;
46 | }
47 |
48 | @Override
49 | public void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | if (getArguments() != null) {
52 | movie_id = getArguments().getString("movie_id");
53 | }
54 | }
55 |
56 | @Override
57 | public void onAttach(Context context) {
58 | super.onAttach(context);
59 |
60 | MovieDetailComponent component = DaggerMovieDetailComponent
61 | .builder()
62 | .appComponent(((AppComponentHolder) getActivity().getApplicationContext()).getAppComponent())
63 | .build();
64 | component.inject(this);
65 |
66 | }
67 |
68 | @Override
69 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
70 | Bundle savedInstanceState) {
71 | binding = DataBindingUtil.inflate(inflater, ir.oveissi.main.R.layout.fragment_movie_detail, container, false);
72 | return binding.getRoot();
73 | }
74 |
75 | @Override
76 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
77 | onLoadMovieDetail(movie_id);
78 | }
79 |
80 |
81 | public void showMovieDetail(Movie movie) {
82 | ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(movie.title);
83 |
84 | binding.tvOverview.setText(movie.plot);
85 |
86 | Picasso.with(getContext())
87 | .load(movie.poster)
88 | .noFade()
89 | .networkPolicy(NetworkPolicy.OFFLINE)
90 | .into(binding.imPoster);
91 | }
92 |
93 |
94 | public void onLoadMovieDetail(String id) {
95 | DisposableSubscriber disposable = apiInterface
96 | .getMovieById(id)
97 | .subscribeOn(Schedulers.io())
98 | .observeOn(AndroidSchedulers.mainThread())
99 | .subscribeWith(new DisposableSubscriber() {
100 | @Override
101 | public void onComplete() {
102 | }
103 |
104 | @Override
105 | public void onError(Throwable e) {
106 | Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
107 | }
108 |
109 | @Override
110 | public void onNext(Movie movie) {
111 | showMovieDetail(movie);
112 | }
113 | });
114 | }
115 |
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/RegisterFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Context;
5 | import android.databinding.DataBindingUtil;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Toast;
14 |
15 | import javax.inject.Inject;
16 |
17 | import dagger.android.support.AndroidSupportInjection;
18 | import io.reactivex.android.schedulers.AndroidSchedulers;
19 | import io.reactivex.schedulers.Schedulers;
20 | import io.reactivex.subscribers.DisposableSubscriber;
21 | import ir.oveissi.authentication.databinding.FragmentAuthRegisterBinding;
22 | import ir.oveissi.core.navigation.NavigationManager;
23 | import ir.oveissi.core.network.ApiInterface;
24 | import ir.oveissi.core.pojo.Register;
25 | import ir.oveissi.core.pojo.RegisterBody;
26 |
27 |
28 | public class RegisterFragment extends Fragment {
29 |
30 | @Inject
31 | ApiInterface apiInterface;
32 |
33 | @Inject
34 | NavigationManager navigationManager;
35 | ProgressDialog progressDialog;
36 | private FragmentAuthRegisterBinding binding;
37 |
38 | public RegisterFragment() {
39 | // Required empty public constructor
40 | }
41 |
42 | public static RegisterFragment newInstance() {
43 | RegisterFragment fragment = new RegisterFragment();
44 | Bundle args = new Bundle();
45 | // args.putString(ARG_PARAM2, param2);
46 | fragment.setArguments(args);
47 | return fragment;
48 | }
49 |
50 | @Override
51 | public void onAttach(Context context) {
52 | super.onAttach(context);
53 | AndroidSupportInjection.inject(this);
54 | }
55 |
56 | @Override
57 | public void onCreate(Bundle savedInstanceState) {
58 | super.onCreate(savedInstanceState);
59 | if (getArguments() != null) {
60 | // mParam1 = getArguments().getString(ARG_PARAM1);
61 | }
62 | }
63 |
64 | @Override
65 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
66 | Bundle savedInstanceState) {
67 | // Inflate the layout for this fragment
68 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_auth_register, container, false);
69 | return binding.getRoot();
70 | }
71 |
72 | @Override
73 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
74 | super.onViewCreated(view, savedInstanceState);
75 |
76 |
77 | binding.btnRegister.setOnClickListener(new View.OnClickListener() {
78 | @Override
79 | public void onClick(View v) {
80 |
81 | progressDialog = ProgressDialog.show(getContext(), "Loading", "Please Wait...");
82 |
83 | RegisterBody registerBody = new RegisterBody();
84 | registerBody.name = binding.etName.getText().toString();
85 | registerBody.email = binding.etEmail.getText().toString();
86 | registerBody.password = binding.etPassword.getText().toString();
87 |
88 | DisposableSubscriber dispose = apiInterface
89 | .register(registerBody)
90 | .subscribeOn(Schedulers.io())
91 | .observeOn(AndroidSchedulers.mainThread())
92 | .subscribeWith(new DisposableSubscriber() {
93 | @Override
94 | public void onNext(Register token) {
95 | navigationManager.getNavigator(RegisterFragment.this)
96 | .navigateBack();
97 | }
98 |
99 | @Override
100 | public void onError(Throwable t) {
101 | Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
102 | if (progressDialog != null)
103 | progressDialog.dismiss();
104 | }
105 |
106 | @Override
107 | public void onComplete() {
108 | if (progressDialog != null)
109 | progressDialog.dismiss();
110 | }
111 | });
112 |
113 | }
114 | });
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/authentication/src/main/java/ir/oveissi/authentication/LoginFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.authentication;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Context;
5 | import android.databinding.DataBindingUtil;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Toast;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import javax.inject.Inject;
19 |
20 | import dagger.android.support.AndroidSupportInjection;
21 | import io.reactivex.android.schedulers.AndroidSchedulers;
22 | import io.reactivex.schedulers.Schedulers;
23 | import io.reactivex.subscribers.DisposableSubscriber;
24 | import ir.oveissi.authentication.databinding.FragmentAuthLoginBinding;
25 | import ir.oveissi.core.navigation.NavigationManager;
26 | import ir.oveissi.core.network.ApiInterface;
27 | import ir.oveissi.core.pojo.Token;
28 | import ir.oveissi.core.user.User;
29 | import ir.oveissi.core.user.UserManager;
30 |
31 |
32 | public class LoginFragment extends Fragment {
33 |
34 | @Inject
35 | ApiInterface apiInterface;
36 | @Inject
37 | UserManager userManager;
38 | @Inject
39 | NavigationManager navigationManager;
40 | ProgressDialog progressDialog;
41 | private FragmentAuthLoginBinding binding;
42 |
43 | public LoginFragment() {
44 | // Required empty public constructor
45 | }
46 |
47 | public static LoginFragment newInstance() {
48 | LoginFragment fragment = new LoginFragment();
49 | Bundle args = new Bundle();
50 | // args.putString(ARG_PARAM2, param2);
51 | fragment.setArguments(args);
52 | return fragment;
53 | }
54 |
55 | @Override
56 | public void onAttach(Context context) {
57 | super.onAttach(context);
58 | AndroidSupportInjection.inject(this);
59 | }
60 |
61 | @Override
62 | public void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | if (getArguments() != null) {
65 | // mParam1 = getArguments().getString(ARG_PARAM1);
66 | }
67 | }
68 |
69 | @Override
70 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
71 | Bundle savedInstanceState) {
72 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_auth_login, container, false);
73 | return binding.getRoot();
74 | }
75 |
76 | @Override
77 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
78 | super.onViewCreated(view, savedInstanceState);
79 |
80 | binding.tvRegister.setOnClickListener(new View.OnClickListener() {
81 | @Override
82 | public void onClick(View v) {
83 | navigationManager.getNavigator(LoginFragment.this)
84 | .open(RegisterFragment.newInstance());
85 | }
86 | });
87 |
88 | binding.btnLogin.setOnClickListener(new View.OnClickListener() {
89 | @Override
90 | public void onClick(View v) {
91 |
92 | progressDialog = ProgressDialog.show(getContext(), "Loading", "Please Wait...");
93 |
94 | Map map = new HashMap<>();
95 | map.put("grant_type", "password");
96 | map.put("username", binding.etEmail.getText().toString());
97 | map.put("password", binding.etPassword.getText().toString());
98 | DisposableSubscriber dispose = apiInterface.getToken(map)
99 | .subscribeOn(Schedulers.io())
100 | .observeOn(AndroidSchedulers.mainThread())
101 | .subscribeWith(new DisposableSubscriber() {
102 | @Override
103 | public void onNext(Token token) {
104 | User user = new User("",
105 | binding.etEmail.getText().toString(),
106 | token.access_token,
107 | token.refresh_token);
108 | userManager.login(user);
109 | getActivity().finish();
110 | }
111 |
112 | @Override
113 | public void onError(Throwable t) {
114 | Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
115 | if (progressDialog != null)
116 | progressDialog.dismiss();
117 | }
118 |
119 | @Override
120 | public void onComplete() {
121 | if (progressDialog != null)
122 | progressDialog.dismiss();
123 | }
124 | });
125 |
126 | }
127 | });
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/ir/oveissi/androidmodularization/MainActivity.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.androidmodularization;
2 |
3 | import android.app.ProgressDialog;
4 | import android.content.Intent;
5 | import android.databinding.DataBindingUtil;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.design.widget.NavigationView;
9 | import android.support.v7.app.ActionBar;
10 | import android.support.v7.app.ActionBarDrawerToggle;
11 | import android.support.v7.app.AppCompatActivity;
12 | import android.view.Menu;
13 | import android.view.MenuItem;
14 | import android.widget.Toast;
15 |
16 | import javax.inject.Inject;
17 |
18 | import io.reactivex.android.schedulers.AndroidSchedulers;
19 | import io.reactivex.schedulers.Schedulers;
20 | import io.reactivex.subscribers.DisposableSubscriber;
21 | import ir.oveissi.androidmodularization.databinding.ActivityMainBinding;
22 | import ir.oveissi.authentication.AuthMainActivity;
23 | import ir.oveissi.core.bases.AppComponentHolder;
24 | import ir.oveissi.core.di.NavigationProvider;
25 | import ir.oveissi.core.navigation.HasNavigatorManager;
26 | import ir.oveissi.core.navigation.NavigationManager;
27 | import ir.oveissi.core.navigation.Navigator;
28 | import ir.oveissi.core.network.ApiInterface;
29 | import ir.oveissi.core.pojo.UserInfo;
30 | import ir.oveissi.core.user.UserManager;
31 | import ir.oveissi.main.MoviesFragment;
32 |
33 | public class MainActivity extends AppCompatActivity implements HasNavigatorManager, NavigationView.OnNavigationItemSelectedListener {
34 |
35 | @Inject
36 | NavigationManager navigationManager;
37 |
38 | @Inject
39 | ApiInterface apiInterface;
40 | @Inject
41 | UserManager userManager;
42 | private MainActivityComponent mainComponent;
43 | private Navigator childNavigator;
44 | private ActionBarDrawerToggle mDrawerToggle;
45 |
46 |
47 | private ActivityMainBinding binding;
48 | private ProgressDialog progressDialog;
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
54 |
55 | mainComponent = DaggerMainActivityComponent.builder()
56 | .appComponent(((AppComponentHolder) getApplicationContext()).getAppComponent())
57 | .navigationProvider(new NavigationProvider(this))
58 | .build();
59 | mainComponent.inject(this);
60 |
61 | childNavigator = navigationManager.init(this, getSupportFragmentManager(), R.id.container);
62 | childNavigator.open(MoviesFragment.newInstance(""));
63 |
64 | mDrawerToggle = new ActionBarDrawerToggle(this,
65 | binding.drawerLayout,
66 | binding.includedToolbar.myToolbar,
67 | R.string.cancel,
68 | R.string.cancel);
69 | mDrawerToggle.syncState();
70 | binding.drawerLayout.addDrawerListener(mDrawerToggle);
71 |
72 | setSupportActionBar(binding.includedToolbar.myToolbar);
73 | ActionBar actionBar = getSupportActionBar();
74 | actionBar.setTitle("Movies");
75 |
76 | Menu navMenu = binding.navigation.getMenu();
77 | binding.navigation.setNavigationItemSelectedListener(this);
78 |
79 | // navMenu.findItem(R.id.navigation_subheader_debug).isVisible = BuildConfig.DEBUG
80 |
81 | }
82 |
83 | @Override
84 | public Navigator provideNavigator() {
85 | return childNavigator;
86 | }
87 |
88 | @Override
89 | public boolean onOptionsItemSelected(MenuItem item) {
90 |
91 | if (mDrawerToggle.onOptionsItemSelected(item)) {
92 | return true;
93 | }
94 | return super.onOptionsItemSelected(item);
95 | }
96 |
97 | @Override
98 | public boolean onNavigationItemSelected(@NonNull MenuItem item) {
99 |
100 |
101 | switch (item.getItemId()) {
102 | case R.id.navigation_login:
103 | startActivity(new Intent(this, AuthMainActivity.class));
104 | return true;
105 | case R.id.navigation_logout:
106 | userManager.logout();
107 | Menu navMenu = binding.navigation.getMenu();
108 | navMenu.findItem(R.id.navigation_logout).setVisible(false);
109 | navMenu.findItem(R.id.navigation_profile).setVisible(false);
110 | navMenu.findItem(R.id.navigation_login).setVisible(true);
111 | return true;
112 | case R.id.navigation_profile:
113 | getUserInfo();
114 | return true;
115 | }
116 | return false;
117 | }
118 |
119 | private void getUserInfo() {
120 | progressDialog = ProgressDialog.show(this, "Loading", "Please Wait...");
121 | DisposableSubscriber dispose = apiInterface
122 | .getUserInfo()
123 | .subscribeOn(Schedulers.io())
124 | .observeOn(AndroidSchedulers.mainThread())
125 | .subscribeWith(new DisposableSubscriber() {
126 | @Override
127 | public void onNext(UserInfo userInfo) {
128 | Toast.makeText(MainActivity.this, "Hi " + userInfo.name + "!", Toast.LENGTH_SHORT).show();
129 | }
130 |
131 | @Override
132 | public void onError(Throwable t) {
133 | Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
134 | if (progressDialog != null)
135 | progressDialog.dismiss();
136 | }
137 |
138 | @Override
139 | public void onComplete() {
140 | if (progressDialog != null)
141 | progressDialog.dismiss();
142 | }
143 | });
144 |
145 | }
146 |
147 |
148 | @Override
149 | protected void onResume() {
150 | super.onResume();
151 |
152 | Menu navMenu = binding.navigation.getMenu();
153 | if (userManager.isUserLogged()) {
154 | navMenu.findItem(R.id.navigation_logout).setVisible(true);
155 | navMenu.findItem(R.id.navigation_profile).setVisible(true);
156 |
157 | navMenu.findItem(R.id.navigation_login).setVisible(false);
158 | } else {
159 | navMenu.findItem(R.id.navigation_logout).setVisible(false);
160 | navMenu.findItem(R.id.navigation_profile).setVisible(false);
161 |
162 | navMenu.findItem(R.id.navigation_login).setVisible(true);
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/main/src/main/java/ir/oveissi/main/MoviesFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.main;
2 |
3 | import android.content.Context;
4 | import android.databinding.DataBindingUtil;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.view.LayoutInflater;
11 | import android.view.Menu;
12 | import android.view.MenuInflater;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.Toast;
17 |
18 | import java.util.ArrayList;
19 |
20 | import javax.inject.Inject;
21 |
22 | import io.reactivex.android.schedulers.AndroidSchedulers;
23 | import io.reactivex.schedulers.Schedulers;
24 | import io.reactivex.subscribers.DisposableSubscriber;
25 | import ir.oveissi.core.bases.AppComponentHolder;
26 | import ir.oveissi.core.bases.BaseFragment;
27 | import ir.oveissi.core.di.NavigationProvider;
28 | import ir.oveissi.core.navigation.NavigationManager;
29 | import ir.oveissi.core.navigation.SearchEntryPoint;
30 | import ir.oveissi.core.network.ApiInterface;
31 | import ir.oveissi.core.pojo.Movie;
32 | import ir.oveissi.core.pojo.Pagination;
33 | import ir.oveissi.main.databinding.FragmentMoviesBinding;
34 |
35 |
36 | public class MoviesFragment extends BaseFragment implements MoviesAdapter.ItemClickListener {
37 |
38 | public String title = "";
39 | public int current_page = 1;
40 | @Inject
41 | ApiInterface apiInterface;
42 | @Inject
43 | NavigationManager navigationManager;
44 | @Inject
45 | SearchEntryPoint searchEntryPoint;
46 | FragmentMoviesBinding binding;
47 | private MoviesAdapter mListAdapter;
48 | private EndlessLinearLayoutRecyclerview.onLoadMoreListener endlessLinearLayoutRecyclerview;
49 |
50 | public MoviesFragment() {
51 | // Required empty public constructor
52 | }
53 |
54 | public static MoviesFragment newInstance(String title) {
55 | MoviesFragment fragment = new MoviesFragment();
56 | Bundle args = new Bundle();
57 | args.putString("title", title);
58 | fragment.setArguments(args);
59 | return fragment;
60 | }
61 |
62 | @Override
63 | public void onCreate(@Nullable Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | if (getArguments() != null) {
66 | title = getArguments().getString("title");
67 | }
68 | }
69 |
70 | @Override
71 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
72 | Bundle savedInstanceState) {
73 | setHasOptionsMenu(true);
74 | binding = DataBindingUtil.inflate(inflater, ir.oveissi.main.R.layout.fragment_movies, container, false);
75 | return binding.getRoot();
76 | }
77 |
78 | @Override
79 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
80 | super.onCreateOptionsMenu(menu, inflater);
81 |
82 | MenuItem search = menu.add("Search");
83 | search.setIcon(ir.oveissi.main.R.drawable.ic_search);
84 | search.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
85 | }
86 |
87 | @Override
88 | public boolean onOptionsItemSelected(MenuItem item) {
89 |
90 | if (item.getTitle() != null && item.getTitle().equals("Search")) {
91 | navigationManager.getNavigator(this).open(searchEntryPoint.openMain());
92 | return true;
93 | }
94 | return super.onOptionsItemSelected(item);
95 |
96 | }
97 |
98 | @Override
99 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
100 | ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Movies");
101 |
102 |
103 | endlessLinearLayoutRecyclerview = new EndlessLinearLayoutRecyclerview.onLoadMoreListener() {
104 | @Override
105 | public void onLoadMore() {
106 | onLoadMoviesByTitle(title, current_page);
107 | }
108 | };
109 |
110 |
111 | if (mListAdapter == null) {
112 | mListAdapter = new MoviesAdapter(getActivity(), new ArrayList());
113 | mListAdapter.setItemClickListener(this);
114 | binding.rvMovies.setAdapter(mListAdapter);
115 |
116 | binding.rvMovies.setLayoutManager(new LinearLayoutManager(getContext()));
117 | binding.rvMovies.setOnLoadMoreListener(endlessLinearLayoutRecyclerview);
118 |
119 | current_page = 1;
120 | onLoadMoviesByTitle(title, current_page);
121 | } else {
122 | binding.rvMovies.setAdapter(mListAdapter);
123 |
124 | binding.rvMovies.setLayoutManager(new LinearLayoutManager(getContext()));
125 | binding.rvMovies.setOnLoadMoreListener(endlessLinearLayoutRecyclerview);
126 | binding.rvMovies.setLoading(false);
127 | }
128 |
129 | }
130 |
131 | @Override
132 | public void onAttach(Context context) {
133 | super.onAttach(context);
134 |
135 | MoviesComponent component = DaggerMoviesComponent
136 | .builder()
137 | .navigationProvider(new NavigationProvider(this))
138 | .appComponent(((AppComponentHolder) getActivity().getApplicationContext()).getAppComponent())
139 | .build();
140 | component.inject(this);
141 | }
142 |
143 | public void showMoreMovies(Pagination movies) {
144 | int lastPage = (movies.metadata.total_count / movies.metadata.per_page) + 1;
145 | if (movies.metadata.current_page != lastPage)
146 | binding.rvMovies.setLoading(false);
147 | current_page++;
148 | for (Movie p : movies.data) {
149 | mListAdapter.addItem(p);
150 | }
151 | }
152 |
153 | public void onLoadMoviesByTitle(String title, int page) {
154 | DisposableSubscriber> disposable = apiInterface
155 | .getMoviesByTitle(title, page)
156 | .subscribeOn(Schedulers.io())
157 | .observeOn(AndroidSchedulers.mainThread())
158 | .subscribeWith(new DisposableSubscriber>() {
159 | @Override
160 | public void onComplete() {
161 | }
162 |
163 | @Override
164 | public void onError(Throwable e) {
165 | Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
166 | }
167 |
168 | @Override
169 | public void onNext(Pagination movies) {
170 | showMoreMovies(movies);
171 | }
172 | });
173 | }
174 |
175 | @Override
176 | public void ItemClicked(int position, Movie item) {
177 | navigationManager.getNavigator(this)
178 | .open(MovieDetailFragment.newInstance(String.valueOf(item.id)));
179 |
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/search/src/main/java/ir/oveissi/search/MovieSearchFragment.java:
--------------------------------------------------------------------------------
1 | package ir.oveissi.search;
2 |
3 | import android.content.Context;
4 | import android.databinding.DataBindingUtil;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.text.Editable;
11 | import android.text.TextWatcher;
12 | import android.view.KeyEvent;
13 | import android.view.LayoutInflater;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.view.inputmethod.EditorInfo;
17 | import android.widget.TextView;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 | import java.util.concurrent.Callable;
22 |
23 | import javax.inject.Inject;
24 |
25 | import io.reactivex.Flowable;
26 | import io.reactivex.android.schedulers.AndroidSchedulers;
27 | import io.reactivex.schedulers.Schedulers;
28 | import io.reactivex.subscribers.DisposableSubscriber;
29 | import ir.oveissi.core.bases.AppComponentHolder;
30 | import ir.oveissi.core.bases.BaseFragment;
31 | import ir.oveissi.core.di.NavigationProvider;
32 | import ir.oveissi.core.navigation.MainEntryPoint;
33 | import ir.oveissi.core.navigation.NavigationManager;
34 | import ir.oveissi.search.databinding.FragmentMovieSearchBinding;
35 | import ir.oveissi.search.db.AppDatabaseSingleton;
36 | import ir.oveissi.search.db.Suggestion;
37 |
38 |
39 | public class MovieSearchFragment extends BaseFragment implements MovieSearchAdapter.ItemClickListener {
40 |
41 | FragmentMovieSearchBinding binding;
42 | @Inject
43 | NavigationManager navigationManager;
44 | @Inject
45 | MainEntryPoint mainEntryPoint;
46 | private MovieSearchAdapter mListAdapter;
47 |
48 | public MovieSearchFragment() {
49 | // Required empty public constructor
50 | }
51 |
52 | public static MovieSearchFragment newInstance() {
53 | MovieSearchFragment fragment = new MovieSearchFragment();
54 | return fragment;
55 | }
56 |
57 | @Override
58 | public void onCreate(@Nullable Bundle savedInstanceState) {
59 | super.onCreate(savedInstanceState);
60 | }
61 |
62 | @Override
63 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
64 | Bundle savedInstanceState) {
65 | binding = DataBindingUtil.inflate(inflater, ir.oveissi.search.R.layout.fragment_movie_search, container, false);
66 | return binding.getRoot();
67 | }
68 |
69 | @Override
70 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
71 | ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Search Movies");
72 |
73 |
74 | binding.rvSuggestion.setAdapter(new MovieSearchAdapter(getContext(), new ArrayList()));
75 | binding.rvSuggestion.setLayoutManager(new LinearLayoutManager(getContext()));
76 |
77 | getSuggestionFromDB("");
78 |
79 |
80 | binding.etSearch.addTextChangedListener(new TextWatcher() {
81 | @Override
82 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
83 |
84 | }
85 |
86 | @Override
87 | public void onTextChanged(CharSequence s, int start, int before, int count) {
88 | getSuggestionFromDB(s.toString());
89 | }
90 |
91 | @Override
92 | public void afterTextChanged(Editable s) {
93 |
94 | }
95 | });
96 |
97 | binding.etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
98 | @Override
99 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
100 | if (actionId == EditorInfo.IME_ACTION_SEARCH) {
101 | search();
102 | return true;
103 | }
104 | return false;
105 | }
106 | });
107 | }
108 |
109 | private void getSuggestionFromDB(String query) {
110 |
111 | Flowable> flowable = null;
112 | if (query.isEmpty()) {
113 | flowable = AppDatabaseSingleton.getIntance(getContext().getApplicationContext())
114 | .suggestionDAO()
115 | .getAll();
116 | } else {
117 | flowable = AppDatabaseSingleton.getIntance(getContext().getApplicationContext())
118 | .suggestionDAO()
119 | .getAllOrderByCount(query);
120 | }
121 | DisposableSubscriber> dispose = flowable
122 | .subscribeOn(Schedulers.io())
123 | .observeOn(AndroidSchedulers.mainThread())
124 | .subscribeWith(new DisposableSubscriber>() {
125 | @Override
126 | public void onNext(List suggestions) {
127 | mListAdapter = new MovieSearchAdapter(getActivity(), suggestions);
128 | binding.rvSuggestion.setAdapter(mListAdapter);
129 | mListAdapter.setItemClickListener(MovieSearchFragment.this);
130 | }
131 |
132 | @Override
133 | public void onError(Throwable t) {
134 |
135 | }
136 |
137 | @Override
138 | public void onComplete() {
139 |
140 | }
141 | });
142 |
143 |
144 | }
145 |
146 | private void search() {
147 | Flowable observable = Flowable.fromCallable(new Callable() {
148 | @Override
149 | public Boolean call() throws Exception {
150 | Suggestion suggestion = new Suggestion();
151 | suggestion.setTitle(binding.etSearch.getText().toString());
152 | suggestion.setCount(0);
153 |
154 | AppDatabaseSingleton.getIntance(getContext().getApplicationContext())
155 | .suggestionDAO()
156 | .insertAll(suggestion);
157 | return true;
158 | }
159 | });
160 | DisposableSubscriber dispose = observable.subscribeOn(Schedulers.io())
161 | .observeOn(AndroidSchedulers.mainThread())
162 | .subscribeWith(new DisposableSubscriber() {
163 | @Override
164 | public void onNext(Boolean aVoid) {
165 | navigationManager.getNavigator(MovieSearchFragment.this)
166 | .open(mainEntryPoint.openMain(binding.etSearch.getText().toString()));
167 | }
168 |
169 | @Override
170 | public void onError(Throwable e) {
171 |
172 | }
173 |
174 | @Override
175 | public void onComplete() {
176 |
177 | }
178 | });
179 |
180 |
181 | }
182 |
183 | @Override
184 | public void onAttach(Context context) {
185 | super.onAttach(context);
186 |
187 | MovieSearchComponent component = DaggerMovieSearchComponent
188 | .builder()
189 | .navigationProvider(new NavigationProvider(this))
190 | .appComponent(((AppComponentHolder) getActivity().getApplicationContext()).getAppComponent())
191 | .build();
192 | component.inject(this);
193 |
194 | }
195 |
196 | @Override
197 | public void ItemClicked(int position, Suggestion item) {
198 | navigationManager.getNavigator(MovieSearchFragment.this)
199 | .open(mainEntryPoint.openMain(item.getTitle()));
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/c,vim,osx,c++,java,linux,macos,cmake,gradle,eclipse,windows,android,intellij,sublimetext,intellij+iml,intellij+all,androidstudio
3 |
4 | ### Android ###
5 | # Built application files
6 | *.apk
7 | *.ap_
8 |
9 | # Files for the ART/Dalvik VM
10 | *.dex
11 |
12 | # Java class files
13 | *.class
14 |
15 | # Generated files
16 | bin/
17 | gen/
18 | out/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # Intellij
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/dictionaries
45 | .idea/libraries
46 |
47 | # External native build folder generated in Android Studio 2.2 and later
48 | .externalNativeBuild
49 |
50 | # Freeline
51 | freeline.py
52 | freeline/
53 | freeline_project_description.json
54 |
55 | ### Android Patch ###
56 | gen-external-apklibs
57 |
58 | ### AndroidStudio ###
59 | # Covers files to be ignored for android development using Android Studio.
60 |
61 | # Built application files
62 |
63 | # Files for the ART/Dalvik VM
64 |
65 | # Java class files
66 |
67 | # Generated files
68 |
69 | # Gradle files
70 | .gradle
71 |
72 | # Signing files
73 | .signing/
74 |
75 | # Local configuration file (sdk path, etc)
76 |
77 | # Proguard folder generated by Eclipse
78 |
79 | # Log Files
80 |
81 | # Android Studio
82 | /*/build/
83 | /*/local.properties
84 | /*/out
85 | /*/*/build
86 | /*/*/production
87 | *.ipr
88 | *~
89 | *.swp
90 |
91 | # Android Patch
92 |
93 | # External native build folder generated in Android Studio 2.2 and later
94 |
95 | # NDK
96 | obj/
97 |
98 | # IntelliJ IDEA
99 | *.iws
100 | /out/
101 |
102 | # User-specific configurations
103 | .idea/libraries/
104 | .idea/.name
105 | .idea/compiler.xml
106 | .idea/copyright/profiles_settings.xml
107 | .idea/encodings.xml
108 | .idea/misc.xml
109 | .idea/modules.xml
110 | .idea/scopes/scope_settings.xml
111 | .idea/vcs.xml
112 | .idea/jsLibraryMappings.xml
113 | .idea/datasources.xml
114 | .idea/dataSources.ids
115 | .idea/sqlDataSources.xml
116 | .idea/dynamic.xml
117 | .idea/uiDesigner.xml
118 |
119 | # OS-specific files
120 | .DS_Store
121 | .DS_Store?
122 | ._*
123 | .Spotlight-V100
124 | .Trashes
125 | ehthumbs.db
126 | Thumbs.db
127 |
128 | # Legacy Eclipse project files
129 | .classpath
130 | .project
131 |
132 | # Mobile Tools for Java (J2ME)
133 | .mtj.tmp/
134 |
135 | # Package Files #
136 | *.war
137 | *.ear
138 |
139 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
140 | hs_err_pid*
141 |
142 | ## Plugin-specific files:
143 |
144 | # mpeltonen/sbt-idea plugin
145 | .idea_modules/
146 |
147 | # JIRA plugin
148 | atlassian-ide-plugin.xml
149 |
150 | # Mongo Explorer plugin
151 | .idea/mongoSettings.xml
152 |
153 | # Crashlytics plugin (for Android Studio and IntelliJ)
154 | com_crashlytics_export_strings.xml
155 | crashlytics.properties
156 | crashlytics-build.properties
157 | fabric.properties
158 |
159 | ### AndroidStudio Patch ###
160 |
161 | !/gradle/wrapper/gradle-wrapper.jar
162 |
163 | ### C ###
164 | # Prerequisites
165 | *.d
166 |
167 | # Object files
168 | *.o
169 | *.ko
170 | *.obj
171 | *.elf
172 |
173 | # Linker output
174 | *.ilk
175 | *.map
176 | *.exp
177 |
178 | # Precompiled Headers
179 | *.gch
180 | *.pch
181 |
182 | # Libraries
183 | *.lib
184 | *.a
185 | *.la
186 | *.lo
187 |
188 | # Shared objects (inc. Windows DLLs)
189 | *.dll
190 | *.so
191 | *.so.*
192 | *.dylib
193 |
194 | # Executables
195 | *.exe
196 | *.out
197 | *.app
198 | *.i*86
199 | *.x86_64
200 | *.hex
201 |
202 | # Debug files
203 | *.dSYM/
204 | *.su
205 | *.idb
206 | *.pdb
207 |
208 | # Kernel Module Compile Results
209 | *.mod*
210 | *.cmd
211 | .tmp_versions/
212 | modules.order
213 | Module.symvers
214 | Mkfile.old
215 | dkms.conf
216 |
217 | ### C++ ###
218 | # Prerequisites
219 |
220 | # Compiled Object files
221 | *.slo
222 |
223 | # Precompiled Headers
224 |
225 | # Compiled Dynamic libraries
226 |
227 | # Fortran module files
228 | *.mod
229 | *.smod
230 |
231 | # Compiled Static libraries
232 | *.lai
233 |
234 | # Executables
235 |
236 | ### CMake ###
237 | CMakeCache.txt
238 | CMakeFiles
239 | CMakeScripts
240 | Testing
241 | Makefile
242 | cmake_install.cmake
243 | install_manifest.txt
244 | compile_commands.json
245 | CTestTestfile.cmake
246 | build
247 |
248 | ### Eclipse ###
249 |
250 | .metadata
251 | tmp/
252 | *.tmp
253 | *.bak
254 | *~.nib
255 | .settings/
256 | .loadpath
257 | .recommenders
258 |
259 | # External tool builders
260 | .externalToolBuilders/
261 |
262 | # Locally stored "Eclipse launch configurations"
263 | *.launch
264 |
265 | # PyDev specific (Python IDE for Eclipse)
266 | *.pydevproject
267 |
268 | # CDT-specific (C/C++ Development Tooling)
269 | .cproject
270 |
271 | # Java annotation processor (APT)
272 | .factorypath
273 |
274 | # PDT-specific (PHP Development Tools)
275 | .buildpath
276 |
277 | # sbteclipse plugin
278 | .target
279 |
280 | # Tern plugin
281 | .tern-project
282 |
283 | # TeXlipse plugin
284 | .texlipse
285 |
286 | # STS (Spring Tool Suite)
287 | .springBeans
288 |
289 | # Code Recommenders
290 | .recommenders/
291 |
292 | # Scala IDE specific (Scala & Java development for Eclipse)
293 | .cache-main
294 | .scala_dependencies
295 | .worksheet
296 |
297 | ### Eclipse Patch ###
298 | # Eclipse Core
299 |
300 | # JDT-specific (Eclipse Java Development Tools)
301 |
302 | ### Intellij ###
303 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
304 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
305 |
306 | # User-specific stuff:
307 | .idea/**/workspace.xml
308 | .idea/**/tasks.xml
309 |
310 | # Sensitive or high-churn files:
311 | .idea/**/dataSources/
312 | .idea/**/dataSources.ids
313 | .idea/**/dataSources.xml
314 | .idea/**/dataSources.local.xml
315 | .idea/**/sqlDataSources.xml
316 | .idea/**/dynamic.xml
317 | .idea/**/uiDesigner.xml
318 |
319 | # Gradle:
320 | .idea/**/gradle.xml
321 | .idea/**/libraries
322 |
323 | # CMake
324 | cmake-build-debug/
325 |
326 | # Mongo Explorer plugin:
327 | .idea/**/mongoSettings.xml
328 |
329 | ## File-based project format:
330 |
331 | ## Plugin-specific files:
332 |
333 | # IntelliJ
334 |
335 | # mpeltonen/sbt-idea plugin
336 |
337 | # JIRA plugin
338 |
339 | # Cursive Clojure plugin
340 | .idea/replstate.xml
341 |
342 | # Ruby plugin and RubyMine
343 | /.rakeTasks
344 |
345 | # Crashlytics plugin (for Android Studio and IntelliJ)
346 |
347 | ### Intellij Patch ###
348 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
349 |
350 | # *.iml
351 | # modules.xml
352 | # .idea/misc.xml
353 | # *.ipr
354 |
355 | # Sonarlint plugin
356 | .idea/sonarlint
357 |
358 | ### Intellij+all ###
359 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
360 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
361 |
362 | # User-specific stuff:
363 |
364 | # Sensitive or high-churn files:
365 |
366 | # Gradle:
367 |
368 | # CMake
369 |
370 | # Mongo Explorer plugin:
371 |
372 | ## File-based project format:
373 |
374 | ## Plugin-specific files:
375 |
376 | # IntelliJ
377 |
378 | # mpeltonen/sbt-idea plugin
379 |
380 | # JIRA plugin
381 |
382 | # Cursive Clojure plugin
383 |
384 | # Ruby plugin and RubyMine
385 |
386 | # Crashlytics plugin (for Android Studio and IntelliJ)
387 |
388 | ### Intellij+all Patch ###
389 | # Ignores the whole idea folder
390 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
391 |
392 | .idea/
393 |
394 | ### Intellij+iml ###
395 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
396 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
397 |
398 | # User-specific stuff:
399 |
400 | # Sensitive or high-churn files:
401 |
402 | # Gradle:
403 |
404 | # CMake
405 |
406 | # Mongo Explorer plugin:
407 |
408 | ## File-based project format:
409 |
410 | ## Plugin-specific files:
411 |
412 | # IntelliJ
413 |
414 | # mpeltonen/sbt-idea plugin
415 |
416 | # JIRA plugin
417 |
418 | # Cursive Clojure plugin
419 |
420 | # Ruby plugin and RubyMine
421 |
422 | # Crashlytics plugin (for Android Studio and IntelliJ)
423 |
424 | ### Intellij+iml Patch ###
425 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
426 |
427 | modules.xml
428 |
429 | ### Java ###
430 | # Compiled class file
431 |
432 | # Log file
433 |
434 | # BlueJ files
435 | *.ctxt
436 |
437 | # Mobile Tools for Java (J2ME)
438 |
439 | # Package Files #
440 | *.jar
441 | *.zip
442 | *.tar.gz
443 | *.rar
444 |
445 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
446 |
447 | ### Linux ###
448 |
449 | # temporary files which can be created if a process still has a handle open of a deleted file
450 | .fuse_hidden*
451 |
452 | # KDE directory preferences
453 | .directory
454 |
455 | # Linux trash folder which might appear on any partition or disk
456 | .Trash-*
457 |
458 | # .nfs files are created when an open file is removed but is still being accessed
459 | .nfs*
460 |
461 | ### macOS ###
462 | *.DS_Store
463 | .AppleDouble
464 | .LSOverride
465 |
466 | # Icon must end with two \r
467 | Icon
468 |
469 | # Thumbnails
470 |
471 | # Files that might appear in the root of a volume
472 | .DocumentRevisions-V100
473 | .fseventsd
474 | .TemporaryItems
475 | .VolumeIcon.icns
476 | .com.apple.timemachine.donotpresent
477 |
478 | # Directories potentially created on remote AFP share
479 | .AppleDB
480 | .AppleDesktop
481 | Network Trash Folder
482 | Temporary Items
483 | .apdisk
484 |
485 | ### OSX ###
486 |
487 | # Icon must end with two \r
488 |
489 | # Thumbnails
490 |
491 | # Files that might appear in the root of a volume
492 |
493 | # Directories potentially created on remote AFP share
494 |
495 | ### SublimeText ###
496 | # cache files for sublime text
497 | *.tmlanguage.cache
498 | *.tmPreferences.cache
499 | *.stTheme.cache
500 |
501 | # workspace files are user-specific
502 | *.sublime-workspace
503 |
504 | # project files should be checked into the repository, unless a significant
505 | # proportion of contributors will probably not be using SublimeText
506 | # *.sublime-project
507 |
508 | # sftp configuration file
509 | sftp-config.json
510 |
511 | # Package control specific files
512 | Package Control.last-run
513 | Package Control.ca-list
514 | Package Control.ca-bundle
515 | Package Control.system-ca-bundle
516 | Package Control.cache/
517 | Package Control.ca-certs/
518 | Package Control.merged-ca-bundle
519 | Package Control.user-ca-bundle
520 | oscrypto-ca-bundle.crt
521 | bh_unicode_properties.cache
522 |
523 | # Sublime-github package stores a github token in this file
524 | # https://packagecontrol.io/packages/sublime-github
525 | GitHub.sublime-settings
526 |
527 | ### Vim ###
528 | # swap
529 | [._]*.s[a-v][a-z]
530 | [._]*.sw[a-p]
531 | [._]s[a-v][a-z]
532 | [._]sw[a-p]
533 | # session
534 | Session.vim
535 | # temporary
536 | .netrwhist
537 | # auto-generated tag files
538 | tags
539 |
540 | ### Windows ###
541 | # Windows thumbnail cache files
542 | ehthumbs_vista.db
543 |
544 | # Folder config file
545 | Desktop.ini
546 |
547 | # Recycle Bin used on file shares
548 | $RECYCLE.BIN/
549 |
550 | # Windows Installer files
551 | *.cab
552 | *.msi
553 | *.msm
554 | *.msp
555 |
556 | # Windows shortcuts
557 | *.lnk
558 |
559 | ### Gradle ###
560 | **/build/
561 |
562 | # Ignore Gradle GUI config
563 | gradle-app.setting
564 |
565 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
566 | !gradle-wrapper.jar
567 |
568 | # Cache of project
569 | .gradletasknamecache
570 |
571 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
572 | # gradle/wrapper/gradle-wrapper.properties
573 |
574 | # End of https://www.gitignore.io/api/c,vim,osx,c++,java,linux,macos,cmake,gradle,eclipse,windows,android,intellij,sublimetext,intellij+iml,intellij+all,androidstudio
575 |
--------------------------------------------------------------------------------