├── .github ├── FUNDING.yml └── workflows │ ├── android-ci.yml │ └── detekt-analysis.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── frogoboxmedia.jks ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── frogobox │ │ └── movie │ │ ├── mvvm │ │ ├── favorite │ │ │ ├── FavoriteFragment.kt │ │ │ ├── FavoriteMovieFragment.kt │ │ │ ├── FavoriteMovieViewModel.kt │ │ │ ├── FavoriteTvShowFragment.kt │ │ │ └── FavoriteTvShowViewModel.kt │ │ ├── main │ │ │ ├── MainActivity.kt │ │ │ ├── MainActivityViewModel.kt │ │ │ ├── MainViewModel.kt │ │ │ ├── SettingActivity.kt │ │ │ └── SettingViewModel.kt │ │ ├── movie │ │ │ ├── DetailMovieActivity.kt │ │ │ ├── DetailMovieViewModel.kt │ │ │ ├── MovieFragment.kt │ │ │ ├── SearchMovieActivity.kt │ │ │ └── SearchMovieViewModel.kt │ │ └── tv │ │ │ ├── DetailTvShowActivity.kt │ │ │ ├── DetailTvShowViewModel.kt │ │ │ ├── SearchTvShowActivity.kt │ │ │ ├── SearchTvShowViewModel.kt │ │ │ └── TvShowFragment.kt │ │ ├── provider │ │ ├── FavoriteMovieProvider.kt │ │ └── FavoriteTvShowProvider.kt │ │ ├── util │ │ ├── BaseAppActivity.kt │ │ └── ViewModelFactory.kt │ │ └── widget │ │ ├── FavoriteBannerWidget.kt │ │ ├── StackRemoteViewFactory.kt │ │ └── StackWidgetService.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher.xml │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_main.xml │ ├── activity_search.xml │ ├── activity_setting.xml │ ├── fragment_favorite.xml │ ├── fragment_tv_movie_grid.xml │ ├── fragment_tv_movie_list.xml │ ├── toolbar_search.xml │ ├── widget_banner_favorite.xml │ └── widget_content_item.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── favorite_banner_widget_info.xml ├── base ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── frogobox │ │ └── base │ │ ├── BaseActivity.kt │ │ ├── BaseApiModel.kt │ │ ├── BaseApplication.kt │ │ ├── BaseCallback.kt │ │ ├── BaseDataSource.kt │ │ ├── BaseFragment.kt │ │ ├── BaseHelper.kt │ │ ├── BaseListener.kt │ │ ├── BaseViewAdapter.kt │ │ ├── BaseViewHolder.kt │ │ ├── BaseViewModel.kt │ │ ├── adapter │ │ ├── FavoriteMovieAdapter.kt │ │ ├── FavoriteTvShowAdapter.kt │ │ ├── MovieAdapter.kt │ │ └── TvShowAdapter.kt │ │ ├── callback │ │ ├── DeleteViewCallback.kt │ │ └── SaveViewCallback.kt │ │ ├── service │ │ ├── AlarmReceiver.kt │ │ └── Notification.kt │ │ ├── source │ │ ├── DataSource.kt │ │ ├── Repository.kt │ │ ├── local │ │ │ ├── AppDatabase.kt │ │ │ ├── LocalDataSource.kt │ │ │ └── dao │ │ │ │ ├── FavoriteMovieDao.kt │ │ │ │ └── FavoriteTvShowDao.kt │ │ ├── model │ │ │ ├── FavoriteMovie.kt │ │ │ ├── FavoriteTvShow.kt │ │ │ ├── Movie.kt │ │ │ └── TvShow.kt │ │ └── remote │ │ │ ├── ApiCallback.kt │ │ │ ├── ApiService.kt │ │ │ └── RemoteDataSource.kt │ │ └── util │ │ ├── AppExecutors.kt │ │ ├── Constant.kt │ │ ├── DiskIOThreadExecutor.kt │ │ ├── Helper.kt │ │ ├── Injection.kt │ │ ├── NullAdapter.kt │ │ ├── PagerHelper.kt │ │ └── SingleLiveEvent.kt │ └── res │ ├── drawable │ ├── background_card.xml │ ├── ic_arrow_back.xml │ ├── ic_close.xml │ ├── ic_favorite.xml │ ├── ic_movie.xml │ ├── ic_notification.xml │ ├── ic_search.xml │ ├── ic_settings.xml │ ├── ic_translate.xml │ ├── ic_tv.xml │ └── ic_un_favorite.xml │ ├── layout │ ├── activity_detail.xml │ ├── empty_view.xml │ ├── item_grid_tv_movie.xml │ └── item_list_tv_movie.xml │ ├── menu │ ├── bottom_nav.xml │ ├── toolbar_favorite.xml │ └── toolbar_main.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ └── strings.xml ├── build.gradle.kts ├── buildSrc ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── AdmobValue.kt │ ├── BuildConstant.kt │ ├── DependencyGradle.kt │ └── ProjectSetting.kt ├── docs └── image │ ├── mad_score.png │ ├── ss_1.png │ ├── ss_2.png │ ├── ss_3.png │ ├── ss_banner.png │ └── ss_play_store.jpg ├── favorite ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── frogobox │ │ └── favorite │ │ ├── mvvm │ │ ├── main │ │ │ └── MainActivity.kt │ │ ├── movie │ │ │ ├── DetailMovieActivity.kt │ │ │ ├── DetailMovieViewModel.kt │ │ │ ├── MovieFragment.kt │ │ │ └── MovieViewModel.kt │ │ └── tv │ │ │ ├── DetailTvShowActivity.kt │ │ │ ├── DetailTvShowViewModel.kt │ │ │ ├── TvShowFragment.kt │ │ │ └── TvShowViewModel.kt │ │ └── util │ │ ├── BaseFavoriteActivity.kt │ │ └── ViewModelFactory.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_main.xml │ └── fragment_tv_movie_list.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://saweria.co/amirisback 13 | -------------------------------------------------------------------------------- /.github/workflows/android-ci.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | env: 4 | # The name of the main module repository 5 | main_project_module: app 6 | 7 | # The name of the Play Store 8 | playstore_name: Frogobox ID 9 | 10 | on: 11 | # Triggers the workflow on push or pull request events but only for default and protected branches 12 | push: 13 | branches: [ master ] 14 | pull_request: 15 | branches: [ master ] 16 | 17 | workflow_dispatch: 18 | # The workflow will be dispatched to the default queue 19 | branches: [ master ] 20 | 21 | jobs: 22 | build: 23 | 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v1 28 | 29 | # Set Current Date As Env Variable 30 | - name: Set current date as env variable 31 | run: echo "date_today=$(date +'%Y-%m-%d')" >> $GITHUB_ENV 32 | 33 | # Set Repository Name As Env Variable 34 | - name: Set repository name as env variable 35 | run: echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV 36 | 37 | - name: Set Up JDK 38 | uses: actions/setup-java@v1 39 | with: 40 | java-version: 11 41 | 42 | - name: Change wrapper permissions 43 | run: chmod +x ./gradlew 44 | 45 | # Run Tests Build 46 | - name: Run gradle tests 47 | run: ./gradlew test 48 | 49 | # Run Build Project 50 | - name: Build gradle project 51 | run: ./gradlew build 52 | 53 | # Create APK Debug 54 | - name: Build apk debug project (APK) - ${{ env.main_project_module }} module 55 | run: ./gradlew assembleDebug 56 | 57 | # Create APK Release 58 | - name: Build apk release project (APK) - ${{ env.main_project_module }} module 59 | run: ./gradlew assemble 60 | 61 | # Create Bundle AAB Release 62 | # Noted for main module build [main_project_module]:bundleRelease 63 | - name: Build app bundle release (AAB) - ${{ env.main_project_module }} module 64 | run: ./gradlew ${{ env.main_project_module }}:bundleRelease 65 | 66 | # Upload Artifact Build 67 | # Noted For Output [main_project_module]/build/outputs/bundle/release/ 68 | - name: Upload AAB (App Bundle) Release - ${{ env.repository_name }} 69 | uses: actions/upload-artifact@v2 70 | with: 71 | name: ${{ env.date_today }} - ${{ env.playstore_name }} - ${{ env.repository_name }} - App bundle(s) AAB release generated 72 | path: ${{ env.main_project_module }}/build/outputs/bundle/release/ 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.gradle 3 | .gradle 4 | .idea 5 | /local.properties 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | .DS_Store 10 | build 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | 15 | built application files 16 | .apk 17 | .ap_ 18 | 19 | files for the dex VM 20 | *.dex 21 | 22 | Java class files 23 | *.class 24 | 25 | generated files 26 | bin/ 27 | gen/ 28 | .externalNativeBuild/ 29 | 30 | Local configuration file (sdk path, etc) 31 | local.properties 32 | app/version.properties 33 | 34 | # SonarQube 35 | .sonar/ 36 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/frogoboxmedia.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/frogoboxmedia.jks -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | 14 | 17 | 18 | 26 | 27 | 30 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 60 | 61 | 65 | 66 | 72 | 73 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/favorite/FavoriteFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.favorite 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import com.frogobox.base.BaseFragment 7 | import com.frogobox.base.util.PagerHelper 8 | 9 | import com.frogobox.movie.R 10 | import com.frogobox.movie.databinding.FragmentFavoriteBinding 11 | 12 | class FavoriteFragment : BaseFragment() { 13 | 14 | override fun setupViewBinding( 15 | inflater: LayoutInflater, 16 | container: ViewGroup 17 | ): FragmentFavoriteBinding { 18 | return FragmentFavoriteBinding.inflate(inflater, container, false) 19 | } 20 | 21 | override fun setupViewModel() { 22 | } 23 | 24 | override fun setupUI(savedInstanceState: Bundle?) { 25 | mActivity.title = getString(R.string.title_favorite) 26 | setupViewPager() 27 | } 28 | 29 | private fun setupViewPager(){ 30 | val pagerAdapter = PagerHelper(childFragmentManager) 31 | pagerAdapter.setupPagerFragment(FavoriteMovieFragment(), resources.getString(R.string.title_favorite_movie)) 32 | pagerAdapter.setupPagerFragment(FavoriteTvShowFragment(), resources.getString(R.string.title_favorite_tv_show)) 33 | binding?.apply { 34 | viewpager.adapter = pagerAdapter 35 | tablayout.setupWithViewPager(viewpager) 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/favorite/FavoriteMovieFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.favorite 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.fragment.app.Fragment 7 | import androidx.lifecycle.Observer 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import com.frogobox.base.source.model.FavoriteMovie 10 | import com.frogobox.base.BaseFragment 11 | import com.frogobox.base.BaseListener 12 | import com.frogobox.movie.R 13 | import com.frogobox.movie.databinding.FragmentTvMovieListBinding 14 | import com.frogobox.movie.mvvm.movie.DetailMovieActivity 15 | import com.frogobox.movie.mvvm.main.MainActivity 16 | 17 | /** 18 | * A simple [Fragment] subclass. 19 | */ 20 | class FavoriteMovieFragment : BaseFragment(), 21 | BaseListener { 22 | 23 | private lateinit var mViewModel: FavoriteMovieViewModel 24 | 25 | override fun setupViewBinding( 26 | inflater: LayoutInflater, 27 | container: ViewGroup 28 | ): FragmentTvMovieListBinding { 29 | return FragmentTvMovieListBinding.inflate(inflater, container, false) 30 | } 31 | 32 | override fun setupViewModel() { 33 | mViewModel = (activity as MainActivity).obtainFavoriteMovieViewModel().apply { 34 | 35 | favMovieListLive.observe(viewLifecycleOwner, Observer { 36 | setupRecyclerView(it) 37 | }) 38 | 39 | eventShowProgress.observe(viewLifecycleOwner, Observer { 40 | setupEventProgressView(binding?.progressBar!!, it) 41 | }) 42 | 43 | eventIsEmpty.observe(viewLifecycleOwner, Observer { 44 | binding?.empty?.emptyView?.let { it1 -> setupEventEmptyView(it1, it) } 45 | }) 46 | 47 | } 48 | } 49 | 50 | override fun setupUI(savedInstanceState: Bundle?) { 51 | getMovie() 52 | } 53 | 54 | override fun onResume() { 55 | super.onResume() 56 | getMovie() 57 | } 58 | 59 | private fun getMovie() { 60 | mViewModel.getFavoriteMovie() 61 | } 62 | 63 | private fun setupRecyclerView(data: List) { 64 | val adapter = com.frogobox.base.adapter.FavoriteMovieAdapter() 65 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_list_tv_movie) } 66 | adapter.setRecyclerViewListener(this) 67 | adapter.setRecyclerViewData(data) 68 | binding?.apply { 69 | recyclerView.adapter = adapter 70 | recyclerView.layoutManager = 71 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 72 | } 73 | } 74 | 75 | override fun onItemClicked(data: FavoriteMovie) { 76 | context?.let { 77 | baseStartActivity( 78 | it, 79 | DetailMovieActivity.EXTRA_FAV_MOVIE, 80 | data 81 | ) 82 | } 83 | } 84 | 85 | override fun onItemLongClicked(data: FavoriteMovie) {} 86 | 87 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/favorite/FavoriteMovieViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.favorite 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.FavoriteMovie 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.SingleLiveEvent 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.movie.viewmodel 25 | * 26 | */ 27 | class FavoriteMovieViewModel(private val context: Application, private val repository: Repository) : 28 | BaseViewModel(context) { 29 | 30 | var favMovieListLive = SingleLiveEvent>() 31 | 32 | fun getFavoriteMovie() { 33 | repository.getFavoriteMovies(object : 34 | DataSource.GetLocalCallBack> { 35 | override fun onShowProgressDialog() { 36 | eventShowProgress.postValue(true) 37 | } 38 | 39 | override fun onHideProgressDialog() { 40 | eventShowProgress.postValue(false) 41 | } 42 | 43 | override fun onSuccess(data: List) { 44 | eventIsEmpty.postValue(false) 45 | favMovieListLive.postValue(data) 46 | } 47 | 48 | override fun onFinish() { 49 | 50 | } 51 | 52 | override fun onEmpty() { 53 | eventIsEmpty.postValue(true) 54 | } 55 | 56 | override fun onFailed(statusCode: Int, errorMessage: String?) { 57 | 58 | } 59 | }) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/favorite/FavoriteTvShowFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.favorite 2 | 3 | 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.lifecycle.Observer 9 | import androidx.recyclerview.widget.LinearLayoutManager 10 | import com.frogobox.base.source.model.FavoriteTvShow 11 | import com.frogobox.base.adapter.FavoriteTvShowAdapter 12 | import com.frogobox.base.BaseFragment 13 | import com.frogobox.base.BaseListener 14 | import com.frogobox.movie.R 15 | import com.frogobox.movie.databinding.FragmentTvMovieListBinding 16 | import com.frogobox.movie.mvvm.tv.DetailTvShowActivity 17 | import com.frogobox.movie.mvvm.main.MainActivity 18 | 19 | /** 20 | * A simple [Fragment] subclass. 21 | */ 22 | class FavoriteTvShowFragment : BaseFragment(), 23 | BaseListener { 24 | 25 | private lateinit var mViewModel: FavoriteTvShowViewModel 26 | 27 | override fun setupViewBinding( 28 | inflater: LayoutInflater, 29 | container: ViewGroup 30 | ): FragmentTvMovieListBinding { 31 | return FragmentTvMovieListBinding.inflate(inflater, container, false) 32 | } 33 | 34 | override fun setupViewModel() { 35 | mViewModel = (activity as MainActivity).obtainFavoriteTvShowViewModel().apply { 36 | 37 | favTvShowListLive.observe(viewLifecycleOwner, Observer { 38 | setupRecyclerView(it) 39 | }) 40 | 41 | eventShowProgress.observe(viewLifecycleOwner, Observer { 42 | setupEventProgressView(binding?.progressBar!!, it) 43 | }) 44 | 45 | eventIsEmpty.observe(viewLifecycleOwner, Observer { 46 | binding?.empty?.emptyView?.let { it1 -> setupEventEmptyView(it1, it) } 47 | }) 48 | 49 | } 50 | } 51 | 52 | override fun setupUI(savedInstanceState: Bundle?) { 53 | getTvShow() 54 | } 55 | 56 | private fun getTvShow() { 57 | mViewModel.getFavoriteTvShow() 58 | } 59 | 60 | override fun onResume() { 61 | super.onResume() 62 | getTvShow() 63 | } 64 | 65 | private fun setupRecyclerView(data: List) { 66 | val adapter = FavoriteTvShowAdapter() 67 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_list_tv_movie) } 68 | adapter.setRecyclerViewListener(this) 69 | adapter.setRecyclerViewData(data) 70 | binding?.apply { 71 | recyclerView.adapter = adapter 72 | recyclerView.layoutManager = 73 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 74 | } 75 | } 76 | 77 | override fun onItemClicked(data: FavoriteTvShow) { 78 | context?.let { 79 | baseStartActivity( 80 | it, 81 | DetailTvShowActivity.EXTRA_FAV_TV, 82 | data 83 | ) 84 | } 85 | } 86 | 87 | override fun onItemLongClicked(data: FavoriteTvShow) {} 88 | 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/favorite/FavoriteTvShowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.favorite 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.FavoriteTvShow 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.SingleLiveEvent 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.movie.viewmodel 25 | * 26 | */ 27 | class FavoriteTvShowViewModel(private val context: Application, private val repository: Repository) : 28 | BaseViewModel(context){ 29 | 30 | var favTvShowListLive = SingleLiveEvent>() 31 | 32 | fun getFavoriteTvShow(){ 33 | repository.getFavoriteTvShows(object : DataSource.GetLocalCallBack>{ 34 | override fun onShowProgressDialog() { 35 | eventShowProgress.postValue(true) 36 | } 37 | 38 | override fun onHideProgressDialog() { 39 | eventShowProgress.postValue(false) 40 | } 41 | 42 | override fun onSuccess(data: List) { 43 | eventIsEmpty.postValue(false) 44 | favTvShowListLive.postValue(data) 45 | } 46 | 47 | override fun onFinish() { 48 | 49 | } 50 | 51 | override fun onEmpty() { 52 | eventIsEmpty.postValue(true) 53 | } 54 | 55 | override fun onFailed(statusCode: Int, errorMessage: String?) { 56 | 57 | } 58 | }) 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/main/MainActivityViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.main 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.Movie 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.SingleLiveEvent 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.movie.viewmodel 25 | * 26 | */ 27 | class MainActivityViewModel(private val context: Application, private val repository: Repository) : 28 | BaseViewModel(context) { 29 | 30 | var eventStateReleaseReminder = SingleLiveEvent() 31 | var eventStateDailyReminder = SingleLiveEvent() 32 | var reminderLive = SingleLiveEvent>() 33 | 34 | fun getReleaseReminder(dateGte: String, dateLte: String){ 35 | repository.reminderReleaseMovie(dateGte, dateLte, object : DataSource.GetRemoteCallback>{ 36 | override fun onEmpty() {} 37 | 38 | override fun onShowProgressDialog() { 39 | eventShowProgress.postValue(true) 40 | } 41 | 42 | override fun onHideProgressDialog() { 43 | eventShowProgress.postValue(false) 44 | } 45 | 46 | override fun onSuccess(data: List) { 47 | reminderLive.postValue(data) 48 | } 49 | 50 | override fun onFinish() {} 51 | 52 | override fun onFailed(statusCode: Int, errorMessage: String?) {} 53 | }) 54 | } 55 | 56 | fun getOprionReminder() { 57 | eventStateReleaseReminder.postValue(repository.getPrefReleaseReminder()) 58 | eventStateDailyReminder.postValue(repository.getPrefDailyReminder()) 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/main/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.main 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.Movie 6 | import com.frogobox.base.source.model.TvShow 7 | import com.frogobox.base.source.DataSource 8 | import com.frogobox.base.source.Repository 9 | import com.frogobox.base.util.SingleLiveEvent 10 | 11 | /** 12 | * Created by Faisal Amir 13 | * FrogoBox Inc License 14 | * ========================================= 15 | * movie 16 | * Copyright (C) 16/11/2019. 17 | * All rights reserved 18 | * ----------------------------------------- 19 | * Name : Muhammad Faisal Amir 20 | * E-mail : faisalamircs@gmail.com 21 | * Github : github.com/amirisback 22 | * LinkedIn : linkedin.com/in/faisalamircs 23 | * ----------------------------------------- 24 | * FrogoBox Software Industries 25 | * com.frogobox.movie.viewmodel 26 | * 27 | */ 28 | class MainViewModel(private val context: Application, private val repository: Repository) : 29 | BaseViewModel(context) { 30 | 31 | var movieListLive = SingleLiveEvent>() 32 | var tvShowListLive = SingleLiveEvent>() 33 | 34 | fun getMovie() { 35 | repository.getMovies(object : DataSource.GetRemoteCallback> { 36 | override fun onEmpty() { 37 | 38 | } 39 | 40 | override fun onShowProgressDialog() { 41 | eventShowProgress.postValue(true) 42 | } 43 | 44 | override fun onHideProgressDialog() { 45 | eventShowProgress.postValue(false) 46 | } 47 | 48 | override fun onSuccess(data: List) { 49 | movieListLive.postValue(data) 50 | } 51 | 52 | override fun onFinish() { 53 | 54 | } 55 | 56 | override fun onFailed(statusCode: Int, errorMessage: String?) { 57 | 58 | } 59 | }) 60 | } 61 | 62 | fun getTvShow() { 63 | repository.getTvShow(object : DataSource.GetRemoteCallback> { 64 | override fun onEmpty() { 65 | 66 | } 67 | 68 | override fun onShowProgressDialog() { 69 | eventShowProgress.postValue(true) 70 | } 71 | 72 | override fun onHideProgressDialog() { 73 | eventShowProgress.postValue(false) 74 | } 75 | 76 | override fun onSuccess(data: List) { 77 | tvShowListLive.postValue(data) 78 | } 79 | 80 | override fun onFinish() { 81 | 82 | } 83 | 84 | override fun onFailed(statusCode: Int, errorMessage: String?) { 85 | 86 | } 87 | }) 88 | 89 | } 90 | 91 | 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/main/SettingViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.main 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.Repository 6 | import com.frogobox.base.util.SingleLiveEvent 7 | 8 | /** 9 | * Created by Faisal Amir 10 | * FrogoBox Inc License 11 | * ========================================= 12 | * movie 13 | * Copyright (C) 16/11/2019. 14 | * All rights reserved 15 | * ----------------------------------------- 16 | * Name : Muhammad Faisal Amir 17 | * E-mail : faisalamircs@gmail.com 18 | * Github : github.com/amirisback 19 | * LinkedIn : linkedin.com/in/faisalamircs 20 | * ----------------------------------------- 21 | * FrogoBox Software Industries 22 | * com.frogobox.movie.viewmodel 23 | * 24 | */ 25 | class SettingViewModel(private val context: Application, private val repository: Repository) : 26 | BaseViewModel(context) { 27 | 28 | var eventStateReleaseReminder = SingleLiveEvent() 29 | var eventStateDailyReminder = SingleLiveEvent() 30 | 31 | fun savePrefReleaseReminder(state: Boolean) { 32 | repository.savePrefReleaseReminder(state) 33 | } 34 | 35 | fun savePrefDailyReminder(state: Boolean) { 36 | repository.savePrefDailyReminder(state) 37 | } 38 | 39 | fun deletePrefReleaseReminder() { 40 | repository.deletePrefReleaseReminder() 41 | } 42 | 43 | fun deletePrefDailyReminder() { 44 | repository.deletePrefDailyReminder() 45 | } 46 | 47 | fun getPref() { 48 | eventStateReleaseReminder.postValue(repository.getPrefReleaseReminder()) 49 | eventStateDailyReminder.postValue(repository.getPrefDailyReminder()) 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/movie/DetailMovieViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.movie 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.callback.DeleteViewCallback 6 | import com.frogobox.base.callback.SaveViewCallback 7 | import com.frogobox.base.source.model.FavoriteMovie 8 | import com.frogobox.base.source.DataSource 9 | import com.frogobox.base.source.Repository 10 | import com.frogobox.base.util.SingleLiveEvent 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.movie.viewmodel 27 | * 28 | */ 29 | class DetailMovieViewModel(private val context: Application, private val repository: Repository) : 30 | BaseViewModel(context) { 31 | 32 | var favoriteMovie = SingleLiveEvent() 33 | var eventIsFavorite = SingleLiveEvent() 34 | 35 | fun saveFavoriteMovie( 36 | data: FavoriteMovie, 37 | callback: com.frogobox.base.callback.SaveViewCallback 38 | ) { 39 | callback.onShowProgress() 40 | if (repository.saveFavoriteMovie(context, data)) { 41 | callback.onHideProgress() 42 | callback.onSuccesInsert() 43 | eventIsEmpty.postValue(false) 44 | eventIsFavorite.postValue(true) 45 | } else { 46 | callback.onHideProgress() 47 | callback.onFailed("Failed") 48 | } 49 | } 50 | 51 | fun deleteFavoriteMovie(tableId: Int, callback: com.frogobox.base.callback.DeleteViewCallback) { 52 | callback.onShowProgress() 53 | if (repository.deleteFavoriteMovie(tableId)) { 54 | callback.onHideProgress() 55 | callback.onSuccesDelete() 56 | eventIsEmpty.postValue(true) 57 | eventIsFavorite.postValue(false) 58 | } else { 59 | callback.onHideProgress() 60 | callback.onFailed("Failed") 61 | } 62 | } 63 | 64 | fun getFavoriteMovie(id: Int) { 65 | repository.getFavoriteMovies(object : 66 | DataSource.GetLocalCallBack> { 67 | override fun onShowProgressDialog() { 68 | eventShowProgress.postValue(true) 69 | } 70 | 71 | override fun onHideProgressDialog() { 72 | eventShowProgress.postValue(false) 73 | } 74 | 75 | override fun onSuccess(data: List) { 76 | 77 | val tempFavoriteList = mutableListOf() 78 | tempFavoriteList.clear() 79 | tempFavoriteList.addAll(data) 80 | 81 | for (i in tempFavoriteList.indices) { 82 | if (tempFavoriteList[i].id!! == id) { 83 | eventIsEmpty.postValue(false) 84 | eventIsFavorite.postValue(true) 85 | favoriteMovie.postValue(tempFavoriteList[i]) 86 | break 87 | } else { 88 | eventIsFavorite.postValue(false) 89 | eventIsEmpty.postValue(true) 90 | } 91 | } 92 | } 93 | 94 | override fun onFinish() {} 95 | 96 | override fun onEmpty() { 97 | eventIsEmpty.postValue(true) 98 | eventIsFavorite.postValue(false) 99 | } 100 | 101 | override fun onFailed(statusCode: Int, errorMessage: String?) {} 102 | }) 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/movie/MovieFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.movie 2 | 3 | 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.lifecycle.Observer 9 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 10 | import com.frogobox.base.source.model.Movie 11 | import com.frogobox.base.adapter.MovieAdapter 12 | import com.frogobox.base.BaseFragment 13 | import com.frogobox.base.BaseListener 14 | import com.frogobox.movie.R 15 | import com.frogobox.movie.databinding.FragmentTvMovieGridBinding 16 | import com.frogobox.movie.mvvm.main.MainActivity 17 | import com.frogobox.movie.mvvm.main.MainViewModel 18 | 19 | /** 20 | * A simple [Fragment] subclass. 21 | */ 22 | class MovieFragment : BaseFragment(), 23 | BaseListener { 24 | 25 | private lateinit var mViewModel: MainViewModel 26 | 27 | override fun setupViewBinding( 28 | inflater: LayoutInflater, 29 | container: ViewGroup 30 | ): FragmentTvMovieGridBinding { 31 | return FragmentTvMovieGridBinding.inflate(inflater, container, false) 32 | } 33 | 34 | override fun setupViewModel() { 35 | mViewModel = (activity as MainActivity).obtainMainViewModel().apply { 36 | 37 | movieListLive.observe(viewLifecycleOwner, Observer { 38 | setupRecyclerView(it) 39 | }) 40 | 41 | eventShowProgress.observe(viewLifecycleOwner, Observer { 42 | setupEventProgressView(binding?.progressBar!!, it) 43 | }) 44 | 45 | } 46 | } 47 | 48 | override fun setupUI(savedInstanceState: Bundle?) { 49 | getMovies() 50 | } 51 | 52 | private fun getMovies() { 53 | mActivity.setTitle(getString(R.string.title_movie)) 54 | mViewModel.getMovie() 55 | } 56 | 57 | private fun setupRecyclerView(data: List) { 58 | val adapter = MovieAdapter() 59 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_grid_tv_movie) } 60 | adapter.setRecyclerViewListener(this) 61 | adapter.setRecyclerViewData(data) 62 | binding?.apply { 63 | recyclerView.adapter = adapter 64 | recyclerView.layoutManager = 65 | StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) 66 | } 67 | } 68 | 69 | override fun onItemClicked(data: Movie) { 70 | context?.let { 71 | baseStartActivity( 72 | it, 73 | DetailMovieActivity.EXTRA_MOVIE, 74 | data 75 | ) 76 | } 77 | } 78 | 79 | override fun onItemLongClicked(data: Movie) {} 80 | 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/movie/SearchMovieActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.movie 2 | 3 | import android.os.Bundle 4 | import android.text.Editable 5 | import android.text.TextWatcher 6 | import androidx.lifecycle.Observer 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import com.frogobox.base.source.model.Movie 9 | import com.frogobox.base.adapter.MovieAdapter 10 | import com.frogobox.base.BaseListener 11 | import com.frogobox.movie.R 12 | import com.frogobox.movie.databinding.ActivitySearchBinding 13 | import com.frogobox.movie.util.BaseAppActivity 14 | 15 | class SearchMovieActivity : BaseAppActivity(), 16 | BaseListener { 17 | 18 | private lateinit var mViewModel: SearchMovieViewModel 19 | private lateinit var adapter: MovieAdapter 20 | 21 | override fun setupViewBinding(): ActivitySearchBinding { 22 | return ActivitySearchBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun setupViewModel() { 26 | adapter = MovieAdapter() 27 | 28 | mViewModel = obtainSearchMovieViewModel().apply { 29 | 30 | movieListLive.observe(this@SearchMovieActivity, Observer { 31 | setupRecyclerView(it) 32 | }) 33 | 34 | eventShowProgress.observe(this@SearchMovieActivity, Observer { 35 | setupEventProgressView(binding.progressBar, it) 36 | }) 37 | 38 | } 39 | } 40 | 41 | override fun setupUI(savedInstanceState: Bundle?) { 42 | setupViewElement() 43 | } 44 | 45 | private fun obtainSearchMovieViewModel(): SearchMovieViewModel = 46 | obtainViewModel(SearchMovieViewModel::class.java) 47 | 48 | private fun searchMovie(query: String) { 49 | mViewModel.searchMovies(query) 50 | } 51 | 52 | private fun setupViewElement() { 53 | 54 | binding.toolbar.apply { 55 | etSearch.addTextChangedListener(object : TextWatcher { 56 | override fun afterTextChanged(p0: Editable?) {} 57 | override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 58 | override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { 59 | val query = etSearch.text.toString() 60 | if (!query.equals("")) { 61 | searchMovie(query) 62 | } else { 63 | adapter.nukeRecyclerViewData() 64 | } 65 | } 66 | }) 67 | 68 | ivBack.setOnClickListener { 69 | finish() 70 | } 71 | 72 | ivClose.setOnClickListener { 73 | etSearch.text.clear() 74 | } 75 | } 76 | } 77 | 78 | private fun setupRecyclerView(data: List) { 79 | adapter.setRecyclerViewLayout(this, R.layout.item_list_tv_movie) 80 | adapter.setRecyclerViewListener(this) 81 | adapter.setRecyclerViewData(data) 82 | binding.apply { 83 | recyclerView.adapter = adapter 84 | recyclerView.layoutManager = 85 | LinearLayoutManager(this@SearchMovieActivity, LinearLayoutManager.VERTICAL, false) 86 | } 87 | } 88 | 89 | override fun onItemClicked(data: Movie) { 90 | baseStartActivity( 91 | this, 92 | DetailMovieActivity.EXTRA_MOVIE, data 93 | ) 94 | } 95 | 96 | override fun onItemLongClicked(data: Movie) {} 97 | 98 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/movie/SearchMovieViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.movie 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.Movie 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.SingleLiveEvent 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.movie.viewmodel 25 | * 26 | */ 27 | class SearchMovieViewModel(private val context: Application, private val repository: Repository) : 28 | BaseViewModel(context) { 29 | 30 | var movieListLive = SingleLiveEvent>() 31 | 32 | fun searchMovies(query: String) { 33 | repository.searchMovies(query, object : DataSource.GetRemoteCallback> { 34 | override fun onEmpty() { 35 | 36 | } 37 | 38 | override fun onShowProgressDialog() { 39 | eventShowProgress.postValue(true) 40 | } 41 | 42 | override fun onHideProgressDialog() { 43 | eventShowProgress.postValue(false) 44 | } 45 | 46 | override fun onSuccess(data: List) { 47 | movieListLive.postValue(data) 48 | } 49 | 50 | override fun onFinish() { 51 | 52 | } 53 | 54 | override fun onFailed(statusCode: Int, errorMessage: String?) { 55 | 56 | } 57 | }) 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/tv/DetailTvShowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.tv 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.callback.DeleteViewCallback 6 | import com.frogobox.base.callback.SaveViewCallback 7 | import com.frogobox.base.source.model.FavoriteTvShow 8 | import com.frogobox.base.source.DataSource 9 | import com.frogobox.base.source.Repository 10 | import com.frogobox.base.util.SingleLiveEvent 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.movie.viewmodel 27 | * 28 | */ 29 | class DetailTvShowViewModel(private val context: Application, private val repository: Repository) : 30 | BaseViewModel(context) { 31 | 32 | var favoriteTvShow = SingleLiveEvent() 33 | var eventIsFavorite = SingleLiveEvent() 34 | 35 | fun saveFavoriteTvShow(data: FavoriteTvShow, callback: com.frogobox.base.callback.SaveViewCallback){ 36 | callback.onShowProgress() 37 | if (repository.saveFavoriteTvShow(context, data)){ 38 | callback.onHideProgress() 39 | callback.onSuccesInsert() 40 | eventIsFavorite.postValue(true) 41 | } else { 42 | callback.onHideProgress() 43 | callback.onFailed("Failed") 44 | } 45 | } 46 | 47 | fun deleteFavoriteTvShow(tableId: Int, callback: com.frogobox.base.callback.DeleteViewCallback){ 48 | callback.onShowProgress() 49 | if (repository.deleteFavoriteTvShow(tableId)){ 50 | callback.onHideProgress() 51 | callback.onSuccesDelete() 52 | eventIsFavorite.postValue(false) 53 | } else { 54 | callback.onHideProgress() 55 | callback.onFailed("Failed") 56 | } 57 | } 58 | 59 | fun getFavoriteTvShow(id: Int){ 60 | repository.getFavoriteTvShows(object : DataSource.GetLocalCallBack>{ 61 | override fun onShowProgressDialog() { 62 | eventShowProgress.postValue(true) 63 | } 64 | 65 | override fun onHideProgressDialog() { 66 | eventShowProgress.postValue(false) 67 | } 68 | 69 | override fun onSuccess(data: List) { 70 | 71 | val tempFavoriteList = mutableListOf() 72 | tempFavoriteList.clear() 73 | tempFavoriteList.addAll(data) 74 | 75 | for (i in tempFavoriteList.indices) { 76 | if (tempFavoriteList[i].id!! == id) { 77 | eventIsEmpty.postValue(false) 78 | eventIsFavorite.postValue(true) 79 | favoriteTvShow.postValue(tempFavoriteList[i]) 80 | break 81 | } else { 82 | eventIsEmpty.postValue(true) 83 | eventIsFavorite.postValue(false) 84 | } 85 | } 86 | } 87 | 88 | override fun onFinish() {} 89 | 90 | override fun onEmpty() { 91 | eventIsEmpty.postValue(true) 92 | eventIsFavorite.postValue(false) 93 | } 94 | 95 | override fun onFailed(statusCode: Int, errorMessage: String?) {} 96 | }) 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/tv/SearchTvShowActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.tv 2 | 3 | import android.os.Bundle 4 | import android.text.Editable 5 | import android.text.TextWatcher 6 | import androidx.lifecycle.Observer 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import com.frogobox.base.source.model.TvShow 9 | import com.frogobox.base.adapter.TvShowAdapter 10 | import com.frogobox.base.BaseListener 11 | import com.frogobox.movie.R 12 | import com.frogobox.movie.databinding.ActivitySearchBinding 13 | import com.frogobox.movie.util.BaseAppActivity 14 | 15 | class SearchTvShowActivity : BaseAppActivity(), 16 | BaseListener { 17 | 18 | private lateinit var mViewModel: SearchTvShowViewModel 19 | private lateinit var adapter: TvShowAdapter 20 | 21 | override fun setupViewBinding(): ActivitySearchBinding { 22 | return ActivitySearchBinding.inflate(layoutInflater) 23 | } 24 | 25 | override fun setupViewModel() { 26 | adapter = TvShowAdapter() 27 | 28 | mViewModel = obtainSearchTvShowViewModel().apply { 29 | 30 | tvShowListLive.observe(this@SearchTvShowActivity, Observer { 31 | setupRecyclerView(it) 32 | }) 33 | 34 | eventShowProgress.observe(this@SearchTvShowActivity, Observer { 35 | setupEventProgressView(binding.progressBar, it) 36 | }) 37 | 38 | } 39 | } 40 | 41 | 42 | override fun setupUI(savedInstanceState: Bundle?) { 43 | setupViewElement() 44 | } 45 | 46 | fun obtainSearchTvShowViewModel(): SearchTvShowViewModel = 47 | obtainViewModel(SearchTvShowViewModel::class.java) 48 | 49 | private fun searchTvShow(query: String) { 50 | mViewModel.searchTvShow(query) 51 | } 52 | 53 | private fun setupViewElement() { 54 | binding.toolbar.apply { 55 | etSearch.addTextChangedListener(object : TextWatcher { 56 | override fun afterTextChanged(p0: Editable?) {} 57 | override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 58 | override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { 59 | val query = etSearch.text.toString() 60 | if (!query.equals("")) { 61 | searchTvShow(query) 62 | } else { 63 | adapter.nukeRecyclerViewData() 64 | } 65 | } 66 | }) 67 | 68 | ivBack.setOnClickListener { 69 | finish() 70 | } 71 | 72 | ivClose.setOnClickListener { 73 | etSearch.text.clear() 74 | } 75 | } 76 | } 77 | 78 | private fun setupRecyclerView(data: List) { 79 | adapter.setRecyclerViewLayout(this, R.layout.item_list_tv_movie) 80 | adapter.setRecyclerViewListener(this) 81 | adapter.setRecyclerViewData(data) 82 | binding.apply { 83 | recyclerView.adapter = adapter 84 | recyclerView.layoutManager = 85 | LinearLayoutManager(this@SearchTvShowActivity, LinearLayoutManager.VERTICAL, false) 86 | } 87 | } 88 | 89 | override fun onItemClicked(data: TvShow) { 90 | baseStartActivity( 91 | this, 92 | DetailTvShowActivity.EXTRA_TV, data 93 | ) 94 | } 95 | 96 | override fun onItemLongClicked(data: TvShow) {} 97 | 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/tv/SearchTvShowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.tv 2 | 3 | import android.app.Application 4 | import com.frogobox.base.BaseViewModel 5 | import com.frogobox.base.source.model.TvShow 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.SingleLiveEvent 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.movie.viewmodel 25 | * 26 | */ 27 | class SearchTvShowViewModel (private val context: Application, private val repository: Repository) : 28 | BaseViewModel(context) { 29 | 30 | var tvShowListLive = SingleLiveEvent>() 31 | 32 | fun searchTvShow(query: String){ 33 | repository.searchTvShow(query, object : DataSource.GetRemoteCallback>{ 34 | override fun onEmpty() { 35 | 36 | } 37 | 38 | override fun onShowProgressDialog() { 39 | eventShowProgress.postValue(true) 40 | } 41 | 42 | override fun onHideProgressDialog() { 43 | eventShowProgress.postValue(false) 44 | } 45 | 46 | override fun onSuccess(data: List) { 47 | tvShowListLive.postValue(data) 48 | } 49 | 50 | override fun onFinish() { 51 | 52 | } 53 | 54 | override fun onFailed(statusCode: Int, errorMessage: String?) { 55 | 56 | } 57 | }) 58 | 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/mvvm/tv/TvShowFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.mvvm.tv 2 | 3 | 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.lifecycle.Observer 9 | import androidx.recyclerview.widget.LinearLayoutManager 10 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 11 | import com.frogobox.base.source.model.TvShow 12 | import com.frogobox.base.adapter.TvShowAdapter 13 | import com.frogobox.base.BaseFragment 14 | import com.frogobox.base.BaseListener 15 | import com.frogobox.movie.R 16 | import com.frogobox.movie.databinding.FragmentTvMovieGridBinding 17 | import com.frogobox.movie.mvvm.main.MainActivity 18 | import com.frogobox.movie.mvvm.main.MainViewModel 19 | 20 | /** 21 | * A simple [Fragment] subclass. 22 | */ 23 | class TvShowFragment : BaseFragment(), 24 | BaseListener { 25 | 26 | private lateinit var mViewModel: MainViewModel 27 | 28 | override fun setupViewBinding( 29 | inflater: LayoutInflater, 30 | container: ViewGroup 31 | ): FragmentTvMovieGridBinding { 32 | return FragmentTvMovieGridBinding.inflate(inflater, container, false) 33 | } 34 | 35 | override fun setupViewModel() { 36 | mViewModel = (activity as MainActivity).obtainMainViewModel().apply { 37 | 38 | tvShowListLive.observe(viewLifecycleOwner, Observer { 39 | setupRecyclerView(it) 40 | }) 41 | 42 | eventShowProgress.observe(viewLifecycleOwner, Observer { 43 | setupEventProgressView(binding?.progressBar!!, it) 44 | }) 45 | 46 | } 47 | } 48 | 49 | override fun setupUI(savedInstanceState: Bundle?) { 50 | getTvShow() 51 | } 52 | 53 | private fun getTvShow() { 54 | mActivity.title = getString(R.string.title_tv) 55 | mViewModel.getTvShow() 56 | } 57 | 58 | private fun setupRecyclerView(data: List) { 59 | val adapter = TvShowAdapter() 60 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_grid_tv_movie) } 61 | adapter.setRecyclerViewListener(this) 62 | adapter.setRecyclerViewData(data) 63 | binding?.apply { 64 | recyclerView.adapter = adapter 65 | recyclerView.layoutManager = 66 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 67 | recyclerView.layoutManager = 68 | StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) 69 | } 70 | } 71 | 72 | override fun onItemClicked(data: TvShow) { 73 | context?.let { 74 | baseStartActivity( 75 | it, 76 | DetailTvShowActivity.EXTRA_TV, 77 | data 78 | ) 79 | } 80 | } 81 | 82 | override fun onItemLongClicked(data: TvShow) {} 83 | 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/util/BaseAppActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.util 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import androidx.viewbinding.ViewBinding 6 | import com.frogobox.base.BaseActivity 7 | 8 | /** 9 | * Created by Faisal Amir 10 | * FrogoBox Inc License 11 | * ========================================= 12 | * movie 13 | * Copyright (C) 16/11/2019. 14 | * All rights reserved 15 | * ----------------------------------------- 16 | * Name : Muhammad Faisal Amir 17 | * E-mail : faisalamircs@gmail.com 18 | * Github : github.com/amirisback 19 | * LinkedIn : linkedin.com/in/faisalamircs 20 | * ----------------------------------------- 21 | * FrogoBox Software Industries 22 | * com.frogobox.movie.util 23 | * 24 | */ 25 | abstract class BaseAppActivity : BaseActivity() { 26 | 27 | fun obtainViewModel(viewModelClass: Class) = 28 | ViewModelProvider(this, ViewModelFactory.getInstance(application)).get(viewModelClass) 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/util/ViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.util 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.ViewModelProvider 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.Injection 9 | import com.frogobox.movie.mvvm.favorite.FavoriteMovieViewModel 10 | import com.frogobox.movie.mvvm.favorite.FavoriteTvShowViewModel 11 | import com.frogobox.movie.mvvm.main.MainActivityViewModel 12 | import com.frogobox.movie.mvvm.main.MainViewModel 13 | import com.frogobox.movie.mvvm.main.SettingViewModel 14 | import com.frogobox.movie.mvvm.movie.DetailMovieViewModel 15 | import com.frogobox.movie.mvvm.movie.SearchMovieViewModel 16 | import com.frogobox.movie.mvvm.tv.DetailTvShowViewModel 17 | import com.frogobox.movie.mvvm.tv.SearchTvShowViewModel 18 | 19 | /** 20 | * Created by Faisal Amir 21 | * FrogoBox Inc License 22 | * ========================================= 23 | * movie 24 | * Copyright (C) 16/11/2019. 25 | * All rights reserved 26 | * ----------------------------------------- 27 | * Name : Muhammad Faisal Amir 28 | * E-mail : faisalamircs@gmail.com 29 | * Github : github.com/amirisback 30 | * LinkedIn : linkedin.com/in/faisalamircs 31 | * ----------------------------------------- 32 | * FrogoBox Software Industries 33 | * com.frogobox.movie.util 34 | * 35 | */ 36 | class ViewModelFactory private constructor( 37 | private val mApplication: Application, 38 | private val repository: Repository 39 | ) : ViewModelProvider.NewInstanceFactory() { 40 | 41 | @Suppress("UNCHECKED_CAST") 42 | override fun create(modelClass: Class): T = 43 | with(modelClass) { 44 | when { 45 | 46 | isAssignableFrom(MainActivityViewModel::class.java) -> 47 | MainActivityViewModel(mApplication, repository) 48 | 49 | isAssignableFrom(MainViewModel::class.java) -> 50 | MainViewModel(mApplication, repository) 51 | 52 | isAssignableFrom(DetailMovieViewModel::class.java) -> 53 | DetailMovieViewModel(mApplication, repository) 54 | 55 | isAssignableFrom(DetailTvShowViewModel::class.java) -> 56 | DetailTvShowViewModel(mApplication, repository) 57 | 58 | isAssignableFrom(FavoriteMovieViewModel::class.java) -> 59 | FavoriteMovieViewModel(mApplication, repository) 60 | 61 | isAssignableFrom(FavoriteTvShowViewModel::class.java) -> 62 | FavoriteTvShowViewModel(mApplication, repository) 63 | 64 | isAssignableFrom(SearchMovieViewModel::class.java) -> 65 | SearchMovieViewModel(mApplication, repository) 66 | 67 | isAssignableFrom(SearchTvShowViewModel::class.java) -> 68 | SearchTvShowViewModel(mApplication, repository) 69 | 70 | isAssignableFrom(SettingViewModel::class.java) -> 71 | SettingViewModel(mApplication, repository) 72 | 73 | else -> 74 | throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") 75 | } 76 | } as T 77 | 78 | companion object { 79 | 80 | @SuppressLint("StaticFieldLeak") 81 | @Volatile private var INSTANCE: ViewModelFactory? = null 82 | 83 | fun getInstance(mApplication: Application) = 84 | INSTANCE 85 | ?: synchronized(ViewModelFactory::class.java) { 86 | INSTANCE 87 | ?: ViewModelFactory( 88 | mApplication, 89 | Injection.provideRepository(mApplication.applicationContext) 90 | ) 91 | .also { INSTANCE = it } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/widget/FavoriteBannerWidget.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.widget 2 | 3 | import android.app.PendingIntent 4 | import android.appwidget.AppWidgetManager 5 | import android.appwidget.AppWidgetProvider 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.net.Uri 9 | import android.widget.RemoteViews 10 | import android.widget.Toast 11 | import com.frogobox.movie.R 12 | 13 | /** 14 | * Created by Faisal Amir 15 | * FrogoBox Inc License 16 | * ========================================= 17 | * movie 18 | * Copyright (C) 16/11/2019. 19 | * All rights reserved 20 | * ----------------------------------------- 21 | * Name : Muhammad Faisal Amir 22 | * E-mail : faisalamircs@gmail.com 23 | * Github : github.com/amirisback 24 | * LinkedIn : linkedin.com/in/faisalamircs 25 | * ----------------------------------------- 26 | * FrogoBox Software Industries 27 | * com.frogobox.movie.ui.widget 28 | * 29 | */ 30 | class FavoriteBannerWidget : AppWidgetProvider() { 31 | 32 | override fun onUpdate( 33 | context: Context, 34 | appWidgetManager: AppWidgetManager, 35 | appWidgetIds: IntArray 36 | ) { 37 | // There may be multiple widgets active, so update all of them 38 | for (appWidgetId in appWidgetIds) { 39 | updateAppWidget(context, appWidgetManager, appWidgetId) 40 | } 41 | } 42 | 43 | override fun onEnabled(context: Context) { 44 | // Enter relevant functionality for when the first widget is created 45 | } 46 | 47 | override fun onDisabled(context: Context) { 48 | // Enter relevant functionality for when the last widget is disabled 49 | } 50 | 51 | override fun onReceive(context: Context, intent: Intent) { 52 | val mgr = AppWidgetManager.getInstance(context) 53 | if (intent.action == TOAST_ACTION) { 54 | val appWidgetId = intent.getIntExtra( 55 | AppWidgetManager.EXTRA_APPWIDGET_ID, 56 | AppWidgetManager.INVALID_APPWIDGET_ID 57 | ) 58 | val viewIndex = intent.getIntExtra(EXTRA_ITEM, 0) + 1 59 | Toast.makeText(context, "Favorite Movie Number : $viewIndex", Toast.LENGTH_SHORT).show() 60 | } 61 | super.onReceive(context, intent) 62 | } 63 | 64 | companion object { 65 | 66 | val TOAST_ACTION = "toast_action" 67 | val EXTRA_ITEM = "extra_movie" 68 | 69 | internal fun updateAppWidget( 70 | context: Context, appWidgetManager: AppWidgetManager, 71 | appWidgetId: Int 72 | ) { 73 | 74 | val intent = Intent(context, StackWidgetService::class.java) 75 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) 76 | intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)) 77 | 78 | val views = RemoteViews(context.packageName, R.layout.widget_banner_favorite) 79 | views.setRemoteAdapter(R.id.stack_view, intent) 80 | views.setEmptyView(R.id.stack_view, R.id.empty_view) 81 | 82 | val toastIntent = Intent(context, FavoriteBannerWidget::class.java) 83 | toastIntent.action = TOAST_ACTION 84 | toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) 85 | 86 | intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)) 87 | val toastPendingIntent = PendingIntent.getBroadcast( 88 | context, 89 | 0, 90 | toastIntent, 91 | PendingIntent.FLAG_UPDATE_CURRENT 92 | ) 93 | views.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent) 94 | 95 | appWidgetManager.updateAppWidget(appWidgetId, views) 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/com/frogobox/movie/widget/StackWidgetService.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.movie.widget 2 | 3 | import android.content.Intent 4 | import android.widget.RemoteViewsService 5 | 6 | /** 7 | * Created by Faisal Amir 8 | * FrogoBox Inc License 9 | * ========================================= 10 | * movie 11 | * Copyright (C) 16/11/2019. 12 | * All rights reserved 13 | * ----------------------------------------- 14 | * Name : Muhammad Faisal Amir 15 | * E-mail : faisalamircs@gmail.com 16 | * Github : github.com/amirisback 17 | * LinkedIn : linkedin.com/in/faisalamircs 18 | * ----------------------------------------- 19 | * FrogoBox Software Industries 20 | * com.frogobox.movie.ui.widget 21 | * 22 | */ 23 | class StackWidgetService : RemoteViewsService() { 24 | 25 | override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { 26 | return StackRemoteViewFactory(this.applicationContext, intent) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 26 | 27 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_favorite.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 23 | 24 | 33 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_tv_movie_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 21 | 22 | 32 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_tv_movie_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 19 | 20 | 27 | 28 | 41 | 42 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_banner_favorite.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_content_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 15 | 16 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello blank fragment 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/favorite_banner_widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /base/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/base/consumer-rules.pro -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseApiModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | /** 6 | * Created by Faisal Amir 7 | * FrogoBox Inc License 8 | * ========================================= 9 | * movie 10 | * Copyright (C) 16/11/2019. 11 | * All rights reserved 12 | * ----------------------------------------- 13 | * Name : Muhammad Faisal Amir 14 | * E-mail : faisalamircs@gmail.com 15 | * Github : github.com/amirisback 16 | * LinkedIn : linkedin.com/in/faisalamircs 17 | * ----------------------------------------- 18 | * FrogoBox Software Industries 19 | * com.frogobox.base 20 | * 21 | */ 22 | data class BaseApiModel( 23 | @SerializedName("code") val code: Int, 24 | @SerializedName("message") val message: String, 25 | @SerializedName("data") val data: T? = null, 26 | 27 | // Remove code below if project is running 28 | var page: Int, 29 | var total_results: Int, 30 | var total_pages: Int, 31 | var results: T? = null 32 | ) -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseApplication.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import android.annotation.TargetApi 4 | import android.app.Application 5 | import android.content.Context 6 | import android.os.Build 7 | import android.preference.PreferenceManager 8 | import java.util.* 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.base 25 | * 26 | */ 27 | class BaseApplication : Application() { 28 | 29 | private val SELECTED_LANGUAGE = "Locale.Helper.Selected.Language" 30 | 31 | fun onAttach(context: Context, defaultLanguage: String): Context { 32 | val lang = getPersistedData(context, defaultLanguage) 33 | return setLocale(context, lang) 34 | } 35 | 36 | fun setLocale(context: Context, language: String?): Context { 37 | persist(context, language) 38 | 39 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 40 | updateResources(context, language) 41 | } else updateResourcesLegacy(context, language) 42 | 43 | } 44 | 45 | private fun getPersistedData(context: Context, defaultLanguage: String): String? { 46 | val preferences = PreferenceManager.getDefaultSharedPreferences(context) 47 | return preferences.getString(SELECTED_LANGUAGE, defaultLanguage) 48 | } 49 | 50 | private fun persist(context: Context, language: String?) { 51 | val preferences = PreferenceManager.getDefaultSharedPreferences(context) 52 | val editor = preferences.edit() 53 | 54 | editor.putString(SELECTED_LANGUAGE, language) 55 | editor.apply() 56 | } 57 | 58 | @TargetApi(Build.VERSION_CODES.N) 59 | private fun updateResources(context: Context, language: String?): Context { 60 | val locale = Locale(language) 61 | Locale.setDefault(locale) 62 | 63 | val configuration = context.resources.configuration 64 | configuration.setLocale(locale) 65 | configuration.setLayoutDirection(locale) 66 | 67 | return context.createConfigurationContext(configuration) 68 | } 69 | 70 | private fun updateResourcesLegacy(context: Context, language: String?): Context { 71 | val locale = Locale(language) 72 | Locale.setDefault(locale) 73 | 74 | val resources = context.resources 75 | 76 | val configuration = resources.configuration 77 | configuration.locale = locale 78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 79 | configuration.setLayoutDirection(locale) 80 | } 81 | 82 | resources.updateConfiguration(configuration, resources.displayMetrics) 83 | 84 | return context 85 | } 86 | 87 | override fun onCreate() { 88 | super.onCreate() 89 | instance = this 90 | 91 | } 92 | 93 | override fun attachBaseContext(base: Context) { 94 | super.attachBaseContext(onAttach(base, "en")) 95 | } 96 | 97 | companion object { 98 | lateinit var instance: BaseApplication 99 | fun getContext(): Context = instance.applicationContext 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseCallback.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import com.google.gson.Gson 4 | import io.reactivex.rxjava3.core.SingleObserver 5 | import io.reactivex.rxjava3.disposables.Disposable 6 | import retrofit2.HttpException 7 | import java.net.SocketTimeoutException 8 | import java.net.UnknownHostException 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.base 25 | * 26 | */ 27 | abstract class BaseCallback : SingleObserver { 28 | 29 | abstract fun onCallbackSucces(data: M) 30 | abstract fun onCallbackError(code: Int, errorMessage: String) 31 | abstract fun onAddSubscribe(disposable: Disposable) 32 | abstract fun onCompleted() 33 | 34 | override fun onSuccess(t: M) { 35 | onCompleted() 36 | if (t == null) { 37 | onCallbackError(200,"Data is empty") 38 | } else { 39 | onCallbackSucces(t) 40 | } 41 | } 42 | 43 | override fun onSubscribe(d: Disposable) { 44 | onAddSubscribe(d) 45 | } 46 | 47 | override fun onError(e: Throwable) { 48 | onCompleted() 49 | e.printStackTrace() 50 | when (e) { 51 | is HttpException -> { 52 | val code = e.code() 53 | var msg = e.message() 54 | var baseDao: BaseApiModel? = null 55 | try { 56 | val body = e.response()?.errorBody() 57 | baseDao = Gson().fromJson>(body!!.string(), BaseApiModel::class.java) 58 | } catch (exception: Exception) { 59 | onCallbackError(code, exception.message!!) 60 | } 61 | 62 | when (code) { 63 | 504 -> { 64 | msg = baseDao?.message?: "Error Response" 65 | } 66 | 502, 404 -> { 67 | msg = baseDao?.message?: "Error Connect or Resource Not Found" 68 | } 69 | 400 -> { 70 | msg = baseDao?.message?: "Bad Request" 71 | } 72 | 401 -> { 73 | msg = baseDao?.message?: "Not Authorized" 74 | } 75 | } 76 | 77 | onCallbackError(code, msg) 78 | } 79 | 80 | is UnknownHostException -> onCallbackError(-1, "Telah terjadi kesalahan ketika koneksi ke server: ${e.message}") 81 | is SocketTimeoutException -> onCallbackError(-1, "Telah terjadi kesalahan ketika koneksi ke server: ${e.message}") 82 | else -> onCallbackError(-1, e.message ?: "Unknown error occured") 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseDataSource.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | 6 | /** 7 | * Created by Faisal Amir 8 | * FrogoBox Inc License 9 | * ========================================= 10 | * movie 11 | * Copyright (C) 16/11/2019. 12 | * All rights reserved 13 | * ----------------------------------------- 14 | * Name : Muhammad Faisal Amir 15 | * E-mail : faisalamircs@gmail.com 16 | * Github : github.com/amirisback 17 | * LinkedIn : linkedin.com/in/faisalamircs 18 | * ----------------------------------------- 19 | * FrogoBox Software Industries 20 | * com.frogobox.base 21 | * 22 | */ 23 | interface BaseDataSource { 24 | interface ResponseCallback { 25 | fun onShowProgressDialog() 26 | fun onHideProgressDialog() 27 | fun onSuccess(data: T) 28 | fun onFinish() 29 | fun onEmpty() 30 | fun onFailed(statusCode: Int, errorMessage: String? = "") 31 | } 32 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseHelper.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import com.google.gson.Gson 4 | 5 | /** 6 | * Created by Faisal Amir 7 | * FrogoBox Inc License 8 | * ========================================= 9 | * movie 10 | * Copyright (C) 16/11/2019. 11 | * All rights reserved 12 | * ----------------------------------------- 13 | * Name : Muhammad Faisal Amir 14 | * E-mail : faisalamircs@gmail.com 15 | * Github : github.com/amirisback 16 | * LinkedIn : linkedin.com/in/faisalamircs 17 | * ----------------------------------------- 18 | * FrogoBox Software Industries 19 | * com.frogobox.base 20 | * 21 | */ 22 | open class BaseHelper { 23 | 24 | fun baseToJson(model: T) : String? { 25 | return Gson().toJson(model) 26 | } 27 | 28 | inline fun baseFromJson(word: String?) : T { 29 | return Gson().fromJson(word, T::class.java) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseListener.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | /** 4 | * Created by Faisal Amir 5 | * FrogoBox Inc License 6 | * ========================================= 7 | * movie 8 | * Copyright (C) 16/11/2019. 9 | * All rights reserved 10 | * ----------------------------------------- 11 | * Name : Muhammad Faisal Amir 12 | * E-mail : faisalamircs@gmail.com 13 | * Github : github.com/amirisback 14 | * LinkedIn : linkedin.com/in/faisalamircs 15 | * ----------------------------------------- 16 | * FrogoBox Software Industries 17 | * com.frogobox.base 18 | * 19 | */ 20 | interface BaseListener { 21 | fun onItemClicked(data: T) 22 | fun onItemLongClicked(data: T) 23 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import android.content.Context 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | /** 7 | * Created by Faisal Amir 8 | * FrogoBox Inc License 9 | * ========================================= 10 | * movie 11 | * Copyright (C) 16/11/2019. 12 | * All rights reserved 13 | * ----------------------------------------- 14 | * Name : Muhammad Faisal Amir 15 | * E-mail : faisalamircs@gmail.com 16 | * Github : github.com/amirisback 17 | * LinkedIn : linkedin.com/in/faisalamircs 18 | * ----------------------------------------- 19 | * FrogoBox Software Industries 20 | * com.frogobox.base 21 | * 22 | */ 23 | abstract class BaseViewAdapter> : RecyclerView.Adapter() { 24 | 25 | protected lateinit var mContext: Context 26 | protected lateinit var mListener: BaseListener 27 | 28 | protected val mRecyclerViewDataList = mutableListOf() 29 | protected var mRecyclerViewLayout: Int = 0 30 | 31 | fun setRecyclerViewLayout(context: Context, layoutItem: Int) { 32 | mContext = context 33 | mRecyclerViewLayout = layoutItem 34 | } 35 | 36 | fun setRecyclerViewListener(listener: BaseListener) { 37 | mListener = listener 38 | } 39 | 40 | fun setRecyclerViewData(dataList: List) { 41 | mRecyclerViewDataList.clear() 42 | mRecyclerViewDataList.addAll(dataList) 43 | notifyDataSetChanged() 44 | } 45 | 46 | fun nukeRecyclerViewData(){ 47 | mRecyclerViewDataList.clear() 48 | notifyDataSetChanged() 49 | } 50 | 51 | override fun onBindViewHolder(holder: Holder, position: Int) { 52 | holder.bindItem(mRecyclerViewDataList[position], mListener) 53 | } 54 | 55 | override fun getItemCount(): Int = mRecyclerViewDataList.size 56 | 57 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | /** 7 | * Created by Faisal Amir 8 | * FrogoBox Inc License 9 | * ========================================= 10 | * movie 11 | * Copyright (C) 16/11/2019. 12 | * All rights reserved 13 | * ----------------------------------------- 14 | * Name : Muhammad Faisal Amir 15 | * E-mail : faisalamircs@gmail.com 16 | * Github : github.com/amirisback 17 | * LinkedIn : linkedin.com/in/faisalamircs 18 | * ----------------------------------------- 19 | * FrogoBox Software Industries 20 | * com.frogobox.base 21 | * 22 | */ 23 | abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) { 24 | 25 | open fun bindItem(data: T, listener: BaseListener){ 26 | onItemViewClicked(data, listener) 27 | initComponent(data) 28 | } 29 | 30 | protected fun onItemViewClicked(data: T, listener: BaseListener){ 31 | itemView.setOnClickListener { 32 | listener.onItemClicked(data) 33 | } 34 | } 35 | 36 | open fun initComponent(data: T){ 37 | // component view 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import com.frogobox.base.util.SingleLiveEvent 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * movie 12 | * Copyright (C) 16/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.base 22 | * 23 | */ 24 | 25 | open class BaseViewModel(application: Application) : AndroidViewModel(application) { 26 | var eventShowProgress = SingleLiveEvent() 27 | var eventIsEmpty = SingleLiveEvent() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/adapter/FavoriteMovieAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import com.bumptech.glide.Glide 9 | import com.frogobox.base.BuildConfig 10 | import com.frogobox.base.source.model.FavoriteMovie 11 | import com.frogobox.base.BaseViewAdapter 12 | import com.frogobox.base.BaseViewHolder 13 | import com.frogobox.base.R 14 | import com.frogobox.base.util.Helper 15 | 16 | /** 17 | * Created by Faisal Amir 18 | * FrogoBox Inc License 19 | * ========================================= 20 | * movie 21 | * Copyright (C) 16/11/2019. 22 | * All rights reserved 23 | * ----------------------------------------- 24 | * Name : Muhammad Faisal Amir 25 | * E-mail : faisalamircs@gmail.com 26 | * Github : github.com/amirisback 27 | * LinkedIn : linkedin.com/in/faisalamircs 28 | * ----------------------------------------- 29 | * FrogoBox Software Industries 30 | * com.frogobox.base.adapter 31 | * 32 | */ 33 | class FavoriteMovieAdapter : 34 | BaseViewAdapter() { 35 | 36 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteMovieAdapter.FavoriteMovieViewHolder = 37 | 38 | FavoriteMovieViewHolder( 39 | LayoutInflater.from(mContext).inflate( 40 | mRecyclerViewLayout, 41 | parent, 42 | false 43 | ) 44 | ) 45 | 46 | inner class FavoriteMovieViewHolder(view: View) : BaseViewHolder(view) { 47 | 48 | private val ivPoster = view.findViewById(R.id.iv_poster) 49 | private val tvTitle = view.findViewById(R.id.tv_title) 50 | private val tvOverview = view.findViewById(R.id.tv_overview) 51 | 52 | override fun initComponent(data: FavoriteMovie) { 53 | super.initComponent(data) 54 | val poster = data.poster_path?.let { Helper.Func.removeBackSlash(it) } 55 | Glide.with(mContext).load(BuildConfig.TMDB_PATH_URL_IMAGE + poster).into(ivPoster) 56 | tvTitle.text = data.title 57 | tvOverview.text = data.overview 58 | } 59 | 60 | } 61 | 62 | 63 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/adapter/FavoriteTvShowAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import com.bumptech.glide.Glide 9 | import com.frogobox.base.BuildConfig 10 | import com.frogobox.base.source.model.FavoriteTvShow 11 | import com.frogobox.base.BaseViewAdapter 12 | import com.frogobox.base.BaseViewHolder 13 | import com.frogobox.base.R 14 | import com.frogobox.base.util.Helper 15 | 16 | /** 17 | * Created by Faisal Amir 18 | * FrogoBox Inc License 19 | * ========================================= 20 | * movie 21 | * Copyright (C) 16/11/2019. 22 | * All rights reserved 23 | * ----------------------------------------- 24 | * Name : Muhammad Faisal Amir 25 | * E-mail : faisalamircs@gmail.com 26 | * Github : github.com/amirisback 27 | * LinkedIn : linkedin.com/in/faisalamircs 28 | * ----------------------------------------- 29 | * FrogoBox Software Industries 30 | * com.frogobox.base.adapter 31 | * 32 | */ 33 | class FavoriteTvShowAdapter : 34 | BaseViewAdapter() { 35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteTvShowViewHolder = 36 | 37 | FavoriteTvShowViewHolder( 38 | LayoutInflater.from(mContext).inflate( 39 | mRecyclerViewLayout, 40 | parent, 41 | false 42 | ) 43 | ) 44 | 45 | inner class FavoriteTvShowViewHolder(view: View) : BaseViewHolder(view) { 46 | 47 | private val ivPoster = view.findViewById(R.id.iv_poster) 48 | private val tvTitle = view.findViewById(R.id.tv_title) 49 | private val tvOverview = view.findViewById(R.id.tv_overview) 50 | 51 | override fun initComponent(data: FavoriteTvShow) { 52 | super.initComponent(data) 53 | val poster = data.poster_path?.let { Helper.Func.removeBackSlash(it) } 54 | Glide.with(mContext).load(BuildConfig.TMDB_PATH_URL_IMAGE + poster).into(ivPoster) 55 | tvTitle.text = data.name 56 | tvOverview.text = data.overview 57 | } 58 | 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/adapter/MovieAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import com.frogobox.base.BuildConfig 9 | import com.frogobox.base.source.model.Movie 10 | import com.frogobox.base.BaseViewAdapter 11 | import com.frogobox.base.BaseViewHolder 12 | import com.frogobox.base.util.Helper.Func.removeBackSlash 13 | import com.bumptech.glide.Glide 14 | import com.frogobox.base.R 15 | 16 | /** 17 | * Created by Faisal Amir 18 | * FrogoBox Inc License 19 | * ========================================= 20 | * movie 21 | * Copyright (C) 16/11/2019. 22 | * All rights reserved 23 | * ----------------------------------------- 24 | * Name : Muhammad Faisal Amir 25 | * E-mail : faisalamircs@gmail.com 26 | * Github : github.com/amirisback 27 | * LinkedIn : linkedin.com/in/faisalamircs 28 | * ----------------------------------------- 29 | * FrogoBox Software Industries 30 | * com.frogobox.base.adapter 31 | * 32 | */ 33 | class MovieAdapter : 34 | BaseViewAdapter() { 35 | 36 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = 37 | MovieViewHolder( 38 | LayoutInflater.from(mContext).inflate( 39 | mRecyclerViewLayout, 40 | parent, 41 | false 42 | ) 43 | ) 44 | 45 | inner class MovieViewHolder(view: View) : BaseViewHolder(view) { 46 | 47 | private val ivPoster = view.findViewById(R.id.iv_poster) 48 | private val tvTitle = view.findViewById(R.id.tv_title) 49 | private val tvOverview = view.findViewById(R.id.tv_overview) 50 | 51 | override fun initComponent(data: Movie) { 52 | super.initComponent(data) 53 | val poster = data.poster_path?.let { removeBackSlash(it) } 54 | Glide.with(mContext).load(BuildConfig.TMDB_PATH_URL_IMAGE + poster).into(ivPoster) 55 | tvTitle.text = data.title 56 | tvOverview.text = data.overview 57 | } 58 | 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/adapter/TvShowAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import com.frogobox.base.BuildConfig 9 | import com.frogobox.base.source.model.TvShow 10 | import com.frogobox.base.BaseViewAdapter 11 | import com.frogobox.base.BaseViewHolder 12 | import com.frogobox.base.util.Helper 13 | import com.bumptech.glide.Glide 14 | import com.frogobox.base.R 15 | 16 | /** 17 | * Created by Faisal Amir 18 | * FrogoBox Inc License 19 | * ========================================= 20 | * movie 21 | * Copyright (C) 16/11/2019. 22 | * All rights reserved 23 | * ----------------------------------------- 24 | * Name : Muhammad Faisal Amir 25 | * E-mail : faisalamircs@gmail.com 26 | * Github : github.com/amirisback 27 | * LinkedIn : linkedin.com/in/faisalamircs 28 | * ----------------------------------------- 29 | * FrogoBox Software Industries 30 | * com.frogobox.base.adapter 31 | * 32 | */ 33 | class TvShowAdapter : BaseViewAdapter() { 34 | 35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = 36 | TvShowViewHolder( 37 | LayoutInflater.from(mContext).inflate( 38 | mRecyclerViewLayout, 39 | parent, 40 | false 41 | ) 42 | ) 43 | 44 | inner class TvShowViewHolder(view: View) : BaseViewHolder(view) { 45 | 46 | private val ivPoster = view.findViewById(R.id.iv_poster) 47 | private val tvTitle = view.findViewById(R.id.tv_title) 48 | private val tvOverview = view.findViewById(R.id.tv_overview) 49 | 50 | override fun initComponent(data: TvShow) { 51 | super.initComponent(data) 52 | val poster = data.poster_path?.let { Helper.Func.removeBackSlash(it) } 53 | Glide.with(mContext).load(BuildConfig.TMDB_PATH_URL_IMAGE + poster).into(ivPoster) 54 | tvTitle.text = data.name 55 | tvOverview.text = data.overview 56 | } 57 | 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/callback/DeleteViewCallback.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.callback 2 | 3 | /** 4 | * Created by Faisal Amir 5 | * FrogoBox Inc License 6 | * ========================================= 7 | * movie 8 | * Copyright (C) 16/11/2019. 9 | * All rights reserved 10 | * ----------------------------------------- 11 | * Name : Muhammad Faisal Amir 12 | * E-mail : faisalamircs@gmail.com 13 | * Github : github.com/amirisback 14 | * LinkedIn : linkedin.com/in/faisalamircs 15 | * ----------------------------------------- 16 | * FrogoBox Software Industries 17 | * com.frogobox.base.callback 18 | * 19 | */ 20 | interface DeleteViewCallback { 21 | fun onShowProgress() 22 | fun onHideProgress() 23 | fun onSuccesDelete() 24 | fun onFailed(message: String) 25 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/callback/SaveViewCallback.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.callback 2 | 3 | /** 4 | * Created by Faisal Amir 5 | * FrogoBox Inc License 6 | * ========================================= 7 | * movie 8 | * Copyright (C) 16/11/2019. 9 | * All rights reserved 10 | * ----------------------------------------- 11 | * Name : Muhammad Faisal Amir 12 | * E-mail : faisalamircs@gmail.com 13 | * Github : github.com/amirisback 14 | * LinkedIn : linkedin.com/in/faisalamircs 15 | * ----------------------------------------- 16 | * FrogoBox Software Industries 17 | * com.frogobox.base.callback 18 | * 19 | */ 20 | interface SaveViewCallback { 21 | fun onShowProgress() 22 | fun onHideProgress() 23 | fun onSuccesInsert() 24 | fun onFailed(message: String) 25 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/service/Notification.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.service 2 | 3 | import android.app.NotificationChannel 4 | import android.app.NotificationManager 5 | import android.app.PendingIntent 6 | import android.content.ComponentName 7 | import android.content.Context 8 | import android.content.Intent 9 | import android.graphics.BitmapFactory 10 | import android.media.RingtoneManager 11 | import android.os.Build 12 | import androidx.core.app.NotificationCompat 13 | import com.frogobox.base.R 14 | import com.frogobox.base.util.Constant.Constant.PACKAGE_ROOT 15 | import com.frogobox.base.util.Constant.Constant.PATH_MAIN_ACTIVITY 16 | 17 | /** 18 | * Created by Faisal Amir 19 | * FrogoBox Inc License 20 | * ========================================= 21 | * movie 22 | * Copyright (C) 16/11/2019. 23 | * All rights reserved 24 | * ----------------------------------------- 25 | * Name : Muhammad Faisal Amir 26 | * E-mail : faisalamircs@gmail.com 27 | * Github : github.com/amirisback 28 | * LinkedIn : linkedin.com/in/faisalamircs 29 | * ----------------------------------------- 30 | * FrogoBox Software Industries 31 | * com.frogobox.base.service 32 | * 33 | */ 34 | class Notification { 35 | 36 | fun sendNotification( 37 | context: Context, 38 | notificationId: Int, 39 | channelId: String?, 40 | channelName: CharSequence?, 41 | contentTitle: String?, 42 | contentText: String? 43 | ) { 44 | 45 | val intent = Intent(Intent.ACTION_MAIN) 46 | intent.component = ComponentName(PACKAGE_ROOT, PATH_MAIN_ACTIVITY) 47 | val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) 48 | val alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) 49 | 50 | val mNotificationManager = 51 | context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 52 | val mBuilder = channelId.let { 53 | it?.let { it1 -> 54 | NotificationCompat.Builder(context, it1) 55 | .setContentIntent(pendingIntent) 56 | .setSmallIcon(R.drawable.ic_notification) 57 | .setLargeIcon( 58 | BitmapFactory.decodeResource( 59 | context.resources, 60 | R.drawable.ic_notification 61 | ) 62 | ) 63 | .setContentTitle(contentTitle) 64 | .setContentText(contentText) 65 | .setSound(alarmSound) 66 | } 67 | } 68 | 69 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 70 | val channel = NotificationChannel( 71 | channelId, 72 | channelName, 73 | NotificationManager.IMPORTANCE_DEFAULT 74 | ) 75 | channelId.let { it?.let { it1 -> mBuilder?.setChannelId(it1) } } 76 | mNotificationManager.createNotificationChannel(channel) 77 | } 78 | 79 | val notification = mBuilder?.build() 80 | mNotificationManager.notify(notificationId, notification) 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/DataSource.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source 2 | 3 | import android.content.Context 4 | import android.database.Cursor 5 | import com.frogobox.base.BaseDataSource 6 | import com.frogobox.base.source.model.FavoriteMovie 7 | import com.frogobox.base.source.model.FavoriteTvShow 8 | import com.frogobox.base.source.model.Movie 9 | import com.frogobox.base.source.model.TvShow 10 | 11 | /** 12 | * Created by Faisal Amir 13 | * FrogoBox Inc License 14 | * ========================================= 15 | * movie 16 | * Copyright (C) 16/11/2019. 17 | * All rights reserved 18 | * ----------------------------------------- 19 | * Name : Muhammad Faisal Amir 20 | * E-mail : faisalamircs@gmail.com 21 | * Github : github.com/amirisback 22 | * LinkedIn : linkedin.com/in/faisalamircs 23 | * ----------------------------------------- 24 | * FrogoBox Software Industries 25 | * com.frogobox.base.source 26 | * 27 | */ 28 | interface DataSource : BaseDataSource { 29 | 30 | // API TMDB ------------------------------------------------------------------------------------ 31 | // Get 32 | fun getMovies(callback: GetRemoteCallback>) 33 | fun getTvShow(callback: GetRemoteCallback>) 34 | 35 | // Search 36 | fun searchMovies(query: String, callback: GetRemoteCallback>) 37 | fun searchTvShow(query: String, callback: GetRemoteCallback>) 38 | 39 | // Reminder 40 | fun reminderReleaseMovie( 41 | dateGte: String, 42 | dateLte: String, 43 | callback: GetRemoteCallback> 44 | ) 45 | 46 | // Room Database ------------------------------------------------------------------------------- 47 | // Create 48 | fun saveFavoriteMovie(context: Context, data: FavoriteMovie): Boolean 49 | fun saveFavoriteTvShow(context: Context, data: FavoriteTvShow): Boolean 50 | 51 | // Read 52 | fun getFavoriteMovies(callback: GetLocalCallBack>) 53 | fun getFavoriteTvShows(callback: GetLocalCallBack>) 54 | 55 | fun getFavoriteMovieCursor(context: Context, callback: GetLocalCallBack) 56 | fun getFavoriteTvShowCursor(context: Context, callback: GetLocalCallBack) 57 | 58 | fun getFavoriteMoviewCursorById(context: Context, id: Int, callback: GetLocalCallBack) 59 | fun getFavoriteTvShowCursorById(context: Context, id: Int, callback: GetLocalCallBack) 60 | 61 | // Delete 62 | fun deleteFavoriteMovie(tableId: Int): Boolean 63 | fun deleteFavoriteTvShow(tableId: Int): Boolean 64 | 65 | fun deleteFavoriteMovieProvider(context: Context, tableId: Int): Boolean 66 | fun deleteFavoriteTvShowProvider(context: Context, tableId: Int): Boolean 67 | 68 | // Clear 69 | fun nukeFavoriteMovies(): Boolean 70 | fun nukeFavoriteTvShows(): Boolean 71 | 72 | // Shared Preference --------------------------------------------------------------------------- 73 | 74 | // Create 75 | fun savePrefReleaseReminder(state: Boolean) 76 | fun savePrefDailyReminder(state: Boolean) 77 | 78 | // Read 79 | fun getPrefReleaseReminder(): Boolean 80 | fun getPrefDailyReminder(): Boolean 81 | 82 | // Delete 83 | fun deletePrefReleaseReminder() 84 | fun deletePrefDailyReminder() 85 | 86 | // Callback ------------------------------------------------------------------------------------ 87 | interface GetRemoteCallback : BaseDataSource.ResponseCallback 88 | interface GetLocalCallBack : BaseDataSource.ResponseCallback 89 | 90 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/local/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.local 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import androidx.room.migration.Migration 8 | import androidx.sqlite.db.SupportSQLiteDatabase 9 | import com.frogobox.base.BuildConfig 10 | import com.frogobox.base.source.local.dao.FavoriteMovieDao 11 | import com.frogobox.base.source.local.dao.FavoriteTvShowDao 12 | import com.frogobox.base.source.model.FavoriteMovie 13 | import com.frogobox.base.source.model.FavoriteTvShow 14 | import com.frogobox.base.util.Constant.RoomDatabase.DATABASE_NAME 15 | 16 | /** 17 | * Created by Faisal Amir 18 | * FrogoBox Inc License 19 | * ========================================= 20 | * movie 21 | * Copyright (C) 16/11/2019. 22 | * All rights reserved 23 | * ----------------------------------------- 24 | * Name : Muhammad Faisal Amir 25 | * E-mail : faisalamircs@gmail.com 26 | * Github : github.com/amirisback 27 | * LinkedIn : linkedin.com/in/faisalamircs 28 | * ----------------------------------------- 29 | * FrogoBox Software Industries 30 | * com.frogobox.base.source.local 31 | * 32 | */ 33 | @Database(entities = [ 34 | (FavoriteMovie::class), 35 | (FavoriteTvShow::class) 36 | ], version = 1) 37 | 38 | 39 | abstract class AppDatabase : RoomDatabase() { 40 | 41 | abstract fun favoriteMovieDao(): FavoriteMovieDao 42 | abstract fun favoriteTvShowDao(): FavoriteTvShowDao 43 | 44 | companion object { 45 | 46 | @Volatile 47 | private var INSTANCE: AppDatabase? = null 48 | 49 | fun getInstance(context: Context): AppDatabase = 50 | INSTANCE ?: synchronized(this) { 51 | INSTANCE 52 | ?: buildDatabase(context).also { 53 | INSTANCE = it 54 | } 55 | } 56 | 57 | private fun buildDatabase(context: Context): AppDatabase { 58 | return if (BuildConfig.DEBUG) { 59 | Room.databaseBuilder(context.applicationContext, 60 | AppDatabase::class.java, DATABASE_NAME) 61 | .addMigrations(MIGRATION_2_3) 62 | .fallbackToDestructiveMigration() // FOR DEVELOPMENT ONLY !!!! 63 | .build() 64 | } else { 65 | Room.databaseBuilder(context.applicationContext, 66 | AppDatabase::class.java, DATABASE_NAME) 67 | .addMigrations(MIGRATION_2_3) 68 | .build() 69 | } 70 | } 71 | 72 | private val MIGRATION_2_3: Migration = object : Migration(2, 3) { 73 | override fun migrate(database: SupportSQLiteDatabase) { 74 | 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/local/dao/FavoriteMovieDao.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.local.dao 2 | 3 | import android.database.Cursor 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.Query 7 | import com.frogobox.base.source.model.FavoriteMovie 8 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_ID 9 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.TABLE_NAME 10 | import com.frogobox.base.util.Constant.RoomDatabase.Movie._ID 11 | import io.reactivex.rxjava3.core.Single 12 | 13 | /** 14 | * Created by Faisal Amir 15 | * FrogoBox Inc License 16 | * ========================================= 17 | * movie 18 | * Copyright (C) 16/11/2019. 19 | * All rights reserved 20 | * ----------------------------------------- 21 | * Name : Muhammad Faisal Amir 22 | * E-mail : faisalamircs@gmail.com 23 | * Github : github.com/amirisback 24 | * LinkedIn : linkedin.com/in/faisalamircs 25 | * ----------------------------------------- 26 | * FrogoBox Software Industries 27 | * com.frogobox.base.source.local.dao 28 | * 29 | */ 30 | @Dao 31 | interface FavoriteMovieDao { 32 | 33 | @Query("SELECT * FROM $TABLE_NAME") 34 | fun getAllData(): Single> 35 | 36 | @Insert 37 | fun insertData(data: FavoriteMovie) 38 | 39 | @Query("DELETE FROM $TABLE_NAME WHERE $COLUMN_ID = :tableId") 40 | fun deleteDataFromTableId(tableId: Int) 41 | 42 | @Query("DELETE FROM $TABLE_NAME ") 43 | fun nukeData() 44 | 45 | // Content Provider ---------------------------------------------------------------------------- 46 | 47 | @Query("SELECT * FROM $TABLE_NAME") 48 | fun getCursorAllData() : Cursor 49 | 50 | @Query("SELECT * FROM $TABLE_NAME WHERE $_ID = :id") 51 | fun getCursorById(id : Long) : Cursor 52 | 53 | @Insert 54 | fun insertCursor(data: FavoriteMovie) : Long 55 | 56 | @Query("DELETE FROM $TABLE_NAME WHERE $_ID = :id") 57 | fun deleteById(id: Long): Int 58 | 59 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/local/dao/FavoriteTvShowDao.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.local.dao 2 | 3 | import android.database.Cursor 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.Query 7 | import com.frogobox.base.source.model.FavoriteTvShow 8 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_ID 9 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.TABLE_NAME 10 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow._ID 11 | import io.reactivex.rxjava3.core.Single 12 | 13 | /** 14 | * Created by Faisal Amir 15 | * FrogoBox Inc License 16 | * ========================================= 17 | * movie 18 | * Copyright (C) 16/11/2019. 19 | * All rights reserved 20 | * ----------------------------------------- 21 | * Name : Muhammad Faisal Amir 22 | * E-mail : faisalamircs@gmail.com 23 | * Github : github.com/amirisback 24 | * LinkedIn : linkedin.com/in/faisalamircs 25 | * ----------------------------------------- 26 | * FrogoBox Software Industries 27 | * com.frogobox.base.source.local.dao 28 | * 29 | */ 30 | @Dao 31 | interface FavoriteTvShowDao { 32 | 33 | @Query("SELECT * FROM $TABLE_NAME") 34 | fun getAllData(): Single> 35 | 36 | @Insert 37 | fun insertData(data: FavoriteTvShow) 38 | 39 | @Query("DELETE FROM $TABLE_NAME WHERE $COLUMN_ID = :tableId") 40 | fun deleteDataFromTableId(tableId: Int) 41 | 42 | @Query("DELETE FROM $TABLE_NAME") 43 | fun nukeData() 44 | 45 | // Content Provider ---------------------------------------------------------------------------- 46 | 47 | @Query("SELECT * FROM $TABLE_NAME") 48 | fun getCursorAllData(): Cursor 49 | 50 | @Query("SELECT * FROM $TABLE_NAME WHERE $_ID = :id") 51 | fun getCursorById(id: Long): Cursor 52 | 53 | @Insert 54 | fun insertCursor(data: FavoriteTvShow): Long 55 | 56 | @Query("DELETE FROM $TABLE_NAME WHERE $_ID = :id") 57 | fun deleteById(id: Long): Int 58 | 59 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/model/FavoriteMovie.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.model 2 | 3 | import android.content.ContentValues 4 | import android.os.Parcelable 5 | import androidx.room.ColumnInfo 6 | import androidx.room.Entity 7 | import androidx.room.PrimaryKey 8 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_BACKDROP_PATH 9 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_ID 10 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_OVERVIEW 11 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_POSTER_PATH 12 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.COLUMN_TITLE 13 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.TABLE_NAME 14 | import com.frogobox.base.util.Constant.RoomDatabase.Movie._ID 15 | import kotlinx.parcelize.Parcelize 16 | 17 | /** 18 | * Created by Faisal Amir 19 | * FrogoBox Inc License 20 | * ========================================= 21 | * movie 22 | * Copyright (C) 16/11/2019. 23 | * All rights reserved 24 | * ----------------------------------------- 25 | * Name : Muhammad Faisal Amir 26 | * E-mail : faisalamircs@gmail.com 27 | * Github : github.com/amirisback 28 | * LinkedIn : linkedin.com/in/faisalamircs 29 | * ----------------------------------------- 30 | * FrogoBox Software Industries 31 | * com.frogobox.base.model 32 | * 33 | */ 34 | @Entity(tableName = TABLE_NAME) 35 | @Parcelize 36 | data class FavoriteMovie( 37 | 38 | @PrimaryKey(autoGenerate = true) 39 | @ColumnInfo(name = _ID) 40 | var table_id: Int = 0, 41 | 42 | @ColumnInfo(name = COLUMN_ID) 43 | var id: Int? = null, 44 | 45 | @ColumnInfo(name = COLUMN_TITLE) 46 | var title: String? = null, 47 | 48 | @ColumnInfo(name = COLUMN_POSTER_PATH) 49 | var poster_path: String? = null, 50 | 51 | @ColumnInfo(name = COLUMN_BACKDROP_PATH) 52 | var backdrop_path: String? = null, 53 | 54 | @ColumnInfo(name = COLUMN_OVERVIEW) 55 | var overview: String? = null 56 | 57 | ) : Parcelable { 58 | 59 | fun fromContentValues(values: ContentValues): FavoriteMovie { 60 | val favoriteMovie = FavoriteMovie() 61 | if (values.containsKey(COLUMN_ID)) { 62 | favoriteMovie.id = values.getAsInteger(COLUMN_ID)!! 63 | } 64 | 65 | if (values.containsKey(COLUMN_TITLE)) { 66 | favoriteMovie.title = values.getAsString(COLUMN_TITLE) 67 | } 68 | 69 | if (values.containsKey(COLUMN_POSTER_PATH)) { 70 | favoriteMovie.poster_path = values.getAsString(COLUMN_POSTER_PATH) 71 | } 72 | 73 | if (values.containsKey(COLUMN_BACKDROP_PATH)) { 74 | favoriteMovie.backdrop_path = values.getAsString(COLUMN_BACKDROP_PATH) 75 | } 76 | 77 | if (values.containsKey(COLUMN_OVERVIEW)) { 78 | favoriteMovie.overview = values.getAsString(COLUMN_OVERVIEW) 79 | } 80 | 81 | return favoriteMovie 82 | } 83 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/model/FavoriteTvShow.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.model 2 | 3 | import android.content.ContentValues 4 | import android.os.Parcelable 5 | import androidx.room.ColumnInfo 6 | import androidx.room.Entity 7 | import androidx.room.PrimaryKey 8 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_BACKDROP_PATH 9 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_ID 10 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_NAME 11 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_OVERVIEW 12 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.COLUMN_POSTER_PATH 13 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.TABLE_NAME 14 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow._ID 15 | import kotlinx.parcelize.Parcelize 16 | 17 | /** 18 | * Created by Faisal Amir 19 | * FrogoBox Inc License 20 | * ========================================= 21 | * movie 22 | * Copyright (C) 16/11/2019. 23 | * All rights reserved 24 | * ----------------------------------------- 25 | * Name : Muhammad Faisal Amir 26 | * E-mail : faisalamircs@gmail.com 27 | * Github : github.com/amirisback 28 | * LinkedIn : linkedin.com/in/faisalamircs 29 | * ----------------------------------------- 30 | * FrogoBox Software Industries 31 | * com.frogobox.base.model 32 | * 33 | */ 34 | @Entity(tableName = TABLE_NAME) 35 | @Parcelize 36 | data class FavoriteTvShow( 37 | 38 | @PrimaryKey(autoGenerate = true) 39 | @ColumnInfo(name = _ID) 40 | var table_id: Int = 0, 41 | 42 | @ColumnInfo(name = COLUMN_ID) 43 | var id: Int? = null, 44 | 45 | @ColumnInfo(name = COLUMN_NAME) 46 | var name: String? = null, 47 | 48 | @ColumnInfo(name = COLUMN_POSTER_PATH) 49 | var poster_path: String? = null, 50 | 51 | @ColumnInfo(name = COLUMN_BACKDROP_PATH) 52 | var backdrop_path: String? = null, 53 | 54 | @ColumnInfo(name = COLUMN_OVERVIEW) 55 | var overview: String? = null 56 | 57 | ) : Parcelable { 58 | 59 | fun fromContentValues(values: ContentValues): FavoriteTvShow { 60 | val favoriteTvShow = FavoriteTvShow() 61 | if (values.containsKey(COLUMN_ID)) { 62 | favoriteTvShow.id = values.getAsInteger(COLUMN_ID)!! 63 | } 64 | 65 | if (values.containsKey(COLUMN_NAME)) { 66 | favoriteTvShow.name = values.getAsString(COLUMN_NAME) 67 | } 68 | 69 | if (values.containsKey(COLUMN_POSTER_PATH)) { 70 | favoriteTvShow.poster_path = values.getAsString(COLUMN_POSTER_PATH) 71 | } 72 | 73 | if (values.containsKey(COLUMN_BACKDROP_PATH)) { 74 | favoriteTvShow.backdrop_path = values.getAsString(COLUMN_BACKDROP_PATH) 75 | } 76 | 77 | if (values.containsKey(COLUMN_OVERVIEW)) { 78 | favoriteTvShow.overview = values.getAsString(COLUMN_OVERVIEW) 79 | } 80 | 81 | return favoriteTvShow 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/model/Movie.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.model 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.parcelize.Parcelize 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * movie 12 | * Copyright (C) 16/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.base.model 22 | * 23 | */ 24 | @Parcelize 25 | data class Movie( 26 | 27 | @SerializedName("id") 28 | var id: Int? = null, 29 | 30 | @SerializedName("title") 31 | var title: String? = null, 32 | 33 | @SerializedName("poster_path") 34 | var poster_path: String? = null, 35 | 36 | @SerializedName("backdrop_path") 37 | var backdrop_path: String? = null, 38 | 39 | @SerializedName("overview") 40 | var overview: String? = null 41 | 42 | 43 | ) : Parcelable -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/model/TvShow.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.model 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.parcelize.Parcelize 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * movie 12 | * Copyright (C) 16/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.base.model 22 | * 23 | */ 24 | @Parcelize 25 | data class TvShow( 26 | 27 | @SerializedName("id") 28 | var id: Int? = null, 29 | 30 | @SerializedName("name") 31 | var name: String? = null, 32 | 33 | @SerializedName("poster_path") 34 | var poster_path: String? = null, 35 | 36 | @SerializedName("backdrop_path") 37 | var backdrop_path: String? = null, 38 | 39 | @SerializedName("overview") 40 | var overview: String? = null 41 | 42 | ) : Parcelable -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/source/remote/ApiCallback.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.source.remote 2 | 3 | import com.frogobox.base.BaseApiModel 4 | import com.google.gson.Gson 5 | import io.reactivex.rxjava3.disposables.Disposable 6 | import io.reactivex.rxjava3.core.Observer 7 | import retrofit2.HttpException 8 | import java.net.SocketTimeoutException 9 | import java.net.UnknownHostException 10 | 11 | /** 12 | * Created by Faisal Amir 13 | * FrogoBox Inc License 14 | * ========================================= 15 | * movie 16 | * Copyright (C) 16/11/2019. 17 | * All rights reserved 18 | * ----------------------------------------- 19 | * Name : Muhammad Faisal Amir 20 | * E-mail : faisalamircs@gmail.com 21 | * Github : github.com/amirisback 22 | * LinkedIn : linkedin.com/in/faisalamircs 23 | * ----------------------------------------- 24 | * FrogoBox Software Industries 25 | * com.frogobox.base.source.remote 26 | * 27 | */ 28 | abstract class ApiCallback : Observer { 29 | 30 | abstract fun onSuccess(model: M) 31 | 32 | abstract fun onFailure(code: Int, errorMessage: String) 33 | 34 | abstract fun onFinish() 35 | 36 | override fun onComplete() { 37 | onFinish() 38 | } 39 | 40 | override fun onNext(t: M) { 41 | onSuccess(t) 42 | } 43 | 44 | override fun onSubscribe(d: Disposable) { 45 | 46 | } 47 | 48 | override fun onError(e: Throwable) { 49 | e.printStackTrace() 50 | when (e) { 51 | is HttpException -> { 52 | val code = e.code() 53 | var msg = e.message() 54 | var baseDao: BaseApiModel? = null 55 | try { 56 | val body = e.response()?.errorBody() 57 | baseDao = Gson().fromJson>(body!!.string(), BaseApiModel::class.java) 58 | } catch (exception: Exception) { 59 | onFailure(code, exception.message!!) 60 | } 61 | 62 | when (code) { 63 | 504 -> { 64 | msg = baseDao?.message ?: "Error Response" 65 | } 66 | 502, 404 -> { 67 | msg = baseDao?.message ?: "Error Connect or Resource Not Found" 68 | } 69 | 400 -> { 70 | msg = baseDao?.message ?: "Bad Request" 71 | } 72 | 401 -> { 73 | msg = baseDao?.message ?: "Not Authorized" 74 | } 75 | } 76 | 77 | onFailure(code, msg) 78 | } 79 | 80 | is UnknownHostException -> onFailure(-1, "Telah terjadi kesalahan ketika koneksi ke server: ${e.message}") 81 | is SocketTimeoutException -> onFailure(-1, "Telah terjadi kesalahan ketika koneksi ke server: ${e.message}") 82 | else -> onFailure(-1, e.message ?: "Unknown error occured") 83 | } 84 | 85 | onFinish() 86 | } 87 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/AppExecutors.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import java.util.concurrent.Executor 6 | import java.util.concurrent.Executors 7 | 8 | /** 9 | * Created by Faisal Amir 10 | * FrogoBox Inc License 11 | * ========================================= 12 | * movie 13 | * Copyright (C) 16/11/2019. 14 | * All rights reserved 15 | * ----------------------------------------- 16 | * Name : Muhammad Faisal Amir 17 | * E-mail : faisalamircs@gmail.com 18 | * Github : github.com/amirisback 19 | * LinkedIn : linkedin.com/in/faisalamircs 20 | * ----------------------------------------- 21 | * FrogoBox Software Industries 22 | * com.frogobox.base.util 23 | * 24 | */ 25 | open class AppExecutors constructor( 26 | val diskIO: Executor = DiskIOThreadExecutor(), 27 | val networkIO: Executor = Executors.newFixedThreadPool(3), 28 | val mainThread: Executor = MainThreadExecutor() 29 | ) { 30 | 31 | private class MainThreadExecutor : Executor { 32 | private val mainThreadHandler = Handler(Looper.getMainLooper()) 33 | 34 | override fun execute(command: Runnable) { 35 | mainThreadHandler.post(command) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/DiskIOThreadExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import java.util.concurrent.Executor 4 | import java.util.concurrent.Executors 5 | 6 | /** 7 | * Created by Faisal Amir 8 | * FrogoBox Inc License 9 | * ========================================= 10 | * movie 11 | * Copyright (C) 16/11/2019. 12 | * All rights reserved 13 | * ----------------------------------------- 14 | * Name : Muhammad Faisal Amir 15 | * E-mail : faisalamircs@gmail.com 16 | * Github : github.com/amirisback 17 | * LinkedIn : linkedin.com/in/faisalamircs 18 | * ----------------------------------------- 19 | * FrogoBox Software Industries 20 | * com.frogobox.base.util 21 | * 22 | */ 23 | class DiskIOThreadExecutor: Executor { 24 | 25 | private val diskIO = Executors.newSingleThreadExecutor() 26 | 27 | override fun execute(command: Runnable) { 28 | diskIO.execute(command) 29 | } 30 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/Injection.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import android.content.Context 4 | import android.preference.PreferenceManager 5 | import com.frogobox.base.source.Repository 6 | import com.frogobox.base.source.local.AppDatabase 7 | import com.frogobox.base.source.local.LocalDataSource 8 | import com.frogobox.base.source.local.dao.FavoriteMovieDao 9 | import com.frogobox.base.source.local.dao.FavoriteTvShowDao 10 | import com.frogobox.base.source.remote.RemoteDataSource 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.base.util 27 | * 28 | */ 29 | object Injection { 30 | 31 | fun provideRepository(context: Context): Repository { 32 | 33 | val favoriteMovieDao: FavoriteMovieDao by lazy { 34 | AppDatabase.getInstance(context).favoriteMovieDao() 35 | } 36 | 37 | val favoriteTvShowDao: FavoriteTvShowDao by lazy { 38 | AppDatabase.getInstance(context).favoriteTvShowDao() 39 | } 40 | 41 | val appExecutors = AppExecutors() 42 | 43 | return Repository.getInstance( 44 | RemoteDataSource, LocalDataSource.getInstance( 45 | appExecutors, 46 | PreferenceManager.getDefaultSharedPreferences(context), 47 | favoriteMovieDao, favoriteTvShowDao)) 48 | } 49 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/NullAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import com.google.gson.TypeAdapter 4 | import com.google.gson.stream.JsonReader 5 | import com.google.gson.stream.JsonWriter 6 | import java.io.IOException 7 | import java.lang.reflect.Field 8 | import java.lang.reflect.InvocationTargetException 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.base.util 25 | * 26 | */ 27 | class NullAdapter : TypeAdapter() { 28 | @Throws(IOException::class) 29 | override fun write(jsonWriter: JsonWriter, o: Any) { 30 | jsonWriter.beginObject() 31 | for (field in o.javaClass.declaredFields) { 32 | val fieldValue = runGetter(field, o) 33 | jsonWriter.name(field.name) 34 | if (fieldValue == null) { 35 | jsonWriter.value("") 36 | } else { 37 | jsonWriter.value(fieldValue.toString()) 38 | } 39 | } 40 | jsonWriter.endObject() 41 | } 42 | 43 | @Throws(IOException::class) 44 | override fun read(jsonReader: JsonReader): Any? { 45 | /* Don't forget to add implementation here to have your Object back alive :) */ 46 | return null 47 | } 48 | 49 | companion object { 50 | /** 51 | * A generic field accessor runner. 52 | * Run the right getter on the field to get its value. 53 | * @param field 54 | * @param o `Object` 55 | * @return 56 | */ 57 | fun runGetter(field: Field, o: Any): Any? { 58 | // MZ: Find the correct method 59 | for (method in o.javaClass.methods) { 60 | if (method.name.startsWith("get") && method.name.length == field.name.length + 3) { 61 | if (method.name.toLowerCase().endsWith(field.name.toLowerCase())) { 62 | try { 63 | return method.invoke(o) 64 | } catch (e: IllegalAccessException) { 65 | e.printStackTrace() 66 | } catch (e: InvocationTargetException) { 67 | e.printStackTrace() 68 | } 69 | } 70 | } 71 | } 72 | return null 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/PagerHelper.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentManager 5 | import androidx.fragment.app.FragmentPagerAdapter 6 | 7 | /** 8 | * Created by Faisal Amir 9 | * FrogoBox Inc License 10 | * ========================================= 11 | * movie 12 | * Copyright (C) 16/11/2019. 13 | * All rights reserved 14 | * ----------------------------------------- 15 | * Name : Muhammad Faisal Amir 16 | * E-mail : faisalamircs@gmail.com 17 | * Github : github.com/amirisback 18 | * LinkedIn : linkedin.com/in/faisalamircs 19 | * ----------------------------------------- 20 | * FrogoBox Software Industries 21 | * com.frogobox.base.util 22 | * 23 | */ 24 | @Suppress("DEPRECATION") 25 | class PagerHelper(fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager){ 26 | 27 | private val fragments = ArrayList() 28 | private val titles = ArrayList() 29 | 30 | override fun getItem(position: Int): Fragment = fragments[position] 31 | 32 | override fun getCount(): Int = fragments.size 33 | 34 | override fun getPageTitle(position: Int): CharSequence? = titles[position] 35 | 36 | fun setupPagerFragment(fragment: Fragment, title: String) { 37 | fragments.add(fragment) 38 | titles.add(title) 39 | } 40 | } -------------------------------------------------------------------------------- /base/src/main/java/com/frogobox/base/util/SingleLiveEvent.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.base.util 2 | 3 | import android.util.Log 4 | import androidx.annotation.MainThread 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.MutableLiveData 7 | import androidx.lifecycle.Observer 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | 10 | /** 11 | * Created by Faisal Amir 12 | * FrogoBox Inc License 13 | * ========================================= 14 | * movie 15 | * Copyright (C) 16/11/2019. 16 | * All rights reserved 17 | * ----------------------------------------- 18 | * Name : Muhammad Faisal Amir 19 | * E-mail : faisalamircs@gmail.com 20 | * Github : github.com/amirisback 21 | * LinkedIn : linkedin.com/in/faisalamircs 22 | * ----------------------------------------- 23 | * FrogoBox Software Industries 24 | * com.frogobox.base.util 25 | * 26 | */ 27 | class SingleLiveEvent : MutableLiveData() { 28 | 29 | private val pending = AtomicBoolean(false) 30 | 31 | @MainThread 32 | override fun observe(owner: LifecycleOwner, observer: Observer) { 33 | 34 | if (hasActiveObservers()) { 35 | Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") 36 | } 37 | 38 | // Observe the internal MutableLiveData 39 | super.observe(owner, Observer { t -> 40 | if (pending.compareAndSet(true, false)) { 41 | observer.onChanged(t) 42 | } 43 | }) 44 | } 45 | 46 | @MainThread 47 | override fun setValue(t: T?) { 48 | pending.set(true) 49 | super.setValue(t) 50 | } 51 | 52 | /** 53 | * Used for cases where T is Void, to make calls cleaner. 54 | */ 55 | @MainThread 56 | fun call() { 57 | value = null 58 | } 59 | 60 | companion object { 61 | private const val TAG = "SingleLiveEvent" 62 | } 63 | } -------------------------------------------------------------------------------- /base/src/main/res/drawable/background_card.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_favorite.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_movie.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_notification.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_translate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_tv.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/ic_un_favorite.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /base/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 34 | 35 | 50 | 51 | -------------------------------------------------------------------------------- /base/src/main/res/layout/empty_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /base/src/main/res/layout/item_grid_tv_movie.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 23 | 24 | 36 | 37 | 46 | -------------------------------------------------------------------------------- /base/src/main/res/layout/item_list_tv_movie.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 22 | 23 | 35 | 36 | 47 | -------------------------------------------------------------------------------- /base/src/main/res/menu/bottom_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /base/src/main/res/menu/toolbar_favorite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /base/src/main/res/menu/toolbar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /base/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 | 22 | #720d5d 23 | #430033 24 | #FF9800 25 | 26 | 27 | #FFFFFF 28 | #000000 29 | #FF0000 30 | #757575 31 | 32 | #E9E7E7 33 | 34 | -------------------------------------------------------------------------------- /base/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 | 8dp 22 | 16dp 23 | -------------------------------------------------------------------------------- /base/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Base 3 | 4 | Movie 5 | TV 6 | Setting 7 | Favorite Movie 8 | Favorite TV Show 9 | Favorite 10 | 11 | Detail Movie 12 | Detail TV Show 13 | Search 14 | 15 | Empty Data 16 | 17 | Success add to favorite 18 | Success delete from favorite 19 | 20 | Release Reminder 21 | Reminder showing the movie that released today 22 | Daily Reminder 23 | Reminder for return to app 24 | Select Language 25 | 26 | Don\'t Forget Check Film Today 27 | I miss you 28 | New Movies! 29 | has been release today! 30 | 31 | 32 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id("com.android.application") version "7.1.2" apply false 4 | id("com.android.library") version "7.1.2" apply false 5 | id("org.jetbrains.kotlin.android") version DependencyGradle.KOTLIN_VERSION apply false 6 | } 7 | 8 | tasks.register("clean", Delete::class) { 9 | delete(rootProject.buildDir) 10 | } -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .gradle 3 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.`kotlin-dsl` 2 | 3 | plugins { 4 | `kotlin-dsl` 5 | } 6 | 7 | repositories { 8 | mavenCentral() 9 | maven { url = uri("https://jitpack.io") } 10 | } 11 | 12 | dependencies{ 13 | // library open-build-src 14 | implementation("com.github.frogobox:open-build-src:1.0.2") 15 | 16 | // library frogo-build-src 17 | implementation("com.github.frogobox:frogo-build-src:1.0.4") 18 | 19 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/AdmobValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by faisalamir on 12/03/22 3 | * movie 4 | * ----------------------------------------- 5 | * Name : Muhammad Faisal Amir 6 | * E-mail : faisalamircs@gmail.com 7 | * Github : github.com/amirisback 8 | * ----------------------------------------- 9 | * Copyright (C) 2022 Frogobox Media Inc. 10 | * All rights reserved 11 | * 12 | */ 13 | 14 | object AdmobValue { 15 | // Declaration admob id for debug 16 | const val debugAdmobAppId = "ca-app-pub-3940256099942544~3347511713" 17 | const val debugAdmobBanner = "ca-app-pub-3940256099942544/6300978111" 18 | const val debugAdmobInterstitial = "ca-app-pub-3940256099942544/1033173712" 19 | const val debugAdmobInterstitialVideo = "ca-app-pub-3940256099942544/8691691433" 20 | const val debugAdmobRewarded = "ca-app-pub-3940256099942544/5224354917" 21 | const val debugAdmobRewardedInterstitial = "ca-app-pub-3940256099942544/5354046379" 22 | const val debugAdmobNativeAdvanced = "ca-app-pub-3940256099942544/2247696110" 23 | const val debugAdmobNativeAdvancedVideo = "ca-app-pub-3940256099942544/1044960115" 24 | 25 | // Declaration admob id for release 26 | const val releaseAdmobAppId = "ca-app-pub-3940256099942544~3347511713" 27 | const val releaseAdmobBanner = "ca-app-pub-3940256099942544/6300978111" 28 | const val releaseAdmobInterstitial = "ca-app-pub-3940256099942544/1033173712" 29 | const val releaseAdmobInterstitialVideo = "ca-app-pub-3940256099942544/8691691433" 30 | const val releaseAdmobRewarded = "ca-app-pub-3940256099942544/5224354917" 31 | const val releaseAdmobRewardedInterstitial = "ca-app-pub-3940256099942544/5354046379" 32 | const val releaseAdmobNativeAdvanced = "ca-app-pub-3940256099942544/2247696110" 33 | const val releaseAdmobNativeAdvancedVideo = "ca-app-pub-3940256099942544/1044960115" 34 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/BuildConstant.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by faisalamir on 19/09/21 3 | * FrogoRecyclerView 4 | * ----------------------------------------- 5 | * Name : Muhammad Faisal Amir 6 | * E-mail : faisalamircs@gmail.com 7 | * Github : github.com/amirisback 8 | * ----------------------------------------- 9 | * Copyright (C) 2021 FrogoBox Inc. 10 | * All rights reserved 11 | * 12 | */ 13 | 14 | object BuildConstant { 15 | 16 | val TMDB_API_KEY = "\"5e922c3d4b1b0e96fffcc6b0b395878f\"" 17 | val TMDB_BASE_URL = "\"https://api.themoviedb.org/\"" 18 | val TMDB_BASE_LANG = "\"en-US\"" 19 | 20 | val TMDB_PATH_URL_IMAGE = "\"https://image.tmdb.org/t/p/w342/\"" 21 | 22 | val TMDB_URL_MOVIE = "\"3/discover/movie?api_key=5e922c3d4b1b0e96fffcc6b0b395878f&language=en-US\"" 23 | val TMDB_URL_TV = "\"3/discover/tv?api_key=5e922c3d4b1b0e96fffcc6b0b395878f&language=en-US\"" 24 | 25 | val TMDB_URL_SEARCH_MOVIE = "\"3/search/movie\"" 26 | val TMDB_URL_SEARCH_TV_SHOW = "\"3/search/tv\"" 27 | 28 | val TMDB_URL_RELEASE_BY_DATE = "\"3/discover/movie?api_key={API KEY}&primary_release_date.gte={TODAY DATE}&primary_release_date.lte={TODAY DATE}\"" 29 | 30 | } 31 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/DependencyGradle.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by faisalamir on 19/09/21 3 | * FrogoRecyclerView 4 | * ----------------------------------------- 5 | * Name : Muhammad Faisal Amir 6 | * E-mail : faisalamircs@gmail.com 7 | * Github : github.com/amirisback 8 | * ----------------------------------------- 9 | * Copyright (C) 2021 FrogoBox Inc. 10 | * All rights reserved 11 | * 12 | */ 13 | 14 | object DependencyGradle { 15 | 16 | const val KOTLIN_VERSION = Version.JetBrains.kotlin 17 | const val COMPOSE_MULTIPLATFORM_VERSION = Version.AndroidX.composeMultiPlatform 18 | 19 | } 20 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/ProjectSetting.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by faisalamir on 19/09/21 3 | * FrogoRecyclerView 4 | * ----------------------------------------- 5 | * Name : Muhammad Faisal Amir 6 | * E-mail : faisalamircs@gmail.com 7 | * Github : github.com/amirisback 8 | * ----------------------------------------- 9 | * Copyright (C) 2021 FrogoBox Inc. 10 | * All rights reserved 11 | * 12 | */ 13 | 14 | object ProjectSetting { 15 | 16 | const val NAME_APP = "FrogoKickStartAndroid" 17 | 18 | const val APP_DOMAIN = "com" 19 | const val APP_PLAY_CONSOLE = "frogobox" 20 | const val APP_NAME = "movie" 21 | 22 | const val APP_NAME_FAV = "favorite" 23 | 24 | const val LIBRARY_NAME_BASE = "base" 25 | 26 | const val MODULE_NAME_SDK = "base" 27 | 28 | // --------------------------------------------------------------------------------------------- 29 | 30 | const val VERSION_MAJOR = 1 31 | const val VERSION_MINOR = 0 32 | const val VERSION_PATCH = 0 33 | 34 | // --------------------------------------------------------------------------------------------- 35 | 36 | const val PROJECT_MIN_SDK = 21 37 | const val PROJECT_COMPILE_SDK = 31 38 | const val PROJECT_TARGET_SDK = PROJECT_COMPILE_SDK 39 | 40 | // --------------------------------------------------------------------------------------------- 41 | 42 | const val BASE_PACAKGE_NAME = "$APP_DOMAIN.$APP_PLAY_CONSOLE" 43 | 44 | const val PROJECT_APP_ID = "$BASE_PACAKGE_NAME.$APP_NAME" 45 | const val PROJECT_APP_FAV_ID = "$BASE_PACAKGE_NAME.$APP_NAME_FAV" 46 | const val PROJECT_LIB_ID_BASE = "$BASE_PACAKGE_NAME.$LIBRARY_NAME_BASE" 47 | 48 | const val PROJECT_VERSION_CODE = (VERSION_MAJOR * 100) + (VERSION_MINOR * 10) + (VERSION_PATCH * 1) 49 | const val PROJECT_VERSION_NAME = "$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" 50 | 51 | val NAME_APK = NAME_APP.toLowerCase().replace(" ", "-") 52 | 53 | val NAME_DB = NAME_APP.toLowerCase().replace(" ", "_") 54 | val DB = "\"$NAME_DB.db\"" 55 | 56 | // --------------------------------------------------------------------------------------------- 57 | 58 | const val PLAYSTORE_STORE_FILE = "frogoboxmedia.jks" 59 | const val PLAYSTORE_STORE_PASSWORD = "amirisback" 60 | const val PLAYSTORE_KEY_ALIAS = "frogoisback" 61 | const val PLAYSTORE_KEY_PASSWORD = "amirisback" 62 | 63 | } 64 | -------------------------------------------------------------------------------- /docs/image/mad_score.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/mad_score.png -------------------------------------------------------------------------------- /docs/image/ss_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/ss_1.png -------------------------------------------------------------------------------- /docs/image/ss_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/ss_2.png -------------------------------------------------------------------------------- /docs/image/ss_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/ss_3.png -------------------------------------------------------------------------------- /docs/image/ss_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/ss_banner.png -------------------------------------------------------------------------------- /docs/image/ss_play_store.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/docs/image/ss_play_store.jpg -------------------------------------------------------------------------------- /favorite/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /favorite/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("org.jetbrains.kotlin.android") 4 | id("kotlin-kapt") 5 | id("kotlin-parcelize") 6 | } 7 | 8 | android { 9 | 10 | compileSdk = ProjectSetting.PROJECT_COMPILE_SDK 11 | 12 | defaultConfig { 13 | applicationId = ProjectSetting.PROJECT_APP_FAV_ID 14 | minSdk = ProjectSetting.PROJECT_MIN_SDK 15 | targetSdk = ProjectSetting.PROJECT_TARGET_SDK 16 | versionCode = ProjectSetting.PROJECT_VERSION_CODE 17 | versionName = ProjectSetting.PROJECT_VERSION_NAME 18 | 19 | multiDexEnabled = true 20 | vectorDrawables.useSupportLibrary = true 21 | 22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 23 | 24 | // Naming APK // AAB 25 | setProperty("archivesBaseName", "${ProjectSetting.NAME_APK}(${versionName})") 26 | 27 | // Declaration apps name debug mode 28 | val debugAttribute = "Development" 29 | val nameAppDebug = "${ProjectSetting.NAME_APP} $debugAttribute" 30 | resourceConfigurations += setOf("en", "id") 31 | 32 | // Inject app name for debug 33 | resValue("string", "app_name", nameAppDebug) 34 | 35 | 36 | } 37 | 38 | buildTypes { 39 | getByName("release") { 40 | isMinifyEnabled = false 41 | 42 | proguardFiles( 43 | getDefaultProguardFile("proguard-android-optimize.txt"), 44 | "proguard-rules.pro" 45 | ) 46 | } 47 | } 48 | 49 | buildFeatures { 50 | viewBinding = true 51 | } 52 | 53 | compileOptions { 54 | sourceCompatibility = JavaVersion.VERSION_11 55 | targetCompatibility = JavaVersion.VERSION_11 56 | } 57 | 58 | tasks.withType { 59 | kotlinOptions { 60 | jvmTarget = JavaVersion.VERSION_11.toString() 61 | } 62 | } 63 | 64 | } 65 | 66 | dependencies { 67 | 68 | implementation(project(":base")) 69 | 70 | implementation(Androidx.appCompat) 71 | implementation(Androidx.Core.ktx) 72 | implementation(Androidx.constraintLayout) 73 | implementation(Androidx.preferenceKtx) 74 | 75 | implementation(Androidx.Lifecycle.viewmodelKtx) 76 | implementation(Androidx.Work.runtimeKtx) 77 | 78 | implementation(Androidx.Room.runtime) 79 | implementation(Androidx.Room.ktx) 80 | implementation(Androidx.Room.rxJava3) 81 | 82 | implementation(Google.gson) 83 | implementation(Google.material) 84 | 85 | implementation(Square.okhttp) 86 | implementation(Square.okhttpLogging) 87 | implementation(Square.Retrofit2.retrofit) 88 | implementation(Square.Retrofit2.converterGson) 89 | implementation(Square.Retrofit2.adapterRxJava3) 90 | 91 | implementation(Reactivex.rxJava3) 92 | implementation(Reactivex.rxAndroid3) 93 | 94 | implementation(Util.glide) 95 | 96 | implementation(Frogo.ui) 97 | implementation(Frogo.recyclerView) 98 | 99 | implementation(Koin.core) 100 | implementation(Koin.android) 101 | implementation(Koin.androidCompat) 102 | implementation(Koin.androidxWorkManager) 103 | implementation(Koin.androidxCompose) 104 | 105 | api("com.google.dagger:dagger:2.38.1") 106 | api(JetBrains.coroutinesCore) 107 | api(JetBrains.coroutinesAndroid) 108 | 109 | kapt(Androidx.Lifecycle.compiler) 110 | kapt(Androidx.Room.compiler) 111 | kapt(Util.glideCompiler) 112 | kapt("com.google.dagger:dagger-compiler:2.37") 113 | 114 | testImplementation("junit:junit:4.13.2") 115 | testImplementation("androidx.room:room-testing:2.4.2") 116 | 117 | androidTestImplementation("androidx.room:room-testing:2.4.2") 118 | androidTestImplementation("androidx.arch.core:core-testing:2.1.0") 119 | androidTestImplementation("androidx.test:runner:1.4.0") 120 | androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") 121 | 122 | } -------------------------------------------------------------------------------- /favorite/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /favorite/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.main 2 | 3 | import android.os.Bundle 4 | import com.frogobox.base.util.PagerHelper 5 | import com.frogobox.favorite.R 6 | import com.frogobox.favorite.databinding.ActivityMainBinding 7 | import com.frogobox.favorite.mvvm.movie.MovieFragment 8 | import com.frogobox.favorite.mvvm.tv.TvShowFragment 9 | import com.frogobox.favorite.util.BaseFavoriteActivity 10 | import com.frogobox.favorite.mvvm.movie.MovieViewModel 11 | import com.frogobox.favorite.mvvm.tv.TvShowViewModel 12 | 13 | class MainActivity : BaseFavoriteActivity() { 14 | 15 | override fun setupViewBinding(): ActivityMainBinding { 16 | return ActivityMainBinding.inflate(layoutInflater) 17 | } 18 | 19 | override fun setupViewModel() {} 20 | 21 | override fun setupUI(savedInstanceState: Bundle?) { 22 | mActivity.title = getString(R.string.title_favorite) 23 | setupViewPager() 24 | } 25 | 26 | fun obtainMovieViewModel(): MovieViewModel = 27 | obtainViewModel(MovieViewModel::class.java) 28 | 29 | fun obtainTvShowViewModel(): TvShowViewModel = 30 | obtainViewModel(TvShowViewModel::class.java) 31 | 32 | private fun setupViewPager() { 33 | val pagerAdapter = PagerHelper(supportFragmentManager) 34 | pagerAdapter.setupPagerFragment( 35 | MovieFragment(), 36 | resources.getString(R.string.title_favorite_movie) 37 | ) 38 | pagerAdapter.setupPagerFragment( 39 | TvShowFragment(), 40 | resources.getString(R.string.title_favorite_tv_show) 41 | ) 42 | binding.apply { 43 | viewpager.adapter = pagerAdapter 44 | tablayout.setupWithViewPager(viewpager) 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/movie/MovieFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.movie 2 | 3 | 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.lifecycle.Observer 9 | import androidx.recyclerview.widget.LinearLayoutManager 10 | import com.frogobox.base.BaseFragment 11 | import com.frogobox.base.BaseListener 12 | import com.frogobox.base.source.model.FavoriteMovie 13 | import com.frogobox.favorite.R 14 | import com.frogobox.favorite.databinding.FragmentTvMovieListBinding 15 | import com.frogobox.favorite.mvvm.main.MainActivity 16 | 17 | /** 18 | * A simple [Fragment] subclass. 19 | */ 20 | class MovieFragment : BaseFragment(), 21 | BaseListener { 22 | 23 | private lateinit var mViewModel: MovieViewModel 24 | 25 | override fun setupViewBinding( 26 | inflater: LayoutInflater, 27 | container: ViewGroup 28 | ): FragmentTvMovieListBinding { 29 | return FragmentTvMovieListBinding.inflate(inflater, container, false) 30 | } 31 | 32 | override fun setupViewModel() { 33 | mViewModel = (activity as MainActivity).obtainMovieViewModel().apply { 34 | 35 | favMovieListLive.observe(viewLifecycleOwner) { 36 | setupRecyclerView(it) 37 | } 38 | 39 | eventShowProgress.observe(viewLifecycleOwner) { 40 | setupEventProgressView(binding?.progressBar!!, it) 41 | } 42 | 43 | eventIsEmpty.observe(viewLifecycleOwner) { 44 | // setupEventEmptyView(empty_view, it) 45 | } 46 | 47 | } 48 | } 49 | 50 | override fun setupUI(savedInstanceState: Bundle?) { 51 | getMovie() 52 | } 53 | 54 | override fun onResume() { 55 | super.onResume() 56 | getMovie() 57 | } 58 | 59 | private fun getMovie() { 60 | mViewModel.getFavoriteMovie() 61 | } 62 | 63 | private fun setupRecyclerView(data: List) { 64 | val adapter = com.frogobox.base.adapter.FavoriteMovieAdapter() 65 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_list_tv_movie) } 66 | adapter.setRecyclerViewListener(this) 67 | adapter.setRecyclerViewData(data) 68 | binding?.apply { 69 | recyclerView.adapter = adapter 70 | recyclerView.layoutManager = 71 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 72 | } 73 | } 74 | 75 | override fun onItemClicked(data: FavoriteMovie) { 76 | context?.let { 77 | baseStartActivity( 78 | it, 79 | DetailMovieActivity.EXTRA_FAV_MOVIE, 80 | data 81 | ) 82 | } 83 | } 84 | 85 | override fun onItemLongClicked(data: FavoriteMovie) {} 86 | 87 | } 88 | -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/movie/MovieViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.movie 2 | 3 | import android.app.Application 4 | import android.database.Cursor 5 | import com.frogobox.base.BaseViewModel 6 | import com.frogobox.base.source.model.FavoriteMovie 7 | import com.frogobox.base.source.DataSource 8 | import com.frogobox.base.source.Repository 9 | import com.frogobox.base.util.Constant.RoomDatabase.Movie.setupCursor 10 | import com.frogobox.base.util.SingleLiveEvent 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.favorite 27 | * 28 | */ 29 | class MovieViewModel(private val context: Application, private val repository: Repository) : 30 | BaseViewModel(context) { 31 | 32 | var favMovieListLive = SingleLiveEvent>() 33 | 34 | fun getFavoriteMovie() { 35 | repository.getFavoriteMovieCursor(context, object : 36 | DataSource.GetLocalCallBack { 37 | override fun onShowProgressDialog() { 38 | eventShowProgress.postValue(true) 39 | } 40 | 41 | override fun onHideProgressDialog() { 42 | eventShowProgress.postValue(false) 43 | } 44 | 45 | override fun onSuccess(data: Cursor) { 46 | favMovieListLive.postValue(setupCursor(data)) 47 | eventIsEmpty.postValue(false) 48 | } 49 | 50 | override fun onFinish() { 51 | 52 | } 53 | 54 | override fun onEmpty() { 55 | eventIsEmpty.postValue(true) 56 | } 57 | 58 | override fun onFailed(statusCode: Int, errorMessage: String?) { 59 | 60 | } 61 | }) 62 | } 63 | 64 | 65 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/tv/DetailTvShowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.tv 2 | 3 | import android.app.Application 4 | import android.database.Cursor 5 | import com.frogobox.base.BaseViewModel 6 | import com.frogobox.base.source.DataSource 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.source.model.FavoriteTvShow 9 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.setupCursor 10 | import com.frogobox.base.util.SingleLiveEvent 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.favorite 27 | * 28 | */ 29 | class DetailTvShowViewModel(private val context: Application, private val repository: Repository) : 30 | BaseViewModel(context) { 31 | 32 | var favoriteTvShow = SingleLiveEvent() 33 | var eventIsFavorite = SingleLiveEvent() 34 | 35 | fun saveFavoriteTvShow( 36 | data: FavoriteTvShow, 37 | callback: com.frogobox.base.callback.SaveViewCallback 38 | ) { 39 | callback.onShowProgress() 40 | if (repository.saveFavoriteTvShow(context, data)) { 41 | callback.onHideProgress() 42 | callback.onSuccesInsert() 43 | eventIsFavorite.postValue(true) 44 | } else { 45 | callback.onHideProgress() 46 | callback.onFailed("Failed") 47 | } 48 | } 49 | 50 | fun deleteFavoriteTvShow( 51 | tableId: Int, 52 | callback: com.frogobox.base.callback.DeleteViewCallback 53 | ) { 54 | callback.onShowProgress() 55 | if (repository.deleteFavoriteMovieProvider(context, tableId)) { 56 | callback.onHideProgress() 57 | callback.onSuccesDelete() 58 | eventIsFavorite.postValue(false) 59 | } else { 60 | callback.onHideProgress() 61 | callback.onFailed("Failed") 62 | } 63 | } 64 | 65 | fun getFavoriteTvShow(id: Int) { 66 | repository.getFavoriteTvShowCursor(context, object : DataSource.GetLocalCallBack { 67 | override fun onShowProgressDialog() { 68 | eventShowProgress.postValue(true) 69 | } 70 | 71 | override fun onHideProgressDialog() { 72 | eventShowProgress.postValue(false) 73 | } 74 | 75 | override fun onSuccess(data: Cursor) { 76 | 77 | val tempFavoriteList = mutableListOf() 78 | tempFavoriteList.clear() 79 | tempFavoriteList.addAll(setupCursor(data)) 80 | 81 | for (i in tempFavoriteList.indices) { 82 | if (tempFavoriteList[i].id!! == id) { 83 | eventIsEmpty.postValue(false) 84 | eventIsFavorite.postValue(true) 85 | favoriteTvShow.postValue(tempFavoriteList[i]) 86 | break 87 | } else { 88 | eventIsEmpty.postValue(true) 89 | eventIsFavorite.postValue(false) 90 | } 91 | } 92 | } 93 | 94 | override fun onFinish() {} 95 | 96 | override fun onEmpty() { 97 | eventIsEmpty.postValue(true) 98 | eventIsFavorite.postValue(false) 99 | } 100 | 101 | override fun onFailed(statusCode: Int, errorMessage: String?) {} 102 | }) 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/tv/TvShowFragment.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.tv 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.fragment.app.Fragment 7 | import androidx.lifecycle.Observer 8 | import androidx.recyclerview.widget.LinearLayoutManager 9 | import com.frogobox.base.source.model.FavoriteTvShow 10 | import com.frogobox.base.adapter.FavoriteTvShowAdapter 11 | import com.frogobox.base.BaseFragment 12 | import com.frogobox.base.BaseListener 13 | import com.frogobox.favorite.mvvm.main.MainActivity 14 | import com.frogobox.favorite.R 15 | import com.frogobox.favorite.databinding.FragmentTvMovieListBinding 16 | 17 | /** 18 | * A simple [Fragment] subclass. 19 | */ 20 | class TvShowFragment : BaseFragment(), 21 | BaseListener { 22 | 23 | private lateinit var mViewModel: TvShowViewModel 24 | 25 | override fun setupViewBinding( 26 | inflater: LayoutInflater, 27 | container: ViewGroup 28 | ): FragmentTvMovieListBinding { 29 | return FragmentTvMovieListBinding.inflate(inflater, container, false) 30 | } 31 | 32 | override fun setupViewModel() { 33 | mViewModel = (activity as MainActivity).obtainTvShowViewModel().apply { 34 | 35 | favTvShowListLive.observe(viewLifecycleOwner) { 36 | setupRecyclerView(it) 37 | } 38 | 39 | eventShowProgress.observe(viewLifecycleOwner) { 40 | setupEventProgressView(binding?.progressBar!!, it) 41 | } 42 | 43 | eventIsEmpty.observe(viewLifecycleOwner) { 44 | // setupEventEmptyView(empty_view, it) 45 | } 46 | 47 | } 48 | } 49 | 50 | 51 | override fun setupUI(savedInstanceState: Bundle?) { 52 | getTvShow() 53 | } 54 | 55 | private fun getTvShow() { 56 | mViewModel.getFavoriteTvShow() 57 | } 58 | 59 | override fun onResume() { 60 | super.onResume() 61 | getTvShow() 62 | } 63 | 64 | private fun setupRecyclerView(data: List) { 65 | val adapter = FavoriteTvShowAdapter() 66 | context?.let { adapter.setRecyclerViewLayout(it, R.layout.item_list_tv_movie) } 67 | adapter.setRecyclerViewListener(this) 68 | adapter.setRecyclerViewData(data) 69 | binding?.apply { 70 | recyclerView.adapter = adapter 71 | recyclerView.layoutManager = 72 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 73 | } 74 | } 75 | 76 | override fun onItemClicked(data: FavoriteTvShow) { 77 | context?.let { 78 | baseStartActivity( 79 | it, 80 | DetailTvShowActivity.EXTRA_FAV_TV, 81 | data 82 | ) 83 | } 84 | } 85 | 86 | override fun onItemLongClicked(data: FavoriteTvShow) {} 87 | 88 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/mvvm/tv/TvShowViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.mvvm.tv 2 | 3 | import android.app.Application 4 | import android.database.Cursor 5 | import com.frogobox.base.BaseViewModel 6 | import com.frogobox.base.source.model.FavoriteTvShow 7 | import com.frogobox.base.source.DataSource 8 | import com.frogobox.base.source.Repository 9 | import com.frogobox.base.util.Constant.RoomDatabase.TvShow.setupCursor 10 | import com.frogobox.base.util.SingleLiveEvent 11 | 12 | /** 13 | * Created by Faisal Amir 14 | * FrogoBox Inc License 15 | * ========================================= 16 | * movie 17 | * Copyright (C) 16/11/2019. 18 | * All rights reserved 19 | * ----------------------------------------- 20 | * Name : Muhammad Faisal Amir 21 | * E-mail : faisalamircs@gmail.com 22 | * Github : github.com/amirisback 23 | * LinkedIn : linkedin.com/in/faisalamircs 24 | * ----------------------------------------- 25 | * FrogoBox Software Industries 26 | * com.frogobox.favorite 27 | * 28 | */ 29 | class TvShowViewModel(private val context: Application, private val repository: Repository) : 30 | BaseViewModel(context) { 31 | 32 | var favTvShowListLive = SingleLiveEvent>() 33 | 34 | fun getFavoriteTvShow() { 35 | repository.getFavoriteTvShowCursor(context, object : DataSource.GetLocalCallBack { 36 | override fun onShowProgressDialog() { 37 | eventShowProgress.postValue(true) 38 | } 39 | 40 | override fun onHideProgressDialog() { 41 | eventShowProgress.postValue(false) 42 | } 43 | 44 | override fun onSuccess(data: Cursor) { 45 | eventIsEmpty.postValue(false) 46 | favTvShowListLive.postValue(setupCursor(data)) 47 | } 48 | 49 | override fun onFinish() { 50 | 51 | } 52 | 53 | override fun onEmpty() { 54 | eventIsEmpty.postValue(true) 55 | } 56 | 57 | override fun onFailed(statusCode: Int, errorMessage: String?) { 58 | 59 | } 60 | }) 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/util/BaseFavoriteActivity.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.util 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import androidx.viewbinding.ViewBinding 6 | import com.frogobox.base.BaseActivity 7 | 8 | /** 9 | * Created by Faisal Amir 10 | * FrogoBox Inc License 11 | * ========================================= 12 | * movie 13 | * Copyright (C) 16/11/2019. 14 | * All rights reserved 15 | * ----------------------------------------- 16 | * Name : Muhammad Faisal Amir 17 | * E-mail : faisalamircs@gmail.com 18 | * Github : github.com/amirisback 19 | * LinkedIn : linkedin.com/in/faisalamircs 20 | * ----------------------------------------- 21 | * FrogoBox Software Industries 22 | * com.frogobox.favorite 23 | * 24 | */ 25 | abstract class BaseFavoriteActivity : BaseActivity() { 26 | 27 | fun obtainViewModel(viewModelClass: Class) = 28 | ViewModelProvider( 29 | this, 30 | ViewModelFactory.getInstance(application) 31 | ).get(viewModelClass) 32 | 33 | } -------------------------------------------------------------------------------- /favorite/src/main/java/com/frogobox/favorite/util/ViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.frogobox.favorite.util 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.ViewModelProvider 7 | import com.frogobox.base.source.Repository 8 | import com.frogobox.base.util.Injection 9 | import com.frogobox.favorite.mvvm.movie.DetailMovieViewModel 10 | import com.frogobox.favorite.mvvm.tv.DetailTvShowViewModel 11 | import com.frogobox.favorite.mvvm.movie.MovieViewModel 12 | import com.frogobox.favorite.mvvm.tv.TvShowViewModel 13 | 14 | /** 15 | * Created by Faisal Amir 16 | * FrogoBox Inc License 17 | * ========================================= 18 | * movie 19 | * Copyright (C) 16/11/2019. 20 | * All rights reserved 21 | * ----------------------------------------- 22 | * Name : Muhammad Faisal Amir 23 | * E-mail : faisalamircs@gmail.com 24 | * Github : github.com/amirisback 25 | * LinkedIn : linkedin.com/in/faisalamircs 26 | * ----------------------------------------- 27 | * FrogoBox Software Industries 28 | * com.frogobox.favorite 29 | * 30 | */ 31 | class ViewModelFactory private constructor( 32 | private val mApplication: Application, 33 | private val repository: Repository 34 | ) : ViewModelProvider.NewInstanceFactory() { 35 | 36 | @Suppress("UNCHECKED_CAST") 37 | override fun create(modelClass: Class): T = 38 | with(modelClass) { 39 | when { 40 | 41 | isAssignableFrom(MovieViewModel::class.java) -> 42 | MovieViewModel(mApplication, repository) 43 | 44 | isAssignableFrom(TvShowViewModel::class.java) -> 45 | TvShowViewModel(mApplication, repository) 46 | 47 | isAssignableFrom(DetailMovieViewModel::class.java) -> 48 | DetailMovieViewModel(mApplication, repository) 49 | 50 | isAssignableFrom(DetailTvShowViewModel::class.java) -> 51 | DetailTvShowViewModel(mApplication, repository) 52 | 53 | 54 | else -> 55 | throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") 56 | } 57 | } as T 58 | 59 | companion object { 60 | 61 | @SuppressLint("StaticFieldLeak") 62 | @Volatile private var INSTANCE: ViewModelFactory? = null 63 | 64 | fun getInstance(mApplication: Application) = 65 | INSTANCE 66 | ?: synchronized(ViewModelFactory::class.java) { 67 | INSTANCE 68 | ?: ViewModelFactory( 69 | mApplication, 70 | Injection.provideRepository(mApplication.applicationContext) 71 | ) 72 | .also { INSTANCE = it } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /favorite/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /favorite/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 22 | 23 | 32 | 33 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /favorite/src/main/res/layout/fragment_tv_movie_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /favorite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/favorite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /favorite/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /favorite/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Hello blank fragment 3 | 4 | -------------------------------------------------------------------------------- /favorite/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frogobox/kick-start-android-modular/8cb6e3c49650be2dfe586570d44dd76c207a4598/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 24 09:00:12 ICT 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-7.2-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 7 | maven { url = uri("https://jitpack.io") } 8 | } 9 | } 10 | 11 | dependencyResolutionManagement { 12 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 13 | repositories { 14 | google() 15 | mavenCentral() 16 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 17 | maven { url = uri("https://jitpack.io") } 18 | } 19 | } 20 | 21 | rootProject.name="movie" 22 | include(":app", ":base", ":favorite") 23 | --------------------------------------------------------------------------------