├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_close.png
│ │ │ │ └── ic_search.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_close.png
│ │ │ │ └── ic_search.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_close.png
│ │ │ │ └── ic_search.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_close.png
│ │ │ │ └── ic_search.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── transition
│ │ │ │ ├── transition_main_exit.xml
│ │ │ │ ├── transition_main_reenter.xml
│ │ │ │ ├── transition_cinema_detail_enter.xml
│ │ │ │ └── transition_shared_element.xml
│ │ │ ├── transition-v21
│ │ │ │ └── transition_cinema_detail_enter.xml
│ │ │ ├── drawable
│ │ │ │ ├── bg_indicator_view.xml
│ │ │ │ ├── more_info_button_selected.xml
│ │ │ │ ├── rating_background_green.xml
│ │ │ │ ├── rating_background_orange.xml
│ │ │ │ ├── search_background.xml
│ │ │ │ ├── ic_big_cinema_in_one_row.xml
│ │ │ │ ├── more_info_button.xml
│ │ │ │ ├── ic_sort.xml
│ │ │ │ ├── ic_download.xml
│ │ │ │ ├── ic_add.xml
│ │ │ │ ├── ic_home.xml
│ │ │ │ ├── ic_arrow_up.xml
│ │ │ │ ├── ic_star.xml
│ │ │ │ ├── ic_dashboard_black_24dp.xml
│ │ │ │ ├── ic_small_cinema_in_one_row.xml
│ │ │ │ ├── ic_delete.xml
│ │ │ │ ├── ic_star_black_24dp.xml
│ │ │ │ ├── rectangle_background.xml
│ │ │ │ ├── ic_actor_default_avatar.xml
│ │ │ │ ├── ic_cinema.xml
│ │ │ │ ├── more_info_button_effect.xml
│ │ │ │ ├── ic_date.xml
│ │ │ │ ├── ic_watch.xml
│ │ │ │ ├── ic_schedule.xml
│ │ │ │ ├── ic_share.xml
│ │ │ │ ├── ic_action_search.xml
│ │ │ │ └── ic_settings.xml
│ │ │ ├── values
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── arrays.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── layout
│ │ │ │ ├── item_actor_photo.xml
│ │ │ │ ├── item_small_cinema_header.xml
│ │ │ │ ├── fragment_cinema_detail_content.xml
│ │ │ │ ├── fragment_list_cinema.xml
│ │ │ │ ├── fragment_actors.xml
│ │ │ │ ├── fragment_small_cinemas.xml
│ │ │ │ ├── item_slider_poster.xml
│ │ │ │ ├── nav_header.xml
│ │ │ │ ├── cinema_detail_floating_action_button_menu.xml
│ │ │ │ ├── activity_posters_slider.xml
│ │ │ │ ├── activity_favourite_list_cinema.xml
│ │ │ │ └── activity_popular_actors.xml
│ │ │ ├── menu
│ │ │ │ ├── menu_main.xml
│ │ │ │ ├── menu_navigation.xml
│ │ │ │ └── menu_drawer_items.xml
│ │ │ ├── drawable-v21
│ │ │ │ └── more_info_button_effect.xml
│ │ │ ├── xml
│ │ │ │ └── pref_mediateka_settings.xml
│ │ │ └── values-v21
│ │ │ │ └── styles.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── ru
│ │ │ └── devit
│ │ │ └── mediateka
│ │ │ ├── presentation
│ │ │ ├── base
│ │ │ │ ├── BaseView.java
│ │ │ │ └── BasePresenter.java
│ │ │ ├── main
│ │ │ │ ├── SyncConnectionListener.java
│ │ │ │ └── MainPresenter.java
│ │ │ ├── common
│ │ │ │ ├── OnActorClickListener.java
│ │ │ │ ├── OnCinemaClickListener.java
│ │ │ │ ├── HolderRenderer.java
│ │ │ │ ├── CinemaTabSelectorView.java
│ │ │ │ ├── OnTabSelectedListener.java
│ │ │ │ ├── OnPageScrolled.java
│ │ │ │ ├── ViewPagerAdapter.java
│ │ │ │ ├── AbstractCinemaListAdapter.java
│ │ │ │ └── PhotoLoader.kt
│ │ │ ├── settings
│ │ │ │ ├── SettingsActivity.java
│ │ │ │ └── PreferenceSettingsFragment.java
│ │ │ ├── smallcinemalist
│ │ │ │ └── SmallCinemaHeaderViewHolder.java
│ │ │ ├── cinemadetail
│ │ │ │ └── CinemaDetailContentPresenter.java
│ │ │ ├── posterslider
│ │ │ │ ├── PosterSliderPresenter.java
│ │ │ │ └── PosterSliderAdapter.java
│ │ │ ├── actordetail
│ │ │ │ ├── ActorDetailContentPresenter.java
│ │ │ │ └── ActorDetailPresenter.java
│ │ │ ├── cinemalist
│ │ │ │ ├── CinemaListAdapter.java
│ │ │ │ └── CinemaTab.java
│ │ │ ├── actorlist
│ │ │ │ ├── ActorListAdapter.java
│ │ │ │ ├── ActorViewHolder.java
│ │ │ │ └── ActorsPresenter.java
│ │ │ ├── popularactors
│ │ │ │ └── PopularActorsPresenter.java
│ │ │ ├── widget
│ │ │ │ ├── HideableFABBehaviour.kt
│ │ │ │ ├── CinemaHeaderView.java
│ │ │ │ └── DateAndTimePicker.java
│ │ │ ├── favouritelistcinema
│ │ │ │ └── CinemaSortingDialog.java
│ │ │ └── search
│ │ │ │ └── SearchPresenter.java
│ │ │ ├── di
│ │ │ ├── ActivityScope.java
│ │ │ ├── actor
│ │ │ │ ├── ActorScope.java
│ │ │ │ ├── ActorListComponent.java
│ │ │ │ ├── ActorComponent.java
│ │ │ │ ├── ActorDetailComponent.java
│ │ │ │ ├── ActorListModule.java
│ │ │ │ ├── ActorDetailModule.java
│ │ │ │ └── ActorModule.java
│ │ │ ├── cinema
│ │ │ │ ├── CinemaScope.java
│ │ │ │ ├── cinemalist
│ │ │ │ │ ├── CinemaListComponent.java
│ │ │ │ │ └── CinemaListModule.java
│ │ │ │ ├── cinemafavourite
│ │ │ │ │ ├── CinemaFavouriteListComponent.java
│ │ │ │ │ └── CinemaFavouriteListModule.java
│ │ │ │ ├── cinemadetail
│ │ │ │ │ └── CinemaDetailComponent.java
│ │ │ │ ├── CinemaComponent.java
│ │ │ │ └── CinemaModule.java
│ │ │ ├── application
│ │ │ │ ├── AppComponent.java
│ │ │ │ ├── AppModule.java
│ │ │ │ └── RetrofitModule.java
│ │ │ └── ComponentsManager.java
│ │ │ ├── domain
│ │ │ ├── SystemTimeCalculator.java
│ │ │ ├── UseCaseSubscriber.java
│ │ │ ├── ActorRepository.java
│ │ │ ├── CinemaRepository.java
│ │ │ ├── actorusecases
│ │ │ │ ├── GetActorById.java
│ │ │ │ ├── GetActors.java
│ │ │ │ └── GetActorsByQuery.java
│ │ │ ├── Actions.java
│ │ │ ├── cinemausecases
│ │ │ │ ├── GetCinemas.java
│ │ │ │ ├── GetTopRatedCinemas.java
│ │ │ │ ├── GetUpComingCinemas.java
│ │ │ │ └── GetCinemaById.java
│ │ │ └── UseCase.java
│ │ │ ├── models
│ │ │ ├── network
│ │ │ │ ├── Poster.java
│ │ │ │ ├── Credits.java
│ │ │ │ ├── ImagesResponse.java
│ │ │ │ ├── ActorResponse.java
│ │ │ │ ├── CinemaResponse.java
│ │ │ │ ├── CrewNetwork.java
│ │ │ │ ├── ActorNetwork.java
│ │ │ │ └── CinemaNetwork.java
│ │ │ ├── IntArrayConverter.java
│ │ │ ├── mapper
│ │ │ │ ├── Mapper.java
│ │ │ │ └── CinemaMapper.java
│ │ │ ├── model
│ │ │ │ └── DateAndTimeInfo.java
│ │ │ └── db
│ │ │ │ └── CinemaActorJoinEntity.java
│ │ │ ├── data
│ │ │ ├── datasource
│ │ │ │ ├── db
│ │ │ │ │ ├── MediatekaDatabase.java
│ │ │ │ │ ├── CinemaActorJoinDao.java
│ │ │ │ │ ├── ActorDao.java
│ │ │ │ │ └── CinemaDao.java
│ │ │ │ └── network
│ │ │ │ │ └── CinemaApiService.java
│ │ │ ├── SharedPreferenceManager.java
│ │ │ ├── ConnectionReceiver.java
│ │ │ └── repository
│ │ │ │ └── actor
│ │ │ │ └── ActorLocalRepository.java
│ │ │ ├── utils
│ │ │ ├── AnimUtils.java
│ │ │ ├── UrlImagePathCreator.kt
│ │ │ └── pagination
│ │ │ │ └── PaginationScrollListener.java
│ │ │ └── MediatekaApp.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── ru
│ │ │ └── devit
│ │ │ └── mediateka
│ │ │ ├── UnitTest.java
│ │ │ ├── presentation
│ │ │ ├── posterslider
│ │ │ │ └── PosterSliderPresenterTest.java
│ │ │ ├── actordetail
│ │ │ │ ├── ActorDetailContentPresenterTest.java
│ │ │ │ └── ActorDetailPresenterTest.java
│ │ │ ├── cinemadetail
│ │ │ │ ├── CinemaDetailContentPresenterTest.java
│ │ │ │ └── CinemaDetailPresenterTest.java
│ │ │ ├── main
│ │ │ │ └── MainPresenterTest.java
│ │ │ ├── actorlist
│ │ │ │ └── ActorsPresenterTest.java
│ │ │ ├── search
│ │ │ │ └── SearchPresenterTest.java
│ │ │ └── smallcinemalist
│ │ │ │ └── SmallCinemasPresenterTest.java
│ │ │ └── models
│ │ │ └── mapper
│ │ │ ├── ActorDetailResponseToActorTest.java
│ │ │ ├── CinemaEntityToCinemaTest.java
│ │ │ └── CinemaResponseToCinemaTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── ru
│ │ └── devit
│ │ └── mediateka
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── logo license
├── .gitignore
├── gradle.properties
├── .travis.yml
├── gradle-scripts
└── dependencies.gradle
└── gradlew.bat
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-hdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-hdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-mdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-mdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-xhdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-xhdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-xxhdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/drawable-xxhdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/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/PopPsyA/Mediateka/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/PopPsyA/Mediateka/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/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopPsyA/Mediateka/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/base/BaseView.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.base;
2 |
3 | public interface BaseView {
4 | void showLoading();
5 | void hideLoading();
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/transition_main_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/main/SyncConnectionListener.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.main;
2 |
3 | public interface SyncConnectionListener {
4 | void onNetworkConnectionChanged(boolean connected);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/transition_main_reenter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/transition-v21/transition_cinema_detail_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/transition_cinema_detail_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/OnActorClickListener.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | public interface OnActorClickListener {
4 | void onActorClicked(int actorId , int viewHolderPosition);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/OnCinemaClickListener.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | public interface OnCinemaClickListener {
4 | void onCinemaClicked(int cinemaId , int viewHolderPosition);
5 | }
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat May 30 23:10:07 MSK 2020
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-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/HolderRenderer.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | /*
4 | Must implement ALL holders!
5 | */
6 |
7 | public interface HolderRenderer {
8 | void render(T item , int adapterPosition);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_indicator_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more_info_button_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rating_background_green.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rating_background_orange.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/search_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_big_cinema_in_one_row.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more_info_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/ActivityScope.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | @Scope
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface ActivityScope {
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorScope.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 |
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 |
7 | import javax.inject.Scope;
8 |
9 | @Scope
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @interface ActorScope {
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/SystemTimeCalculator.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 | import com.ru.devit.mediateka.models.model.DateAndTimeInfo;
4 |
5 | public interface SystemTimeCalculator {
6 | long futureTimeInMillisFromDateAndTimeInfo(DateAndTimeInfo dateAndTimeInfo);
7 | long currentTimeInMillis();
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/CinemaScope.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | @Scope
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface CinemaScope {
11 | }
12 |
--------------------------------------------------------------------------------
/logo license:
--------------------------------------------------------------------------------
1 | 
This work is licensed under a Creative Commons Attribution 4.0 International License.
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/Poster.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | public class Poster {
6 | @SerializedName("file_path") private String posterUrl;
7 | public String getPosterUrl() {
8 | return posterUrl;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sort.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_download.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/CinemaTabSelectorView.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | import com.ru.devit.mediateka.presentation.base.BaseView;
4 |
5 | public interface CinemaTabSelectorView extends BaseView {
6 | void onPopularTabSelected();
7 | void onTopRatedTabSelected();
8 | void onUpComingTabSelected();
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_actor_photo.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_up.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dashboard_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_small_cinema_in_one_row.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorListComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 |
4 | import com.ru.devit.mediateka.di.ActivityScope;
5 | import com.ru.devit.mediateka.presentation.actorlist.ActorsFragment;
6 |
7 | import dagger.Subcomponent;
8 |
9 | @ActivityScope
10 | @Subcomponent(modules = ActorListModule.class)
11 | public interface ActorListComponent {
12 | void inject(ActorsFragment actorsFragment);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rectangle_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_actor_default_avatar.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/transition_shared_element.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/base/BasePresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.base;
2 |
3 | public abstract class BasePresenter {
4 |
5 | private View view;
6 |
7 | protected View getView(){
8 | return view;
9 | }
10 |
11 | public void setView(View view){
12 | this.view = view;
13 | }
14 |
15 | public abstract void initialize();
16 | public abstract void onDestroy();
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/cinemalist/CinemaListComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema.cinemalist;
2 |
3 | import com.ru.devit.mediateka.di.ActivityScope;
4 | import com.ru.devit.mediateka.presentation.cinemalist.CinemaListFragment;
5 |
6 | import dagger.Subcomponent;
7 |
8 | @ActivityScope
9 | @Subcomponent(modules = CinemaListModule.class)
10 | public interface CinemaListComponent {
11 |
12 | void inject(CinemaListFragment cinemaListFragment);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/UseCaseSubscriber.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 |
4 | import io.reactivex.subscribers.DisposableSubscriber;
5 |
6 | public abstract class UseCaseSubscriber extends DisposableSubscriber {
7 | @Override
8 | public void onNext(T t) {
9 |
10 | }
11 |
12 | @Override
13 | public void onError(Throwable e) {
14 |
15 | }
16 |
17 | @Override
18 | public void onComplete() {
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/Credits.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import java.util.List;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | public class Credits{
7 |
8 | @SerializedName("cast") private List casts;
9 | @SerializedName("crew") private List crews;
10 |
11 | public List getCast(){
12 | return casts;
13 | }
14 |
15 | public List getCrews(){
16 | return crews;
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cinema.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/ActorRepository.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 | import com.ru.devit.mediateka.models.model.Actor;
4 |
5 | import java.util.List;
6 |
7 | import io.reactivex.Flowable;
8 | import io.reactivex.Observable;
9 | import io.reactivex.Single;
10 |
11 | public interface ActorRepository {
12 |
13 | Flowable> searchActors(String query);
14 | Single getActorById(int id);
15 | Single> getPopularActors(int page);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/more_info_button_effect.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/cinemafavourite/CinemaFavouriteListComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema.cinemafavourite;
2 |
3 | import com.ru.devit.mediateka.di.ActivityScope;
4 | import com.ru.devit.mediateka.presentation.favouritelistcinema.FavouriteListCinemaActivity;
5 |
6 | import dagger.Subcomponent;
7 |
8 | @ActivityScope
9 | @Subcomponent(modules = CinemaFavouriteListModule.class)
10 | public interface CinemaFavouriteListComponent {
11 |
12 | void inject(FavouriteListCinemaActivity activity);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_date.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_watch.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_small_cinema_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_schedule.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/OnTabSelectedListener.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | import android.support.design.widget.TabLayout;
4 |
5 | public abstract class OnTabSelectedListener implements TabLayout.OnTabSelectedListener {
6 | @Override
7 | public void onTabSelected(TabLayout.Tab tab) {
8 |
9 | }
10 |
11 | @Override
12 | public void onTabUnselected(TabLayout.Tab tab) {
13 |
14 | }
15 |
16 | @Override
17 | public void onTabReselected(TabLayout.Tab tab) {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 | import com.ru.devit.mediateka.presentation.popularactors.PopularActorsActivity;
4 |
5 | import dagger.Subcomponent;
6 |
7 | @ActorScope
8 | @Subcomponent(modules = ActorModule.class)
9 | public interface ActorComponent {
10 |
11 | void inject(PopularActorsActivity popularActors);
12 |
13 | ActorListComponent plusActorListComponent(ActorListModule actorListModule);
14 | ActorDetailComponent plusActorDetailComponent(ActorDetailModule actorDetailModule);
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/UnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka;
2 |
3 | import org.junit.Before;
4 | import org.junit.runner.RunWith;
5 | import org.junit.runners.JUnit4;
6 | import org.mockito.MockitoAnnotations;
7 |
8 | @RunWith(JUnit4.class)
9 | public abstract class UnitTest {
10 |
11 | @Before
12 | public final void setUp(){
13 | initializeMocks();
14 | onSetUp();
15 | }
16 |
17 | private void initializeMocks() {
18 | MockitoAnnotations.initMocks(this);
19 | }
20 |
21 | protected abstract void onSetUp();
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/OnPageScrolled.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | import android.support.v4.view.ViewPager;
4 |
5 | public abstract class OnPageScrolled implements ViewPager.OnPageChangeListener {
6 | @Override
7 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
8 |
9 | }
10 |
11 | @Override
12 | public void onPageSelected(int position) {
13 |
14 | }
15 |
16 | @Override
17 | public void onPageScrollStateChanged(int state) {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/more_info_button_effect.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_cinema_detail_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorDetailComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 | import com.ru.devit.mediateka.di.ActivityScope;
4 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailActivity;
5 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailContentFragment;
6 |
7 | import dagger.Subcomponent;
8 |
9 | @ActivityScope
10 | @Subcomponent(modules = ActorDetailModule.class)
11 | public interface ActorDetailComponent {
12 |
13 | void inject(ActorDetailActivity actorDetailActivity);
14 |
15 | void inject(ActorDetailContentFragment actorDetailContentFragment);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/CinemaRepository.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 | import com.ru.devit.mediateka.models.model.Cinema;
4 |
5 | import java.util.List;
6 |
7 | import io.reactivex.Flowable;
8 | import io.reactivex.Observable;
9 | import io.reactivex.Single;
10 |
11 | public interface CinemaRepository {
12 |
13 | Single> getCinemas(int pageIndex);
14 | Single> getTopRatedCinemas(int pageIndex);
15 | Single> getUpComingCinemas(int pageIndex);
16 | Single getCinemaById(int cinemaId);
17 | Flowable> searchCinemas(String query);
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 | /*/build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Idea
36 | .idea/
37 | *.iml
38 |
39 | __MACOSX
40 | .DS_Store
41 | /release
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - By cinema date
6 | - By cinema title
7 | - By cinema genre
8 |
9 |
10 |
11 | - 185p
12 | - 780p
13 | - 1280p
14 |
15 |
16 |
17 | - 185
18 | - 780
19 | - 1280
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list_cinema.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ru/devit/mediateka/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Instrumented test, which will subscribe on an Android device.
7 | *
8 | * @see Testing documentation
9 | */
10 | //@RunWith(AndroidJUnit4.class)
11 | public class ExampleInstrumentedTest {
12 | // @Test
13 | // public void useAppContext() throws Exception {
14 | // // Context of the app under test.
15 | // Context appContext = InstrumentationRegistry.getTargetContext();
16 | //
17 | // assertEquals("com.ru.devit.mediateka", appContext.getPackageName());
18 | // }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/IntArrayConverter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models;
2 |
3 | import android.arch.persistence.room.TypeConverter;
4 |
5 | import com.google.gson.Gson;
6 | import com.google.gson.reflect.TypeToken;
7 |
8 | import java.lang.reflect.Type;
9 |
10 | public class IntArrayConverter {
11 |
12 | @TypeConverter
13 | public static String toJson(final int[] ints){
14 | Gson gson = new Gson();
15 | return gson.toJson(ints);
16 | }
17 |
18 | @TypeConverter
19 | public static int[] fromJson(final String val){
20 | final Type type = new TypeToken(){}.getType();
21 | return new Gson().fromJson(val , type);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_action_search.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/ImagesResponse.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 |
7 | public class ImagesResponse {
8 | @SerializedName("posters") private List cinemaPosters;
9 | @SerializedName("profiles") private List actorPosters; // only for actor !!!
10 | @SerializedName("backdrops") private List cinemaBackgroundPosters;
11 |
12 | public List getCinemaPosters() {
13 | return cinemaPosters;
14 | }
15 |
16 | public List getActorPosters() {
17 | return actorPosters;
18 | }
19 |
20 | public List getCinemaBackgroundPosters() {
21 | return cinemaBackgroundPosters;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/ActorResponse.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 |
7 | public class ActorResponse {
8 | @SerializedName("page") private int page;
9 | @SerializedName("total_pages") private int totalPages;
10 | @SerializedName("total_results") private int totalResults;
11 | @SerializedName("results") private List actors;
12 |
13 | public int getPage() {
14 | return page;
15 | }
16 |
17 | public int getTotalPages() {
18 | return totalPages;
19 | }
20 |
21 | public int getTotalResults() {
22 | return totalResults;
23 | }
24 |
25 | public List getActors() {
26 | return actors;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/settings/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.settings;
2 |
3 | import android.os.Bundle;
4 | import android.view.MenuItem;
5 |
6 | public class SettingsActivity extends AppCompatPreferenceActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 |
12 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
13 | getFragmentManager().beginTransaction().add(android.R.id.content , new PreferenceSettingsFragment()).commit();
14 | }
15 |
16 | @Override
17 | public boolean onOptionsItemSelected(MenuItem item) {
18 | if (item.getItemId() == android.R.id.home) {
19 | onBackPressed();
20 | }
21 | return super.onOptionsItemSelected(item);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/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 actorName 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 actorName.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx4028m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | org.gradle.parallel=true
18 | THE_MOVIE_DB_API_KEY="536eef71f9569a963770135812cba068"
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_actors.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/mapper/Mapper.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.mapper;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public abstract class Mapper {
7 |
8 | public abstract T2 map(T1 value);
9 |
10 | public abstract T1 reverseMap(T2 value);
11 |
12 | public List map(List values) {
13 | List returnValues = new ArrayList<>(values.size());
14 | for (T1 value : values) {
15 | returnValues.add(map(value));
16 | }
17 | return returnValues;
18 | }
19 |
20 | public List reverseMap(List values) {
21 | List returnValues = new ArrayList<>(values.size());
22 | for (T2 value : values) {
23 | returnValues.add(reverseMap(value));
24 | }
25 | return returnValues;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_small_cinemas.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/cinemadetail/CinemaDetailComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema.cinemadetail;
2 |
3 | import com.ru.devit.mediateka.di.ActivityScope;
4 | import com.ru.devit.mediateka.di.cinema.cinemadetail.CinemaDetailModule;
5 | import com.ru.devit.mediateka.presentation.cinemadetail.CinemaDetailContentFragment;
6 | import com.ru.devit.mediateka.presentation.cinemadetail.CinemaDetailsActivity;
7 | import com.ru.devit.mediateka.presentation.smallcinemalist.SmallCinemasFragment;
8 |
9 | import dagger.Subcomponent;
10 |
11 | @ActivityScope
12 | @Subcomponent(modules = CinemaDetailModule.class)
13 | public interface CinemaDetailComponent {
14 |
15 | void inject(CinemaDetailsActivity cinemaDetailsActivity);
16 | void inject(SmallCinemasFragment smallCinemasFragment);
17 | void inject(CinemaDetailContentFragment cinemaDetailContentFragment);
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/CinemaResponse.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.util.List;
7 |
8 | public class CinemaResponse {
9 | @SerializedName("page") private int page;
10 | @SerializedName("total_pages") private int totalPages;
11 | @SerializedName("total_results") private int totalResults;
12 | @SerializedName("results") private List cinemas;
13 |
14 | public int getPage() {
15 | return page;
16 | }
17 |
18 | public int getTotalPages() {
19 | return totalPages;
20 | }
21 |
22 | public int getTotalResults() {
23 | return totalResults;
24 | }
25 |
26 | public List getCinemas() {
27 | return cinemas;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/pref_mediateka_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
15 |
16 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/datasource/db/MediatekaDatabase.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.datasource.db;
2 |
3 | import android.arch.persistence.room.Database;
4 | import android.arch.persistence.room.RoomDatabase;
5 | import android.arch.persistence.room.TypeConverters;
6 |
7 | import com.ru.devit.mediateka.models.IntArrayConverter;
8 | import com.ru.devit.mediateka.models.db.ActorEntity;
9 | import com.ru.devit.mediateka.models.db.CinemaActorJoinEntity;
10 | import com.ru.devit.mediateka.models.db.CinemaEntity;
11 |
12 | @Database(entities = {CinemaEntity.class , ActorEntity.class , CinemaActorJoinEntity.class} ,
13 | version = 1 ,
14 | exportSchema = false)
15 | @TypeConverters(IntArrayConverter.class)
16 | public abstract class MediatekaDatabase extends RoomDatabase {
17 | public abstract CinemaDao getCinemaDao();
18 | public abstract ActorDao getActorDao();
19 | public abstract CinemaActorJoinDao getCinemaActorJoinDao();
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/smallcinemalist/SmallCinemaHeaderViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.smallcinemalist;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.ru.devit.mediateka.R;
9 |
10 | public class SmallCinemaHeaderViewHolder extends RecyclerView.ViewHolder{
11 |
12 | private TextView mTextViewCinemaCount;
13 |
14 | public SmallCinemaHeaderViewHolder(View itemView) {
15 | super(itemView);
16 | mTextViewCinemaCount = itemView.findViewById(R.id.tv_actor_detail_cinemas_count);
17 | }
18 |
19 | public void render(int cinemaCount){
20 | mTextViewCinemaCount.setText(getContext()
21 | .getString(R.string.cinemas_count , cinemaCount));
22 | }
23 |
24 | private Context getContext(){
25 | return itemView.getContext();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/posterslider/PosterSliderPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.posterslider;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 |
5 | import org.junit.Test;
6 | import org.mockito.Mock;
7 |
8 | import static org.junit.Assert.*;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 |
12 | public class PosterSliderPresenterTest extends UnitTest {
13 |
14 | @Mock private PosterSliderPresenter.View view;
15 |
16 | private PosterSliderPresenter presenter;
17 |
18 | @Override
19 | protected void onSetUp() {
20 | presenter = new PosterSliderPresenter();
21 | }
22 |
23 | @Test
24 | public void shouldChangeCurrentPositionWhenPosterSlide(){
25 | presenter.setView(view);
26 | presenter.countPosters(2 , 10);
27 |
28 | verify(view , times(1)).showCurrentPosition("3/10"); // 3/10 because position start from 0 i.e currentPos += 1 :)
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/cinemadetail/CinemaDetailContentPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.cinemadetail;
2 |
3 | import com.ru.devit.mediateka.models.model.Cinema;
4 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
5 | import com.ru.devit.mediateka.presentation.base.BaseView;
6 |
7 | public class CinemaDetailContentPresenter extends BasePresenter {
8 |
9 | private Cinema cinema;
10 |
11 | public CinemaDetailContentPresenter(){}
12 |
13 | @Override
14 | public void initialize() {
15 | getView().showLoading();
16 | }
17 |
18 | public void setCinema(Cinema cinema) {
19 | this.cinema = cinema;
20 | getView().showCinemaContent(cinema);
21 | getView().hideLoading();
22 | }
23 |
24 | public void onDestroy() {
25 | setView(null);
26 | }
27 |
28 | public interface View extends BaseView {
29 | void showCinemaContent(Cinema cinema);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/posterslider/PosterSliderPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.posterslider;
2 |
3 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
4 | import com.ru.devit.mediateka.presentation.base.BaseView;
5 |
6 | import java.util.Locale;
7 |
8 | import javax.inject.Inject;
9 |
10 | public class PosterSliderPresenter extends BasePresenter {
11 |
12 | @Inject public PosterSliderPresenter() {}
13 |
14 | @Override
15 | public void initialize() {
16 | getView().showLoading();
17 | }
18 |
19 | @Override
20 | public void onDestroy() {
21 | setView(null);
22 | }
23 |
24 | void countPosters(int currentItem, int count) {
25 | currentItem += 1;
26 | getView().showCurrentPosition(String.format(Locale.getDefault() , "%d/%d" , currentItem , count));
27 | }
28 |
29 | interface View extends BaseView {
30 | void showCurrentPosition(String position);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/CinemaComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema;
2 |
3 | import com.ru.devit.mediateka.di.cinema.cinemadetail.CinemaDetailComponent;
4 | import com.ru.devit.mediateka.di.cinema.cinemadetail.CinemaDetailModule;
5 | import com.ru.devit.mediateka.di.cinema.cinemafavourite.CinemaFavouriteListComponent;
6 | import com.ru.devit.mediateka.di.cinema.cinemafavourite.CinemaFavouriteListModule;
7 | import com.ru.devit.mediateka.di.cinema.cinemalist.CinemaListComponent;
8 | import com.ru.devit.mediateka.di.cinema.cinemalist.CinemaListModule;
9 |
10 | import dagger.Subcomponent;
11 |
12 | @CinemaScope
13 | @Subcomponent(modules = CinemaModule.class)
14 | public interface CinemaComponent {
15 |
16 | CinemaListComponent plusCinemaListComponent(CinemaListModule cinemaListModule);
17 | CinemaDetailComponent plusCinemaDetailComponent(CinemaDetailModule cinemaDetailModule);
18 | CinemaFavouriteListComponent plusCinemaFavouriteListComponent(CinemaFavouriteListModule cinemaFavouriteListModule);
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/actorusecases/GetActorById.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.actorusecases;
2 |
3 | import com.ru.devit.mediateka.domain.ActorRepository;
4 | import com.ru.devit.mediateka.domain.UseCase;
5 | import com.ru.devit.mediateka.models.model.Actor;
6 |
7 | import io.reactivex.Flowable;
8 | import io.reactivex.Scheduler;
9 |
10 | public class GetActorById extends UseCase {
11 |
12 | private final ActorRepository repository;
13 | private int actorId;
14 |
15 | public GetActorById(Scheduler executorThread ,
16 | Scheduler uiThread ,
17 | ActorRepository repository){
18 | super(executorThread , uiThread);
19 | this.repository = repository;
20 | }
21 |
22 | public void searchActorById(int actorId) {
23 | this.actorId = actorId;
24 | }
25 |
26 | @Override
27 | protected Flowable createUseCase() {
28 | return repository.getActorById(actorId)
29 | .toFlowable();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/Actions.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 | import io.reactivex.functions.Action;
4 |
5 | public class Actions {
6 |
7 | private Action actionOnNext;
8 | private Action actionOnDataLoaded;
9 | private Action actionOnClearAdapter;
10 |
11 | public Actions(Action actionOnNext, Action actionOnDataLoaded, Action actionOnClearAdapter) {
12 | this.actionOnNext = actionOnNext;
13 | this.actionOnDataLoaded = actionOnDataLoaded;
14 | this.actionOnClearAdapter = actionOnClearAdapter;
15 | }
16 |
17 | public void onNext() throws Exception {
18 | actionOnNext.run();
19 | }
20 |
21 | public void onDataLoaded() throws Exception {
22 | actionOnDataLoaded.run();
23 | }
24 |
25 | public void onClearAdapter() throws Exception {
26 | actionOnClearAdapter.run();
27 | }
28 |
29 | public void removeActions(){
30 | actionOnNext = null;
31 | actionOnDataLoaded = null;
32 | actionOnClearAdapter = null;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/actorusecases/GetActors.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.actorusecases;
2 |
3 | import com.ru.devit.mediateka.domain.ActorRepository;
4 | import com.ru.devit.mediateka.domain.UseCase;
5 | import com.ru.devit.mediateka.models.model.Actor;
6 |
7 | import java.util.List;
8 |
9 | import javax.inject.Inject;
10 | import javax.inject.Named;
11 |
12 | import io.reactivex.Flowable;
13 | import io.reactivex.Scheduler;
14 |
15 | public class GetActors extends UseCase> {
16 |
17 | private final ActorRepository repository;
18 |
19 | @Inject public GetActors(@Named("executor_thread") Scheduler executorThread ,
20 | @Named("ui_thread") Scheduler uiThread ,
21 | ActorRepository repository) {
22 | super(executorThread, uiThread);
23 | this.repository = repository;
24 | }
25 |
26 | @Override
27 | protected Flowable> createUseCase() {
28 | return repository.getPopularActors(1)
29 | .toFlowable();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/SharedPreferenceManager.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data;
2 |
3 | import android.content.SharedPreferences;
4 |
5 | public class SharedPreferenceManager {
6 |
7 | private final SharedPreferences preferences;
8 | private static final String PREF_CINEMA_SORTING_POSITION = "pref_cinema_sorting_position";
9 |
10 | public SharedPreferenceManager(SharedPreferences preferences) {
11 | this.preferences = preferences;
12 | }
13 |
14 | public void saveCinemaSortingPosition(int position){
15 | edit(editor -> editor.putInt(PREF_CINEMA_SORTING_POSITION , position));
16 | }
17 |
18 | public int getCinemaSortingPosition(){
19 | return preferences.getInt(PREF_CINEMA_SORTING_POSITION , 0);
20 | }
21 |
22 | private void edit(Consumer consumer){
23 | SharedPreferences.Editor editor = preferences.edit();
24 | consumer.apply(editor);
25 | editor.apply();
26 | }
27 |
28 | @FunctionalInterface
29 | private interface Consumer{
30 | void apply(T t);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/model/DateAndTimeInfo.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.model;
2 |
3 | import java.io.Serializable;
4 |
5 | public class DateAndTimeInfo implements Serializable {
6 |
7 | private int year;
8 | private int month;
9 | private int day;
10 | private int hour;
11 | private int minute;
12 |
13 | public int getYear() {
14 | return year;
15 | }
16 |
17 | public void setYear(int year) {
18 | this.year = year;
19 | }
20 |
21 | public int getMonth() {
22 | return month;
23 | }
24 |
25 | public void setMonth(int month) {
26 | this.month = month;
27 | }
28 |
29 | public int getDay() {
30 | return day;
31 | }
32 |
33 | public void setDay(int day) {
34 | this.day = day;
35 | }
36 |
37 | public int getHour() {
38 | return hour;
39 | }
40 |
41 | public void setHour(int hour) {
42 | this.hour = hour;
43 | }
44 |
45 | public int getMinute() {
46 | return minute;
47 | }
48 |
49 | public void setMinute(int minute) {
50 | this.minute = minute;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorListModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 | import com.ru.devit.mediateka.domain.ActorRepository;
4 | import com.ru.devit.mediateka.di.ActivityScope;
5 | import com.ru.devit.mediateka.domain.actorusecases.GetActorsByQuery;
6 | import com.ru.devit.mediateka.presentation.actorlist.ActorsPresenter;
7 |
8 | import javax.inject.Named;
9 |
10 | import dagger.Module;
11 | import dagger.Provides;
12 | import io.reactivex.Scheduler;
13 |
14 | @Module
15 | public class ActorListModule {
16 |
17 | @ActivityScope
18 | @Provides
19 | ActorsPresenter provideActorsPresenter(GetActorsByQuery getActorsByQuery){
20 | return new ActorsPresenter(getActorsByQuery);
21 | }
22 |
23 | @ActivityScope
24 | @Provides
25 | GetActorsByQuery provideGetActorByQuery(@Named("executor_thread") Scheduler executorThread ,
26 | @Named("ui_thread") Scheduler uiThread ,
27 | ActorRepository repository){
28 | return new GetActorsByQuery(executorThread , uiThread , repository);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/CrewNetwork.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | public class CrewNetwork{
6 |
7 | // @SerializedName("gender")
8 | // private int gender;
9 | //
10 | // @SerializedName("credit_id")
11 | // private String creditId;
12 |
13 | @SerializedName("name")
14 | private String name;
15 |
16 | // @SerializedName("profile_path")
17 | // private String profilePath;
18 | //
19 | // @SerializedName("id")
20 | // private int id;
21 | //
22 | // @SerializedName("department")
23 | // private String department;
24 |
25 | @SerializedName("job")
26 | private String job;
27 |
28 | // public int getGender(){
29 | // return gender;
30 | // }
31 | //
32 | // public String getCreditId(){
33 | // return creditId;
34 | // }
35 |
36 | public String getName(){
37 | return name;
38 | }
39 |
40 | // public String getProfilePath(){
41 | // return profilePath;
42 | // }
43 | //
44 | // public int getActorId(){
45 | // return id;
46 | // }
47 | //
48 | // public String getDepartment(){
49 | // return department;
50 | // }
51 |
52 | public String getJob(){
53 | return job;
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_settings.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 100dp
6 | 16dp
7 | 16dp
8 | 30dp
9 | 20dp
10 | 120dp
11 | 30dp
12 | 16dp
13 | 40dp
14 | 10dp
15 | 190dp
16 | 20dp
17 | 150dp
18 | 100dp
19 | 200dp
20 | 8dp
21 | 80dp
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_slider_poster.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/ViewPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentStatePagerAdapter;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class ViewPagerAdapter extends FragmentStatePagerAdapter {
11 |
12 |
13 | private final List mFragmentList = new ArrayList<>();
14 | private final List mFragmentTitleList = new ArrayList<>();
15 |
16 |
17 | public ViewPagerAdapter(FragmentManager fm) {
18 | super(fm);
19 | }
20 |
21 | @Override
22 | public Fragment getItem(int position) {
23 | return mFragmentList.get(position);
24 | }
25 |
26 | @Override
27 | public int getCount() {
28 | return mFragmentList.size();
29 | }
30 |
31 | @Override
32 | public CharSequence getPageTitle(int position) {
33 | return mFragmentTitleList.get(position);
34 | }
35 |
36 | public void addFragment(Fragment fragment, String title) {
37 | mFragmentList.add(fragment);
38 | mFragmentTitleList.add(title);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/actordetail/ActorDetailContentPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actordetail;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.models.model.Actor;
5 |
6 | import org.junit.Test;
7 | import org.mockito.Mock;
8 |
9 | import static org.junit.Assert.*;
10 | import static org.mockito.Matchers.any;
11 | import static org.mockito.Mockito.times;
12 | import static org.mockito.Mockito.verify;
13 |
14 | public class ActorDetailContentPresenterTest extends UnitTest {
15 |
16 | @Mock private ActorDetailContentPresenter.View view;
17 |
18 | private ActorDetailContentPresenter presenter;
19 |
20 | @Override
21 | protected void onSetUp() {
22 | presenter = new ActorDetailContentPresenter();
23 | }
24 |
25 | @Test
26 | public void shouldShowLoadingWhenInitialize(){
27 | presenter.setView(view);
28 | presenter.initialize();
29 |
30 | verify(view , times(1)).showLoading();
31 | }
32 |
33 | @Test
34 | public void shouldSetActor(){
35 | presenter.setView(view);
36 | presenter.setActor(any(Actor.class));
37 |
38 | verify(view , times(1)).showActorInfo(any(Actor.class));
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
24 |
25 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/ConnectionReceiver.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.ConnectivityManager;
7 | import android.net.NetworkInfo;
8 |
9 | import com.ru.devit.mediateka.presentation.main.SyncConnectionListener;
10 |
11 | public class ConnectionReceiver extends BroadcastReceiver {
12 |
13 | public static SyncConnectionListener connectionListener;
14 |
15 | @Override
16 | public void onReceive(Context context, Intent arg) {
17 | boolean isConnected = checkConnection(context);
18 |
19 | if (connectionListener != null) {
20 | connectionListener.onNetworkConnectionChanged(isConnected);
21 | }
22 | }
23 |
24 | public boolean isInternetConnected(Context context){
25 | return checkConnection(context);
26 | }
27 |
28 | private boolean checkConnection(Context context) {
29 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
30 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
31 |
32 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/actordetail/ActorDetailContentPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actordetail;
2 |
3 | import com.ru.devit.mediateka.models.model.Actor;
4 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
5 | import com.ru.devit.mediateka.presentation.base.BaseView;
6 |
7 | import java.util.List;
8 |
9 | public class ActorDetailContentPresenter extends BasePresenter {
10 |
11 | private Actor actor;
12 |
13 | public ActorDetailContentPresenter() {}
14 |
15 | @Override
16 | public void initialize() {
17 | getView().showLoading();
18 | }
19 |
20 | @Override
21 | public void onDestroy() {
22 | setView(null);
23 | }
24 |
25 | public void setActor(Actor actor){
26 | this.actor = actor;
27 | getView().showActorInfo(this.actor);
28 | }
29 |
30 | public void onPhotoClicked(int position, int viewHolderPos) {
31 | getView().showDetailedPhoto(position , viewHolderPos , actor.getPostersUrl());
32 | }
33 |
34 | public interface View extends BaseView{
35 | void showActorInfo(Actor actor);
36 | void showDetailedPhoto(int position , int viewHolderPos , List posterUrls);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/cinemalist/CinemaListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.cinemalist;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.ru.devit.mediateka.R;
10 | import com.ru.devit.mediateka.models.model.Cinema;
11 | import com.ru.devit.mediateka.presentation.common.AbstractCinemaListAdapter;
12 | import com.ru.devit.mediateka.presentation.common.OnCinemaClickListener;
13 |
14 | import java.util.ArrayList;
15 | import java.util.LinkedHashSet;
16 | import java.util.List;
17 | import java.util.Set;
18 |
19 | public class CinemaListAdapter extends AbstractCinemaListAdapter {
20 |
21 | CinemaListAdapter(OnCinemaClickListener onCinemaClickListener) {
22 | super(onCinemaClickListener);
23 | }
24 |
25 | @Override
26 | @NonNull
27 | public CinemaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
28 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_cinema , parent , false);
29 | return new CinemaViewHolder(view , onCinemaClickListener);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/cinemausecases/GetCinemas.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.cinemausecases;
2 |
3 |
4 | import com.ru.devit.mediateka.domain.CinemaRepository;
5 | import com.ru.devit.mediateka.domain.UseCase;
6 | import com.ru.devit.mediateka.models.model.Cinema;
7 |
8 | import org.reactivestreams.Publisher;
9 |
10 | import java.util.List;
11 |
12 | import io.reactivex.Flowable;
13 | import io.reactivex.Scheduler;
14 | import io.reactivex.functions.Consumer;
15 | import io.reactivex.functions.Function;
16 |
17 | public class GetCinemas extends UseCase> {
18 |
19 | private final CinemaRepository repository;
20 |
21 | public GetCinemas(Scheduler executorThread ,
22 | Scheduler uiThread ,
23 | CinemaRepository repository){
24 | super(executorThread , uiThread);
25 | this.repository = repository;
26 | }
27 |
28 | @Override
29 | public Flowable> createUseCase() {
30 | return repository.getCinemas(pageIndex)
31 | .toFlowable()
32 | .flatMap(Flowable::fromIterable)
33 | .filter(cinema -> !cinema.getDescription().isEmpty())
34 | .toList()
35 | .toFlowable();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/posterslider/PosterSliderAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.posterslider;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentStatePagerAdapter;
6 |
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | public class PosterSliderAdapter extends FragmentStatePagerAdapter {
11 |
12 | private List posterUrls;
13 | private final boolean isForBackgroundPoster;
14 |
15 | public PosterSliderAdapter(FragmentManager fm , List posterUrls , boolean isForBackgroundPoster) {
16 | super(fm);
17 | this.posterUrls = posterUrls;
18 | this.isForBackgroundPoster = isForBackgroundPoster;
19 | checkNotNull(posterUrls);
20 | }
21 |
22 | @Override
23 | public Fragment getItem(int position) {
24 | return PosterSliderFragment.newInstance(posterUrls.get(position) , isForBackgroundPoster);
25 | }
26 |
27 | @Override
28 | public int getCount() {
29 | return posterUrls.size();
30 | }
31 |
32 | private void checkNotNull(List urls){
33 | if (urls == null){
34 | posterUrls = Collections.emptyList();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/datasource/db/CinemaActorJoinDao.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.datasource.db;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Insert;
5 | import android.arch.persistence.room.OnConflictStrategy;
6 | import android.arch.persistence.room.Query;
7 |
8 | import com.ru.devit.mediateka.models.db.ActorEntity;
9 | import com.ru.devit.mediateka.models.db.CinemaActorJoinEntity;
10 | import com.ru.devit.mediateka.models.db.CinemaEntity;
11 |
12 | import java.util.List;
13 |
14 | @Dao
15 | public interface CinemaActorJoinDao {
16 |
17 | @Insert(onConflict = OnConflictStrategy.REPLACE)
18 | void insert(CinemaActorJoinEntity cinemaActorJoinEntity);
19 |
20 | @Query("SELECT * FROM ActorTable INNER JOIN CinemaActorJoinTable " +
21 | "ON ActorTable.actorId = CinemaActorJoinTable.actor_id " +
22 | "WHERE CinemaActorJoinTable.cinema_id = :cinemaId ORDER BY `order`" )
23 | List getActorsForCinema(final int cinemaId);
24 |
25 | @Query("SELECT * FROM CinemaTable INNER JOIN CinemaActorJoinTable " +
26 | "ON CinemaTable.cinemaId = CinemaActorJoinTable.cinema_id " +
27 | "WHERE CinemaActorJoinTable.actor_id = :actorId")
28 | List getCinemasForActor(final int actorId);
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/db/CinemaActorJoinEntity.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.db;
2 |
3 | import android.arch.persistence.room.ColumnInfo;
4 | import android.arch.persistence.room.Entity;
5 | import android.arch.persistence.room.ForeignKey;
6 | import android.support.annotation.NonNull;
7 |
8 | @Entity(
9 | tableName = "CinemaActorJoinTable" ,
10 | primaryKeys = {"cinema_id" , "actor_id"} ,
11 | foreignKeys = {
12 | @ForeignKey(entity = CinemaEntity.class ,
13 | parentColumns = "cinemaId" ,
14 | childColumns = "cinema_id") ,
15 | @ForeignKey(entity = ActorEntity.class ,
16 | parentColumns = "actorId",
17 | childColumns = "actor_id")
18 | })
19 |
20 | public class CinemaActorJoinEntity {
21 | @ColumnInfo(name = "cinema_id") private final int cinemaId;
22 | @ColumnInfo(name = "actor_id") private final int actorId;
23 |
24 | public CinemaActorJoinEntity(final int cinemaId , final int actorId) {
25 | this.cinemaId = cinemaId;
26 | this.actorId = actorId;
27 | }
28 |
29 | public int getCinemaId() {
30 | return cinemaId;
31 | }
32 |
33 | public int getActorId() {
34 | return actorId;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/cinemausecases/GetTopRatedCinemas.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.cinemausecases;
2 |
3 | import com.ru.devit.mediateka.domain.CinemaRepository;
4 | import com.ru.devit.mediateka.domain.UseCase;
5 | import com.ru.devit.mediateka.models.model.Cinema;
6 |
7 | import java.util.List;
8 |
9 | import io.reactivex.Flowable;
10 | import io.reactivex.Scheduler;
11 |
12 | import static com.ru.devit.mediateka.utils.FormatterUtils.DEFAULT_VALUE;
13 |
14 | public class GetTopRatedCinemas extends UseCase> {
15 |
16 | private final CinemaRepository repository;
17 |
18 | public GetTopRatedCinemas(Scheduler executorThread ,
19 | Scheduler uiThread ,
20 | CinemaRepository repository) {
21 | super(executorThread, uiThread);
22 | this.repository = repository;
23 | }
24 |
25 | @Override
26 | public Flowable> createUseCase() {
27 | return repository.getTopRatedCinemas(pageIndex)
28 | .toFlowable()
29 | .flatMap(Flowable::fromIterable)
30 | .filter(cinema -> !cinema.getDescription().equals(""))
31 | .filter(cinema -> !cinema.getReleaseDate().equals(DEFAULT_VALUE))
32 | .toList()
33 | .toFlowable();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/ActorNetwork.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | public class ActorNetwork {
6 |
7 | @SerializedName("id") private int actorId;
8 | @SerializedName("cast_id") private int castId;
9 | @SerializedName("character") private String character;
10 | @SerializedName("gender") private int gender;
11 | @SerializedName("credit_id") private String creditId;
12 | @SerializedName("name") private String name;
13 | @SerializedName("profile_path") private String profilePath;
14 | @SerializedName("order") private int order;
15 | @SerializedName("popularity") private double popularity;
16 |
17 | public int getCastId(){
18 | return castId;
19 | }
20 |
21 | public String getCharacter(){
22 | return character;
23 | }
24 |
25 | public int getGender(){
26 | return gender;
27 | }
28 |
29 | public String getCreditId(){
30 | return creditId;
31 | }
32 |
33 | public String getName(){
34 | return name;
35 | }
36 |
37 | public String getProfilePath(){
38 | return profilePath;
39 | }
40 |
41 | public int getActorId(){
42 | return actorId;
43 | }
44 |
45 | public int getOrder(){
46 | return order;
47 | }
48 |
49 | public double getPopularity() {
50 | return popularity;
51 | }
52 |
53 | public void setPopularity(double popularity) {
54 | this.popularity = popularity;
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/cinemausecases/GetUpComingCinemas.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.cinemausecases;
2 |
3 | import com.ru.devit.mediateka.domain.CinemaRepository;
4 | import com.ru.devit.mediateka.domain.UseCase;
5 | import com.ru.devit.mediateka.models.model.Cinema;
6 |
7 | import java.util.List;
8 |
9 | import io.reactivex.Flowable;
10 | import io.reactivex.Scheduler;
11 |
12 | import static com.ru.devit.mediateka.utils.FormatterUtils.DEFAULT_VALUE;
13 |
14 | public class GetUpComingCinemas extends UseCase> {
15 |
16 | private final CinemaRepository repository;
17 |
18 | public GetUpComingCinemas(Scheduler executorThread ,
19 | Scheduler uiThread ,
20 | CinemaRepository repository) {
21 | super(executorThread, uiThread);
22 | this.repository = repository;
23 | }
24 |
25 | @Override
26 | public Flowable> createUseCase() {
27 | return repository.getUpComingCinemas(pageIndex)
28 | .toFlowable()
29 | .flatMap(Flowable::fromIterable)
30 | .filter(cinema -> !cinema.getDescription().isEmpty())
31 | .filter(cinema -> !cinema.getReleaseDate().equals(DEFAULT_VALUE) && cinema.getVoteAverage() == 0)
32 | .toList()
33 | .toFlowable();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/application/AppComponent.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.application;
2 |
3 | import com.ru.devit.mediateka.di.actor.ActorComponent;
4 | import com.ru.devit.mediateka.di.cinema.CinemaComponent;
5 | import com.ru.devit.mediateka.di.actor.ActorModule;
6 | import com.ru.devit.mediateka.di.cinema.CinemaModule;
7 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailActivity;
8 | import com.ru.devit.mediateka.presentation.main.MainActivity;
9 | import com.ru.devit.mediateka.presentation.posterslider.PosterSliderActivity;
10 | import com.ru.devit.mediateka.presentation.search.SearchActivity;
11 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailContentFragment;
12 | import com.ru.devit.mediateka.presentation.actorlist.ActorsFragment;
13 | import com.ru.devit.mediateka.presentation.cinemadetail.CinemaDetailContentFragment;
14 |
15 | import javax.inject.Singleton;
16 |
17 | import dagger.Component;
18 |
19 | @Singleton
20 | @Component(modules = {RetrofitModule.class , AppModule.class})
21 | public interface AppComponent {
22 |
23 | void inject(MainActivity mainActivity);
24 | void inject(SearchActivity searchActivity);
25 | void inject(PosterSliderActivity posterSliderActivity);
26 |
27 | CinemaComponent plusCinemaComponent(CinemaModule cinemaModule);
28 | ActorComponent plusActorComponent(ActorModule actorModule);
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/cinemadetail/CinemaDetailContentPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.cinemadetail;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.models.model.Cinema;
5 | import com.ru.devit.mediateka.presentation.cinemadetail.CinemaDetailContentPresenter;
6 |
7 | import org.junit.Test;
8 | import org.mockito.Mock;
9 |
10 | import static org.junit.Assert.*;
11 | import static org.mockito.Matchers.any;
12 | import static org.mockito.Mockito.times;
13 | import static org.mockito.Mockito.verify;
14 |
15 | public class CinemaDetailContentPresenterTest extends UnitTest {
16 |
17 | @Mock private CinemaDetailContentPresenter.View view;
18 |
19 | private CinemaDetailContentPresenter presenter;
20 |
21 | @Override
22 | protected void onSetUp() {
23 | presenter = new CinemaDetailContentPresenter();
24 | }
25 |
26 | @Test
27 | public void shouldShowLoadingWhenInitialize(){
28 | presenter.setView(view);
29 | presenter.initialize();
30 |
31 | verify(view , times(1)).showLoading();
32 | }
33 |
34 | @Test
35 | public void shouldSetCinema(){
36 | presenter.setView(view);
37 | presenter.setCinema(any(Cinema.class));
38 |
39 | verify(view).showCinemaContent(any(Cinema.class));
40 | verify(view).hideLoading();
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorDetailModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 | import com.ru.devit.mediateka.domain.ActorRepository;
4 | import com.ru.devit.mediateka.di.ActivityScope;
5 | import com.ru.devit.mediateka.domain.actorusecases.GetActorById;
6 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailContentPresenter;
7 | import com.ru.devit.mediateka.presentation.actordetail.ActorDetailPresenter;
8 |
9 | import javax.inject.Named;
10 |
11 | import dagger.Module;
12 | import dagger.Provides;
13 | import io.reactivex.Scheduler;
14 |
15 | @Module
16 | public class ActorDetailModule {
17 |
18 | @ActivityScope
19 | @Provides
20 | ActorDetailPresenter provideActorDetailPresenter(GetActorById getActorById){
21 | return new ActorDetailPresenter(getActorById);
22 | }
23 |
24 | @ActivityScope
25 | @Provides
26 | ActorDetailContentPresenter provideActorDetailContentPresenter(){
27 | return new ActorDetailContentPresenter();
28 | }
29 |
30 | @ActivityScope
31 | @Provides
32 | GetActorById provideGetActorById(@Named("executor_thread")Scheduler executorThread ,
33 | @Named("ui_thread")Scheduler uiThread ,
34 | ActorRepository repository){
35 | return new GetActorById(executorThread , uiThread , repository);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_drawer_items.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #333333
4 | #1F1F1F
5 | #1e88e5
6 | #FFFFFF
7 | #FFFFFF
8 | #333333
9 | #18df0a
10 | #effb0d
11 | #FF000000
12 | #FFF40808
13 | #c30000
14 | #FFFFFF
15 | #FFFF9100
16 | #c56200
17 | #009933
18 | #B3613D
19 | #8e24aa
20 | #5c007a
21 | #1FFFFFFF
22 | #ffa6c575
23 | #FF1976D2
24 | #FF424242
25 | #242425
26 | #fa315b
27 | #40c4ff
28 | #00b0ff
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/cinemafavourite/CinemaFavouriteListModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema.cinemafavourite;
2 |
3 | import com.ru.devit.mediateka.data.SharedPreferenceManager;
4 | import com.ru.devit.mediateka.data.repository.cinema.CinemaLocalRepository;
5 | import com.ru.devit.mediateka.di.ActivityScope;
6 | import com.ru.devit.mediateka.domain.cinemausecases.GetFavouriteListCinema;
7 | import com.ru.devit.mediateka.presentation.favouritelistcinema.FavouriteListCinemaPresenter;
8 |
9 | import javax.inject.Named;
10 |
11 | import dagger.Module;
12 | import dagger.Provides;
13 | import io.reactivex.Scheduler;
14 |
15 | @Module
16 | public class CinemaFavouriteListModule {
17 |
18 | @ActivityScope
19 | @Provides
20 | FavouriteListCinemaPresenter provideFavouriteListCinemaPresenter(GetFavouriteListCinema useCase ,
21 | SharedPreferenceManager sharedPreferenceManager){
22 | return new FavouriteListCinemaPresenter(useCase , sharedPreferenceManager);
23 | }
24 |
25 | @ActivityScope
26 | @Provides
27 | GetFavouriteListCinema provideGetFavouriteListCinema(@Named("executor_thread")Scheduler executorThread ,
28 | @Named("ui_thread")Scheduler uiThread ,
29 | CinemaLocalRepository repository){
30 | return new GetFavouriteListCinema(executorThread , uiThread , repository);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/main/MainPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.main;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 |
5 | import org.junit.Test;
6 | import org.mockito.Mock;
7 |
8 | import static org.junit.Assert.*;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 |
12 | public class MainPresenterTest extends UnitTest {
13 |
14 | @Mock private MainPresenter.View view;
15 |
16 | private MainPresenter presenter;
17 |
18 | @Override
19 | protected void onSetUp() {
20 | presenter = new MainPresenter();
21 | }
22 |
23 | @Test
24 | public void shouldShowErrorWhenInternetNotConnected(){
25 | presenter.setView(view);
26 | presenter.initialize();
27 | presenter.onNetworkConnectionChanged(false);
28 |
29 | verify(view , times(1)).startToListenInternetConnection();
30 | verify(view , times(1)).showNetworkError();
31 | }
32 |
33 | @Test
34 | public void shouldShowErrorIfInternetNotConnectedWhenRetryButtonClicked(){
35 | presenter.setView(view);
36 | presenter.onRetryButtonClicked(false);
37 |
38 | verify(view , times(1)).showNetworkError();
39 | }
40 |
41 | @Test
42 | public void shouldScrollToFirstPositionWhenFABClicked(){
43 | presenter.setView(view);
44 | presenter.onFABScrollUpClicked();
45 |
46 | verify(view , times(1)).scrollToFirstPosition();
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/utils/AnimUtils.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.utils;
2 |
3 | import android.animation.Animator;
4 | import android.support.v4.view.animation.FastOutSlowInInterpolator;
5 | import android.view.View;
6 | import android.view.ViewAnimationUtils;
7 |
8 |
9 | public class AnimUtils {
10 | public static void startRevealAnimation(View view){
11 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
12 | view.post(() -> {
13 | view.setVisibility(View.INVISIBLE);
14 | int cx = view.getWidth() / 2;
15 | int cy = view.getHeight();
16 |
17 | float endRadius = (float) Math.hypot(cx , cy);
18 | Animator animator = ViewAnimationUtils.createCircularReveal(view , cx , cy , 0 , endRadius);
19 | view.setVisibility(View.VISIBLE);
20 | animator.start();
21 | });
22 | }
23 | }
24 |
25 | public static void startRevealAnimationWithOutVisibility(View view){
26 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
27 | view.post(() -> {
28 | int cx = view.getWidth();
29 | int cy = view.getHeight();
30 |
31 | float endRadius = (float) Math.hypot(cx , cy);
32 | Animator animator = ViewAnimationUtils.createCircularReveal(view , cx , cy , 0 , endRadius);
33 | animator.setInterpolator(new FastOutSlowInInterpolator());
34 | animator.start();
35 | });
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/cinemausecases/GetCinemaById.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.cinemausecases;
2 |
3 | import com.ru.devit.mediateka.domain.CinemaRepository;
4 | import com.ru.devit.mediateka.domain.SystemTimeCalculator;
5 | import com.ru.devit.mediateka.domain.UseCase;
6 | import com.ru.devit.mediateka.models.model.Cinema;
7 | import com.ru.devit.mediateka.models.model.DateAndTimeInfo;
8 |
9 | import io.reactivex.Flowable;
10 | import io.reactivex.Scheduler;
11 |
12 | public class GetCinemaById extends UseCase {
13 |
14 | private int cinemaId;
15 | private final CinemaRepository repository;
16 | private final SystemTimeCalculator systemTimeCalculator;
17 |
18 | public GetCinemaById(Scheduler executorThread,
19 | Scheduler uiThread,
20 | CinemaRepository repository,
21 | SystemTimeCalculator systemTimeCalculator){
22 | super(executorThread , uiThread);
23 | this.repository = repository;
24 | this.systemTimeCalculator = systemTimeCalculator;
25 | }
26 |
27 | public void searchCinemaById(int cinemaId){
28 | this.cinemaId = cinemaId;
29 | }
30 |
31 | @Override
32 | protected Flowable createUseCase() {
33 | return repository.getCinemaById(cinemaId)
34 | .toFlowable();
35 | }
36 |
37 | public boolean isRetrievedTimeMoreThanCurrentTime(DateAndTimeInfo dateAndTimeInfo){
38 | return systemTimeCalculator.futureTimeInMillisFromDateAndTimeInfo(dateAndTimeInfo) > systemTimeCalculator.currentTimeInMillis();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/datasource/db/ActorDao.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.datasource.db;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Insert;
5 | import android.arch.persistence.room.OnConflictStrategy;
6 | import android.arch.persistence.room.Query;
7 |
8 | import com.ru.devit.mediateka.models.db.ActorEntity;
9 | import com.ru.devit.mediateka.models.db.CinemaEntity;
10 |
11 |
12 | import java.util.List;
13 |
14 | import io.reactivex.Flowable;
15 | import io.reactivex.Single;
16 |
17 | import static android.arch.persistence.room.OnConflictStrategy.*;
18 |
19 | @Dao
20 | public interface ActorDao {
21 |
22 | @Query("SELECT * FROM ActorTable WHERE actorId = :actorId")
23 | Single getActorById(final int actorId);
24 |
25 | @Query("SELECT * FROM ActorTable")
26 | List getAllActors();
27 |
28 | @Query("SELECT * FROM ActorTable WHERE actorName LIKE :actorName")
29 | Flowable> getAllActorsByName(final String actorName);
30 |
31 | @Insert(onConflict = REPLACE)
32 | void insertActors(List actorEntities);
33 |
34 | @Insert(onConflict = OnConflictStrategy.IGNORE)
35 | void insertCinemas(List cinemaEntities);
36 |
37 | @Query("UPDATE ActorTable SET biography = :biography , birthDay = :birthDay , age = :age , placeOfBirth = :placeOfBirth " +
38 | "WHERE actorId = :actorId")
39 | void updateActor(int actorId , String biography, String birthDay, String age, String placeOfBirth);
40 |
41 | @Query("SELECT * FROM ActorTable ORDER BY popularity DESC")
42 | Single> getPopularActors();
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/cinema_detail_floating_action_button_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
29 |
30 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/mapper/CinemaMapper.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.mapper;
2 |
3 | import com.ru.devit.mediateka.models.db.ActorEntity;
4 | import com.ru.devit.mediateka.models.db.CinemaEntity;
5 | import com.ru.devit.mediateka.models.model.Actor;
6 | import com.ru.devit.mediateka.models.model.Cinema;
7 | import com.ru.devit.mediateka.models.network.CinemaDetailResponse;
8 | import com.ru.devit.mediateka.models.network.CinemaResponse;
9 |
10 | import java.util.List;
11 |
12 | public class CinemaMapper {
13 |
14 | private CinemaResponseToCinema cinemaResponseToCinema;
15 | private CinemaEntityToCinema cinemaEntityToCinema;
16 |
17 |
18 | public CinemaMapper(CinemaResponseToCinema cinemaResponseToCinema,
19 | CinemaEntityToCinema cinemaEntityToCinema) {
20 | this.cinemaResponseToCinema = cinemaResponseToCinema;
21 | this.cinemaEntityToCinema = cinemaEntityToCinema;
22 | }
23 |
24 | public Cinema map(CinemaDetailResponse response){
25 | return cinemaResponseToCinema.map(response);
26 | }
27 |
28 | public List map(CinemaResponse response){
29 | return cinemaResponseToCinema.map(response);
30 | }
31 |
32 | public CinemaEntity map(Cinema cinema){
33 | return cinemaEntityToCinema.map(cinema);
34 | }
35 |
36 | public List map(List cinemas){
37 | return cinemaEntityToCinema.map(cinemas);
38 | }
39 |
40 | public List mapActors(List actors){
41 | return cinemaEntityToCinema.mapActors(actors);
42 | }
43 |
44 | public CinemaEntityToCinema getCinemaEntityToCinema() {
45 | return cinemaEntityToCinema;
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/utils/UrlImagePathCreator.kt:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.utils
2 |
3 |
4 | object UrlImagePathCreator {
5 |
6 | private const val IMG_PATH_W1280 = "https://image.tmdb.org/t/p/w1280/"
7 | private const val IMG_PATH_W780 = "https://image.tmdb.org/t/p/w780/"
8 | private const val IMG_PATH_W185 = "https://image.tmdb.org/t/p/w185/"
9 |
10 | fun createPictureUrlFromQuality(qualityType: Quality, imgUrl: String?): String {
11 | for (quality in Quality.values()) {
12 | if (quality == qualityType) {
13 | return qualityType.quality(imgUrl.orEmpty())
14 | }
15 | }
16 | throw IllegalArgumentException("No such $qualityType")
17 | }
18 |
19 | enum class Quality {
20 | Quality1280 {
21 | override fun quality(imgUrl: String): String {
22 | return create1280pPictureUrl(imgUrl)
23 | }
24 | },
25 | Quality780 {
26 | override fun quality(imgUrl: String): String {
27 | return create780pPictureUrl(imgUrl)
28 | }
29 | },
30 | Quality185 {
31 | override fun quality(imgUrl: String): String {
32 | return create185pPictureUrl(imgUrl)
33 | }
34 | };
35 |
36 | abstract fun quality(imgUrl: String): String
37 | }
38 |
39 | private fun create1280pPictureUrl(imgUrl: String): String {
40 | return IMG_PATH_W1280 + imgUrl
41 | }
42 |
43 | private fun create780pPictureUrl(imgUrl: String): String {
44 | return IMG_PATH_W780 + imgUrl
45 | }
46 |
47 | private fun create185pPictureUrl(imgUrl: String): String {
48 | return IMG_PATH_W185 + imgUrl
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/MediatekaApp.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka;
2 |
3 | import android.app.Application;
4 | import android.arch.persistence.room.Room;
5 |
6 | import com.ru.devit.mediateka.data.ConnectionReceiver;
7 | import com.ru.devit.mediateka.data.datasource.db.MediatekaDatabase;
8 | import com.ru.devit.mediateka.di.ComponentsManager;
9 | import com.ru.devit.mediateka.presentation.main.SyncConnectionListener;
10 |
11 | import io.reactivex.internal.functions.Functions;
12 | import io.reactivex.plugins.RxJavaPlugins;
13 |
14 |
15 | public class MediatekaApp extends Application {
16 |
17 | private static ComponentsManager componentsManager;
18 | private static MediatekaDatabase database;
19 | private static final String DATABASE_NAME = "mediateka_db";
20 |
21 | @Override
22 | public void onCreate() {
23 | super.onCreate();
24 | initComponentsManager();
25 | initAppComponent();
26 | RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
27 | }
28 |
29 | public static ComponentsManager getComponentsManager(){
30 | return componentsManager;
31 | }
32 |
33 | public void setConnectionListener(SyncConnectionListener connectionListener){
34 | ConnectionReceiver.connectionListener = connectionListener;
35 | }
36 |
37 | public MediatekaDatabase getDatabaseInstance(){
38 | if (database == null){
39 | database = Room.databaseBuilder(this , MediatekaDatabase.class , DATABASE_NAME).build();
40 | }
41 | return database;
42 | }
43 |
44 | private void initComponentsManager(){
45 | componentsManager = new ComponentsManager(this);
46 | }
47 |
48 | private void initAppComponent(){
49 | componentsManager.getAppComponent();
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/main/MainPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.main;
2 |
3 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
4 | import com.ru.devit.mediateka.presentation.cinemalist.CinemaTabPositionPicker;
5 | import com.ru.devit.mediateka.presentation.common.CinemaTabSelectorView;
6 |
7 | import javax.inject.Inject;
8 |
9 | public class MainPresenter extends BasePresenter implements SyncConnectionListener {
10 |
11 | private final CinemaTabPositionPicker cinemaTabPositionPicker = new CinemaTabPositionPicker();
12 |
13 | @Inject public MainPresenter(){}
14 |
15 | @Override
16 | public void initialize() {
17 | getView().startToListenInternetConnection();
18 | }
19 |
20 | @Override
21 | public void onDestroy() {
22 | setView(null);
23 | }
24 |
25 | @Override
26 | public void onNetworkConnectionChanged(boolean internetConnected) {
27 | if (!internetConnected){
28 | getView().showNetworkError();
29 | }
30 | }
31 |
32 | void onRetryButtonClicked(boolean internetConnected) {
33 | if (!internetConnected){
34 | getView().showNetworkError();
35 | } else {
36 | getView().hideNetworkError();
37 | }
38 | }
39 |
40 | public void onFABScrollUpClicked() {
41 | getView().scrollToFirstPosition();
42 | }
43 |
44 | public void onTabSelected(int position) {
45 | cinemaTabPositionPicker.loadCinemaFromCinemaPosition(position , getView());
46 | }
47 |
48 | public interface View extends CinemaTabSelectorView {
49 | void startToListenInternetConnection();
50 | void showNetworkError();
51 | void hideNetworkError();
52 | void scrollToFirstPosition();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
15 |
16 |
20 |
21 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
41 |
42 |
44 |
45 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/actorlist/ActorsPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actorlist;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.domain.actorusecases.GetActorsByQuery;
5 |
6 | import org.junit.Test;
7 | import org.mockito.InOrder;
8 | import org.mockito.Mock;
9 |
10 | import static org.mockito.Mockito.*;
11 | import static org.mockito.Mockito.verify;
12 |
13 | public class ActorsPresenterTest extends UnitTest {
14 |
15 | @Mock private ActorsPresenter.View view;
16 | @Mock private GetActorsByQuery useCaseGetActorsByQueryMock;
17 |
18 | private ActorsPresenter presenter;
19 |
20 | @Override
21 | protected void onSetUp() {
22 | presenter = new ActorsPresenter(useCaseGetActorsByQueryMock);
23 | }
24 |
25 | @Test
26 | public void shouldOpenActorDetailedScreenWhenActorClicked(){
27 | presenter.setView(view);
28 | presenter.onActorClicked(17890 , 3);
29 |
30 | verify(view , times(1)).openActor(17890 , 3);
31 | }
32 |
33 | @Test
34 | public void shouldUseCaseDisposeWhenPresenterDestroy(){
35 | presenter.onDestroy();
36 |
37 | verify(useCaseGetActorsByQueryMock).removeActions();
38 | verify(useCaseGetActorsByQueryMock).dispose();
39 | }
40 |
41 | @Test
42 | public void shouldShowLoadingWhenInitialize(){
43 | presenter.setView(view);
44 | presenter.initialize();
45 |
46 | InOrder inOrder = inOrder(view);
47 |
48 | inOrder.verify(view).showLoading();
49 | inOrder.verify(view).hideLoading();
50 | }
51 |
52 | @Test
53 | public void shouldSetQueryWhenPresenterSetQuery(){
54 | presenter.onGetTextFromSearchField("Transformers");
55 |
56 | verify(useCaseGetActorsByQueryMock).setQuery("Transformers");
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/actorlist/ActorListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actorlist;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.ru.devit.mediateka.R;
10 | import com.ru.devit.mediateka.models.model.Actor;
11 | import com.ru.devit.mediateka.presentation.common.OnActorClickListener;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class ActorListAdapter extends RecyclerView.Adapter {
17 |
18 | private final List actors;
19 | private final OnActorClickListener onActorClickListener;
20 |
21 | public ActorListAdapter(OnActorClickListener onActorClickListener) {
22 | actors = new ArrayList<>();
23 | this.onActorClickListener = onActorClickListener;
24 | }
25 |
26 | @Override
27 | @NonNull
28 | public ActorViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
29 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_actor , parent , false);
30 | return new ActorViewHolder(view , onActorClickListener);
31 | }
32 |
33 | @Override
34 | public void onBindViewHolder(@NonNull ActorViewHolder holder, int position) {
35 | Actor actor = actors.get(position);
36 | holder.render(actor , holder.getAdapterPosition());
37 | }
38 |
39 | @Override
40 | public int getItemCount() {
41 | return actors.size();
42 | }
43 |
44 | public void addAll(List actorList){
45 | actors.clear();
46 | actors.addAll(actorList);
47 | notifyDataSetChanged();
48 | }
49 |
50 | public void clear(){
51 | actors.clear();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/models/network/CinemaNetwork.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.network;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | public class CinemaNetwork {
7 | @SerializedName("id") private int id;
8 | @SerializedName("overview") private String description;
9 | @SerializedName("poster_path") private String posterUrl;
10 | @SerializedName("adult") private boolean isAdult;
11 | @SerializedName("title") private String title;
12 | @SerializedName("release_date") private String releaseDate;
13 | @SerializedName("vote_average") private float voteAverage;
14 | @SerializedName("popularity") private float popularity;
15 | @SerializedName("genre_ids") private int[] genreIds;
16 | @SerializedName("character") private String character;
17 |
18 | public int getId() {
19 | return id;
20 | }
21 |
22 | public void setId(int id) {
23 | this.id = id;
24 | }
25 |
26 | public String getDescription() {
27 | return description;
28 | }
29 |
30 | public String getPosterUrl() {
31 | return posterUrl;
32 | }
33 |
34 | public boolean isAdult() {
35 | return isAdult;
36 | }
37 |
38 | public String getTitle() {
39 | return title;
40 | }
41 |
42 | public void setTitle(String title) {
43 | this.title = title;
44 | }
45 |
46 | public String getReleaseDate() {
47 | return releaseDate;
48 | }
49 |
50 | public float getVoteAverage() {
51 | return voteAverage;
52 | }
53 |
54 | public int[] getGenreIds() {
55 | return genreIds;
56 | }
57 |
58 | public float getPopularity() {
59 | return popularity;
60 | }
61 | public String getCharacter() {
62 | return character;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/UseCase.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain;
2 |
3 | import io.reactivex.CompletableTransformer;
4 | import io.reactivex.Flowable;
5 | import io.reactivex.Scheduler;
6 | import io.reactivex.disposables.CompositeDisposable;
7 | import io.reactivex.subscribers.DisposableSubscriber;
8 |
9 | public abstract class UseCase {
10 |
11 | protected int pageIndex = 1;
12 |
13 | private final Scheduler executorThread;
14 | private final Scheduler uiThread;
15 | private final CompositeDisposable compositeDisposable;
16 |
17 | public UseCase(Scheduler executorThread,
18 | Scheduler uiThread) {
19 | this.executorThread = executorThread;
20 | this.uiThread = uiThread;
21 | compositeDisposable = new CompositeDisposable();
22 | }
23 |
24 | public void subscribe(DisposableSubscriber disposableSubscriber){
25 | if (disposableSubscriber == null){
26 | throw new IllegalArgumentException("subscriber must not be null");
27 | }
28 | Flowable flowable = createUseCase()
29 | .subscribeOn(executorThread)
30 | .observeOn(uiThread);
31 |
32 | DisposableSubscriber subscriber = flowable.subscribeWith(disposableSubscriber);
33 | compositeDisposable.add(subscriber);
34 | }
35 |
36 | public void setCurrentPage(int pageIndex){
37 | this.pageIndex = pageIndex;
38 | }
39 |
40 | public void dispose(){
41 | if (!compositeDisposable.isDisposed()){
42 | compositeDisposable.dispose();
43 | }
44 | }
45 |
46 | protected CompletableTransformer applyCompletableSchedulers(){
47 | return upstream -> upstream.subscribeOn(executorThread)
48 | .observeOn(uiThread);
49 | }
50 |
51 | protected abstract Flowable createUseCase();
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/utils/pagination/PaginationScrollListener.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.utils.pagination;
2 |
3 | import android.support.v7.widget.GridLayoutManager;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.support.v7.widget.RecyclerView;
6 |
7 | public abstract class PaginationScrollListener extends RecyclerView.OnScrollListener {
8 |
9 | private RecyclerView.LayoutManager layoutManager;
10 |
11 | public PaginationScrollListener(RecyclerView.LayoutManager layoutManager) {
12 | this.layoutManager = layoutManager;
13 | }
14 |
15 | @Override
16 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
17 | super.onScrolled(recyclerView, dx, dy);
18 |
19 | int visibleItemCount = layoutManager.getChildCount();
20 | int totalItemCount = layoutManager.getItemCount();
21 | int firstVisibleItemPosition = 0;
22 | final int OFFSET = 7; // 7 cinemas before we start loadMoreItems
23 | if (layoutManager instanceof LinearLayoutManager){
24 | LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
25 | firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
26 | } else if (layoutManager instanceof GridLayoutManager){
27 | GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
28 | firstVisibleItemPosition = gridLayoutManager.findFirstVisibleItemPosition();
29 | }
30 |
31 | if (!isLastPage()){
32 | if (((visibleItemCount + OFFSET) + firstVisibleItemPosition) >= totalItemCount
33 | && firstVisibleItemPosition >= 0){
34 | loadMoreItems();
35 | }
36 | }
37 | }
38 |
39 | protected abstract void loadMoreItems();
40 |
41 | protected abstract boolean isLastPage();
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/popularactors/PopularActorsPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.popularactors;
2 |
3 | import com.ru.devit.mediateka.domain.actorusecases.GetActors;
4 | import com.ru.devit.mediateka.models.model.Actor;
5 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
6 | import com.ru.devit.mediateka.presentation.base.BaseView;
7 |
8 | import java.util.List;
9 |
10 | import javax.inject.Inject;
11 |
12 | import io.reactivex.subscribers.DisposableSubscriber;
13 |
14 | public class PopularActorsPresenter extends BasePresenter {
15 |
16 | private final GetActors useCaseGetActors;
17 |
18 | @Inject public PopularActorsPresenter(GetActors useCaseGetActors) {
19 | this.useCaseGetActors = useCaseGetActors;
20 | }
21 |
22 | @Override
23 | public void initialize() {
24 | getView().showLoading();
25 | useCaseGetActors.subscribe(new DisposableSubscriber>() {
26 | @Override
27 | public void onNext(List actors) {
28 | getView().showPopularActors(actors);
29 | }
30 |
31 | @Override
32 | public void onError(Throwable t) {
33 | getView().showOnError();
34 | }
35 |
36 | @Override
37 | public void onComplete() {
38 | getView().hideLoading();
39 | }
40 | });
41 | }
42 |
43 | @Override
44 | public void onDestroy() {
45 | useCaseGetActors.dispose();
46 | }
47 |
48 | public void onActorClicked(int actorId, int viewHolderPosition) {
49 | getView().showActorDetail(actorId , viewHolderPosition);
50 | }
51 |
52 | public interface View extends BaseView {
53 | void showPopularActors(List actors);
54 | void showOnError();
55 | void showActorDetail(int actorId , int viewHolderPos);
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/ComponentsManager.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di;
2 |
3 | import android.content.Context;
4 |
5 | import com.ru.devit.mediateka.di.actor.ActorComponent;
6 | import com.ru.devit.mediateka.di.actor.ActorModule;
7 | import com.ru.devit.mediateka.di.application.AppComponent;
8 |
9 |
10 | import com.ru.devit.mediateka.di.application.DaggerAppComponent;
11 | import com.ru.devit.mediateka.di.cinema.CinemaComponent;
12 | import com.ru.devit.mediateka.di.cinema.CinemaModule;
13 |
14 | import com.ru.devit.mediateka.di.application.AppModule;
15 |
16 |
17 | public class ComponentsManager {
18 |
19 | private AppComponent appComponent;
20 | private CinemaComponent cinemaComponent;
21 | private ActorComponent actorComponent;
22 |
23 | private final Context context;
24 |
25 | public ComponentsManager(Context context) {
26 | this.context = context.getApplicationContext();
27 | }
28 |
29 | public AppComponent getAppComponent(){
30 | if (appComponent == null){
31 | appComponent = DaggerAppComponent.builder()
32 | .appModule(new AppModule(context))
33 | .build();
34 | }
35 | return appComponent;
36 | }
37 |
38 | public CinemaComponent plusCinemaComponent(){
39 | if (cinemaComponent == null){
40 | cinemaComponent = appComponent.plusCinemaComponent(new CinemaModule());
41 | }
42 | return cinemaComponent;
43 | }
44 |
45 | public ActorComponent plusActorComponent(){
46 | if (actorComponent == null){
47 | actorComponent = appComponent.plusActorComponent(new ActorModule());
48 | }
49 | return actorComponent;
50 | }
51 |
52 | public void clearActorComponent(){
53 | actorComponent = null;
54 | }
55 |
56 | public void clearCinemaComponent(){
57 | cinemaComponent = null;
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/models/mapper/ActorDetailResponseToActorTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.mapper;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.models.model.Actor;
5 | import com.ru.devit.mediateka.models.network.ActorDetailResponse;
6 | import com.ru.devit.mediateka.models.network.ActorNetwork;
7 | import com.ru.devit.mediateka.models.network.ActorResponse;
8 |
9 | import org.junit.Test;
10 | import org.mockito.Mock;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import static org.hamcrest.CoreMatchers.is;
16 | import static org.junit.Assert.*;
17 | import static org.mockito.Mockito.*;
18 |
19 | @SuppressWarnings("ResultOfMethodCallIgnored")
20 | public class ActorDetailResponseToActorTest extends UnitTest {
21 |
22 | @Mock private ActorDetailResponse responseDetailMock;
23 | @Mock private ActorResponse responseMock;
24 | @Mock private List actorNetworkListMock;
25 |
26 | private ActorDetailResponseToActor mapper;
27 |
28 | @Override
29 | protected void onSetUp() {
30 | mapper = new ActorDetailResponseToActor();
31 | }
32 |
33 | @Test
34 | public void shouldMapId() {
35 | when(responseDetailMock.getId()).thenReturn(22);
36 |
37 | Actor actor = mapper.map(responseDetailMock);
38 | assertThat(actor.getActorId() , is(22));
39 | }
40 |
41 | @Test
42 | public void shouldMapListOfActor() {
43 | List spyListOfActors = spy(new ArrayList<>());
44 | spyListOfActors.add(new ActorNetwork());
45 | doReturn(1).when(actorNetworkListMock).size();
46 | doReturn(spyListOfActors).when(responseMock).getActors();
47 |
48 | final List actorList = mapper.map(responseMock);
49 | assertNotNull(actorList);
50 | assertFalse(actorList.isEmpty());
51 | assertEquals(responseMock.getActors().size() , actorList.size());
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/search/SearchPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.search;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 |
5 | import org.junit.Test;
6 | import org.mockito.Mock;
7 |
8 | import static org.junit.Assert.*;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 |
12 | public class SearchPresenterTest extends UnitTest {
13 |
14 | @Mock private SearchPresenter.View view;
15 |
16 | private SearchPresenter presenter;
17 | private static final String TEST_QUERY = "test query";
18 | private static final int CINEMAS_TAB_POSITION = 0;
19 | private static final int ACTORS_TAB_POSITION = 1;
20 |
21 |
22 | @Override
23 | protected void onSetUp() {
24 | presenter = new SearchPresenter();
25 | }
26 |
27 | @Test
28 | public void shouldSelectCinemaPositionWhenCinemaTabSelected(){
29 | presenter.setView(view);
30 | presenter.onTabSelected(CINEMAS_TAB_POSITION);
31 |
32 | verify(view).onCinemaTabSelected();
33 | }
34 |
35 | @Test
36 | public void shouldSelectActorPositionWhenActorTabSelected(){
37 | presenter.setView(view);
38 | presenter.onTabSelected(ACTORS_TAB_POSITION);
39 |
40 | verify(view).onActorTabSelected();
41 | }
42 |
43 | @Test
44 | public void shouldSendQueryToCinemaTabWhenCinemaTabSelected(){
45 | presenter.setView(view);
46 | presenter.onTabSelected(CINEMAS_TAB_POSITION);
47 | presenter.onTextChanged(TEST_QUERY);
48 |
49 | verify(view , times(1)).textFromCinemaTab(TEST_QUERY);
50 | }
51 |
52 | @Test
53 | public void shouldSendQueryToActorTabWhenActorTabSelected(){
54 | presenter.setView(view);
55 | presenter.onTabSelected(ACTORS_TAB_POSITION);
56 | presenter.onTextChanged(TEST_QUERY);
57 |
58 | verify(view , times(1)).textFromActorTab(TEST_QUERY);
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_posters_slider.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
27 |
28 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/datasource/network/CinemaApiService.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.datasource.network;
2 |
3 | import com.ru.devit.mediateka.models.network.ActorDetailResponse;
4 | import com.ru.devit.mediateka.models.network.ActorResponse;
5 | import com.ru.devit.mediateka.models.network.CinemaDetailResponse;
6 | import com.ru.devit.mediateka.models.network.CinemaResponse;
7 | import com.ru.devit.mediateka.models.network.ImagesResponse;
8 |
9 | import io.reactivex.Flowable;
10 | import io.reactivex.Single;
11 | import retrofit2.http.GET;
12 | import retrofit2.http.Path;
13 | import retrofit2.http.Query;
14 |
15 | public interface CinemaApiService {
16 |
17 | @GET("movie/popular")
18 | Single getCinemas(@Query("page") int pageIndex);
19 |
20 | @GET("movie/top_rated")
21 | Single getTopRatedCinemas(@Query("page") int pageIndex);
22 |
23 | @GET("movie/upcoming")
24 | Single getUpComingCinemas(@Query("page") int pageIndex);
25 |
26 | @GET("movie/{movie_id}")
27 | Single getCinemaById(@Path("movie_id") int movieId , @Query("append_to_response") String appendToResponse);
28 |
29 | @GET("person/{person_id}")
30 | Single getActorById(@Path("person_id") int actorId , @Query("append_to_response") String appendToResponse);
31 |
32 | @GET("person/popular")
33 | Single getPopularActors(@Query("page") int pageIndex);
34 |
35 | @GET("search/movie")
36 | Flowable searchCinemas(@Query("query") String searchQuery);
37 |
38 | @GET("search/person")
39 | Flowable searchActors(@Query("query") String searchQuery);
40 |
41 | @GET("movie/{movie_id}/images")
42 | Single getImagesForCinema(@Path("movie_id") int cinemaId , @Query("include_image_language") String includedLanguages);
43 |
44 | @GET("person/{person_id}/images")
45 | Single getImagesForActor(@Path("person_id") int actorId);
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/application/AppModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.application;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import com.ru.devit.mediateka.MediatekaApp;
7 | import com.ru.devit.mediateka.data.SharedPreferenceManager;
8 | import com.ru.devit.mediateka.data.datasource.db.CinemaActorJoinDao;
9 |
10 |
11 | import javax.inject.Named;
12 | import javax.inject.Singleton;
13 |
14 | import dagger.Module;
15 | import dagger.Provides;
16 | import io.reactivex.Scheduler;
17 | import io.reactivex.android.schedulers.AndroidSchedulers;
18 | import io.reactivex.schedulers.Schedulers;
19 |
20 | @Module
21 | public class AppModule {
22 |
23 | private final Context context;
24 | private final String APP_PREFS = "Application_preferences";
25 |
26 | public AppModule(Context context){
27 | this.context = context.getApplicationContext();
28 | }
29 |
30 | @Singleton
31 | @Provides
32 | Context provideContext(){
33 | return context;
34 | }
35 |
36 | @Singleton
37 | @Provides
38 | SharedPreferences provideSharedPreference(){
39 | return context.getSharedPreferences(APP_PREFS , Context.MODE_PRIVATE);
40 | }
41 |
42 | @Singleton
43 | @Provides
44 | SharedPreferenceManager provideSharedPrefernceManager(SharedPreferences sharedPreferences){
45 | return new SharedPreferenceManager(sharedPreferences);
46 | }
47 |
48 | @Singleton
49 | @Provides
50 | CinemaActorJoinDao provideCinemaActorJoinDao(){
51 | MediatekaApp mediatekaApp = (MediatekaApp) context;
52 | return mediatekaApp.getDatabaseInstance()
53 | .getCinemaActorJoinDao();
54 | }
55 |
56 | @Named("executor_thread")
57 | @Provides
58 | Scheduler provideExecutorThread(){
59 | return Schedulers.io();
60 | }
61 |
62 | @Named("ui_thread")
63 | @Provides
64 | Scheduler provideUiThread(){
65 | return AndroidSchedulers.mainThread();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/actordetail/ActorDetailPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actordetail;
2 |
3 | import com.ru.devit.mediateka.domain.actorusecases.GetActorById;
4 | import com.ru.devit.mediateka.domain.UseCaseSubscriber;
5 | import com.ru.devit.mediateka.models.model.Actor;
6 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
7 | import com.ru.devit.mediateka.presentation.base.BaseView;
8 |
9 | import java.util.List;
10 |
11 | public class ActorDetailPresenter extends BasePresenter {
12 |
13 | private final GetActorById useCaseGetCinemaById;
14 | private int actorId;
15 |
16 | public ActorDetailPresenter(GetActorById useCaseGetCinemaById) {
17 | this.useCaseGetCinemaById = useCaseGetCinemaById;
18 | }
19 |
20 | @Override
21 | public void initialize() {
22 | getView().showLoading();
23 | useCaseGetCinemaById.searchActorById(actorId);
24 | useCaseGetCinemaById.subscribe(new ActorDetailSubscriber());
25 | }
26 |
27 | @Override
28 | public void onDestroy() {
29 | useCaseGetCinemaById.dispose();
30 | setView(null);
31 | }
32 |
33 | public void setActorId(int actorId) {
34 | this.actorId = actorId;
35 | }
36 |
37 | public void onAvatarClicked(List posterUrls) {
38 | getView().showPosters(posterUrls);
39 | }
40 |
41 | public interface View extends BaseView{
42 | void showActorDetail(Actor actor);
43 | void showPosters(List posterUrls);
44 | }
45 |
46 | public class ActorDetailSubscriber extends UseCaseSubscriber{
47 | @Override
48 | public void onNext(Actor actor) {
49 | getView().showActorDetail(actor);
50 | }
51 |
52 | @Override
53 | public void onError(Throwable e) {
54 | e.printStackTrace();
55 | getView().hideLoading();
56 | }
57 |
58 | @Override
59 | public void onComplete() {
60 | getView().hideLoading();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/settings/PreferenceSettingsFragment.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.settings;
2 |
3 | import android.os.Bundle;
4 | import android.preference.ListPreference;
5 | import android.preference.Preference;
6 | import android.preference.PreferenceFragment;
7 | import android.preference.PreferenceManager;
8 | import android.support.annotation.Nullable;
9 |
10 | import com.ru.devit.mediateka.R;
11 |
12 | public class PreferenceSettingsFragment extends PreferenceFragment {
13 |
14 | @Override
15 | public void onCreate(@Nullable Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | addPreferencesFromResource(R.xml.pref_mediateka_settings);
18 |
19 | bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_key_background_image_quality_type)));
20 | }
21 |
22 | private void bindPreferenceSummaryToValue(Preference preference) {
23 | preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
24 |
25 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
26 | PreferenceManager
27 | .getDefaultSharedPreferences(preference.getContext())
28 | .getString(preference.getKey(), ""));
29 | }
30 |
31 | private final Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = (preference, newValue) -> {
32 | String stringValue = newValue.toString();
33 |
34 | if (preference instanceof ListPreference) {
35 |
36 | ListPreference listPreference = (ListPreference) preference;
37 | int index = listPreference.findIndexOfValue(stringValue);
38 |
39 | preference.setSummary(index >= 0 ?
40 | qualitySummary(listPreference.getEntries()[index].toString())
41 | : qualitySummary(listPreference.getEntries()[0].toString()));
42 | }
43 | return true;
44 | };
45 |
46 | private String qualitySummary(String quality){
47 | return String.format("%s: %s" , getString(R.string.background_poster_image_quality) , quality);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/AbstractCinemaListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.ViewGroup;
6 |
7 | import com.ru.devit.mediateka.models.model.Cinema;
8 |
9 | import java.util.ArrayList;
10 | import java.util.LinkedHashSet;
11 | import java.util.List;
12 | import java.util.Set;
13 |
14 | /*
15 | Must extends all adapters which related with Cinema.
16 | */
17 |
18 | public abstract class AbstractCinemaListAdapter , VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter {
19 |
20 | protected final OnCinemaClickListener onCinemaClickListener;
21 | protected final List cinemas;
22 | private final Set cinemaSet;
23 |
24 | public AbstractCinemaListAdapter(OnCinemaClickListener onCinemaClickListener) {
25 | this.onCinemaClickListener = onCinemaClickListener;
26 | cinemas = new ArrayList<>();
27 | cinemaSet = new LinkedHashSet<>();
28 | }
29 |
30 | @Override
31 | @NonNull
32 | public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
33 |
34 | @Override
35 | public void onBindViewHolder(@NonNull VH holder, int position) {
36 | Cinema cinema = cinemas.get(position);
37 | HOLDER_RENDERER typedHolder = (HOLDER_RENDERER) holder;
38 | typedHolder.render(cinema , holder.getAdapterPosition());
39 | }
40 |
41 | @Override
42 | public int getItemCount() {
43 | return cinemas.size();
44 | }
45 |
46 | public void addAll(List cinemaEntities){
47 | cinemaSet.addAll(cinemaEntities);
48 | cinemas.clear();
49 | cinemas.addAll(cinemaSet);
50 | notifyItemInserted(getItemCount());
51 | }
52 |
53 | public void notifyRemoveEach() {
54 | for (int i = 0; i < cinemas.size(); i++) {
55 | notifyItemRemoved(i);
56 | }
57 | }
58 |
59 | public void notifyAddEach() {
60 | for (int i = 0; i < cinemas.size(); i++) {
61 | notifyItemInserted(i);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: false
3 | jdk: oraclejdk8
4 |
5 | notifications:
6 | email: false
7 |
8 | before_cache:
9 |
10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
12 |
13 | cache:
14 |
15 | directories:
16 |
17 | - $HOME/.gradle/caches/
18 | - $HOME/.gradle/wrapper/
19 | - $HOME/.android/build-cache
20 |
21 | env:
22 |
23 | global:
24 |
25 | - ANDROID_API_LEVEL=26
26 | - ANDROID_BUILD_TOOLS_VERSION=28.0.0
27 | - ANDROID_EMU_API_LEVEL=21
28 | - ANDROID_ABI=armeabi-v7a
29 | - ADB_INSTALL_TIMEOUT=5 # minutes
30 |
31 | android:
32 |
33 | components:
34 |
35 | - tools
36 | - platform-tools
37 | - tools
38 | - build-tools-$ANDROID_BUILD_TOOLS
39 | - android-$ANDROID_API_LEVEL
40 | - android-$ANDROID_EMU_API_LEVEL
41 | - extra-google-m2repository
42 | - extra-android-m2repository # for design library
43 | - sys-img-armeabi-v7a-google_apis-$ANDROID_API_LEVEL
44 | - sys-img-armeabi-v7a-google_apis-$ANDROID_EMU_API_LEVEL
45 |
46 | licenses:
47 |
48 | - android-sdk-preview-license-.+
49 | - android-sdk-license-.+
50 | - google-gdk-license-.+
51 |
52 | before_install:
53 |
54 | - yes | sdkmanager "platforms;android-27"
55 | - mkdir "$ANDROID_HOME/licenses" || true
56 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"
57 | - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license"
58 | - chmod +x gradlew
59 | - ./gradlew dependencies || true
60 |
61 | install:
62 | - echo y | android update sdk -u -a -t tools
63 | - echo y | android update sdk -u -a -t platform-tools
64 | - echo y | android update sdk -u -a -t build-tools-28.0.0
65 | - echo y | android update sdk -u -a -t android-26
66 | - echo y | android update sdk -u -a -t extra-google-m2repository
67 | - echo y | android update sdk -u -a -t extra-android-m2repository
68 |
69 | before_script:
70 |
71 | - echo no | android create avd --force -n test -t android-$ANDROID_EMU_API_LEVEL --abi google_apis/$ANDROID_ABI
72 | - emulator -avd test -no-window &
73 | - android-wait-for-emulator
74 | - adb shell input keyevent 82 &
75 |
76 | script:
77 |
78 | - ./gradlew clean test build
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/widget/HideableFABBehaviour.kt:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.widget
2 |
3 | import android.content.Context
4 | import android.support.design.widget.CoordinatorLayout
5 | import android.support.design.widget.FloatingActionButton
6 | import android.support.v4.view.ViewCompat
7 | import android.util.AttributeSet
8 | import android.view.View
9 |
10 | class HideableFABBehaviour(context: Context, attrs: AttributeSet) : FloatingActionButton.Behavior(context, attrs) {
11 |
12 | override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout,
13 | child: FloatingActionButton,
14 | directTargetChild: View,
15 | target: View,
16 | axes: Int,
17 | type: Int): Boolean {
18 | return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target,
19 | axes, type)
20 | }
21 |
22 | override fun onNestedScroll(coordinatorLayout: CoordinatorLayout,
23 | child: FloatingActionButton,
24 | target: View,
25 | dxConsumed: Int,
26 | dyConsumed: Int,
27 | dxUnconsumed: Int,
28 | dyUnconsumed: Int,
29 | type: Int) {
30 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed,
31 | dyConsumed, dxUnconsumed, dyUnconsumed, type)
32 |
33 | if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
34 | child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
35 | override fun onHidden(fab: FloatingActionButton?) {
36 | super.onHidden(fab)
37 | fab!!.visibility = View.INVISIBLE
38 | }
39 | })
40 | } else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
41 | child.show()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 28
6 | buildToolsVersion "28.0.3"
7 | defaultConfig {
8 | applicationId "com.ru.devit.mediateka"
9 | minSdkVersion 19
10 | targetSdkVersion 28
11 | versionCode 2
12 | versionName "1.0"
13 | vectorDrawables.useSupportLibrary= true
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | buildTypes.each {
22 | it.buildConfigField 'String', 'THE_MOVIE_DB_API_KEY', THE_MOVIE_DB_API_KEY
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | lintOptions {
29 | abortOnError false
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation supportDependencies.appCompat
35 | implementation supportDependencies.design
36 | implementation supportDependencies.supportAnnotation
37 | implementation supportDependencies.constraintLayout
38 |
39 | implementation daggerDependencies.dagger
40 | annotationProcessor daggerDependencies.daggerCompiler
41 |
42 | implementation rxDependencies.rxJava
43 | implementation rxDependencies.rxAndroid
44 |
45 | implementation cardView
46 | implementation picasso
47 | implementation retrofit
48 |
49 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
50 | implementation gsonConverter
51 |
52 | implementation 'android.arch.persistence.room:runtime:1.1.1'
53 | annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
54 | implementation 'android.arch.persistence.room:rxjava2:1.1.1'
55 |
56 | implementation 'de.hdodenhof:circleimageview:2.1.0'
57 | implementation 'com.android.support:palette-v7:28.0.0'
58 |
59 | testImplementation testingDependencies.junit
60 | testImplementation testingDependencies.mockito
61 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
62 | }
63 | repositories {
64 | mavenCentral()
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/favouritelistcinema/CinemaSortingDialog.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.favouritelistcinema;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.DialogFragment;
9 | import android.support.v4.app.Fragment;
10 | import android.support.v7.app.AlertDialog;
11 | import android.util.Log;
12 |
13 | import com.ru.devit.mediateka.R;
14 |
15 | public class CinemaSortingDialog extends DialogFragment {
16 |
17 | private String[] arrayCinemaSortingVariants;
18 | private OnDialogItemClicked onDialogItemClicked;
19 | private int position = 0;
20 |
21 | public static CinemaSortingDialog newInstance(){
22 | return new CinemaSortingDialog();
23 | }
24 |
25 | @NonNull
26 | @Override
27 | public Dialog onCreateDialog(Bundle savedInstanceState) {
28 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(requireActivity());
29 | alertDialogBuilder.setTitle(getString(R.string.sort_favourite_list));
30 | alertDialogBuilder.setSingleChoiceItems(arrayCinemaSortingVariants, position , (dialog, which) -> {
31 | onDialogItemClicked.onDialogItemClicked(which);
32 | dialog.dismiss();
33 | });
34 |
35 | return alertDialogBuilder.create();
36 | }
37 |
38 | public void setPosition(int position){
39 | this.position = position;
40 | }
41 |
42 | @Override
43 | public void onAttach(Context context) {
44 | super.onAttach(context);
45 | try {
46 | onDialogItemClicked = (OnDialogItemClicked) context;
47 | } catch (ClassCastException exception){
48 | throw new ClassCastException(context.toString()
49 | + " must implement OnDialogItemClicked");
50 | }
51 | }
52 |
53 | @Override
54 | public void onCreate(@Nullable Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | arrayCinemaSortingVariants = getResources().getStringArray(R.array.array_sorting_cinema_variants);
57 | }
58 |
59 | public interface OnDialogItemClicked{
60 | void onDialogItemClicked(int position);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_favourite_list_cinema.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/cinemalist/CinemaListModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema.cinemalist;
2 |
3 | import com.ru.devit.mediateka.domain.CinemaRepository;
4 | import com.ru.devit.mediateka.di.ActivityScope;
5 | import com.ru.devit.mediateka.domain.cinemausecases.GetCinemas;
6 | import com.ru.devit.mediateka.domain.cinemausecases.GetTopRatedCinemas;
7 | import com.ru.devit.mediateka.domain.cinemausecases.GetUpComingCinemas;
8 | import com.ru.devit.mediateka.presentation.cinemalist.CinemaListPresenter;
9 |
10 | import javax.inject.Named;
11 |
12 | import dagger.Module;
13 | import dagger.Provides;
14 | import io.reactivex.Scheduler;
15 |
16 | @Module
17 | public class CinemaListModule {
18 |
19 | @ActivityScope
20 | @Provides
21 | CinemaListPresenter provideCinemaListPresenter(GetCinemas getCinemas ,
22 | GetTopRatedCinemas getTopRatedCinemas ,
23 | GetUpComingCinemas getUpComingCinemas){
24 | return new CinemaListPresenter(getCinemas , getTopRatedCinemas , getUpComingCinemas);
25 | }
26 |
27 | @ActivityScope
28 | @Provides
29 | GetCinemas provideGetCinemas(@Named("executor_thread")Scheduler executorThread ,
30 | @Named("ui_thread")Scheduler uiThread ,
31 | CinemaRepository repository){
32 | return new GetCinemas(executorThread , uiThread , repository);
33 | }
34 |
35 | @ActivityScope
36 | @Provides
37 | GetTopRatedCinemas provideGetTopRatedCinemas(@Named("executor_thread")Scheduler executorThread ,
38 | @Named("ui_thread")Scheduler uiThread ,
39 | CinemaRepository repository){
40 | return new GetTopRatedCinemas(executorThread , uiThread , repository);
41 | }
42 |
43 | @ActivityScope
44 | @Provides
45 | GetUpComingCinemas provideGetUpComingCinemas(@Named("executor_thread")Scheduler executorThread ,
46 | @Named("ui_thread")Scheduler uiThread ,
47 | CinemaRepository repository){
48 | return new GetUpComingCinemas(executorThread , uiThread , repository);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/models/mapper/CinemaEntityToCinemaTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.mapper;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.models.db.CinemaEntity;
5 | import com.ru.devit.mediateka.models.model.Cinema;
6 |
7 | import org.junit.Test;
8 | import org.mockito.Mock;
9 |
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.*;
12 | import static org.mockito.Mockito.doReturn;
13 |
14 | @SuppressWarnings("ResultOfMethodCallIgnored")
15 | public class CinemaEntityToCinemaTest extends UnitTest {
16 |
17 | @Mock private Cinema cinemaMock;
18 | @Mock private CinemaEntity cinemaEntityMock;
19 |
20 | private CinemaEntityToCinema mapper;
21 |
22 | @Override
23 | protected void onSetUp() {
24 | mapper = new CinemaEntityToCinema();
25 | }
26 |
27 | @Test
28 | public void testMap() {
29 | doReturn(62).when(cinemaMock).getId();
30 | doReturn("Star wars").when(cinemaMock).getTitle();
31 | doReturn("Best movie").when(cinemaMock).getDescription();
32 | doReturn(200000000).when(cinemaMock).getBudget();
33 |
34 | final CinemaEntity cinemaEntity = mapper.map(cinemaMock);
35 | assertThat(cinemaEntity.getCinemaId() , is(62));
36 | assertThat(cinemaEntity.getTitle() , is("Star wars"));
37 | assertThat(cinemaEntity.getDescription() , is("Best movie"));
38 | assertThat(cinemaEntity.getBudget() , is(200000000));
39 | }
40 |
41 | @Test
42 | public void testReverseMap() {
43 | doReturn(20).when(cinemaEntityMock).getCinemaId();
44 | doReturn("Avengers").when(cinemaEntityMock).getTitle();
45 | doReturn("Avengers:description").when(cinemaEntityMock).getDescription();
46 | doReturn("Stan Lee").when(cinemaEntityMock).getDirectorName();
47 | doReturn("2004.12.03").when(cinemaEntityMock).getReleaseDate();
48 |
49 | final Cinema cinema = mapper.reverseMap(cinemaEntityMock);
50 | assertThat(cinema.getId() , is(20));
51 | assertThat(cinema.getTitle() , is("Avengers"));
52 | assertThat(cinema.getDescription() , is("Avengers:description"));
53 | assertThat(cinema.getDirectorName() , is("Stan Lee"));
54 | assertThat(cinema.getReleaseDate() , is("2004.12.03"));
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/smallcinemalist/SmallCinemasPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.smallcinemalist;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.domain.cinemausecases.GetCinemaByQuery;
5 | import com.ru.devit.mediateka.models.model.Cinema;
6 |
7 | import org.junit.Test;
8 | import org.mockito.Mock;
9 |
10 | import java.util.List;
11 |
12 | import static org.junit.Assert.*;
13 | import static org.mockito.Mockito.times;
14 | import static org.mockito.Mockito.verify;
15 |
16 | public class SmallCinemasPresenterTest extends UnitTest {
17 |
18 | @Mock private SmallCinemasPresenter.View view;
19 | @Mock private GetCinemaByQuery useCaseGetCinemaByQueryMock;
20 | @Mock private List cinemaListMock;
21 |
22 | private static final int TEST_CINEMA_ID = 987;
23 |
24 | private SmallCinemasPresenter presenter;
25 |
26 | @Override
27 | protected void onSetUp() {
28 | presenter = new SmallCinemasPresenter(useCaseGetCinemaByQueryMock);
29 | }
30 |
31 | @Test
32 | public void shouldOpenCinemaDetailScreenWhenCinemaClicked(){
33 | presenter.setView(view);
34 | presenter.onCinemaClicked(TEST_CINEMA_ID , 6);
35 |
36 | verify(view).openCinema(TEST_CINEMA_ID , 6);
37 | }
38 |
39 | @Test
40 | public void shouldShowLoadingWhenInitialize(){
41 | presenter.setView(view);
42 | presenter.initialize();
43 |
44 | verify(view , times(1)).showLoading();
45 | verify(view , times(1)).hideLoading();
46 | }
47 |
48 | @Test
49 | public void shouldSetQueryFromSearchFieldWhenCome(){
50 | presenter.onGetTextFromSearchField("Predator");
51 |
52 | verify(useCaseGetCinemaByQueryMock).onNextQuery("Predator");
53 | }
54 |
55 | @Test
56 | public void shouldSortCinemasByDateWhenSetCinemas(){
57 | presenter.setView(view);
58 | presenter.setCinemas(cinemaListMock);
59 |
60 | verify(view).showCinemas(cinemaListMock);
61 | verify(view).hideLoading();
62 | }
63 |
64 | @Test
65 | public void shouldUseCaseDisposeWhenPresenterDestroy(){
66 | presenter.onDestroy();
67 |
68 | verify(useCaseGetCinemaByQueryMock).removeActions();
69 | verify(useCaseGetCinemaByQueryMock).dispose();
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/models/mapper/CinemaResponseToCinemaTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.models.mapper;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.models.model.Cinema;
5 | import com.ru.devit.mediateka.models.network.CinemaDetailResponse;
6 | import com.ru.devit.mediateka.models.network.CinemaNetwork;
7 | import com.ru.devit.mediateka.models.network.CinemaResponse;
8 |
9 | import org.junit.Test;
10 | import org.mockito.Mock;
11 | import org.mockito.Mockito;
12 |
13 | import java.util.Iterator;
14 | import java.util.List;
15 |
16 | import static org.hamcrest.CoreMatchers.is;
17 | import static org.junit.Assert.*;
18 | import static org.mockito.Matchers.eq;
19 | import static org.mockito.Mockito.doReturn;
20 |
21 | @SuppressWarnings("ResultOfMethodCallIgnored")
22 | public class CinemaResponseToCinemaTest extends UnitTest {
23 |
24 | @Mock private CinemaDetailResponse cinemaDetailResponseMock;
25 | @Mock private CinemaResponse cinemaResponseMock;
26 | @Mock private List cinemaNetworkListMock;
27 | @Mock private Iterator cinemaNetworkIteratorMock;
28 |
29 | private CinemaResponseToCinema mapper;
30 |
31 | @Override
32 | protected void onSetUp() {
33 | mapper = new CinemaResponseToCinema();
34 | }
35 |
36 | @Test
37 | public void shouldMapId(){
38 | doReturn(77).when(cinemaDetailResponseMock).getId();
39 |
40 | final Cinema cinema = mapper.map(cinemaDetailResponseMock);
41 | assertThat(cinema.getId() , is(cinemaDetailResponseMock.getId()));
42 | }
43 |
44 | @Test
45 | public void shouldMapListOfCinemas(){
46 | CinemaNetwork cinemaNetwork = Mockito.mock(CinemaNetwork.class);
47 | doReturn(cinemaNetwork).when(cinemaNetworkListMock).get(eq(0));
48 | doReturn(true).when(cinemaNetworkIteratorMock).hasNext();
49 | doReturn(1).when(cinemaNetworkListMock).size();
50 | doReturn(cinemaNetworkIteratorMock).when(cinemaNetworkListMock).iterator();
51 | doReturn(cinemaNetworkListMock).when(cinemaResponseMock).getCinemas();
52 |
53 | final List cinemaList = mapper.map(cinemaResponseMock);
54 | assertNotNull(cinemaList);
55 | assertEquals(cinemaNetworkListMock.size() , cinemaList.size());
56 | assertFalse(cinemaList.isEmpty());
57 | }
58 | }
--------------------------------------------------------------------------------
/gradle-scripts/dependencies.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 |
3 | //Version
4 | supportVersion = '28.0.0'
5 |
6 | rxJavaVersion = '2.1.14'
7 | rxAndroidVersion = '2.0.1'
8 |
9 | picassoVersion = '2.5.2'
10 | daggerVersion = '2.16'
11 |
12 | javaxVersion = '1.0'
13 |
14 | junitVersion = '4.12'
15 | mockitoVersion = '1.10.19'
16 | gsonVersion = '2.8.0'
17 |
18 | //Packages
19 | supportPackage = 'com.android.support'
20 |
21 | reactivePackage = 'io.reactivex.rxjava2'
22 | picassoPackage = 'com.squareup.picasso'
23 | daggerPackage = 'com.google.dagger'
24 | javaxPackage = 'javax.annotation'
25 | gsonPackage = 'com.google.code.gson'
26 |
27 | junitPackage = 'junit'
28 | mockitoPackage = 'org.mockito'
29 |
30 | //Support Libraries dependencies
31 | supportDependencies = [
32 | design : buildDependency(supportPackage, 'design', supportVersion),
33 | appCompat : buildDependency(supportPackage, 'appcompat-v7', supportVersion),
34 | supportAnnotation: buildDependency(supportPackage, 'support-annotations', supportVersion),
35 | constraintLayout : 'com.android.support.constraint:constraint-layout:1.0.2']
36 |
37 | //RX Libraries dependencies
38 | rxDependencies = [
39 | rxJava : buildDependency(reactivePackage, 'rxjava', rxJavaVersion),
40 | rxAndroid: buildDependency(reactivePackage, 'rxandroid', rxAndroidVersion)]
41 |
42 | //Dagger Libraries dependencies
43 | daggerDependencies = [
44 | dagger : buildDependency(daggerPackage, 'dagger', daggerVersion),
45 | daggerCompiler : buildDependency(daggerPackage, 'dagger-compiler', daggerVersion)]
46 |
47 |
48 |
49 | //Elemental Libraries dependencies
50 | picasso = buildDependency(picassoPackage, 'picasso', picassoVersion)
51 | gson = buildDependency(gsonPackage, 'gson', gsonVersion)
52 | gsonConverter = 'com.squareup.retrofit2:converter-gson:2.1.0'
53 | retrofit = 'com.squareup.retrofit2:retrofit:2.3.0'
54 | cardView = 'com.android.support:cardview-v7:28.0.0'
55 |
56 | //Testing
57 | testingDependencies = [
58 | junit : buildDependency(junitPackage, 'junit', junitVersion),
59 | mockito : buildDependency(mockitoPackage, 'mockito-core', mockitoVersion)]
60 | }
61 |
62 | static String buildDependency(String pack, String dependency, String version) {
63 | return "${pack}:${dependency}:${version}"
64 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_popular_actors.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
17 |
18 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
48 |
49 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/datasource/db/CinemaDao.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.datasource.db;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Insert;
5 | import android.arch.persistence.room.OnConflictStrategy;
6 | import android.arch.persistence.room.Query;
7 | import android.arch.persistence.room.Update;
8 |
9 | import com.ru.devit.mediateka.models.db.ActorEntity;
10 | import com.ru.devit.mediateka.models.db.CinemaEntity;
11 | import com.ru.devit.mediateka.models.model.Cinema;
12 |
13 | import java.util.List;
14 |
15 | import io.reactivex.Flowable;
16 | import io.reactivex.Maybe;
17 | import io.reactivex.Single;
18 |
19 | @Dao
20 | public interface CinemaDao {
21 |
22 | @Query("SELECT * FROM CinemaTable WHERE page = :page ORDER BY popularity DESC")
23 | Single> getCinemas(int page);
24 |
25 | @Query("SELECT * FROM CinemaTable WHERE page = :page AND vote_average > 8 ORDER BY vote_average DESC")
26 | Single> getTopRatedCinemas(int page);
27 |
28 | @Query("SELECT * FROM CinemaTable WHERE page = :page AND vote_average = 0 AND release_date >= :currentYear")
29 | Single> getUpComingCinemas(int page , int currentYear);
30 |
31 | @Query("SELECT * FROM CinemaTable WHERE cinemaId = :id")
32 | Single getCinemaById(final int id);
33 |
34 | @Insert(onConflict = OnConflictStrategy.ROLLBACK)
35 | void insertAll(List cinemaEntities);
36 |
37 | @Query("UPDATE CinemaTable SET budget = :budget , revenue = :revenue , cinema_duration = :cinemaDuration , director_name = :directorName " +
38 | "WHERE cinemaId = :cinemaId")
39 | void updateCinema(int cinemaId , int budget , int revenue , int cinemaDuration , String directorName );
40 |
41 | @Insert(onConflict = OnConflictStrategy.IGNORE)
42 | void insertActors(List actorEntities);
43 |
44 | @Query("SELECT * FROM CinemaTable WHERE title LIKE :cinemaName")
45 | Flowable> getCinemasByName(String cinemaName);
46 |
47 | @Query("SELECT * FROM CinemaTable WHERE is_favourite")
48 | Maybe> getFavouriteListCinema();
49 |
50 | @Query("UPDATE CinemaTable SET is_favourite = :isFavourite WHERE cinemaId = :cinemaId")
51 | void insertFavouriteCinema(int cinemaId , boolean isFavourite);
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/application/RetrofitModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.application;
2 |
3 | import com.ru.devit.mediateka.BuildConfig;
4 | import com.ru.devit.mediateka.data.datasource.network.CinemaApiService;
5 |
6 | import java.io.IOException;
7 | import java.util.Locale;
8 |
9 | import javax.inject.Singleton;
10 |
11 | import dagger.Module;
12 | import dagger.Provides;
13 | import io.reactivex.schedulers.Schedulers;
14 | import okhttp3.HttpUrl;
15 | import okhttp3.Interceptor;
16 | import okhttp3.OkHttpClient;
17 | import okhttp3.Request;
18 | import okhttp3.Response;
19 | import retrofit2.Retrofit;
20 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
21 | import retrofit2.converter.gson.GsonConverterFactory;
22 |
23 | @Module
24 | class RetrofitModule {
25 |
26 | private static final String BASE_URL = "https://api.themoviedb.org/3/";
27 | private static final String LOCAL_LANGUAGE = Locale.getDefault().getLanguage();
28 |
29 | @Provides
30 | @Singleton
31 | Retrofit provideRetrofit(OkHttpClient client){
32 | return new Retrofit.Builder()
33 | .baseUrl(BASE_URL)
34 | .client(client)
35 | .addConverterFactory(GsonConverterFactory.create())
36 | .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
37 | .build();
38 | }
39 |
40 | @Provides
41 | @Singleton
42 | OkHttpClient provideOkHttpClient(){
43 | OkHttpClient.Builder client = new OkHttpClient.Builder();
44 | client.addInterceptor(chain -> {
45 | Request original = chain.request();
46 | HttpUrl originalUrl = original.url();
47 |
48 | HttpUrl url = originalUrl.newBuilder()
49 | .addQueryParameter("api_key" , BuildConfig.THE_MOVIE_DB_API_KEY)
50 | .addQueryParameter("language" , LOCAL_LANGUAGE)
51 | .build();
52 | Request.Builder request = original.newBuilder()
53 | .url(url);
54 |
55 | return chain.proceed(request.build());
56 | });
57 | return client.build();
58 | }
59 |
60 | @Provides
61 | @Singleton
62 | CinemaApiService provideCinemaApiService(Retrofit retrofit){
63 | return retrofit.create(CinemaApiService.class);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/widget/CinemaHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.widget;
2 |
3 | import android.content.Context;
4 | import android.support.constraint.ConstraintLayout;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.widget.ProgressBar;
8 | import android.widget.TextView;
9 |
10 | import com.ru.devit.mediateka.R;
11 | import com.ru.devit.mediateka.models.model.Cinema;
12 | import com.ru.devit.mediateka.utils.FormatterUtils;
13 |
14 | public class CinemaHeaderView extends ConstraintLayout {
15 |
16 | private View rootView;
17 | private TextView releaseDateTextView , durationTextView , titleTextView , genresTextView;
18 | private ProgressBar mProgressBar;
19 |
20 | public CinemaHeaderView(Context context) {
21 | super(context);
22 | init(context);
23 | }
24 |
25 | public CinemaHeaderView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | init(context);
28 | }
29 |
30 | public CinemaHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
31 | super(context, attrs, defStyleAttr);
32 | init(context);
33 | }
34 |
35 | public void render(Cinema cinema){
36 | titleTextView.setText(cinema.getTitle());
37 | releaseDateTextView.setText(FormatterUtils.getYearFromDate(cinema.getReleaseDate()));
38 | durationTextView.setText(FormatterUtils.formatDuration(cinema.getDuration() , getContext()));
39 | genresTextView.setText(FormatterUtils.formatGenres(cinema.getGenres() , getContext()));
40 | }
41 |
42 | public void startProgress(){
43 | mProgressBar.setVisibility(VISIBLE);
44 | }
45 |
46 | public void hideProgress(){
47 | mProgressBar.setVisibility(GONE);
48 | }
49 |
50 | private void init(Context context){
51 | if (rootView == null){
52 | rootView = inflate(context , R.layout.cinema_detail_header_view, this);
53 | }
54 | releaseDateTextView = rootView.findViewById(R.id.cinema_release_date);
55 | durationTextView = rootView.findViewById(R.id.cinema_duration);
56 | titleTextView = rootView.findViewById(R.id.cinema_title);
57 | genresTextView = rootView.findViewById(R.id.cinema_genres);
58 | mProgressBar = rootView.findViewById(R.id.progressBar);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/actor/ActorModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.actor;
2 |
3 | import android.content.Context;
4 |
5 | import com.ru.devit.mediateka.MediatekaApp;
6 | import com.ru.devit.mediateka.data.datasource.db.ActorDao;
7 | import com.ru.devit.mediateka.data.datasource.db.CinemaActorJoinDao;
8 | import com.ru.devit.mediateka.data.datasource.network.CinemaApiService;
9 | import com.ru.devit.mediateka.data.repository.actor.ActorLocalRepository;
10 | import com.ru.devit.mediateka.data.repository.actor.ActorRemoteRepository;
11 | import com.ru.devit.mediateka.domain.ActorRepository;
12 | import com.ru.devit.mediateka.models.mapper.ActorDetailEntityToActor;
13 | import com.ru.devit.mediateka.models.mapper.ActorDetailResponseToActor;
14 |
15 | import dagger.Module;
16 | import dagger.Provides;
17 |
18 | @Module
19 | public class ActorModule {
20 |
21 | @ActorScope
22 | @Provides
23 | ActorRepository provideRepository(CinemaApiService apiService ,
24 | ActorDetailResponseToActor networkMapper ,
25 | ActorDetailEntityToActor dbMapper ,
26 | ActorLocalRepository cache ,
27 | CinemaActorJoinDao cinemaActorJoinDao){
28 | return new ActorRemoteRepository(apiService , networkMapper , dbMapper , cache , cinemaActorJoinDao);
29 | }
30 |
31 | @ActorScope
32 | @Provides
33 | ActorLocalRepository provideActorLocalRepository(ActorDao actorDao ,
34 | ActorDetailEntityToActor mapper ,
35 | CinemaActorJoinDao cinemaActorJoinDao){
36 | return new ActorLocalRepository(actorDao , mapper , cinemaActorJoinDao);
37 | }
38 |
39 | @ActorScope
40 | @Provides
41 | ActorDao providesActorDao(Context context){
42 | MediatekaApp mediatekaApp = (MediatekaApp) context;
43 | return mediatekaApp.getDatabaseInstance()
44 | .getActorDao();
45 | }
46 |
47 | @ActorScope
48 | @Provides
49 | ActorDetailEntityToActor provideActorDetailEntityToActor(){
50 | return new ActorDetailEntityToActor();
51 | }
52 |
53 | @ActorScope
54 | @Provides
55 | ActorDetailResponseToActor provideActorDetailResponseToActor(){
56 | return new ActorDetailResponseToActor();
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/actordetail/ActorDetailPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actordetail;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.domain.actorusecases.GetActorById;
5 |
6 | import org.junit.Test;
7 | import org.mockito.Mock;
8 |
9 | import java.util.List;
10 |
11 | import static org.junit.Assert.*;
12 | import static org.mockito.Matchers.any;
13 | import static org.mockito.Mockito.doReturn;
14 | import static org.mockito.Mockito.doThrow;
15 | import static org.mockito.Mockito.times;
16 | import static org.mockito.Mockito.verify;
17 |
18 | public class ActorDetailPresenterTest extends UnitTest {
19 |
20 | @Mock private ActorDetailPresenter.View view;
21 | @Mock private GetActorById useCaseGetActorByIdMock;
22 | @Mock private List posterUrlsMock;
23 | @Mock private ActorDetailPresenter.ActorDetailSubscriber subscriberMock;
24 |
25 | private static final int TEST_USER_ID = 123;
26 |
27 | private ActorDetailPresenter presenter;
28 |
29 | @Override
30 | protected void onSetUp() {
31 | presenter = new ActorDetailPresenter(useCaseGetActorByIdMock);
32 | }
33 |
34 | @Test
35 | public void shouldSearchActorByIdWhenInitialize(){
36 | presenter.setView(view);
37 | presenter.initialize();
38 | presenter.setActorId(TEST_USER_ID);
39 | useCaseGetActorByIdMock.searchActorById(TEST_USER_ID);
40 |
41 | verify(view , times(1)).showLoading();
42 | verify(useCaseGetActorByIdMock).searchActorById(TEST_USER_ID);
43 | }
44 |
45 | @Test
46 | public void shouldHideLoadingWhenRuntimeException(){
47 | // presenter.setView(view);
48 | // presenter.initialize();
49 | // doThrow(new RuntimeException()).when(subscriberMock).onError(any(Throwable.class));
50 | //
51 | // verify(view , times(1)).showLoading();
52 | // verify(view).hideLoading();
53 | }
54 |
55 | @Test
56 | public void shouldShowPostersWhenUserAvatarClicked(){
57 | presenter.setView(view);
58 | presenter.onAvatarClicked(posterUrlsMock);
59 |
60 | verify(view , times(1)).showPosters(posterUrlsMock);
61 | }
62 |
63 | @Test
64 | public void shouldUseCaseDisposeWhenPresenterDestroy(){
65 | presenter.onDestroy();
66 |
67 | verify(useCaseGetActorByIdMock , times(1)).dispose();
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/data/repository/actor/ActorLocalRepository.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.data.repository.actor;
2 |
3 | import com.ru.devit.mediateka.data.datasource.db.ActorDao;
4 | import com.ru.devit.mediateka.data.datasource.db.CinemaActorJoinDao;
5 | import com.ru.devit.mediateka.domain.ActorRepository;
6 | import com.ru.devit.mediateka.models.db.ActorEntity;
7 | import com.ru.devit.mediateka.models.db.CinemaEntity;
8 | import com.ru.devit.mediateka.models.mapper.ActorDetailEntityToActor;
9 | import com.ru.devit.mediateka.models.model.Actor;
10 |
11 | import java.util.List;
12 |
13 | import io.reactivex.Flowable;
14 | import io.reactivex.Single;
15 |
16 | public class ActorLocalRepository implements ActorRepository {
17 |
18 | private final ActorDao actorDao;
19 | private final ActorDetailEntityToActor mapper;
20 | private final CinemaActorJoinDao cinemaActorJoinDao;
21 |
22 | public ActorLocalRepository(ActorDao actorDao ,
23 | ActorDetailEntityToActor mapper ,
24 | CinemaActorJoinDao cinemaActorJoinDao) {
25 | this.actorDao = actorDao;
26 | this.mapper = mapper;
27 | this.cinemaActorJoinDao = cinemaActorJoinDao;
28 | }
29 |
30 | @Override
31 | public Single getActorById(final int actorId) {
32 | return actorDao.getActorById(actorId)
33 | .map(actorEntity -> mapper.mapDetailActor(actorEntity ,
34 | cinemaActorJoinDao.getCinemasForActor(actorId)));
35 | }
36 |
37 | @Override
38 | public Flowable> searchActors(String query) {
39 | return actorDao.getAllActorsByName(query)
40 | .map(mapper::reverseMap);
41 |
42 | }
43 |
44 | @Override
45 | public Single> getPopularActors(int page){
46 | return actorDao.getPopularActors() //TODO FIX THIS PAGE
47 | .map(mapper::reverseMap);
48 | }
49 |
50 | public void insertCinemasForActor(List cinemaEntities) {
51 | actorDao.insertCinemas(cinemaEntities);
52 | }
53 |
54 | public void updateActor(int actorId, String biography, String birthDay, String age, String placeOfBirth) {
55 | actorDao.updateActor(actorId , biography , birthDay , age , placeOfBirth);
56 | }
57 |
58 | public void insertActors(List actorEntities){
59 | actorDao.insertActors(actorEntities);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/search/SearchPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.search;
2 |
3 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
4 | import com.ru.devit.mediateka.presentation.base.BaseView;
5 |
6 | import javax.inject.Inject;
7 |
8 |
9 | public class SearchPresenter extends BasePresenter {
10 |
11 | private String query = "";
12 |
13 | private static int currentTabPosition = 0;
14 |
15 | private static final int CINEMAS_TAB_POSITION = 0;
16 | private static final int ACTORS_TAB_POSITION = 1;
17 |
18 | @Inject public SearchPresenter() {
19 | }
20 |
21 | @Override
22 | public void initialize() {
23 | getView().showLoading();
24 | }
25 |
26 | @Override
27 | public void onDestroy() {
28 | }
29 |
30 | public void onTabSelected(int position) {
31 | switch (position) {
32 | case CINEMAS_TAB_POSITION : {
33 | getView().onCinemaTabSelected();
34 | currentTabPosition = CINEMAS_TAB_POSITION;
35 | break;
36 | }
37 | case ACTORS_TAB_POSITION : {
38 | getView().onActorTabSelected();
39 | currentTabPosition = ACTORS_TAB_POSITION;
40 | break;
41 | }
42 | }
43 | }
44 |
45 | public void onTextChanged(String query) {
46 | this.query = query;
47 | checkClearEditTextBtnState();
48 | switch (currentTabPosition) {
49 | case CINEMAS_TAB_POSITION : {
50 | getView().textFromCinemaTab(query);
51 | break;
52 | }
53 | case ACTORS_TAB_POSITION : {
54 | getView().textFromActorTab(query);
55 | break;
56 | }
57 | }
58 | }
59 |
60 | public void onClearEditTextClicked() {
61 | getView().clearEditText("");
62 | checkClearEditTextBtnState();
63 | }
64 |
65 | private void checkClearEditTextBtnState(){
66 | if (query.isEmpty()){
67 | getView().hideClearEditTextBtn();
68 | } else {
69 | getView().showEditTextBtn();
70 | }
71 | }
72 |
73 | public interface View extends BaseView{
74 | void onCinemaTabSelected();
75 | void onActorTabSelected();
76 | void textFromCinemaTab(String query);
77 | void textFromActorTab(String query);
78 | void hideClearEditTextBtn();
79 | void showEditTextBtn();
80 | void clearEditText(String s);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ru/devit/mediateka/presentation/cinemadetail/CinemaDetailPresenterTest.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.cinemadetail;
2 |
3 | import com.ru.devit.mediateka.UnitTest;
4 | import com.ru.devit.mediateka.domain.cinemausecases.GetCinemaById;
5 | import com.ru.devit.mediateka.domain.cinemausecases.GetFavouriteListCinema;
6 |
7 | import org.junit.Test;
8 | import org.mockito.Mock;
9 |
10 | import java.util.List;
11 |
12 | import io.reactivex.Completable;
13 | import io.reactivex.functions.Action;
14 |
15 | import static org.junit.Assert.*;
16 | import static org.mockito.Mockito.doReturn;
17 | import static org.mockito.Mockito.verify;
18 |
19 | public class CinemaDetailPresenterTest extends UnitTest {
20 |
21 | @Mock private CinemaDetailPresenter.View view;
22 | @Mock private GetCinemaById useCaseGetCinemaByIdMock;
23 | @Mock private GetFavouriteListCinema useCaseGetFavouriteListCinema;
24 | @Mock private List posterUrlsMock;
25 |
26 | private static final int TEST_CINEMA_ID = 456;
27 |
28 | private CinemaDetailPresenter presenter;
29 |
30 | @Override
31 | protected void onSetUp() {
32 | presenter = new CinemaDetailPresenter(useCaseGetCinemaByIdMock , useCaseGetFavouriteListCinema);
33 | }
34 |
35 | @Test
36 | public void shouldSearchCinemaByIdWhenInitialize(){
37 | presenter.setView(view);
38 | presenter.initialize();
39 | presenter.setCinemaId(TEST_CINEMA_ID);
40 | useCaseGetCinemaByIdMock.searchCinemaById(TEST_CINEMA_ID);
41 |
42 | verify(view).showLoading();
43 | verify(useCaseGetCinemaByIdMock).searchCinemaById(TEST_CINEMA_ID);
44 | }
45 |
46 | @Test
47 | public void shouldSaveCinemaToFavouriteWhenAddToFavouriteClicked(){
48 | presenter.setView(view);
49 | presenter.setCinemaId(TEST_CINEMA_ID);
50 | doReturn(Completable.complete()).when(useCaseGetFavouriteListCinema).saveFavouriteCinema(TEST_CINEMA_ID);
51 | presenter.onAddFavouriteCinemaClicked();
52 |
53 | verify(view).hideFABCinemaMenu();
54 | verify(useCaseGetFavouriteListCinema).saveFavouriteCinema(TEST_CINEMA_ID);
55 | }
56 |
57 | @Test
58 | public void shouldShowPostersWhenPosterClicked(){
59 | presenter.setView(view);
60 | presenter.onSmallPosterClicked(posterUrlsMock);
61 |
62 | verify(view).showListPosters(posterUrlsMock);
63 | }
64 |
65 | @Test
66 | public void shouldUseCaseDisposeWhenPresenterDestroy(){
67 | presenter.onDestroy();
68 |
69 | verify(useCaseGetCinemaByIdMock).dispose();
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/actorlist/ActorViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actorlist;
2 |
3 | import android.content.Context;
4 | import android.support.graphics.drawable.VectorDrawableCompat;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.View;
7 | import android.widget.TextView;
8 |
9 | import com.ru.devit.mediateka.R;
10 | import com.ru.devit.mediateka.models.model.Actor;
11 | import com.ru.devit.mediateka.presentation.common.OnActorClickListener;
12 | import com.ru.devit.mediateka.utils.UrlImagePathCreator;
13 | import com.ru.devit.mediateka.utils.UrlImagePathCreator.Quality;
14 | import com.squareup.picasso.Picasso;
15 |
16 | import de.hdodenhof.circleimageview.CircleImageView;
17 |
18 | class ActorViewHolder extends RecyclerView.ViewHolder{
19 |
20 | private CircleImageView avatar;
21 | private TextView actorName , actorRole;
22 | private View rootView;
23 |
24 | private final OnActorClickListener onActorClickListener;
25 |
26 | ActorViewHolder(View itemView , OnActorClickListener onActorClickListener) {
27 | super(itemView);
28 | this.onActorClickListener = onActorClickListener;
29 | rootView = itemView;
30 | avatar = itemView.findViewById(R.id.iv_actor_avatar);
31 | actorName = itemView.findViewById(R.id.tv_actor_name);
32 | actorRole = itemView.findViewById(R.id.tv_actor_role);
33 | }
34 |
35 | void render(Actor actor , int viewHolderPosition){
36 | renderAvatar(UrlImagePathCreator.INSTANCE.createPictureUrlFromQuality(Quality.Quality185 , actor.getProfileUrl()));
37 | onActorClicked(actor.getActorId() , viewHolderPosition);
38 | actorName.setText(actor.getName());
39 | if (actor.getCharacter() == null){
40 | return;
41 | }
42 | actorRole.setText(actor.getCharacter().isEmpty() ? "" : getContext().getString(R.string.role , actor.getCharacter()));
43 | }
44 |
45 | private void onActorClicked(int actorId , int viewHolderPosition){
46 | rootView.setOnClickListener(view -> onActorClickListener.onActorClicked(actorId , viewHolderPosition));
47 | }
48 |
49 | @SuppressWarnings("ConstantConditions")
50 | private void renderAvatar(String url){
51 | Picasso.with(getContext())
52 | .load(url)
53 | .error(VectorDrawableCompat.create(getContext().getResources() , R.drawable.ic_actor_default_avatar , getContext().getTheme()))
54 | .into(avatar);
55 | }
56 |
57 | private Context getContext(){
58 | return itemView.getContext();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/di/cinema/CinemaModule.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.di.cinema;
2 |
3 | import android.content.Context;
4 |
5 | import com.ru.devit.mediateka.MediatekaApp;
6 | import com.ru.devit.mediateka.data.datasource.db.CinemaActorJoinDao;
7 | import com.ru.devit.mediateka.data.datasource.db.CinemaDao;
8 | import com.ru.devit.mediateka.data.datasource.network.CinemaApiService;
9 | import com.ru.devit.mediateka.data.repository.cinema.CinemaLocalRepository;
10 | import com.ru.devit.mediateka.data.repository.cinema.CinemaRemoteRepository;
11 | import com.ru.devit.mediateka.domain.CinemaRepository;
12 | import com.ru.devit.mediateka.models.mapper.CinemaEntityToCinema;
13 | import com.ru.devit.mediateka.models.mapper.CinemaMapper;
14 | import com.ru.devit.mediateka.models.mapper.CinemaResponseToCinema;
15 |
16 | import dagger.Module;
17 | import dagger.Provides;
18 |
19 | @Module
20 | public class CinemaModule {
21 |
22 | @CinemaScope
23 | @Provides
24 | CinemaRepository provideRepository(CinemaLocalRepository cache ,
25 | CinemaApiService apiService ,
26 | CinemaMapper mapper ,
27 | CinemaActorJoinDao cinemaActorJoinDao){
28 | return new CinemaRemoteRepository(cache , apiService , mapper , cinemaActorJoinDao);
29 | }
30 |
31 | @CinemaScope
32 | @Provides
33 | CinemaLocalRepository provideCinemaLocalRepository(CinemaDao cinemaDao ,
34 | CinemaMapper cinemaMapper ,
35 | CinemaActorJoinDao cinemaActorJoinDao){
36 | return new CinemaLocalRepository(cinemaDao , cinemaMapper , cinemaActorJoinDao);
37 | }
38 |
39 | @CinemaScope
40 | @Provides
41 | CinemaDao providesCinemaDao(Context context){
42 | MediatekaApp mediatekaApp = (MediatekaApp) context;
43 | return mediatekaApp.getDatabaseInstance()
44 | .getCinemaDao();
45 | }
46 |
47 | @CinemaScope
48 | @Provides
49 | CinemaMapper provideCinemaMapper(CinemaResponseToCinema cinemaResponseToCinema , CinemaEntityToCinema cinemaEntityToCinema){
50 | return new CinemaMapper(cinemaResponseToCinema , cinemaEntityToCinema);
51 | }
52 |
53 | @CinemaScope
54 | @Provides
55 | CinemaResponseToCinema provideCinemaResponseToCinema(){
56 | return new CinemaResponseToCinema();
57 | }
58 |
59 | @CinemaScope
60 | @Provides
61 | CinemaEntityToCinema provideCinemaEntityToCinema(){
62 | return new CinemaEntityToCinema();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/domain/actorusecases/GetActorsByQuery.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.domain.actorusecases;
2 |
3 | import com.ru.devit.mediateka.domain.Actions;
4 | import com.ru.devit.mediateka.domain.ActorRepository;
5 | import com.ru.devit.mediateka.domain.UseCase;
6 | import com.ru.devit.mediateka.models.model.Actor;
7 |
8 | import java.util.List;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import io.reactivex.Flowable;
12 | import io.reactivex.Scheduler;
13 | import io.reactivex.processors.FlowableProcessor;
14 | import io.reactivex.processors.PublishProcessor;
15 |
16 | public class GetActorsByQuery extends UseCase> {
17 |
18 | private Actions actions;
19 | private boolean isDataLoaded = false;
20 | private final ActorRepository repository;
21 | private final FlowableProcessor processor = PublishProcessor.create();
22 |
23 | public GetActorsByQuery(Scheduler executorThread ,
24 | Scheduler uiThread ,
25 | ActorRepository repository) {
26 | super(executorThread, uiThread);
27 | this.repository = repository;
28 | }
29 |
30 | public void setQuery(String query) {
31 | processor.onNext(query);
32 | }
33 |
34 | public void setActions(Actions actions){
35 | this.actions = actions;
36 | }
37 |
38 | public void removeActions(){
39 | actions.removeActions();
40 | }
41 |
42 | @Override
43 | protected Flowable> createUseCase() {
44 | return processor
45 | .flatMap(s -> {
46 | if (!isDataLoaded){
47 | return Flowable.just(s)
48 | .doOnNext(s1 -> {
49 | if (s1.isEmpty()) actions.onDataLoaded();
50 | else actions.onNext();
51 | });
52 | } else {
53 | actions.onDataLoaded();
54 | return Flowable.just(s)
55 | .doOnComplete(() -> {
56 | actions.onClearAdapter();
57 | isDataLoaded = false;
58 | });
59 | }
60 | })
61 | .debounce(800 , TimeUnit.MILLISECONDS)
62 | .filter(query -> !query.isEmpty())
63 | .distinctUntilChanged()
64 | .switchMap(s -> {
65 | isDataLoaded = true;
66 | return repository.searchActors(s);
67 | });
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/widget/DateAndTimePicker.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.widget;
2 |
3 | import android.app.DatePickerDialog;
4 | import android.app.TimePickerDialog;
5 | import android.content.Context;
6 |
7 | import com.ru.devit.mediateka.models.model.DateAndTimeInfo;
8 |
9 | import java.util.Calendar;
10 |
11 | public class DateAndTimePicker {
12 |
13 | private final Context context;
14 | private final Calendar calendar = Calendar.getInstance();
15 | private final int year;
16 | private final int month;
17 | private final int day;
18 | private final int hour;
19 | private final int minute;
20 |
21 | public DateAndTimePicker(Context context) {
22 | this.context = context;
23 | year = calendar.get(Calendar.YEAR);
24 | month = calendar.get(Calendar.MONTH);
25 | day = calendar.get(Calendar.DAY_OF_MONTH);
26 | hour = calendar.get(Calendar.HOUR_OF_DAY);
27 | minute = calendar.get(Calendar.MINUTE);
28 | }
29 |
30 | public void showDateAndTimePickerDialog(DateAndTimePickerListener listener){
31 | DateAndTimeInfo dateAndTimeInfo = new DateAndTimeInfo();
32 | showDatePickerDialog((year, month, dayOfMonth) -> {
33 | dateAndTimeInfo.setYear(year);
34 | dateAndTimeInfo.setMonth(month);
35 | dateAndTimeInfo.setDay(dayOfMonth);
36 | }, () -> showTimePickerDialog((hour, minute) -> {
37 | dateAndTimeInfo.setHour(hour);
38 | dateAndTimeInfo.setMinute(minute);
39 | listener.onDateAndTimeSet(dateAndTimeInfo);
40 | }));
41 | }
42 |
43 | private void showDatePickerDialog(DataPickerListener listener , Then then){
44 | DatePickerDialog datePickerDialog = new DatePickerDialog(context, (view, year, month, dayOfMonth) -> {
45 | listener.onDateSet(year , month , dayOfMonth);
46 | then.then();
47 | }, year , month , day);
48 | datePickerDialog.show();
49 | }
50 |
51 | private void showTimePickerDialog(TimePickerListener listener){
52 | TimePickerDialog timePickerDialog = new TimePickerDialog(context, (view, hourOfDay, minute) -> {
53 | listener.onTimeSet(hourOfDay , minute);
54 | }, hour , minute , true);
55 | timePickerDialog.show();
56 | }
57 |
58 | @FunctionalInterface
59 | interface Then{
60 | void then();
61 | }
62 |
63 | public interface DateAndTimePickerListener{
64 | void onDateAndTimeSet(DateAndTimeInfo dateAndTimeInfo);
65 | }
66 |
67 | interface DataPickerListener {
68 | void onDateSet(int year , int month , int dayOfMonth);
69 | }
70 |
71 | interface TimePickerListener{
72 | void onTimeSet(int hour , int minute);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/common/PhotoLoader.kt:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.common
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.graphics.Bitmap
6 | import android.graphics.drawable.Drawable
7 | import android.net.Uri
8 | import android.os.Environment
9 | import android.widget.Toast
10 | import com.ru.devit.mediateka.R
11 | import com.squareup.picasso.Picasso
12 | import com.squareup.picasso.Target
13 | import io.reactivex.Completable
14 | import io.reactivex.android.schedulers.AndroidSchedulers
15 | import io.reactivex.schedulers.Schedulers
16 | import java.io.File
17 | import java.io.FileOutputStream
18 |
19 | class PhotoLoader(private val context: Context , private val pictureName: String): Target {
20 |
21 | companion object {
22 | private const val ALBUM_NAME = "Mediateka"
23 | }
24 |
25 | override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
26 |
27 | }
28 |
29 | override fun onBitmapFailed(errorDrawable: Drawable?) {
30 | }
31 |
32 | override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
33 | saveBitmapToGallery(bitmap)
34 | .subscribe({
35 | Toast.makeText(context , context.getString(R.string.message_picture_download_successully) , Toast.LENGTH_LONG).show()
36 | } , {
37 | it.printStackTrace()
38 | })
39 | }
40 |
41 | private fun saveBitmapToGallery(bitmap: Bitmap?): Completable{
42 | return Completable.create {
43 | val albumDir = File(Environment.getExternalStorageDirectory() , ALBUM_NAME)
44 | if (!albumDir.exists()){
45 | albumDir.mkdir()
46 | }
47 | val pathToPicture = albumDir.path.plus("/").plus(pictureName)
48 | val pictureFile = File(pathToPicture)
49 | if (!pictureFile.exists()){
50 | pictureFile.createNewFile()
51 | val fileOutputStream = FileOutputStream(pictureFile)
52 | fileOutputStream.use {
53 | bitmap?.compress(Bitmap.CompressFormat.JPEG , 80 , fileOutputStream)
54 | fileOutputStream.flush()
55 | }
56 | sendPictureToGallery(pathToPicture)
57 | }
58 | it.onComplete()
59 | }
60 | .subscribeOn(Schedulers.io())
61 | .observeOn(AndroidSchedulers.mainThread())
62 | }
63 |
64 | private fun sendPictureToGallery(pathToPicture: String){
65 | val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
66 | val pictureFile = File(pathToPicture)
67 | val pictureUri = Uri.fromFile(pictureFile)
68 | intent.data = pictureUri
69 | context.sendBroadcast(intent)
70 | }
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/actorlist/ActorsPresenter.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.actorlist;
2 |
3 | import com.ru.devit.mediateka.domain.Actions;
4 | import com.ru.devit.mediateka.domain.actorusecases.GetActorsByQuery;
5 | import com.ru.devit.mediateka.domain.UseCaseSubscriber;
6 | import com.ru.devit.mediateka.models.model.Actor;
7 | import com.ru.devit.mediateka.presentation.base.BasePresenter;
8 | import com.ru.devit.mediateka.presentation.base.BaseView;
9 |
10 | import java.util.List;
11 |
12 | public class ActorsPresenter extends BasePresenter {
13 |
14 | private List actors;
15 | private final GetActorsByQuery getActorsByQuery;
16 |
17 | public ActorsPresenter(GetActorsByQuery getActorsByQuery) {
18 | this.getActorsByQuery = getActorsByQuery;
19 | }
20 |
21 | @Override
22 | public void initialize() {
23 | getView().showLoading();
24 | getActorsByQuery.subscribe(new ActorsSubscriber());
25 | getActorsByQuery.setActions(new Actions(
26 | () -> getView().showLoading() ,
27 | () -> getView().hideLoading() ,
28 | () -> getView().clearAdapter()
29 | ));
30 | getView().hideLoading();
31 | }
32 |
33 | public void setActors(List actors){
34 | this.actors = actors;
35 | showActors(this.actors);
36 | }
37 |
38 | public void onGetTextFromSearchField(String query) {
39 | getActorsByQuery.setQuery(query);
40 | }
41 |
42 | public void onActorClicked(int actorId , int viewHolderPosition){
43 | getView().openActor(actorId , viewHolderPosition);
44 | }
45 |
46 | private void showActors(List actors){
47 | getView().showActors(actors);
48 | getView().hideLoading();
49 | }
50 |
51 | public void onDestroy() {
52 | getActorsByQuery.removeActions();
53 | getActorsByQuery.dispose();
54 | setView(null);
55 | }
56 |
57 | final class ActorsSubscriber extends UseCaseSubscriber>{
58 | @Override
59 | public void onNext(List actors) {
60 | getView().showActors(actors);
61 | getView().hideLoading();
62 | }
63 |
64 | @Override
65 | public void onError(Throwable t) {
66 | t.printStackTrace();
67 | getView().hideLoading();
68 | }
69 |
70 | @Override
71 | public void onComplete() {
72 | getView().hideLoading();
73 | }
74 | }
75 |
76 | public interface View extends BaseView{
77 | void openActor(int actorId , int viewHolderPosition);
78 | void showActors(List actors);
79 | void clearAdapter();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ru/devit/mediateka/presentation/cinemalist/CinemaTab.java:
--------------------------------------------------------------------------------
1 | package com.ru.devit.mediateka.presentation.cinemalist;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import android.util.Log;
6 |
7 | import com.ru.devit.mediateka.domain.UseCase;
8 | import com.ru.devit.mediateka.domain.UseCaseSubscriber;
9 | import com.ru.devit.mediateka.models.model.Cinema;
10 | import com.ru.devit.mediateka.presentation.common.CinemaTabSelectorView;
11 |
12 | import java.util.List;
13 |
14 | import io.reactivex.disposables.CompositeDisposable;
15 |
16 | public enum CinemaTab {
17 |
18 | POPULAR {
19 | @Override
20 | public void loadCinemaList(UseCase> useCase,
21 | UseCaseSubscriber> subscriber,
22 | @NonNull T view) {
23 | super.loadCinemaList(useCase , subscriber , view);
24 | view.onPopularTabSelected();
25 | }
26 | },
27 | TOP_RATED {
28 | @Override
29 | public void loadCinemaList(UseCase> useCase,
30 | UseCaseSubscriber> subscriber,
31 | @NonNull T view) {
32 | super.loadCinemaList(useCase , subscriber , view);
33 | view.onTopRatedTabSelected();
34 | }
35 | },
36 | UP_COMING {
37 | @Override
38 | public void loadCinemaList(UseCase> useCase,
39 | UseCaseSubscriber> subscriber,
40 | @NonNull T view) {
41 | super.loadCinemaList(useCase , subscriber , view);
42 | view.onUpComingTabSelected();
43 | }
44 | };
45 |
46 | private final CompositeDisposable compositeDisposable = new CompositeDisposable();
47 |
48 | public void dispose(){
49 | if (!compositeDisposable.isDisposed()){
50 | compositeDisposable.dispose();
51 | }
52 | }
53 |
54 | public void loadCinemaList(@Nullable UseCase> useCase,
55 | @Nullable UseCaseSubscriber> subscriber,
56 | @NonNull T view){
57 | if (useCase != null && subscriber != null){
58 | useCase.subscribe(subscriber);
59 | compositeDisposable.add(subscriber);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------