├── .editorconfig
├── .github
└── workflows
│ ├── develop.yml
│ └── master.yml
├── .gitignore
├── .idea
└── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── Contributing.md
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
├── sampledata
│ ├── candidates.csv
│ ├── faq.csv
│ └── party.csv
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── com
│ │ │ └── popstack
│ │ │ └── mvoter2015
│ │ │ ├── MVoterApp.kt
│ │ │ ├── config
│ │ │ └── AppFirstTimeConfig.kt
│ │ │ ├── core
│ │ │ ├── BaseActivity.kt
│ │ │ ├── BaseController.kt
│ │ │ ├── LifeCycleAwareController.kt
│ │ │ ├── mvp
│ │ │ │ ├── InvalidMvpImplementationException.kt
│ │ │ │ └── MvvmController.kt
│ │ │ └── recyclerview
│ │ │ │ └── ViewBindingViewHolder.kt
│ │ │ ├── di
│ │ │ ├── AppInjector.kt
│ │ │ ├── Injectable.kt
│ │ │ ├── component
│ │ │ │ └── AppComponent.kt
│ │ │ ├── conductor
│ │ │ │ ├── ConductorInjection.kt
│ │ │ │ └── ConductorInjectionModule.kt
│ │ │ ├── module
│ │ │ │ └── AppModule.kt
│ │ │ └── viewmodel
│ │ │ │ ├── DaggerViewModelFactory.kt
│ │ │ │ ├── ViewModelFactoryModule.kt
│ │ │ │ └── ViewModelKey.kt
│ │ │ ├── exception
│ │ │ ├── GlobalExceptionHandler.kt
│ │ │ └── PagingExceptionWrapper.kt
│ │ │ ├── feature
│ │ │ ├── HasRouter.kt
│ │ │ ├── HostActivity.kt
│ │ │ ├── about
│ │ │ │ ├── AboutController.kt
│ │ │ │ ├── AboutFeatureModule.kt
│ │ │ │ └── LicenseController.kt
│ │ │ ├── analytics
│ │ │ │ ├── location
│ │ │ │ │ ├── FakeLoggingSelectedLocationAnalyticsImpl.kt
│ │ │ │ │ ├── RealSelectedLocationAnalyticsImpl.kt
│ │ │ │ │ ├── SelectedLocationAnalytics.kt
│ │ │ │ │ └── SelectedLocationAnalyticsModule.kt
│ │ │ │ └── screen
│ │ │ │ │ ├── CanTrackScreen.kt
│ │ │ │ │ ├── ScreenTrackAnalytics.kt
│ │ │ │ │ ├── ScreenTrackAnalyticsImpl.kt
│ │ │ │ │ └── ScreenTrackAnalyticsProvider.kt
│ │ │ ├── appupdate
│ │ │ │ └── RelaxedAppUpdateBottomSheet.kt
│ │ │ ├── browser
│ │ │ │ ├── OpenBrowserDelegate.kt
│ │ │ │ └── OpenBrowserHandler.kt
│ │ │ ├── candidate
│ │ │ │ ├── CandidateFeatureModule.kt
│ │ │ │ ├── detail
│ │ │ │ │ ├── CandidateDetailController.kt
│ │ │ │ │ ├── CandidateDetailViewModel.kt
│ │ │ │ │ └── CandidateDetailsViewItem.kt
│ │ │ │ ├── listing
│ │ │ │ │ ├── CandidateListController.kt
│ │ │ │ │ ├── CandidateListHousePagerAdapter.kt
│ │ │ │ │ ├── CandidateListHouseViewItem.kt
│ │ │ │ │ ├── CandidateListPagerParentRouter.kt
│ │ │ │ │ ├── CandidateListRecyclerViewAdapter.kt
│ │ │ │ │ ├── CandidateListViewCache.kt
│ │ │ │ │ ├── CandidateListViewItem.kt
│ │ │ │ │ ├── CandidateListViewModel.kt
│ │ │ │ │ ├── CandidateListViewPagerTrackScreen.kt
│ │ │ │ │ ├── lowerhouse
│ │ │ │ │ │ ├── LowerHouseCandidateListController.kt
│ │ │ │ │ │ └── LowerHouseCandidateListViewModel.kt
│ │ │ │ │ ├── regionalhouse
│ │ │ │ │ │ ├── RegionalHouseCandidateListController.kt
│ │ │ │ │ │ └── RegionalHouseCandidateListViewModel.kt
│ │ │ │ │ ├── upperhouse
│ │ │ │ │ │ ├── UpperHouseCandidateListController.kt
│ │ │ │ │ │ └── UpperHouseCandidateListViewModel.kt
│ │ │ │ │ └── viewholders
│ │ │ │ │ │ └── CandidateItemViewHolder.kt
│ │ │ │ └── search
│ │ │ │ │ ├── CandidateSearchController.kt
│ │ │ │ │ ├── CandidateSearchPagingAdapter.kt
│ │ │ │ │ ├── CandidateSearchResultViewItem.kt
│ │ │ │ │ └── CandidateSearchViewModel.kt
│ │ │ ├── faq
│ │ │ │ ├── FaqCategoryExt.kt
│ │ │ │ ├── FaqCategoryRecyclerViewAdapter.kt
│ │ │ │ ├── FaqCategorySelectActivity.kt
│ │ │ │ ├── FaqController.kt
│ │ │ │ ├── FaqFeatureModule.kt
│ │ │ │ ├── FaqPagingAdapter.kt
│ │ │ │ ├── FaqViewItem.kt
│ │ │ │ ├── FaqViewModel.kt
│ │ │ │ ├── ballot
│ │ │ │ │ ├── BallotCategoryRecyclerViewAdapter.kt
│ │ │ │ │ ├── BallotExampleCategoryExt.kt
│ │ │ │ │ ├── BallotExampleController.kt
│ │ │ │ │ ├── BallotExampleRecyclerViewAdapter.kt
│ │ │ │ │ ├── BallotExampleSelectActivity.kt
│ │ │ │ │ ├── BallotExampleViewItem.kt
│ │ │ │ │ └── BallotExampleViewModel.kt
│ │ │ │ └── search
│ │ │ │ │ ├── FaqSearchController.kt
│ │ │ │ │ ├── FaqSearchPagingAdapter.kt
│ │ │ │ │ ├── FaqSearchViewItem.kt
│ │ │ │ │ └── FaqSearchViewModel.kt
│ │ │ ├── home
│ │ │ │ ├── BottomNavigationHostController.kt
│ │ │ │ └── BottomNavigationHostViewModelStore.kt
│ │ │ ├── image
│ │ │ │ └── FullScreenImageViewActivity.kt
│ │ │ ├── location
│ │ │ │ ├── LocationUpdateController.kt
│ │ │ │ ├── LocationUpdateFeatureModule.kt
│ │ │ │ ├── LocationUpdateViewModel.kt
│ │ │ │ ├── StateRegionTownshipRecyclerViewAdapter.kt
│ │ │ │ ├── StateRegionTownshipViewItem.kt
│ │ │ │ ├── TownshipChooserController.kt
│ │ │ │ ├── TownshipChooserViewModel.kt
│ │ │ │ ├── TownshipRecyclerViewAdapter.kt
│ │ │ │ ├── TownshipViewItem.kt
│ │ │ │ ├── WardChooserController.kt
│ │ │ │ ├── WardChooserViewModel.kt
│ │ │ │ └── WardRecyclerViewAdapter.kt
│ │ │ ├── news
│ │ │ │ ├── NewsController.kt
│ │ │ │ ├── NewsDateTimeFormatter.kt
│ │ │ │ ├── NewsFeatureModule.kt
│ │ │ │ ├── NewsRecyclerViewAdapter.kt
│ │ │ │ ├── NewsViewItem.kt
│ │ │ │ ├── NewsViewModel.kt
│ │ │ │ └── search
│ │ │ │ │ ├── NewsSearchController.kt
│ │ │ │ │ ├── NewsSearchPagingAdapter.kt
│ │ │ │ │ ├── NewsSearchViewItem.kt
│ │ │ │ │ └── NewsSearchViewModel.kt
│ │ │ ├── party
│ │ │ │ ├── PartyFeatureModule.kt
│ │ │ │ ├── PartySharedElementTransitionChangeHandler.kt
│ │ │ │ ├── detail
│ │ │ │ │ ├── PartyContactViewItem.kt
│ │ │ │ │ ├── PartyDetailController.kt
│ │ │ │ │ ├── PartyDetailViewItem.kt
│ │ │ │ │ ├── PartyDetailViewModel.kt
│ │ │ │ │ └── PartyTimelineRecyclerViewAdapter.kt
│ │ │ │ ├── listing
│ │ │ │ │ ├── PartyListController.kt
│ │ │ │ │ ├── PartyListHeaderRecycleViewAdapter.kt
│ │ │ │ │ ├── PartyListViewItem.kt
│ │ │ │ │ ├── PartyListViewItemRecyclerViewAdapter.kt
│ │ │ │ │ └── PartyListViewModel.kt
│ │ │ │ └── search
│ │ │ │ │ ├── PartySearchController.kt
│ │ │ │ │ ├── PartySearchPagingAdapter.kt
│ │ │ │ │ ├── PartySearchResultViewItem.kt
│ │ │ │ │ └── PartySearchViewModel.kt
│ │ │ ├── settings
│ │ │ │ ├── AppSettings.kt
│ │ │ │ ├── AppTheme.kt
│ │ │ │ ├── AppThemeSpinnerAdapter.kt
│ │ │ │ ├── SettingsController.kt
│ │ │ │ └── SettingsFeatureModule.kt
│ │ │ ├── share
│ │ │ │ └── ShareUrlFactory.kt
│ │ │ ├── splash
│ │ │ │ └── SplashController.kt
│ │ │ ├── voterlist
│ │ │ │ ├── VoterListController.kt
│ │ │ │ ├── VoterListFeatureModule.kt
│ │ │ │ ├── VoterListLinkRecyclerViewAdapter.kt
│ │ │ │ └── VoterListLinks.kt
│ │ │ └── votingguide
│ │ │ │ ├── CountDownCalculator.kt
│ │ │ │ ├── VotingGuideController.kt
│ │ │ │ ├── VotingGuideFeatureModule.kt
│ │ │ │ ├── VotingGuideRecyclerViewAdapter.kt
│ │ │ │ ├── VotingGuideViewItem.kt
│ │ │ │ ├── VotingGuideViewModel.kt
│ │ │ │ └── viewholders
│ │ │ │ ├── CheckVoterListViewHolder.kt
│ │ │ │ ├── HeaderViewHolder.kt
│ │ │ │ ├── SectionTitleViewHolder.kt
│ │ │ │ ├── StepViewHolder.kt
│ │ │ │ └── VotingGuideViewHolder.kt
│ │ │ ├── font
│ │ │ ├── BurmeseFont.kt
│ │ │ ├── FontOverrideSearchView.kt
│ │ │ ├── FontOverrideTextInputEditText.kt
│ │ │ ├── FontOverrideTextInputLayout.kt
│ │ │ ├── MDetect.kt
│ │ │ └── Rabbit.kt
│ │ │ ├── helper
│ │ │ ├── AndroidDispatcherProvider.kt
│ │ │ ├── ConstituencyTab.kt
│ │ │ ├── LocalityUtils.kt
│ │ │ ├── RecyclerViewMarginDecoration.kt
│ │ │ ├── StringList.kt
│ │ │ ├── ViewVisibilityDebounceHandler.kt
│ │ │ ├── asyncviewstate
│ │ │ │ ├── AsyncViewState.kt
│ │ │ │ └── AsyncViewStateLiveData.kt
│ │ │ ├── conductor
│ │ │ │ ├── BNVRouterPagerAdapter.kt
│ │ │ │ └── ConductorExtensions.kt
│ │ │ ├── diff
│ │ │ │ └── Diff.kt
│ │ │ ├── extensions
│ │ │ │ ├── ActivityExtensions.kt
│ │ │ │ ├── ContextExtensions.kt
│ │ │ │ ├── DimensionExtensions.kt
│ │ │ │ ├── MenuExtensions.kt
│ │ │ │ ├── SpinnerExtensions.kt
│ │ │ │ ├── TabLayoutExtension.kt
│ │ │ │ ├── TextViewExtensions.kt
│ │ │ │ ├── ViewExtensions.kt
│ │ │ │ └── ViewHolderExtensions.kt
│ │ │ ├── intent
│ │ │ │ └── Intents.kt
│ │ │ ├── livedata
│ │ │ │ └── SingleLiveEvent.kt
│ │ │ ├── recyclerview
│ │ │ │ └── StickyHeaderDecoration.kt
│ │ │ ├── search
│ │ │ │ └── DebounceSearchQueryListener.kt
│ │ │ ├── spannable
│ │ │ │ └── CenteredImageSpan.kt
│ │ │ └── viewpager
│ │ │ │ └── NonScrollableViewPager.kt
│ │ │ ├── logging
│ │ │ ├── BreadcrumbControllerChangeHandler.kt
│ │ │ └── HasTag.kt
│ │ │ └── paging
│ │ │ └── CommonLoadStateAdapter.kt
│ └── res
│ │ ├── color
│ │ ├── tint_button_policy.xml
│ │ └── tint_button_policy_text.xml
│ │ ├── drawable-hdpi
│ │ ├── ic_candidate_filled.png
│ │ ├── ic_candidate_outline.png
│ │ ├── ic_how_to_vote_filled.png
│ │ ├── ic_how_to_vote_outline.png
│ │ ├── ic_info_filled.png
│ │ ├── ic_info_outline.png
│ │ ├── ic_news_filled.png
│ │ ├── ic_news_outline.png
│ │ ├── ic_party_filled.png
│ │ ├── ic_party_outline.png
│ │ ├── ic_vote_result_filled.png
│ │ └── ic_vote_result_outline.png
│ │ ├── drawable-mdpi
│ │ ├── ic_candidate_filled.png
│ │ ├── ic_candidate_outline.png
│ │ ├── ic_how_to_vote_filled.png
│ │ ├── ic_how_to_vote_outline.png
│ │ ├── ic_info_filled.png
│ │ ├── ic_info_outline.png
│ │ ├── ic_news_filled.png
│ │ ├── ic_news_outline.png
│ │ ├── ic_party_filled.png
│ │ ├── ic_party_outline.png
│ │ ├── ic_vote_result_filled.png
│ │ └── ic_vote_result_outline.png
│ │ ├── drawable-night-nodpi
│ │ ├── mvoter_logo.webp
│ │ ├── mvoter_logo_horizontal.webp
│ │ ├── no_photo.webp
│ │ ├── no_recording.webp
│ │ ├── no_selfie.webp
│ │ ├── no_video.webp
│ │ └── popstack_logo.webp
│ │ ├── drawable-nodpi
│ │ ├── ballot_stack.webp
│ │ ├── ic_clock_4pm.webp
│ │ ├── ic_clock_6am.webp
│ │ ├── mvoter_logo.webp
│ │ ├── mvoter_logo_horizontal.webp
│ │ ├── no_photo.webp
│ │ ├── no_recording.webp
│ │ ├── no_selfie.webp
│ │ ├── no_video.webp
│ │ ├── popstack_logo.webp
│ │ └── uec_logo.webp
│ │ ├── drawable-xhdpi
│ │ ├── ic_candidate_filled.png
│ │ ├── ic_candidate_outline.png
│ │ ├── ic_how_to_vote_filled.png
│ │ ├── ic_how_to_vote_outline.png
│ │ ├── ic_info_filled.png
│ │ ├── ic_info_outline.png
│ │ ├── ic_news_filled.png
│ │ ├── ic_news_outline.png
│ │ ├── ic_party_filled.png
│ │ ├── ic_party_outline.png
│ │ ├── ic_vote_result_filled.png
│ │ └── ic_vote_result_outline.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_candidate_filled.png
│ │ ├── ic_candidate_outline.png
│ │ ├── ic_how_to_vote_filled.png
│ │ ├── ic_how_to_vote_outline.png
│ │ ├── ic_info_filled.png
│ │ ├── ic_info_outline.png
│ │ ├── ic_news_filled.png
│ │ ├── ic_news_outline.png
│ │ ├── ic_party_filled.png
│ │ ├── ic_party_outline.png
│ │ ├── ic_vote_result_filled.png
│ │ └── ic_vote_result_outline.png
│ │ ├── drawable-xxxhdpi
│ │ ├── ic_candidate_filled.png
│ │ ├── ic_candidate_outline.png
│ │ ├── ic_how_to_vote_filled.png
│ │ ├── ic_how_to_vote_outline.png
│ │ ├── ic_info_filled.png
│ │ ├── ic_info_outline.png
│ │ ├── ic_news_filled.png
│ │ ├── ic_news_outline.png
│ │ ├── ic_party_filled.png
│ │ ├── ic_party_outline.png
│ │ ├── ic_vote_result_filled.png
│ │ └── ic_vote_result_outline.png
│ │ ├── drawable
│ │ ├── accent_oval.xml
│ │ ├── background_accent_oval.xml
│ │ ├── bg_accent_oval_ring.xml
│ │ ├── bg_chip_accent.xml
│ │ ├── bg_chip_accent_transparent.xml
│ │ ├── bg_how_to_vote_header.xml
│ │ ├── color_state_bottom_nav.xml
│ │ ├── ic_arrow_drop_down_24.xml
│ │ ├── ic_arrow_left_24.xml
│ │ ├── ic_arrow_right_24.xml
│ │ ├── ic_arrow_right_text_primary_24.xml
│ │ ├── ic_baseline_access_time_24.xml
│ │ ├── ic_baseline_check_white_24.xml
│ │ ├── ic_baseline_clear_24.xml
│ │ ├── ic_baseline_keyboard_arrow_down_24.xml
│ │ ├── ic_check_circle_24.xml
│ │ ├── ic_check_voter_list.xml
│ │ ├── ic_clock_4pm_18.xml
│ │ ├── ic_clock_6am_18.xml
│ │ ├── ic_close_24.xml
│ │ ├── ic_close_circle_24.xml
│ │ ├── ic_close_circle_transparent_24.xml
│ │ ├── ic_download.xml
│ │ ├── ic_email_24.xml
│ │ ├── ic_facebook_24.xml
│ │ ├── ic_flag_24.xml
│ │ ├── ic_gavel.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_location_on_24.xml
│ │ ├── ic_menu_candidate.xml
│ │ ├── ic_menu_how_to_vote.xml
│ │ ├── ic_menu_info.xml
│ │ ├── ic_menu_news.xml
│ │ ├── ic_menu_party.xml
│ │ ├── ic_menu_vote_result.xml
│ │ ├── ic_nav_on_primary_bg.xml
│ │ ├── ic_nav_white_always.xml
│ │ ├── ic_newspaper.xml
│ │ ├── ic_outlined_flag_24.xml
│ │ ├── ic_over_flow_white_always.xml
│ │ ├── ic_people_fill.xml
│ │ ├── ic_place_24.xml
│ │ ├── ic_search_24.xml
│ │ ├── ic_share_24.xml
│ │ ├── ic_share_accent_24.xml
│ │ ├── ic_star_12.xml
│ │ ├── ic_star_24.xml
│ │ ├── ic_star_white_12.xml
│ │ ├── ic_website_24.xml
│ │ ├── party_seal_placeholder_rect.xml
│ │ ├── placeholder_oval.xml
│ │ ├── placeholder_rect.xml
│ │ ├── tab_indicator.xml
│ │ └── timeline_dot.xml
│ │ ├── font
│ │ ├── pyidaungsu.xml
│ │ ├── pyidaungsu_bold.ttf
│ │ ├── pyidaungsu_regular.ttf
│ │ └── zawgyi.ttf
│ │ ├── layout-ldpi
│ │ └── controller_location.xml
│ │ ├── layout-mdpi
│ │ └── controller_location.xml
│ │ ├── layout-sw600dp
│ │ ├── controller_candidate_detail.xml
│ │ ├── controller_party_detail.xml
│ │ ├── item_faq_ballot_example.xml
│ │ ├── item_faq_check_voter_list.xml
│ │ └── item_faq_polling_station_prohibition.xml
│ │ ├── layout
│ │ ├── activity_ballot_category_select.xml
│ │ ├── activity_faq_category_select.xml
│ │ ├── activity_host.xml
│ │ ├── activity_image_full_screen_view.xml
│ │ ├── bottom_sheet_relaxed_app_update.xml
│ │ ├── controller_about.xml
│ │ ├── controller_ballot_example.xml
│ │ ├── controller_bottom_nav_host.xml
│ │ ├── controller_candidate_detail.xml
│ │ ├── controller_candidate_list.xml
│ │ ├── controller_candidate_search.xml
│ │ ├── controller_constituent_candidate_list.xml
│ │ ├── controller_faq.xml
│ │ ├── controller_faq_search.xml
│ │ ├── controller_how_to_vote.xml
│ │ ├── controller_license.xml
│ │ ├── controller_location.xml
│ │ ├── controller_lower_house_candidate_list.xml
│ │ ├── controller_news.xml
│ │ ├── controller_news_search.xml
│ │ ├── controller_party_detail.xml
│ │ ├── controller_party_list.xml
│ │ ├── controller_party_search.xml
│ │ ├── controller_regional_candidate_list.xml
│ │ ├── controller_settings.xml
│ │ ├── controller_township_chooser.xml
│ │ ├── controller_upper_house_candidate_list.xml
│ │ ├── controller_voter_list.xml
│ │ ├── controller_ward_chooser.xml
│ │ ├── footer_pager_load_state.xml
│ │ ├── item_ballot.xml
│ │ ├── item_ballot_category.xml
│ │ ├── item_candidate.xml
│ │ ├── item_candidate_section.xml
│ │ ├── item_election_countdown.xml
│ │ ├── item_faq.xml
│ │ ├── item_faq_ballot_example.xml
│ │ ├── item_faq_category.xml
│ │ ├── item_faq_check_voter_list.xml
│ │ ├── item_faq_laws_and_unfair_practice.xml
│ │ ├── item_faq_polling_station_prohibition.xml
│ │ ├── item_how_to_vote_check_voter_list.xml
│ │ ├── item_how_to_vote_header.xml
│ │ ├── item_how_to_vote_section_title.xml
│ │ ├── item_how_to_vote_step.xml
│ │ ├── item_news_no_preview.xml
│ │ ├── item_news_with_preview.xml
│ │ ├── item_party.xml
│ │ ├── item_party_header.xml
│ │ ├── item_party_timeline.xml
│ │ ├── item_state_region_township.xml
│ │ ├── item_township.xml
│ │ ├── item_voter_list.xml
│ │ ├── item_ward.xml
│ │ └── tab_constituency.xml
│ │ ├── menu
│ │ ├── menu_bottom_nav.xml
│ │ ├── menu_candidate.xml
│ │ ├── menu_candidate_detail.xml
│ │ ├── menu_faq.xml
│ │ ├── menu_image_view.xml
│ │ ├── menu_news.xml
│ │ ├── menu_party.xml
│ │ └── menu_party_detail.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ └── open_source_license.html
│ │ ├── values-night
│ │ └── colors.xml
│ │ ├── values-sw600dp
│ │ └── attrs.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── content_descriptions.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── styles_text.xml
│ └── test
│ └── java
│ └── com
│ └── popstack
│ └── mvoter2015
│ ├── feature
│ └── votingguide
│ │ └── CountDownCalculatorTest.kt
│ └── helper
│ └── LocalityUtilsTest.kt
├── build.gradle.kts
├── buildSrc
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── AndroidDepHandlers.kt
│ ├── AndroidXActivityDep.kt
│ ├── AndroidXArch.kt
│ ├── AndroidXDataStore.kt
│ ├── AndroidXEspressoDep.kt
│ ├── AndroidXFragmentDep.kt
│ ├── AndroidXNavigationDep.kt
│ ├── AndroidXTestDep.kt
│ ├── DaggerDep.kt
│ ├── Dependencies.kt
│ ├── MockitoDep.kt
│ └── MoshiDep.kt
├── coroutinetestrule
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── aungkyawpaing
│ └── coroutinetestrule
│ └── CoroutineTestRule.kt
├── data
├── android
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── popstack
│ │ │ └── mvoter2015
│ │ │ └── data
│ │ │ └── android
│ │ │ ├── AndroidDataModule.kt
│ │ │ ├── appupdate
│ │ │ ├── AndroidAppUpdateManager.kt
│ │ │ ├── AppUpdateModule.kt
│ │ │ ├── AppVersionProviderImpl.kt
│ │ │ ├── NoOpAppUpdateManager.kt
│ │ │ ├── SkipVersionCache.kt
│ │ │ └── SkipVersionCacheImpl.kt
│ │ │ ├── candidate
│ │ │ ├── CandidatePagerFactory.kt
│ │ │ └── CandidateSearchPagingSource.kt
│ │ │ ├── faq
│ │ │ ├── FaqPagerFactory.kt
│ │ │ ├── FaqPagingSource.kt
│ │ │ └── FaqSearchPagingSource.kt
│ │ │ ├── news
│ │ │ ├── NewsPagerFactory.kt
│ │ │ ├── NewsPagingSource.kt
│ │ │ └── NewsSearchPagingSource.kt
│ │ │ └── party
│ │ │ ├── PartyPagerFactory.kt
│ │ │ ├── PartyPagingSource.kt
│ │ │ └── PartySearchPagingSource.kt
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── popstack
│ │ └── mvoter2015
│ │ └── data
│ │ └── android
│ │ └── appupdate
│ │ ├── AndroidAppUpdateManagerTest.kt
│ │ ├── FakeAppVersionProvider.kt
│ │ └── FakeSkipVersionCache.kt
├── cache
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── popstack
│ │ │ │ └── mvoter2015
│ │ │ │ └── data
│ │ │ │ └── cache
│ │ │ │ ├── DbProvider.kt
│ │ │ │ ├── TableAdapters.kt
│ │ │ │ ├── appupdate
│ │ │ │ ├── AppUpdateCacheSourceImpl.kt
│ │ │ │ └── AppUpdateSerializer.kt
│ │ │ │ ├── columnadapter
│ │ │ │ ├── BallotExampleIdColumnAdapter.kt
│ │ │ │ ├── CandidateIdColumnAdapter.kt
│ │ │ │ ├── CandidateParentColumnAdapter.kt
│ │ │ │ ├── ConstituencyIdColumnAdapter.kt
│ │ │ │ ├── FaqIdColumnAdapter.kt
│ │ │ │ ├── LocalDateColumnAdapter.kt
│ │ │ │ ├── LocalDateTimeColumnAdapter.kt
│ │ │ │ ├── NewsIdColumnAdapter.kt
│ │ │ │ ├── PartyIdColumnAdapter.kt
│ │ │ │ └── StringListColumnAdapter.kt
│ │ │ │ ├── di
│ │ │ │ ├── CacheModule.kt
│ │ │ │ └── SqlDelightModule.kt
│ │ │ │ ├── map
│ │ │ │ └── TableToModelMap.kt
│ │ │ │ └── source
│ │ │ │ ├── CandidateCacheSourceImpl.kt
│ │ │ │ ├── FaqCacheSourceImpl.kt
│ │ │ │ ├── LocationCacheSourceImpl.kt
│ │ │ │ ├── NewsCacheSourceImpl.kt
│ │ │ │ ├── PartyCacheSourceImpl.kt
│ │ │ │ └── location
│ │ │ │ ├── StateTownshipSerializer.kt
│ │ │ │ └── WardSerializer.kt
│ │ ├── proto
│ │ │ └── mvoter2015
│ │ │ │ ├── appupdate.proto
│ │ │ │ ├── stateTownship.proto
│ │ │ │ └── ward.proto
│ │ └── sqldelight
│ │ │ ├── com
│ │ │ └── popstack
│ │ │ │ └── mvoter2015
│ │ │ │ └── data
│ │ │ │ └── cache
│ │ │ │ └── entity
│ │ │ │ ├── BallotExampleTable.sq
│ │ │ │ ├── CandidateTable.sq
│ │ │ │ ├── CandidateWithConstituencyView.sq
│ │ │ │ ├── ConstitutencyTable.sq
│ │ │ │ ├── FaqTable.sq
│ │ │ │ ├── NewsTable.sq
│ │ │ │ └── PartyTable.sq
│ │ │ └── migrations
│ │ │ ├── 1.sqm
│ │ │ └── 2.sqm
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── popstack
│ │ └── mvoter2015
│ │ └── data
│ │ └── cache
│ │ ├── TestDbProvider.kt
│ │ └── columnadapter
│ │ ├── CandidateParentColumnAdapterTest.kt
│ │ └── StringListColumnAdapterTest.kt
├── common
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── popstack
│ │ └── mvoter2015
│ │ └── data
│ │ └── common
│ │ ├── appupdate
│ │ ├── AppUpdate.kt
│ │ ├── AppUpdateCacheSource.kt
│ │ └── AppUpdateNetworkSource.kt
│ │ ├── candidate
│ │ ├── CandidateCacheSource.kt
│ │ ├── CandidateNetworkSource.kt
│ │ └── CandidateRepositoryImpl.kt
│ │ ├── di
│ │ └── DataModule.kt
│ │ ├── faq
│ │ ├── FaqCacheSource.kt
│ │ ├── FaqNetworkSource.kt
│ │ └── FaqRepositoryImpl.kt
│ │ ├── location
│ │ ├── LocationCacheSource.kt
│ │ ├── LocationNetworkSource.kt
│ │ └── LocationRepositoryImpl.kt
│ │ ├── news
│ │ ├── NewsCacheSource.kt
│ │ ├── NewsNetworkSource.kt
│ │ └── NewsRepositoryImpl.kt
│ │ └── party
│ │ ├── PartyCacheSource.kt
│ │ ├── PartyNetworkSource.kt
│ │ └── PartyRepositoryImpl.kt
├── fakenetwork
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── fakestack
│ │ │ └── mvoter2015
│ │ │ └── data
│ │ │ └── network
│ │ │ ├── FakeAppUpdateNetworkDataSource.kt
│ │ │ ├── FakeCandidateNetworkDataSource.kt
│ │ │ ├── FakeFaqNetworkDataSource.kt
│ │ │ ├── FakeLocationNetworkDataSource.kt
│ │ │ ├── FakeNewsNetworkDataSource.kt
│ │ │ ├── FakePartyNetworkDataSource.kt
│ │ │ ├── di
│ │ │ └── NetworkModule.kt
│ │ │ ├── jsonadapter
│ │ │ ├── BallotExampleCategoryJsonAdapter.kt
│ │ │ ├── FaqCategoryJsonAdapter.kt
│ │ │ └── LocalDateJsonAdapter.kt
│ │ │ └── model
│ │ │ ├── FakeBallotExampleApiModel.kt
│ │ │ ├── FakeCandidateApiModel.kt
│ │ │ ├── FakeFaqApiModel.kt
│ │ │ ├── FakeNewsApiModel.kt
│ │ │ └── FakePartyApiModel.kt
│ │ └── res
│ │ └── raw
│ │ ├── ballot_example.json
│ │ ├── candidate_lower_house.json
│ │ ├── candidate_state_region.json
│ │ ├── candidate_upper_house.json
│ │ ├── faq.json
│ │ ├── news.json
│ │ └── party.json
└── network
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── popstack
│ └── mvoter2015
│ └── data
│ └── network
│ ├── api
│ ├── CandidateApiModel.kt
│ ├── ConstituencyApiModel.kt
│ ├── FaqApiModel.kt
│ ├── GetBallotExampleListResponse.kt
│ ├── GetStateRegionListResponse.kt
│ ├── GetTownshipListResponse.kt
│ ├── MvoterApi.kt
│ ├── NewsApiModel.kt
│ ├── PartyApiModel.kt
│ └── WardApiModel.kt
│ ├── appupdate
│ └── AppUpdateNetworkSourceImpl.kt
│ ├── auth
│ ├── AuthTokenInterceptor.kt
│ ├── AuthTokenStore.kt
│ ├── AuthTokenStoreImpl.kt
│ └── RefreshAuthenticator.kt
│ ├── di
│ ├── NetworkModule.kt
│ ├── OkHttpProvider.kt
│ ├── RetrofitProvider.kt
│ └── ServiceModule.kt
│ ├── helper
│ ├── OkHttpExtensions.kt
│ └── RetrofitExtension.kt
│ ├── jsonadapter
│ ├── BallotExampleCategoryJsonAdapter.kt
│ ├── FaqCategoryJsonAdapter.kt
│ └── LocalDateJsonAdapter.kt
│ └── source
│ ├── CandidateNetworkSourceImpl.kt
│ ├── FaqNetworkSourceImpl.kt
│ ├── LocationNetworkSourceImpl.kt
│ ├── NewsNetworkSourceImpl.kt
│ └── PartyNetworkSourceImpl.kt
├── domain
├── .gitignore
├── build.gradle.kts
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── popstack
│ │ └── mvoter2015
│ │ └── domain
│ │ ├── CoroutineUseCase.kt
│ │ ├── DispatcherProvider.kt
│ │ ├── candidate
│ │ ├── CandidateRepository.kt
│ │ ├── model
│ │ │ ├── Candidate.kt
│ │ │ ├── CandidateGender.kt
│ │ │ ├── CandidateId.kt
│ │ │ └── CandidateParent.kt
│ │ └── usecase
│ │ │ ├── GetCandidate.kt
│ │ │ ├── GetMyLowerHouseCandidateList.kt
│ │ │ ├── GetMyStateRegionHouseCandidateList.kt
│ │ │ ├── GetMyUpperHouseCandidateList.kt
│ │ │ ├── GetRivalCandidateList.kt
│ │ │ └── exception
│ │ │ └── NoStateRegionConstituencyException.kt
│ │ ├── constituency
│ │ ├── model
│ │ │ ├── Constituency.kt
│ │ │ ├── ConstituencyId.kt
│ │ │ └── HouseType.kt
│ │ ├── repository
│ │ │ └── ConstituencyRepository.kt
│ │ └── usecase
│ │ │ ├── GetMyLowerHouseConstituency.kt
│ │ │ ├── GetMyStateRegionConstituency.kt
│ │ │ └── GetMyUpperHouseConstituency.kt
│ │ ├── exception
│ │ └── NetworkException.kt
│ │ ├── faq
│ │ ├── FaqRepository.kt
│ │ ├── model
│ │ │ ├── BallotExample.kt
│ │ │ ├── BallotExampleCategory.kt
│ │ │ ├── BallotExampleId.kt
│ │ │ ├── Faq.kt
│ │ │ ├── FaqCategory.kt
│ │ │ └── FaqId.kt
│ │ └── usecase
│ │ │ ├── GetBallotExampleList.kt
│ │ │ ├── GetFaq.kt
│ │ │ ├── GetFaqCategories.kt
│ │ │ └── GetFaqList.kt
│ │ ├── infra
│ │ ├── AppUpdateManager.kt
│ │ └── AppVersionProvider.kt
│ │ ├── location
│ │ ├── FlowUseCase.kt
│ │ ├── LocationRepository.kt
│ │ ├── model
│ │ │ ├── CombinedLocation.kt
│ │ │ ├── LatLng.kt
│ │ │ ├── StateRegion.kt
│ │ │ ├── StateRegionPCode.kt
│ │ │ ├── StateRegionTownship.kt
│ │ │ ├── StateRegionType.kt
│ │ │ ├── Township.kt
│ │ │ ├── TownshipPCode.kt
│ │ │ ├── Ward.kt
│ │ │ └── WardId.kt
│ │ └── usecase
│ │ │ ├── GetStateRegionList.kt
│ │ │ ├── GetTownshipsForStateRegion.kt
│ │ │ ├── GetUserSelectedLocation.kt
│ │ │ ├── GetUserStateRegion.kt
│ │ │ ├── GetUserStateRegionTownship.kt
│ │ │ ├── GetUserTownship.kt
│ │ │ ├── GetUserWard.kt
│ │ │ ├── GetWardDetails.kt
│ │ │ ├── GetWardsForTownship.kt
│ │ │ ├── SaveUserStateRegionTownship.kt
│ │ │ ├── SaveUserWard.kt
│ │ │ └── UpdateWardDetails.kt
│ │ ├── news
│ │ ├── NewsRepository.kt
│ │ ├── model
│ │ │ ├── News.kt
│ │ │ └── NewsId.kt
│ │ └── usecase
│ │ │ └── GetNewsList.kt
│ │ ├── party
│ │ ├── model
│ │ │ ├── Party.kt
│ │ │ └── PartyId.kt
│ │ └── usecase
│ │ │ ├── GetParty.kt
│ │ │ ├── GetPartyList.kt
│ │ │ ├── PartyRepository.kt
│ │ │ └── SearchParty.kt
│ │ └── utils
│ │ ├── BurmeseNumberUtils.kt
│ │ ├── InputSanitizer.kt
│ │ ├── MyanmarPhoneNumberExtractorWithZeroOneSupport.kt
│ │ └── ZeroOneRule.kt
│ └── test
│ └── java
│ └── com
│ └── popstack
│ └── mvoter2015
│ └── domain
│ ├── candidate
│ └── usecase
│ │ └── GetMyUpperHouseCandidateListBehaviorTest.kt
│ └── utils
│ └── MyanmarPhoneNumberExtractorWithZeroOneSupportTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── release.properties
├── self_host_deploy.gradle
├── settings.gradle.kts
└── simplespinneradapter
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
└── main
├── AndroidManifest.xml
├── java
└── com
│ └── aungkyawpaing
│ └── simplespinneradapter
│ ├── SimpleSpinnerAdapter.kt
│ └── StringSimpleSpinnerAdapter.kt
└── res
└── layout
└── spinner_row.xml
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{kt,kts}]
2 | # possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
3 | indent_size=2
4 | # true (recommended) / false
5 | insert_final_newline=false
6 | # possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
7 | # it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide)
8 | max_line_length=off
9 | # To prevent ordering issue currently not supported by IntelliJ
10 | # https://github.com/pinterest/ktlint/issues/527
11 | disabled_rules=import-ordering,comment-spacing,no-blank-line-before-rbrace
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Firebase
2 | # https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=android
3 | -keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
4 | -keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
5 |
6 | -keep class androidx.datastore.preferences.** { *; }
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/config/AppFirstTimeConfig.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.config
2 |
3 | import android.content.Context
4 | import androidx.datastore.preferences.createDataStore
5 | import androidx.datastore.preferences.edit
6 | import androidx.datastore.preferences.preferencesKey
7 | import kotlinx.coroutines.flow.first
8 | import javax.inject.Inject
9 |
10 | class AppFirstTimeConfig @Inject constructor(context: Context) {
11 |
12 | private val sharedPreferences = context.createDataStore("mvoter2020_app_first_time")
13 |
14 | companion object {
15 | private val KEY_IS_FIRST_TIME = preferencesKey("mvoter2020_is_first_time")
16 | }
17 |
18 | suspend fun isFirstTime(): Boolean {
19 | return sharedPreferences.data.first()[KEY_IS_FIRST_TIME] ?: true
20 | }
21 |
22 | suspend fun setFirstTimeStatus(isFirstTime: Boolean) {
23 | sharedPreferences.edit {
24 | it[KEY_IS_FIRST_TIME] = isFirstTime
25 | }
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/core/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.core
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.viewbinding.ViewBinding
6 |
7 | abstract class BaseActivity : AppCompatActivity() {
8 |
9 | abstract val binding: VB
10 |
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | setContentView(binding.root)
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/core/LifeCycleAwareController.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.core
2 |
3 | import android.os.Bundle
4 | import androidx.lifecycle.Lifecycle
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.viewbinding.ViewBinding
7 | import com.bluelinelabs.conductor.archlifecycle.ControllerLifecycleOwner
8 |
9 | abstract class LifeCycleAwareController(args: Bundle? = null) :
10 | BaseController(args), LifecycleOwner {
11 |
12 | protected val lifecycleOwner by lazy {
13 | ControllerLifecycleOwner(this)
14 | }
15 |
16 | override fun getLifecycle(): Lifecycle {
17 | return lifecycleOwner.lifecycle
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/core/mvp/InvalidMvpImplementationException.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.core.mvp
2 |
3 | class InvalidMvpImplementationException : Throwable()
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/core/recyclerview/ViewBindingViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.core.recyclerview
2 |
3 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
4 | import androidx.viewbinding.ViewBinding
5 |
6 | abstract class ViewBindingViewHolder(
7 | val binding: VB
8 | ) : ViewHolder(binding.root)
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/Injectable.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di
2 |
3 | /**
4 | * Created by Vincent on 2019-05-23
5 | */
6 | interface Injectable
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/conductor/ConductorInjection.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di.conductor
2 |
3 | import com.bluelinelabs.conductor.Controller
4 | import dagger.android.HasAndroidInjector
5 |
6 | object ConductorInjection {
7 |
8 | fun inject(controller: Controller) {
9 |
10 | val activity = controller.activity!!
11 |
12 | if (activity is HasAndroidInjector) {
13 | activity.androidInjector().inject(controller)
14 | } else {
15 | val application = activity.application
16 |
17 | if (application is HasAndroidInjector) {
18 | application.androidInjector().inject(controller)
19 | } else {
20 | throw UnsupportedOperationException("Either activity or android need to implement AndroidInjector")
21 | }
22 | }
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/conductor/ConductorInjectionModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di.conductor
2 |
3 | import com.bluelinelabs.conductor.Controller
4 | import dagger.Module
5 | import dagger.android.AndroidInjectionModule
6 | import dagger.internal.Beta
7 | import dagger.internal.Factory
8 | import dagger.multibindings.Multibinds
9 |
10 | @Beta
11 | @Module(includes = [AndroidInjectionModule::class])
12 | abstract class ConductorInjectionModule {
13 |
14 | @Multibinds
15 | abstract fun controllerInjectorFactories(): Map?, Factory?>?
16 |
17 | @Multibinds
18 | abstract fun controllerInjectorFactoriesWithStringKeys(): Map?>?
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/viewmodel/DaggerViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import javax.inject.Inject
6 | import javax.inject.Provider
7 | import javax.inject.Singleton
8 |
9 | /**
10 | * Created by Vincent on 12/6/18
11 | */
12 | @Singleton
13 | class DaggerViewModelFactory @Inject constructor(
14 | private val creators: Map, @JvmSuppressWildcards Provider>
15 | ) : ViewModelProvider.Factory {
16 | override fun create(modelClass: Class): T {
17 | val creator = creators[modelClass] ?: creators.entries.firstOrNull {
18 | modelClass.isAssignableFrom(it.key)
19 | }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
20 | try {
21 | @Suppress("UNCHECKED_CAST")
22 | return creator.get() as T
23 | } catch (e: Exception) {
24 | throw RuntimeException(e)
25 | }
26 |
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/viewmodel/ViewModelFactoryModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di.viewmodel
2 |
3 | import androidx.lifecycle.ViewModelProvider
4 | import dagger.Binds
5 | import dagger.Module
6 |
7 | /**
8 | * Created by Vincent on 12/11/18
9 | */
10 | @Module
11 | abstract class ViewModelFactoryModule {
12 |
13 | @Binds
14 | abstract fun viewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/di/viewmodel/ViewModelKey.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.di.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import dagger.MapKey
5 | import kotlin.reflect.KClass
6 |
7 | /**
8 | * Created by Vincent on 12/6/18
9 | */
10 | @MustBeDocumented
11 | @Target(
12 | AnnotationTarget.FUNCTION,
13 | AnnotationTarget.PROPERTY_GETTER,
14 | AnnotationTarget.PROPERTY_SETTER
15 | )
16 | @Retention(AnnotationRetention.RUNTIME)
17 | @MapKey
18 | annotation class ViewModelKey(val value: KClass)
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/exception/PagingExceptionWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.exception
2 |
3 | data class PagingExceptionWrapper(
4 | val errorMessage: String,
5 | val originalException: Throwable
6 | ) : Throwable() {
7 |
8 | override val message: String?
9 | get() = errorMessage
10 |
11 | override val cause: Throwable?
12 | get() = originalException
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/HasRouter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature
2 |
3 | import com.bluelinelabs.conductor.Router
4 |
5 | interface HasRouter {
6 |
7 | fun router(): Router
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/about/AboutFeatureModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.about
2 |
3 | import dagger.Module
4 | import dagger.android.ContributesAndroidInjector
5 |
6 | @Module
7 | abstract class AboutFeatureModule {
8 |
9 | @ContributesAndroidInjector
10 | abstract fun aboutController(): AboutController
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/location/FakeLoggingSelectedLocationAnalyticsImpl.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.location
2 |
3 | import com.popstack.mvoter2015.domain.location.model.CombinedLocation
4 | import timber.log.Timber
5 | import javax.inject.Inject
6 |
7 | class FakeLoggingSelectedLocationAnalyticsImpl @Inject constructor() : SelectedLocationAnalytics {
8 |
9 | override fun logLocation(combinedLocation: CombinedLocation) {
10 | Timber.i("state: ${combinedLocation.stateRegion}, township : ${combinedLocation.township}, ward : ${combinedLocation.ward ?: "N/A"} ")
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/location/RealSelectedLocationAnalyticsImpl.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.location
2 |
3 | import android.content.Context
4 | import com.google.firebase.analytics.FirebaseAnalytics
5 | import com.popstack.mvoter2015.domain.location.model.CombinedLocation
6 | import javax.inject.Inject
7 |
8 | class RealSelectedLocationAnalyticsImpl @Inject constructor(
9 | private val context: Context
10 | ) : SelectedLocationAnalytics {
11 |
12 | companion object {
13 | private const val PROPERTY_NAME_GEO = "geo"
14 | }
15 |
16 | override fun logLocation(combinedLocation: CombinedLocation) {
17 | val property = with(combinedLocation) {
18 | if (ward == null) {
19 | "$stateRegion|$township"
20 | } else {
21 | "$ward|$township|$stateRegion"
22 | }
23 | }
24 | FirebaseAnalytics.getInstance(context).setUserProperty(PROPERTY_NAME_GEO, property)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/location/SelectedLocationAnalytics.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.location
2 |
3 | import com.popstack.mvoter2015.domain.location.model.CombinedLocation
4 |
5 | interface SelectedLocationAnalytics {
6 |
7 | fun logLocation(combinedLocation: CombinedLocation)
8 |
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/location/SelectedLocationAnalyticsModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.location
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.data.cache.BuildConfig
5 | import dagger.Module
6 | import dagger.Provides
7 |
8 | @Module
9 | abstract class SelectedLocationAnalyticsModule {
10 |
11 | companion object {
12 |
13 | @Provides
14 | fun selectedLocationAnalytics(context: Context): SelectedLocationAnalytics {
15 | return if (BuildConfig.DEBUG) {
16 | FakeLoggingSelectedLocationAnalyticsImpl()
17 | } else {
18 | RealSelectedLocationAnalyticsImpl(context)
19 | }
20 | }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/screen/CanTrackScreen.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.screen
2 |
3 | interface CanTrackScreen {
4 |
5 | val screenName: String
6 |
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/screen/ScreenTrackAnalytics.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.screen
2 |
3 | interface ScreenTrackAnalytics {
4 |
5 | fun trackScreen(canTrackScreen: CanTrackScreen)
6 |
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/screen/ScreenTrackAnalyticsImpl.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.screen
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import com.google.firebase.analytics.FirebaseAnalytics
6 | import javax.inject.Inject
7 |
8 | class ScreenTrackAnalyticsImpl @Inject constructor(
9 | context: Context
10 | ) : ScreenTrackAnalytics {
11 |
12 | private val analytics = FirebaseAnalytics.getInstance(context)
13 |
14 | override fun trackScreen(canTrackScreen: CanTrackScreen) {
15 | val bundle = Bundle()
16 | bundle.putString(FirebaseAnalytics.Param.SCREEN_NAME, canTrackScreen.screenName)
17 | bundle.putString(FirebaseAnalytics.Param.SCREEN_CLASS, canTrackScreen.screenName)
18 | analytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW, bundle)
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/analytics/screen/ScreenTrackAnalyticsProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.analytics.screen
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * Manual injection cuz I don't want to use dagger inside [BaseController]
7 | * Should prob write accessor to app component but whatever, this works and requires minimal effort
8 | */
9 | object ScreenTrackAnalyticsProvider {
10 |
11 | fun screenTackAnalytics(context: Context): ScreenTrackAnalytics {
12 | return ScreenTrackAnalyticsImpl(context)
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/browser/OpenBrowserDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.browser
2 |
3 | import com.popstack.mvoter2015.feature.settings.AppSettings
4 | import javax.inject.Inject
5 |
6 | class OpenBrowserDelegate @Inject constructor(
7 | private val appSettings: AppSettings
8 | ) {
9 |
10 | suspend fun browserHandler(): OpenBrowserHandler {
11 | return if (appSettings.getUseExternalBrowser()) {
12 | ExternalBrowserHandler()
13 | } else {
14 | InAppBrowserHandler()
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/candidate/listing/CandidateListPagerParentRouter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.candidate.listing
2 |
3 | import com.bluelinelabs.conductor.Router
4 |
5 | /**
6 | * A static variable that should not be used
7 | * But used for easier access to parent router so we can navigate to detail page from pager's child page
8 | */
9 | internal object CandidateListPagerParentRouter {
10 |
11 | var router: Router? = null
12 |
13 | fun setParentRouter(router: Router) {
14 | this.router = router
15 | }
16 |
17 | fun destroy() {
18 | router = null
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/candidate/listing/CandidateListViewPagerTrackScreen.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.candidate.listing
2 |
3 | import com.popstack.mvoter2015.feature.analytics.screen.CanTrackScreen
4 |
5 | data class CandidateListViewPagerTrackScreen(
6 | override val screenName: String
7 | ) : CanTrackScreen
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/candidate/listing/viewholders/CandidateItemViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.candidate.listing.viewholders
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 | import com.popstack.mvoter2015.feature.candidate.listing.CandidateViewItem
6 |
7 | abstract class CandidateItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
8 | abstract fun bind(viewItem: CandidateViewItem)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/candidate/search/CandidateSearchResultViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.party.search
2 |
3 | data class CandidateSearchResultViewItem(
4 | val id: String,
5 | val name: String,
6 | val photoUrl: String,
7 | val partyName: String,
8 | val partyImageUrl: String
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/faq/FaqCategoryExt.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.faq
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.R
5 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory
6 |
7 | internal fun BallotExampleCategory.displayString(context: Context): CharSequence {
8 | return when (this) {
9 | BallotExampleCategory.NORMAL -> context.getString(R.string.ballot_category_normal)
10 | BallotExampleCategory.ADVANCED -> context.getString(R.string.ballot_category_advanced)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/faq/FaqViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.faq
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.FaqId
4 |
5 | sealed class FaqViewItem {
6 |
7 | object BallotExample : FaqViewItem()
8 |
9 | object PollingStationProhibition : FaqViewItem()
10 |
11 | object CheckVoterList : FaqViewItem()
12 |
13 | object LawAndUnfairPractices : FaqViewItem()
14 |
15 | data class QuestionAndAnswer(
16 | val faqId: FaqId,
17 | val question: String,
18 | val answer: String,
19 | val source: String?
20 | ) : FaqViewItem()
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/faq/ballot/BallotExampleCategoryExt.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.faq.ballot
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.R
5 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
6 |
7 | internal fun FaqCategory.displayString(context: Context): CharSequence {
8 | return when (this) {
9 | FaqCategory.VOTER_LIST -> context.getString(R.string.faq_category_voter_list)
10 | FaqCategory.DIPLOMATIC -> context.getString(R.string.faq_category_diplomatic)
11 | FaqCategory.INTERNATIONAL_OBSERVER -> context.getString(R.string.faq_category_international_observer)
12 | FaqCategory.CANDIDATE -> context.getString(R.string.faq_category_candidate)
13 | FaqCategory.CONFLICT_RESOLUTION -> context.getString(R.string.faq_category_conflict_resolution)
14 | FaqCategory.MEDIATION_COMMITTEES -> context.getString(R.string.faq_category_mediation_committees)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/faq/ballot/BallotExampleViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.faq.ballot
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleId
4 |
5 | data class BallotExampleViewItem(
6 | val id: BallotExampleId,
7 | val image: String,
8 | val isValid: Boolean,
9 | val reason: String
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/faq/search/FaqSearchViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.faq.search
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.FaqId
4 |
5 | data class FaqSearchViewItem(
6 | val faqId: FaqId,
7 | val question: String,
8 | val answer: String,
9 | val source: String?
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/home/BottomNavigationHostViewModelStore.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.home
2 |
3 | import androidx.lifecycle.ViewModelStore
4 |
5 | object BottomNavigationHostViewModelStore {
6 |
7 | var viewModelStore: ViewModelStore? = null
8 |
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/location/StateRegionTownshipViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.location
2 |
3 | data class StateRegionTownshipViewItem(
4 | val name: String,
5 | val isSelected: Boolean = false,
6 | val isLoading: Boolean = false,
7 | val error: String = "",
8 | val townshipList: List = ArrayList()
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/location/TownshipViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.location
2 |
3 | data class TownshipViewItem(
4 | val uniqueIdentifier: String,
5 | val name: String
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/news/NewsViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.news
2 |
3 | import com.popstack.mvoter2015.domain.news.model.NewsId
4 |
5 | data class NewsViewItem(
6 | val id: NewsId,
7 | val title: String,
8 | val summary: String,
9 | val imageUrl: String?,
10 | val publishedDate: String,
11 | val url: String
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/news/search/NewsSearchViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.news.search
2 |
3 | import com.popstack.mvoter2015.domain.news.model.NewsId
4 |
5 | data class NewsSearchViewItem(
6 | val id: NewsId,
7 | val title: String,
8 | val summary: String,
9 | val imageUrl: String?,
10 | val publishedDate: String,
11 | val url: String
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/party/detail/PartyContactViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.party.detail
2 |
3 | data class PartyContactViewItem(
4 | val text: String,
5 | val number: String
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/party/listing/PartyListViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.party.listing
2 |
3 | import com.popstack.mvoter2015.domain.party.model.PartyId
4 |
5 | data class PartyListViewItem(
6 | val partyId: PartyId,
7 | val sealImage: String,
8 | val name: String,
9 | val region: String
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/party/search/PartySearchResultViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.party.search
2 |
3 | import com.popstack.mvoter2015.domain.party.model.PartyId
4 |
5 | data class PartySearchResultViewItem(
6 | val partyId: PartyId,
7 | val flagImageUrl: String,
8 | val name: String,
9 | val region: String
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/settings/AppTheme.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.settings
2 |
3 | enum class AppTheme {
4 | SYSTEM_DEFAULT,
5 | LIGHT,
6 | DARK
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/settings/AppThemeSpinnerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.settings
2 |
3 | import android.content.Context
4 | import androidx.core.content.ContextCompat
5 | import com.aungkyawpaing.simplespinneradapter.SimpleSpinnerAdapter
6 | import com.popstack.mvoter2015.R
7 |
8 | class AppThemeSpinnerAdapter constructor(
9 | private val context: Context
10 | ) : SimpleSpinnerAdapter(
11 | itemList = context.resources.getStringArray(R.array.settings_themes).toList()
12 | ) {
13 |
14 | override fun getDisplayString(position: Int, context: Context): String {
15 | return getItem(position)
16 | }
17 |
18 | override fun getItemId(position: Int): Long {
19 | return position.toLong()
20 | }
21 |
22 | override fun modifyHolder(holder: ViewHolder) {
23 | super.modifyHolder(holder)
24 | holder.binding.tvSpinnerItem.setTextColor(ContextCompat.getColor(holder.binding.tvSpinnerItem.context, R.color.text_primary))
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/settings/SettingsFeatureModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.settings
2 |
3 | import dagger.Module
4 | import dagger.android.ContributesAndroidInjector
5 |
6 | @Module
7 | abstract class SettingsFeatureModule {
8 |
9 | @ContributesAndroidInjector
10 | abstract fun settingsController(): SettingsController
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/share/ShareUrlFactory.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.share
2 |
3 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
4 | import com.popstack.mvoter2015.domain.faq.model.FaqId
5 | import com.popstack.mvoter2015.domain.party.model.PartyId
6 |
7 | class ShareUrlFactory {
8 |
9 | companion object {
10 | private const val BASE_WEB_APP_URL = "https://web.mvoterapp.com"
11 | }
12 |
13 | fun candidate(candidateId: CandidateId): String {
14 | return "$BASE_WEB_APP_URL/candidates/${candidateId.value}"
15 | }
16 |
17 | fun party(partyId: PartyId): String {
18 | return "$BASE_WEB_APP_URL/parties/${partyId.value}"
19 | }
20 |
21 | fun faq(faqId: FaqId): String {
22 | return "$BASE_WEB_APP_URL/faqs/${faqId.value}"
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/voterlist/VoterListFeatureModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.voterlist
2 |
3 | import dagger.Module
4 | import dagger.android.ContributesAndroidInjector
5 |
6 | @Module
7 | abstract class VoterListFeatureModule {
8 |
9 | @ContributesAndroidInjector
10 | abstract fun voterListController(): VoterListController
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/VotingGuideFeatureModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.popstack.mvoter2015.di.viewmodel.ViewModelKey
5 | import dagger.Binds
6 | import dagger.Module
7 | import dagger.android.ContributesAndroidInjector
8 | import dagger.multibindings.IntoMap
9 |
10 | @Module
11 | abstract class VotingGuideFeatureModule {
12 |
13 | @ContributesAndroidInjector
14 | abstract fun votingGuideController(): VotingGuideController
15 |
16 | @Binds
17 | @IntoMap
18 | @ViewModelKey(VotingGuideViewModel::class)
19 | abstract fun votingGuideViewModel(viewModel: VotingGuideViewModel): ViewModel
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/VotingGuideViewItem.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide
2 |
3 | sealed class VotingGuideViewItem
4 |
5 | object Header : VotingGuideViewItem()
6 |
7 | object CheckVoterList : VotingGuideViewItem()
8 |
9 | class SectionTitle(val text: String) : VotingGuideViewItem()
10 |
11 | class Step(
12 | val text: String,
13 | val shouldShowUpperLine: Boolean,
14 | val shouldShowLowerLine: Boolean
15 | ) : VotingGuideViewItem()
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/viewholders/CheckVoterListViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide.viewholders
2 |
3 | import com.popstack.mvoter2015.databinding.ItemHowToVoteCheckVoterListBinding
4 | import com.popstack.mvoter2015.feature.votingguide.VotingGuideViewItem
5 |
6 | class CheckVoterListViewHolder(val binding: ItemHowToVoteCheckVoterListBinding) :
7 | VotingGuideViewHolder(binding.root) {
8 |
9 | override fun bind(viewItem: VotingGuideViewItem) {}
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/viewholders/HeaderViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide.viewholders
2 |
3 | import com.popstack.mvoter2015.databinding.ItemHowToVoteHeaderBinding
4 | import com.popstack.mvoter2015.feature.votingguide.VotingGuideViewItem
5 |
6 | class HeaderViewHolder(binding: ItemHowToVoteHeaderBinding) :
7 | VotingGuideViewHolder(binding.root) {
8 |
9 | override fun bind(viewItem: VotingGuideViewItem) {}
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/viewholders/SectionTitleViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide.viewholders
2 |
3 | import com.popstack.mvoter2015.databinding.ItemHowToVoteSectionTitleBinding
4 | import com.popstack.mvoter2015.feature.votingguide.SectionTitle
5 | import com.popstack.mvoter2015.feature.votingguide.VotingGuideViewItem
6 |
7 | class SectionTitleViewHolder(private val binding: ItemHowToVoteSectionTitleBinding) :
8 | VotingGuideViewHolder(binding.root) {
9 |
10 | override fun bind(viewItem: VotingGuideViewItem) {
11 | val sectionTitle = viewItem as? SectionTitle ?: return
12 | binding.tvSectionTitle.text = sectionTitle.text
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/viewholders/StepViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide.viewholders
2 |
3 | import androidx.core.view.isVisible
4 | import com.popstack.mvoter2015.databinding.ItemHowToVoteStepBinding
5 | import com.popstack.mvoter2015.feature.votingguide.Step
6 | import com.popstack.mvoter2015.feature.votingguide.VotingGuideViewItem
7 |
8 | class StepViewHolder(private val binding: ItemHowToVoteStepBinding) :
9 | VotingGuideViewHolder(binding.root) {
10 |
11 | override fun bind(viewItem: VotingGuideViewItem) {
12 | (viewItem as? Step)?.run {
13 | binding.tvStep.text = text
14 | binding.lineTop.isVisible = shouldShowUpperLine
15 | binding.lineBottom.isVisible = shouldShowLowerLine
16 | } ?: return
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/feature/votingguide/viewholders/VotingGuideViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.feature.votingguide.viewholders
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 | import com.popstack.mvoter2015.feature.votingguide.VotingGuideViewItem
6 |
7 | abstract class VotingGuideViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
8 | abstract fun bind(viewItem: VotingGuideViewItem)
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/font/BurmeseFont.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.font
2 |
3 | enum class BurmeseFont {
4 | ZAWGYI,
5 | UNICODE
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/AndroidDispatcherProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper
2 |
3 | import com.popstack.mvoter2015.domain.DispatcherProvider
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 | import javax.inject.Inject
7 |
8 | class AndroidDispatcherProvider @Inject constructor() : DispatcherProvider {
9 | override fun main(): CoroutineDispatcher = Dispatchers.Main
10 | override fun io(): CoroutineDispatcher = Dispatchers.IO
11 | override fun default(): CoroutineDispatcher = Dispatchers.Default
12 | override fun unconfined(): CoroutineDispatcher = Dispatchers.Unconfined
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/LocalityUtils.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper
2 |
3 | object LocalityUtils {
4 | fun isTownshipFromNPT(townshipName: String) = arrayOf(
5 | "ဇမ္ဗူသီရိမြို့နယ်",
6 | "ဇေယျာသီရိမြို့နယ်",
7 | "တပ်ကုန်းမြို့နယ်",
8 | "ဒက္ခိဏသီရိမြို့နယ်",
9 | "ပုဗ္ဗသီရိမြို့နယ်",
10 | "ပျဉ်းမနားမြို့နယ်",
11 | "လယ်ဝေးမြို့နယ်",
12 | "ဥတ္တရသီရိမြို့နယ်"
13 | ).firstOrNull { it == townshipName.replace(" ", "") } != null
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/StringList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper
2 |
3 | import java.util.StringJoiner
4 |
5 | fun List.format(delimiter: CharSequence): String {
6 | val stringJoiner = StringJoiner(delimiter)
7 | this.forEach {
8 | stringJoiner.add(it)
9 | }
10 | return stringJoiner.toString()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/asyncviewstate/AsyncViewState.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.asyncviewstate
2 |
3 | sealed class AsyncViewState {
4 |
5 | open operator fun invoke(): T? = null
6 |
7 | class Loading : AsyncViewState()
8 |
9 | data class Success(val value: T) : AsyncViewState()
10 |
11 | data class Error(val exception: Throwable, val errorMessage: String) : AsyncViewState()
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/conductor/ConductorExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.conductor
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import androidx.appcompat.app.ActionBar
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.appcompat.widget.Toolbar
8 | import com.bluelinelabs.conductor.Controller
9 |
10 | fun Controller.requireContext(): Context {
11 | return applicationContext!!
12 | }
13 |
14 | fun Controller.requireActivity(): Activity {
15 | return activity!!
16 | }
17 |
18 | fun Controller.requireActivityAsAppCompatActivity(): AppCompatActivity {
19 | return requireActivity() as AppCompatActivity
20 | }
21 |
22 | fun Controller.setSupportActionBar(toolbar: Toolbar) {
23 | requireActivityAsAppCompatActivity().setSupportActionBar(toolbar)
24 | }
25 |
26 | fun Controller.supportActionBar(): ActionBar? {
27 | return requireActivityAsAppCompatActivity().supportActionBar
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/diff/Diff.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.diff
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | inline fun diffCallBackWith(
6 | crossinline areItemTheSame: ((@ParameterName("item1") T, @ParameterName("item2") T) -> Boolean),
7 | crossinline areContentsTheSame: ((@ParameterName("item1") T, @ParameterName("item2") T) -> Boolean)
8 | ): DiffUtil.ItemCallback {
9 | return object : DiffUtil.ItemCallback() {
10 | override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
11 | return areItemTheSame.invoke(oldItem, newItem)
12 | }
13 |
14 | override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
15 | return areContentsTheSame.invoke(oldItem, newItem)
16 | }
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/extensions/DimensionExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.extensions
2 |
3 | import android.content.res.Resources
4 |
5 | /**
6 | * Created by Vincent on 2/13/20
7 | */
8 | fun Int.toPx(): Float = (this * Resources.getSystem().displayMetrics.density)
9 |
10 | fun Int.toDp(): Float = (this / Resources.getSystem().displayMetrics.density)
11 |
12 | fun Float.toPx(): Float = (this * Resources.getSystem().displayMetrics.density)
13 |
14 | fun Float.toDp(): Float = (this / Resources.getSystem().displayMetrics.density)
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/extensions/MenuExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.extensions
2 |
3 | import android.view.Menu
4 | import androidx.core.view.contains
5 | import androidx.core.view.get
6 |
7 | fun Menu.filter(predicate: (android.view.MenuItem) -> Boolean): Boolean {
8 | if (size() == 0) return false
9 | for (index in (0 until size())) if (!predicate(get(index))) return false
10 | return true
11 | }
12 |
13 | fun Menu.indexOf(item: android.view.MenuItem): Int {
14 | require(size() > 0) { "Menu is empty!" }
15 | require(contains(item)) { "Item is not part of menu!" }
16 |
17 | for (index in (0..size())) if (get(index) == item) return index
18 | throw Exception("Item is not part of menu!")
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/extensions/TextViewExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.extensions
2 |
3 | import android.annotation.SuppressLint
4 | import android.graphics.drawable.Drawable
5 | import android.text.Layout
6 | import android.widget.TextView
7 |
8 | fun TextView.setCompoundDrawablesKt(
9 | left: Drawable? = null,
10 | right: Drawable? = null,
11 | top: Drawable? = null,
12 | bottom: Drawable? = null
13 | ) {
14 | this.setCompoundDrawables(left, top, right, bottom)
15 | }
16 |
17 | fun TextView.setCompoundDrawableWithIntrinsicBoundsKt(
18 | start: Drawable? = null,
19 | end: Drawable? = null,
20 | top: Drawable? = null,
21 | bottom: Drawable? = null
22 | ) {
23 | this.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom)
24 | }
25 |
26 | @SuppressLint("WrongConstant")
27 | fun TextView.justify() {
28 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
29 | justificationMode = Layout.JUSTIFICATION_MODE_INTER_WORD
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/extensions/ViewHolderExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.extensions
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
5 |
6 | /**
7 | * Created by Vincent on 2/13/20
8 | */
9 | fun ViewHolder.withSafeAdapterPosition(
10 | onUnsafePosition: ((Unit) -> (Unit)) = { },
11 | function: ((@ParameterName("position") Int) -> (Unit))
12 | ) {
13 | val position = adapterPosition
14 | if (position != RecyclerView.NO_POSITION) {
15 | function.invoke(position)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/helper/intent/Intents.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.helper.intent
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import androidx.core.net.toUri
6 |
7 | object Intents {
8 |
9 | fun viewUrl(url: String): Intent {
10 | return Intent(Intent.ACTION_VIEW, url.toUri())
11 | }
12 |
13 | fun shareUrl(url: String): Intent {
14 | val shareIntent = Intent(Intent.ACTION_SEND)
15 | shareIntent.type = "text/plain"
16 | shareIntent.putExtra(Intent.EXTRA_TEXT, url)
17 | return shareIntent
18 | }
19 |
20 | fun dialIntent(number: String): Intent {
21 | val intent = Intent()
22 | intent.action = Intent.ACTION_DIAL // Action for what intent called for
23 | intent.data =
24 | Uri.parse("tel:$number") // Data with intent respective action on intent
25 | return intent
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/popstack/mvoter2015/logging/HasTag.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.logging
2 |
3 | interface HasTag {
4 |
5 | abstract val tag: String
6 | }
--------------------------------------------------------------------------------
/app/src/main/res/color/tint_button_policy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/color/tint_button_policy_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_candidate_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_candidate_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_candidate_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_candidate_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_how_to_vote_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_how_to_vote_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_how_to_vote_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_how_to_vote_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_info_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_info_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_info_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_info_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_news_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_news_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_news_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_news_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_party_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_party_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_party_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_party_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_vote_result_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_vote_result_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_vote_result_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-hdpi/ic_vote_result_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_candidate_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_candidate_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_candidate_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_candidate_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_how_to_vote_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_how_to_vote_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_how_to_vote_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_how_to_vote_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_info_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_info_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_info_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_info_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_news_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_news_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_news_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_news_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_party_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_party_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_party_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_party_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_vote_result_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_vote_result_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_vote_result_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-mdpi/ic_vote_result_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/mvoter_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/mvoter_logo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/mvoter_logo_horizontal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/mvoter_logo_horizontal.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/no_photo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/no_photo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/no_recording.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/no_recording.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/no_selfie.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/no_selfie.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/no_video.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/no_video.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night-nodpi/popstack_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-night-nodpi/popstack_logo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/ballot_stack.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/ballot_stack.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/ic_clock_4pm.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/ic_clock_4pm.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/ic_clock_6am.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/ic_clock_6am.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/mvoter_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/mvoter_logo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/mvoter_logo_horizontal.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/mvoter_logo_horizontal.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/no_photo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/no_photo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/no_recording.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/no_recording.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/no_selfie.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/no_selfie.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/no_video.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/no_video.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/popstack_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/popstack_logo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/uec_logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-nodpi/uec_logo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_candidate_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_candidate_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_candidate_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_candidate_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_how_to_vote_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_how_to_vote_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_how_to_vote_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_how_to_vote_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_info_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_info_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_info_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_info_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_news_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_news_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_news_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_news_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_party_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_party_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_party_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_party_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_vote_result_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_vote_result_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_vote_result_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xhdpi/ic_vote_result_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_candidate_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_candidate_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_candidate_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_candidate_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_how_to_vote_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_how_to_vote_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_how_to_vote_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_how_to_vote_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_info_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_info_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_info_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_info_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_news_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_news_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_news_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_news_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_party_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_party_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_party_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_party_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_vote_result_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_vote_result_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_vote_result_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxhdpi/ic_vote_result_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_candidate_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_candidate_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_candidate_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_candidate_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_how_to_vote_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_how_to_vote_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_how_to_vote_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_how_to_vote_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_info_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_info_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_info_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_info_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_news_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_news_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_news_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_news_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_party_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_party_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_party_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_party_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_vote_result_filled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_vote_result_filled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_vote_result_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/drawable-xxxhdpi/ic_vote_result_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/accent_oval.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_accent_oval.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_accent_oval_ring.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_chip_accent.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_chip_accent_transparent.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_how_to_vote_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/color_state_bottom_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_drop_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_left_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_right_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_right_text_primary_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_access_time_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_check_white_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_clear_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_circle_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_voter_list.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clock_4pm_18.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clock_6am_18.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close_circle_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close_circle_transparent_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_download.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_email_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_facebook_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_flag_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_location_on_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_candidate.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_how_to_vote.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_news.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_party.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_vote_result.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nav_on_primary_bg.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nav_white_always.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_newspaper.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outlined_flag_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_over_flow_white_always.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_place_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share_accent_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star_12.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_star_white_12.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/party_seal_placeholder_rect.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/placeholder_oval.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/placeholder_rect.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tab_indicator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timeline_dot.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/font/pyidaungsu.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/font/pyidaungsu_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/font/pyidaungsu_bold.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/pyidaungsu_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/font/pyidaungsu_regular.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/zawgyi.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/font/zawgyi.ttf
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_host.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_how_to_vote_check_voter_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_how_to_vote_section_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_township.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_voter_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_ward.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_candidate.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_candidate_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_faq.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_image_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_news.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_party.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_party_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 | #000000
5 | #2071DA
6 | #FFFFFF
7 |
8 | #FFFFFF
9 | #EBEBEB
10 | #80FFFFFF
11 |
12 | #80FFFFFF
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 | true
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 | false
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/content_descriptions.xml:
--------------------------------------------------------------------------------
1 |
2 | Candidate
3 | Party
4 | How to Vote
5 | Tips
6 | Voting Result
7 | News
8 |
9 | Winner
10 |
11 | Retry
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8dp
5 |
6 |
7 |
8 |
9 | 16dp
10 | 20dp
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 |
3 | repositories {
4 | jcenter()
5 | }
6 |
7 | plugins {
8 | `kotlin-dsl`
9 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AndroidDepHandlers.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 |
4 | fun DependencyHandler.implementation(depName: String) {
5 | add("implementation", depName)
6 | }
7 |
8 | fun DependencyHandler.kapt(depName: String) {
9 | add("kapt", depName)
10 | }
11 |
12 | fun DependencyHandler.compileOnly(depName: String) {
13 | add("compileOnly", depName)
14 | }
15 |
16 | fun DependencyHandler.api(depName: String) {
17 | add("api", depName)
18 | }
19 |
20 | fun DependencyHandler.testImplementation(depName: String) {
21 | add("testImplementation", depName)
22 | }
23 |
24 | fun DependencyHandler.androidTestImplementation(depName: String) {
25 | add("androidTestImplementation", depName)
26 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AndroidXActivityDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.androidxActivity() {
4 | implementation(AndroidXActivity.activity_ktx)
5 | }
6 |
7 |
8 | object AndroidXActivity {
9 | private const val version = "1.2.0-alpha05"
10 |
11 | const val activity = "androidx.activity:activity:$version"
12 | const val activity_ktx = "androidx.activity:activity-ktx:$version"
13 | }
14 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AndroidXDataStore.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.androidxProtoDataStore() {
4 | implementation(AndroidXDataStore.core)
5 | implementation(Wire.runtime)
6 | }
7 |
8 | object AndroidXDataStore {
9 | private const val version = "1.0.0-alpha01"
10 |
11 | const val core = "androidx.datastore:datastore-core:$version"
12 | const val preferences = "androidx.datastore:datastore-preferences:$version"
13 | }
14 |
15 | object Protobuffer {
16 | private const val version = "3.13.0"
17 |
18 | const val gradle_plugin = "com.google.protobuf:protobuf-gradle-plugin:0.8.13"
19 |
20 | const val java_lite = "com.google.protobuf:protobuf-javalite:$version"
21 | const val artifact = "com.google.protobuf:protoc:$version"
22 | }
23 |
24 | object Wire {
25 | private const val version = "3.3.0"
26 |
27 | const val runtime = "com.squareup.wire:wire-runtime:$version"
28 | const val gradle_plugin = "com.squareup.wire:wire-gradle-plugin:$version"
29 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AndroidXEspressoDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.androidXEspresso() {
4 | androidTestImplementation(AndroidXEspresso.core)
5 | androidTestImplementation(AndroidXEspresso.contrib)
6 | androidTestImplementation(AndroidXEspresso.intents)
7 | androidTestImplementation(AndroidXEspresso.idling_resource)
8 | androidTestImplementation(AndroidXEspresso.idling_concurrent)
9 | }
10 |
11 |
12 | object AndroidXEspresso {
13 | private const val version = "3.3.0-alpha05"
14 |
15 | const val core = "androidx.test.espresso:espresso-core:$version"
16 | const val contrib = "androidx.test.espresso:espresso-contrib:$version"
17 | const val intents = "androidx.test.espresso:espresso-intents:$version"
18 | const val idling_resource = "androidx.test.espresso:espresso-idling-resource:$version"
19 | const val idling_concurrent = "androidx.test.espresso.idling:idling-concurrent:$version"
20 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/AndroidXFragmentDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.androidxFragment() {
4 | implementation(AndroidXFragment.fragment_ktx)
5 | testImplementation(AndroidXFragment.fragment_testing)
6 | androidTestImplementation(AndroidXFragment.fragment_testing)
7 | }
8 |
9 |
10 | object AndroidXFragment {
11 | private const val version = "1.3.0-alpha05"
12 |
13 | const val fragment = "androidx.fragment:fragment:$version"
14 | const val fragment_ktx = "androidx.fragment:fragment-ktx:$version"
15 | const val fragment_testing = "androidx.fragment:fragment-testing:$version"
16 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/DaggerDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 |
4 | fun DependencyHandler.daggerAndroid() {
5 | implementation(Dagger.core)
6 | implementation(Dagger.android_core)
7 | implementation(Dagger.android_support)
8 | kapt(Dagger.compiler)
9 | kapt(Dagger.android_processor)
10 | }
11 |
12 |
13 | fun DependencyHandler.daggerJvm() {
14 | implementation(Dagger.core)
15 | kapt(Dagger.compiler)
16 | }
17 |
18 | object Dagger {
19 | private const val version = "2.28"
20 |
21 | const val core = "com.google.dagger:dagger:$version"
22 | const val compiler = "com.google.dagger:dagger-compiler:$version"
23 | const val android_core = "com.google.dagger:dagger-android:$version"
24 | const val android_support = "com.google.dagger:dagger-android-support:$version"
25 | const val android_processor = "com.google.dagger:dagger-android-processor:$version"
26 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/MockitoDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.mockito() {
4 | testImplementation(Mockito.core)
5 | testImplementation(Mockito.inline)
6 | testImplementation(Mockito.kotlin)
7 | }
8 |
9 | fun DependencyHandler.mockitoAndroid() {
10 | testImplementation(Mockito.android)
11 | androidTestImplementation(Mockito.core)
12 | androidTestImplementation(Mockito.inline)
13 | androidTestImplementation(Mockito.kotlin)
14 | }
15 |
16 |
17 | object Mockito {
18 | private const val version = "3.0.0"
19 |
20 | const val core = "org.mockito:mockito-core:$version"
21 | const val android = "org.mockito:mockito-android:$version"
22 | const val inline = "org.mockito:mockito-inline:$version"
23 | const val kotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
24 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/MoshiDep.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.artifacts.dsl.DependencyHandler
2 |
3 | fun DependencyHandler.moshi() {
4 | implementation(Moshi.core)
5 | implementation(Moshi.adapters)
6 | implementation(Moshi.kotlin)
7 | kapt(Moshi.code_gen)
8 | }
9 |
10 |
11 | internal object Moshi {
12 | private const val version = "1.9.2"
13 |
14 | const val core = "com.squareup.moshi:moshi:$version"
15 | const val adapters = "com.squareup.moshi:moshi-adapters:$version"
16 | const val kotlin = "com.squareup.moshi:moshi-kotlin:$version"
17 | const val code_gen = "com.squareup.moshi:moshi-kotlin-codegen:$version"
18 | }
19 |
--------------------------------------------------------------------------------
/coroutinetestrule/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/coroutinetestrule/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("kotlin")
4 | }
5 |
6 | dependencies {
7 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
8 | implementation(project(":domain"))
9 |
10 | implementation(KotlinCoroutine.core)
11 | api(KotlinCoroutine.test)
12 | implementation(CommonLibs.junit)
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/data/android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/android/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/data/android/consumer-rules.pro
--------------------------------------------------------------------------------
/data/android/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.kts.
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
--------------------------------------------------------------------------------
/data/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/data/android/src/main/java/com/popstack/mvoter2015/data/android/AndroidDataModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.android
2 |
3 | import com.popstack.mvoter2015.data.cache.di.CacheModule
4 | import com.popstack.mvoter2015.data.common.di.DataModule
5 | import com.popstack.mvoter2015.data.network.di.NetworkModule
6 | import dagger.Module
7 |
8 | @Module(includes = [DataModule::class, NetworkModule::class, CacheModule::class])
9 | abstract class AndroidDataModule
--------------------------------------------------------------------------------
/data/android/src/main/java/com/popstack/mvoter2015/data/android/appupdate/NoOpAppUpdateManager.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.android.appupdate
2 |
3 | import com.popstack.mvoter2015.domain.infra.AppUpdateManager
4 | import javax.inject.Inject
5 |
6 | /**
7 | * An App Update Manager that always returns NotRequired
8 | * To be used in debug builds
9 | */
10 | class NoOpAppUpdateManager @Inject constructor() : AppUpdateManager {
11 |
12 | override suspend fun checkForUpdate(): AppUpdateManager.UpdateResult {
13 | return AppUpdateManager.UpdateResult.NotRequired
14 | }
15 |
16 | override suspend fun skipCurrentUpdate() {
17 | //DO NOTHING
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/data/android/src/main/java/com/popstack/mvoter2015/data/android/appupdate/SkipVersionCache.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.android.appupdate
2 |
3 | interface SkipVersionCache {
4 |
5 | suspend fun saveSkipVersion(versionCode: Long)
6 |
7 | suspend fun getSkipVersion(): Long?
8 |
9 | suspend fun flush()
10 |
11 | }
--------------------------------------------------------------------------------
/data/android/src/test/java/com/popstack/mvoter2015/data/android/appupdate/FakeAppVersionProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.android.appupdate
2 |
3 | import com.popstack.mvoter2015.domain.infra.AppVersionProvider
4 | import javax.inject.Inject
5 |
6 | class FakeAppVersionProvider @Inject constructor() : AppVersionProvider {
7 |
8 | var versionCode = 0L
9 | var versionName = "0.0.0"
10 |
11 | override fun versionCode(): Long {
12 | return versionCode
13 | }
14 |
15 | override fun versionName(): String {
16 | return versionName
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/data/android/src/test/java/com/popstack/mvoter2015/data/android/appupdate/FakeSkipVersionCache.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.android.appupdate
2 |
3 | import javax.inject.Inject
4 |
5 | class FakeSkipVersionCache @Inject constructor() : SkipVersionCache {
6 |
7 | var skippedVersionCode: Long? = null
8 |
9 | override suspend fun saveSkipVersion(versionCode: Long) {
10 | skippedVersionCode = versionCode
11 | }
12 |
13 | override suspend fun getSkipVersion(): Long? {
14 | return skippedVersionCode
15 | }
16 |
17 | override suspend fun flush() {
18 | skippedVersionCode = null
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/data/cache/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/cache/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | #-keep class com.squareup.wire.** { *; }
2 | #-keep class com.popstack.mvoter2015.data.cache.WardProto { *; }
3 | #-keep class com.popstack.mvoter2015.data.cache.AppUpdateProto { *; }
4 | #-keep class com.popstack.mvoter2015.data.cache.ConstituencyProto { *; }
5 | #-keep class com.popstack.mvoter2015.data.cache.StateTownshipProto { *; }
6 |
7 | -keep class androidx.datastore.preferences.** { *; }
--------------------------------------------------------------------------------
/data/cache/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.kts.
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
--------------------------------------------------------------------------------
/data/cache/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/DbProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache
2 |
3 | import com.squareup.sqldelight.db.SqlDriver
4 |
5 | object DbProvider {
6 |
7 | fun create(driver: SqlDriver): MVoterDb {
8 | return MVoterDb(
9 | driver = driver,
10 | CandidateTableAdapter = TableAdapters.candidateTableAdapter(),
11 | PartyTableAdapter = TableAdapters.partyTableAdapter(),
12 | FaqTableAdapter = TableAdapters.faqTableAdapter(),
13 | NewsTableAdapter = TableAdapters.newsTableAdapter(),
14 | BallotExampleTableAdapter = TableAdapters.ballotExampleTableAdapter(),
15 | ConstituencyTableAdapter = TableAdapters.ConsitutencyTableAdapter()
16 | )
17 | }
18 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/appupdate/AppUpdateSerializer.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.appupdate
2 |
3 | import androidx.datastore.CorruptionException
4 | import androidx.datastore.Serializer
5 | import com.popstack.mvoter2015.data.cache.AppUpdateProto
6 | import java.io.IOException
7 | import java.io.InputStream
8 | import java.io.OutputStream
9 |
10 | object AppUpdateSerializer : Serializer {
11 |
12 | override fun readFrom(input: InputStream): AppUpdateProto {
13 | return try {
14 | AppUpdateProto.ADAPTER.decode(input)
15 | } catch (exception: IOException) {
16 | throw CorruptionException("Cannot read proto", exception)
17 | }
18 | }
19 |
20 | override fun writeTo(
21 | t: AppUpdateProto,
22 | output: OutputStream
23 | ) {
24 | t.encode(output)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/BallotExampleIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object BallotExampleIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): BallotExampleId {
9 | return BallotExampleId(databaseValue)
10 | }
11 |
12 | override fun encode(value: BallotExampleId): String {
13 | return value.value
14 | }
15 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/CandidateIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object CandidateIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): CandidateId {
9 | return CandidateId(
10 | databaseValue
11 | )
12 | }
13 |
14 | override fun encode(value: CandidateId): String {
15 | return value.value
16 | }
17 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/ConstituencyIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object ConstituencyIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): ConstituencyId {
9 | return ConstituencyId(
10 | databaseValue
11 | )
12 | }
13 |
14 | override fun encode(value: ConstituencyId): String {
15 | return value.value
16 | }
17 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/FaqIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.FaqId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object FaqIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): FaqId {
9 | return FaqId(databaseValue)
10 | }
11 |
12 | override fun encode(value: FaqId): String {
13 | return value.value
14 | }
15 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/LocalDateColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.squareup.sqldelight.ColumnAdapter
4 | import java.time.Instant
5 | import java.time.LocalDate
6 | import java.time.ZoneOffset
7 |
8 | object LocalDateColumnAdapter : ColumnAdapter {
9 |
10 | override fun decode(databaseValue: Long): LocalDate {
11 | return Instant.ofEpochMilli(databaseValue).atOffset(ZoneOffset.UTC).toLocalDate()
12 | }
13 |
14 | override fun encode(value: LocalDate): Long {
15 | return value.atStartOfDay().atOffset(ZoneOffset.UTC).toInstant().toEpochMilli()
16 | }
17 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/LocalDateTimeColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.squareup.sqldelight.ColumnAdapter
4 | import java.time.Instant
5 | import java.time.LocalDateTime
6 | import java.time.ZoneOffset
7 |
8 | object LocalDateTimeColumnAdapter : ColumnAdapter {
9 |
10 | override fun decode(databaseValue: Long): LocalDateTime {
11 | return Instant.ofEpochMilli(databaseValue).atOffset(ZoneOffset.UTC).toLocalDateTime()
12 | }
13 |
14 | override fun encode(value: LocalDateTime): Long {
15 | return value.toInstant(ZoneOffset.UTC).toEpochMilli()
16 | }
17 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/NewsIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.news.model.NewsId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object NewsIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): NewsId {
9 | return NewsId(databaseValue)
10 | }
11 |
12 | override fun encode(value: NewsId): String {
13 | return value.value
14 | }
15 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/PartyIdColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.popstack.mvoter2015.domain.party.model.PartyId
4 | import com.squareup.sqldelight.ColumnAdapter
5 |
6 | object PartyIdColumnAdapter : ColumnAdapter {
7 |
8 | override fun decode(databaseValue: String): PartyId {
9 | return PartyId(databaseValue)
10 | }
11 |
12 | override fun encode(value: PartyId): String {
13 | return value.value
14 | }
15 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/columnadapter/StringListColumnAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import com.squareup.sqldelight.ColumnAdapter
4 | import java.util.StringJoiner
5 |
6 | internal class StringListColumnAdapter(
7 | private val delimiter: String = "|"
8 | ) : ColumnAdapter, String> {
9 |
10 | override fun decode(databaseValue: String): List {
11 | if (databaseValue.isEmpty()) return emptyList()
12 | return databaseValue.split(delimiter)
13 | }
14 |
15 | override fun encode(value: List): String {
16 | val stringJoiner = StringJoiner(delimiter)
17 | value.forEach {
18 | stringJoiner.add(it)
19 | }
20 | return stringJoiner.toString()
21 | }
22 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/di/SqlDelightModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.di
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.data.cache.DbProvider
5 | import com.popstack.mvoter2015.data.cache.MVoterDb
6 | import com.squareup.sqldelight.android.AndroidSqliteDriver
7 | import com.squareup.sqldelight.db.SqlDriver
8 | import dagger.Module
9 | import dagger.Provides
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | abstract class SqlDelightModule {
14 |
15 | companion object {
16 |
17 | @Provides
18 | @Singleton
19 | fun sqlDriver(context: Context): SqlDriver {
20 | return AndroidSqliteDriver(MVoterDb.Schema, context, "mvoter2020.db")
21 | }
22 |
23 | @Provides
24 | @Singleton
25 | fun database(sqlDriver: SqlDriver): MVoterDb {
26 | return DbProvider.create(sqlDriver)
27 | }
28 |
29 | }
30 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/source/location/StateTownshipSerializer.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.source.location
2 |
3 | import androidx.datastore.CorruptionException
4 | import androidx.datastore.Serializer
5 | import com.popstack.mvoter2015.data.cache.StateTownshipProto
6 | import java.io.IOException
7 | import java.io.InputStream
8 | import java.io.OutputStream
9 |
10 | object StateTownshipSerializer : Serializer {
11 |
12 | override fun readFrom(input: InputStream): StateTownshipProto {
13 | return try {
14 | StateTownshipProto.ADAPTER.decode(input)
15 | } catch (exception: IOException) {
16 | throw CorruptionException("Cannot read proto", exception)
17 | }
18 | }
19 |
20 | override fun writeTo(
21 | t: StateTownshipProto,
22 | output: OutputStream
23 | ) {
24 | t.encode(output)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/data/cache/src/main/java/com/popstack/mvoter2015/data/cache/source/location/WardSerializer.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.source.location
2 |
3 | import androidx.datastore.CorruptionException
4 | import androidx.datastore.Serializer
5 | import com.popstack.mvoter2015.data.cache.WardProto
6 | import java.io.IOException
7 | import java.io.InputStream
8 | import java.io.OutputStream
9 |
10 | object WardSerializer : Serializer {
11 |
12 | override fun readFrom(input: InputStream): WardProto {
13 | return try {
14 | WardProto.ADAPTER.decode(input)
15 | } catch (exception: IOException) {
16 | throw CorruptionException("Cannot read proto", exception)
17 | }
18 | }
19 |
20 | override fun writeTo(
21 | t: WardProto,
22 | output: OutputStream
23 | ) {
24 | t.encode(output)
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/data/cache/src/main/proto/mvoter2015/appupdate.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package mvoter2015;
4 |
5 | option java_package = "com.popstack.mvoter2015.data.cache";
6 | option java_multiple_files = true;
7 |
8 | message AppUpdateProto {
9 | int64 latest_version_code = 1;
10 | bool require_forced_update = 2;
11 | string play_store_link = 3;
12 | string download_link = 4;
13 | }
--------------------------------------------------------------------------------
/data/cache/src/main/proto/mvoter2015/stateTownship.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package mvoter2015;
4 |
5 | option java_package = "com.popstack.mvoter2015.data.cache";
6 | option java_multiple_files = true;
7 |
8 | message StateTownshipProto {
9 | string state_region = 1;
10 | string township = 2;
11 | }
--------------------------------------------------------------------------------
/data/cache/src/main/proto/mvoter2015/ward.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package mvoter2015;
4 |
5 | option java_package = "com.popstack.mvoter2015.data.cache";
6 | option java_multiple_files = true;
7 |
8 | message WardProto {
9 | string id = 1;
10 | string name = 2;
11 | ConstituencyProto lower_constituency = 3;
12 | ConstituencyProto upper_constituency = 4;
13 | ConstituencyProto state_region_constituency = 5;
14 | }
15 |
16 | message ConstituencyProto {
17 | enum HouseTypeProto {
18 | TYPE_LOWER_HOUSE = 0;
19 | TYPE_UPPER_HOUSE = 1;
20 | TYPE_STATE_REGION_HOUSE = 2;
21 | }
22 |
23 | string id = 1;
24 | string name = 2;
25 | HouseTypeProto houseType = 3;
26 | string remark = 4;
27 | }
--------------------------------------------------------------------------------
/data/cache/src/main/sqldelight/com/popstack/mvoter2015/data/cache/entity/BallotExampleTable.sq:
--------------------------------------------------------------------------------
1 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory;
2 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleId;
3 | import java.lang.Boolean;
4 |
5 | CREATE TABLE BallotExampleTable(
6 | id TEXT AS BallotExampleId PRIMARY KEY NOT NULL,
7 | image TEXT NOT NULL,
8 | isValid INTEGER AS Boolean NOT NULL,
9 | reason TEXT,
10 | category TEXT AS BallotExampleCategory NOT NULL
11 | );
12 |
13 | insertOrReplace:
14 | INSERT OR REPLACE INTO BallotExampleTable VALUES(
15 | :id, :image, :isValid, :reason, :category
16 | );
17 |
18 | selectAllWithCategory:
19 | SELECT * FROM BallotExampleTable WHERE category = :category;
20 |
21 | selectById:
22 | SELECT * FROM BallotExampleTable WHERE id = :id;
23 |
24 | deleteByCategory:
25 | DELETE FROM BallotExampleTable WHERE category = :category;
--------------------------------------------------------------------------------
/data/cache/src/main/sqldelight/com/popstack/mvoter2015/data/cache/entity/ConstitutencyTable.sq:
--------------------------------------------------------------------------------
1 | import com.popstack.mvoter2015.domain.candidate.model.CandidateGender;
2 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId;
3 | import com.popstack.mvoter2015.domain.candidate.model.CandidateParent;
4 | import com.popstack.mvoter2015.domain.party.model.PartyId;
5 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId;
6 | import com.popstack.mvoter2015.domain.constituency.model.HouseType;
7 | import java.time.LocalDate;
8 | import java.lang.Boolean;
9 |
10 | CREATE TABLE ConstituencyTable(
11 | id TEXT PRIMARY KEY NOT NULL,
12 | name TEXT NOT NULL,
13 | house TEXT AS HouseType NOT NULL,
14 | remark TEXT
15 | );
16 |
17 | insertOrReplace:
18 | INSERT OR REPLACE INTO ConstituencyTable VALUES(
19 | :id, :name, :house,:remark
20 | );
21 |
22 | deleteAll:
23 | DELETE FROM ConstituencyTable;
--------------------------------------------------------------------------------
/data/cache/src/main/sqldelight/com/popstack/mvoter2015/data/cache/entity/NewsTable.sq:
--------------------------------------------------------------------------------
1 | import com.popstack.mvoter2015.domain.news.model.NewsId;
2 | import java.time.LocalDate;
3 |
4 | CREATE TABLE NewsTable(
5 | id TEXT AS NewsId PRIMARY KEY NOT NULL,
6 | title TEXT NOT NULL,
7 | summary TEXT NOT NULL,
8 | body TEXT,
9 | imageUrl TEXT,
10 | publishedDate INTEGER AS LocalDate NOT NULL,
11 | url TEXT NOT NULL
12 | );
13 |
14 | insertOrReplace:
15 | INSERT OR REPLACE INTO NewsTable VALUES(
16 | :id, :title, :summary, :body, :imageUrl, :publishedDate, :url
17 | );
18 |
19 | getWithPage:
20 | SELECT * FROM NewsTable ORDER BY NewsTable.publishedDate DESC LIMIT :limit OFFSET :offset;
21 |
22 | countAll:
23 | SELECT count(*) FROM NewsTable;
24 |
25 | deleteAll:
26 | DELETE FROM NewsTable;
--------------------------------------------------------------------------------
/data/cache/src/main/sqldelight/migrations/1.sqm:
--------------------------------------------------------------------------------
1 | ALTER TABLE CandidateTable
2 | ADD isElected INTEGER;
--------------------------------------------------------------------------------
/data/cache/src/test/java/com/popstack/mvoter2015/data/cache/TestDbProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache
2 |
3 | import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
4 |
5 | class TestDbProvider {
6 |
7 | fun create(): MVoterDb {
8 | val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
9 | MVoterDb.Schema.create(driver)
10 | return DbProvider.create(driver)
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/data/cache/src/test/java/com/popstack/mvoter2015/data/cache/columnadapter/StringListColumnAdapterTest.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.cache.columnadapter
2 |
3 | import org.junit.Assert
4 | import org.junit.Test
5 |
6 | class StringListColumnAdapterTest {
7 |
8 | @Test
9 | fun encodeEmptyList() {
10 | val input = emptyList()
11 | val expected = ""
12 | val actual = StringListColumnAdapter().encode(input)
13 |
14 | Assert.assertEquals(expected, actual)
15 | }
16 |
17 | @Test
18 | fun decodeEmptyString() {
19 | val input = ""
20 | val expected = emptyList()
21 | val actual = StringListColumnAdapter().decode(input).toList()
22 |
23 | Assert.assertEquals(expected, actual)
24 | }
25 | }
--------------------------------------------------------------------------------
/data/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("kotlin")
4 | id("kotlin-kapt")
5 | id(KtLint.name)
6 | }
7 |
8 | dependencies {
9 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
10 | api(project(":domain"))
11 |
12 | testImplementation(CommonLibs.junit)
13 | testImplementation(project(":coroutinetestrule"))
14 | mockito()
15 | daggerJvm()
16 |
17 | //For paging
18 | api(AndroidXPaging.common)
19 |
20 | implementation(Kotlin.stdblib_jdk)
21 | api(KotlinCoroutine.core)
22 |
23 | implementation(CommonLibs.javaxInject)
24 | }
25 |
26 | ktlint {
27 | android.set(false)
28 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/appupdate/AppUpdate.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.appupdate
2 |
3 | data class AppUpdate(
4 | val latestVersionCode: Long,
5 | val requireForcedUpdate: Boolean,
6 | val playStoreLink: String,
7 | val selfHostedLink: String
8 | )
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/appupdate/AppUpdateCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.appupdate
2 |
3 | interface AppUpdateCacheSource {
4 |
5 | suspend fun getLatestUpdate(): AppUpdate?
6 |
7 | suspend fun putLatestUpdate(appUpdate: AppUpdate)
8 |
9 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/appupdate/AppUpdateNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.appupdate
2 |
3 | interface AppUpdateNetworkSource {
4 |
5 | fun getLatestUpdate(deviceVersionCode: Long): AppUpdate
6 |
7 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/candidate/CandidateCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.candidate
2 |
3 | import com.popstack.mvoter2015.domain.candidate.model.Candidate
4 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
5 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId
6 |
7 | interface CandidateCacheSource {
8 |
9 | fun putCandidate(candidate: Candidate)
10 |
11 | fun putRivalCandidateList(candidateList: List, queryConstituencyId: ConstituencyId)
12 |
13 | fun putCandidateList(candidateList: List, queryConstituencyId: ConstituencyId)
14 |
15 | fun getCandidateList(constituencyId: ConstituencyId): List
16 |
17 | fun getRivalCandidateList(constituencyId: ConstituencyId): List
18 |
19 | fun getCandidate(candidateId: CandidateId): Candidate?
20 |
21 | fun flushUnderConstituency(constituencyId: ConstituencyId)
22 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/candidate/CandidateNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.candidate
2 |
3 | import com.popstack.mvoter2015.domain.candidate.model.Candidate
4 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
5 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId
6 |
7 | interface CandidateNetworkSource {
8 |
9 | fun getCandidateList(
10 | constituencyId: ConstituencyId
11 | ): List
12 |
13 | fun getCandidate(candidateId: CandidateId): Candidate
14 |
15 | fun searchCandidate(
16 | query: String,
17 | pageNo: Int,
18 | resultsPerPage: Int = 20
19 | ): List
20 |
21 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/faq/FaqCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.faq
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.BallotExample
4 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory
5 | import com.popstack.mvoter2015.domain.faq.model.Faq
6 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
7 |
8 | interface FaqCacheSource {
9 |
10 | fun putFaqList(faqList: List)
11 |
12 | fun getFaqList(page: Int, itemsPerPage: Int, category: FaqCategory): List
13 |
14 | fun flushFaqUnderCategory(category: FaqCategory)
15 |
16 | fun getBallotExampleList(ballotExampleCategory: BallotExampleCategory): List
17 |
18 | fun putBallotExampleList(ballotExampleList: List)
19 |
20 | fun flushBallotExampleUnderCategory(category: BallotExampleCategory)
21 |
22 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/faq/FaqNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.faq
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.BallotExample
4 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory
5 | import com.popstack.mvoter2015.domain.faq.model.Faq
6 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
7 |
8 | interface FaqNetworkSource {
9 |
10 | fun getFaqList(page: Int, itemsPerPage: Int, category: FaqCategory? = null, query: String? = null): List
11 |
12 | fun getBallotExampleList(category: BallotExampleCategory): List
13 |
14 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/location/LocationCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.location
2 |
3 | import com.popstack.mvoter2015.domain.location.model.CombinedLocation
4 | import com.popstack.mvoter2015.domain.location.model.StateRegionTownship
5 | import com.popstack.mvoter2015.domain.location.model.Ward
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface LocationCacheSource {
9 |
10 | suspend fun getUserWard(): Ward?
11 |
12 | suspend fun saveUserWard(ward: Ward)
13 |
14 | suspend fun getUserStateRegionTownship(): StateRegionTownship?
15 |
16 | suspend fun saveUserStateRegionTownship(stateRegionTownship: StateRegionTownship)
17 |
18 | fun selectedLocationFlow(): Flow
19 |
20 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/location/LocationNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.location
2 |
3 | import com.popstack.mvoter2015.domain.location.model.Township
4 | import com.popstack.mvoter2015.domain.location.model.Ward
5 |
6 | interface LocationNetworkSource {
7 | fun getStateRegionList(): List
8 | fun getTownshipsListForStateRegion(stateRegionIdentifier: String): List
9 | fun getWardsForTownship(stateRegion: String, township: String): List
10 | fun getWardDetails(stateRegion: String, township: String, wardName: String): Ward
11 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/news/NewsCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.news
2 |
3 | import com.popstack.mvoter2015.domain.news.model.News
4 |
5 | interface NewsCacheSource {
6 |
7 | fun putNews(news: News)
8 |
9 | fun putNews(newsList: List)
10 |
11 | fun getNewsList(page: Int, itemPerPage: Int): List
12 |
13 | fun flush()
14 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/news/NewsNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.news
2 |
3 | import com.popstack.mvoter2015.domain.news.model.News
4 |
5 | interface NewsNetworkSource {
6 |
7 | fun getNewsList(page: Int, itemPerPage: Int, query: String? = null): List
8 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/party/PartyCacheSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.party
2 |
3 | import com.popstack.mvoter2015.domain.party.model.Party
4 | import com.popstack.mvoter2015.domain.party.model.PartyId
5 |
6 | interface PartyCacheSource {
7 |
8 | fun putParty(party: Party)
9 |
10 | fun putParty(partyList: List)
11 |
12 | fun getPartyList(page: Int, itemPerPage: Int): List
13 |
14 | fun getParty(partyId: PartyId): Party?
15 |
16 | fun flush()
17 | }
--------------------------------------------------------------------------------
/data/common/src/main/java/com/popstack/mvoter2015/data/common/party/PartyNetworkSource.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.common.party
2 |
3 | import com.popstack.mvoter2015.domain.party.model.Party
4 | import com.popstack.mvoter2015.domain.party.model.PartyId
5 |
6 | interface PartyNetworkSource {
7 |
8 | fun getPartyList(page: Int, itemPerPage: Int, query: String? = null): List
9 |
10 | fun getParty(input: PartyId): Party
11 | }
--------------------------------------------------------------------------------
/data/fakenetwork/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/fakenetwork/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/data/fakenetwork/consumer-rules.pro
--------------------------------------------------------------------------------
/data/fakenetwork/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.kts.
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
--------------------------------------------------------------------------------
/data/fakenetwork/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | /
5 |
--------------------------------------------------------------------------------
/data/fakenetwork/src/main/java/com/fakestack/mvoter2015/data/network/FakeAppUpdateNetworkDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.fakestack.mvoter2015.data.network
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.data.common.appupdate.AppUpdate
5 | import com.popstack.mvoter2015.data.common.appupdate.AppUpdateNetworkSource
6 | import javax.inject.Inject
7 |
8 | class FakeAppUpdateNetworkDataSource @Inject constructor(
9 | private val context: Context
10 | ) : AppUpdateNetworkSource {
11 |
12 | override fun getLatestUpdate(deviceVersionCode: Long): AppUpdate {
13 | return AppUpdate(
14 | latestVersionCode = deviceVersionCode,
15 | requireForcedUpdate = true,
16 | playStoreLink = "",
17 | selfHostedLink = ""
18 | )
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/data/network/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/network/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/data/network/consumer-rules.pro
--------------------------------------------------------------------------------
/data/network/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.kts.
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
--------------------------------------------------------------------------------
/data/network/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | /
5 |
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/api/ConstituencyApiModel.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.api
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ConstituencyApiResponse(
8 | @Json(name = "id") val id: String,
9 | @Json(name = "attributes") val attributes: CandidateConstituencyAttributes
10 | )
11 |
12 | @JsonClass(generateAdapter = true)
13 | data class CandidateConstituencyAttributes(
14 | @Json(name = "name") val name: String,
15 | @Json(name = "house") val house: String,
16 | @Json(name = "remark") val remark: String?
17 | )
18 |
19 | @JsonClass(generateAdapter = true)
20 | data class ConstituencyApiModel(
21 | @Json(name = "id") val id: Long,
22 | @Json(name = "name") val name: String,
23 | @Json(name = "remark") val remark: String?,
24 | )
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/api/GetStateRegionListResponse.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.api
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class GetStateRegionListResponse(
8 | @Json(name = "data") val data: List
9 | )
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/api/GetTownshipListResponse.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.api
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class GetTownshipListResponse(
8 | @Json(name = "data") val data: List
9 | )
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/auth/AuthTokenInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.auth
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | internal class AuthTokenInterceptor constructor(
7 | private val authTokenStore: AuthTokenStore
8 | ) : Interceptor {
9 |
10 | override fun intercept(chain: Interceptor.Chain): Response {
11 | val authToken = authTokenStore.getToken()
12 |
13 | if (authToken != null) {
14 | val newRequest = chain.request().newBuilder()
15 | .addHeader("api-token", authToken)
16 | .build()
17 | return chain.proceed(newRequest)
18 | }
19 |
20 | return chain.proceed(chain.request())
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/auth/AuthTokenStore.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.auth
2 |
3 | internal interface AuthTokenStore {
4 |
5 | fun storeToken(token: String)
6 |
7 | fun getToken(): String?
8 |
9 | fun flush()
10 |
11 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/auth/AuthTokenStoreImpl.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.auth
2 |
3 | import android.content.Context
4 | import androidx.core.content.edit
5 | import javax.inject.Inject
6 |
7 | internal class AuthTokenStoreImpl @Inject constructor(
8 | context: Context
9 | ) : AuthTokenStore {
10 |
11 | private val sharedPreferences = context.getSharedPreferences("auth", Context.MODE_PRIVATE)
12 |
13 | companion object {
14 | private const val KEY_TOKEN = "auth_token"
15 | }
16 |
17 | override fun storeToken(token: String) {
18 | sharedPreferences.edit {
19 | putString(KEY_TOKEN, token)
20 | }
21 | }
22 |
23 | override fun getToken(): String? {
24 | return sharedPreferences.getString(KEY_TOKEN, null)
25 | }
26 |
27 | override fun flush() {
28 | sharedPreferences.edit {
29 | clear()
30 | }
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/di/ServiceModule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.di
2 |
3 | import android.content.Context
4 | import com.popstack.mvoter2015.data.network.api.MvoterApi
5 | import dagger.Module
6 | import dagger.Provides
7 | import javax.inject.Singleton
8 |
9 | @Module
10 | abstract class ServiceModule {
11 |
12 | companion object {
13 |
14 | @Provides
15 | @Singleton
16 | fun mVoterApi(context: Context): MvoterApi {
17 | return RetrofitProvider.retrofit(context).create(MvoterApi::class.java)
18 | }
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/helper/OkHttpExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.helper
2 |
3 | import com.popstack.mvoter2015.domain.exception.NetworkException
4 | import okhttp3.Call
5 | import okhttp3.Response
6 | import okhttp3.ResponseBody
7 |
8 | fun Call.executeOrThrow(): ResponseBody {
9 |
10 | val response = this.execute()
11 |
12 | return response.getBodyOrThrowNetworkException()
13 | }
14 |
15 | fun Response.getBodyOrThrowNetworkException(): ResponseBody {
16 |
17 | if (this.isSuccessful.not()) {
18 | val errorString = this.body!!.byteStream()
19 | .bufferedReader()
20 | .use { it.readText() }
21 | throw NetworkException(errorString, this.code)
22 | }
23 |
24 | val body = this.body ?: throw NetworkException()
25 |
26 | return body
27 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/helper/RetrofitExtension.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.helper
2 |
3 | import com.popstack.mvoter2015.domain.exception.NetworkException
4 | import retrofit2.Call
5 | import retrofit2.Response
6 |
7 | fun Call.executeOrThrow(): T {
8 |
9 | val response = this.execute()
10 |
11 | return response.getBodyOrThrowNetworkException()
12 | }
13 |
14 | fun Response.getBodyOrThrowNetworkException(): T {
15 |
16 | if (this.isSuccessful.not()) {
17 | val errorString = this.errorBody()!!
18 | .byteStream()
19 | .bufferedReader()
20 | .use { it.readText() }
21 | throw NetworkException(errorString, this.code())
22 | }
23 |
24 | val body = this.body() ?: throw NetworkException()
25 |
26 | return body
27 | }
--------------------------------------------------------------------------------
/data/network/src/main/java/com/popstack/mvoter2015/data/network/source/NewsNetworkSourceImpl.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.data.network.source
2 |
3 | import com.popstack.mvoter2015.data.common.news.NewsNetworkSource
4 | import com.popstack.mvoter2015.data.network.api.MvoterApi
5 | import com.popstack.mvoter2015.data.network.api.NewsApiModel
6 | import com.popstack.mvoter2015.data.network.helper.executeOrThrow
7 | import com.popstack.mvoter2015.domain.news.model.News
8 | import javax.inject.Inject
9 |
10 | class NewsNetworkSourceImpl @Inject constructor(
11 | private val api: MvoterApi
12 | ) : NewsNetworkSource {
13 |
14 | override fun getNewsList(page: Int, itemPerPage: Int, query: String?): List {
15 | return api.newsList(
16 | page = page,
17 | itemPerPage = itemPerPage,
18 | query = query
19 | ).executeOrThrow().data.map(NewsApiModel::mapToNews)
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("kotlin")
4 | id(KtLint.name)
5 | }
6 |
7 | dependencies {
8 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
9 |
10 | implementation(Kotlin.stdblib_jdk)
11 | api(KotlinCoroutine.core)
12 |
13 | implementation(CommonLibs.javaxInject)
14 | implementation(CommonLibs.mm_ph_number)
15 |
16 | testImplementation(project(":coroutinetestrule"))
17 | testImplementation(CommonLibs.junit)
18 | mockito()
19 | }
20 |
21 | ktlint {
22 | android.set(false)
23 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/CoroutineUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain
2 |
3 | import kotlinx.coroutines.withContext
4 |
5 | abstract class CoroutineUseCase constructor(
6 | protected val dispatcherProvider: DispatcherProvider
7 | ) {
8 |
9 | suspend fun execute(input: Input): Output {
10 | return withContext(dispatcherProvider.io()) {
11 | provide(input)
12 | }
13 | }
14 |
15 | protected abstract suspend fun provide(input: Input): Output
16 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/DispatcherProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 |
5 | interface DispatcherProvider {
6 | fun main(): CoroutineDispatcher
7 | fun io(): CoroutineDispatcher
8 | fun default(): CoroutineDispatcher
9 | fun unconfined(): CoroutineDispatcher
10 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/CandidateRepository.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate
2 |
3 | import com.popstack.mvoter2015.domain.candidate.model.Candidate
4 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
5 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId
6 |
7 | interface CandidateRepository {
8 |
9 | suspend fun getCandidateList(constituencyId: ConstituencyId): List
10 |
11 | fun getCandidate(candidateId: CandidateId): Candidate
12 |
13 | fun searchCandidate(query: String, pageNo: Int, resultPerPage: Int): List
14 |
15 | fun getRivalCandidatesInConstituency(constituencyId: ConstituencyId): List
16 |
17 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/model/Candidate.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.model
2 |
3 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
4 | import com.popstack.mvoter2015.domain.party.model.Party
5 | import java.time.LocalDate
6 |
7 | data class Candidate(
8 | val id: CandidateId,
9 | val name: String,
10 | val sortingName: String,
11 | val sortingBallotOrder: Long,
12 | val gender: CandidateGender,
13 | val occupation: String,
14 | val photoUrl: String,
15 | val education: String,
16 | val religion: String,
17 | val age: Int?,
18 | val birthDate: LocalDate?,
19 | val constituency: Constituency,
20 | val ethnicity: String,
21 | val father: CandidateParent?,
22 | val mother: CandidateParent?,
23 | val individualLogo: String?,
24 | val party: Party?,
25 | val residentialAddress: String?,
26 | val isEthnicCandidate: Boolean,
27 | val representingEthnicity: String?,
28 | val isElected: Boolean
29 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/model/CandidateGender.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.model
2 |
3 | enum class CandidateGender {
4 | Male,
5 | Female,
6 | Others;
7 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/model/CandidateId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.model
2 |
3 | inline class CandidateId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/model/CandidateParent.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.model
2 |
3 | data class CandidateParent(
4 | val name: String,
5 | val religion: String,
6 | val ethnicity: String,
7 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/usecase/GetCandidate.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.candidate.CandidateRepository
6 | import com.popstack.mvoter2015.domain.candidate.model.Candidate
7 | import com.popstack.mvoter2015.domain.candidate.model.CandidateId
8 | import javax.inject.Inject
9 |
10 | class GetCandidate @Inject constructor(
11 | dispatcherProvider: DispatcherProvider,
12 | private val candidateRepository: CandidateRepository
13 | ) :
14 | CoroutineUseCase(
15 | dispatcherProvider
16 | ) {
17 |
18 | data class Params(
19 | val candidateId: CandidateId
20 | )
21 |
22 | override suspend fun provide(input: Params): Candidate {
23 | return candidateRepository.getCandidate(input.candidateId)
24 | }
25 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/usecase/GetRivalCandidateList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.candidate.CandidateRepository
6 | import com.popstack.mvoter2015.domain.candidate.model.Candidate
7 | import com.popstack.mvoter2015.domain.constituency.model.ConstituencyId
8 | import javax.inject.Inject
9 |
10 | class GetRivalCandidateList @Inject constructor(
11 | dispatcherProvider: DispatcherProvider,
12 | private val candidateRepository: CandidateRepository
13 | ) :
14 | CoroutineUseCase>(
15 | dispatcherProvider
16 | ) {
17 |
18 | data class Params(
19 | val constituencyId: ConstituencyId
20 | )
21 |
22 | override suspend fun provide(input: Params): List = input.run {
23 | candidateRepository.getRivalCandidatesInConstituency(constituencyId)
24 | }
25 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/candidate/usecase/exception/NoStateRegionConstituencyException.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.candidate.usecase.exception
2 |
3 | class NoStateRegionConstituencyException : Exception()
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/model/Constituency.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.model
2 |
3 | data class Constituency(
4 | val id: ConstituencyId,
5 | val name: String,
6 | val house: HouseType,
7 | val remark: String?
8 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/model/ConstituencyId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.model
2 |
3 | inline class ConstituencyId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/model/HouseType.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.model
2 |
3 | enum class HouseType {
4 | LOWER_HOUSE,
5 | UPPER_HOUSE,
6 | REGIONAL_HOUSE
7 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/repository/ConstituencyRepository.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.repository
2 |
3 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
4 |
5 | interface ConstituencyRepository {
6 |
7 | fun getUserLowerHouseConstituency(): Constituency
8 |
9 | fun getUserUpperHouseConstituency(): Constituency
10 |
11 | fun getUserStateRegionHouseConstituency(): Constituency
12 |
13 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/usecase/GetMyLowerHouseConstituency.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
6 | import com.popstack.mvoter2015.domain.location.LocationRepository
7 | import javax.inject.Inject
8 |
9 | class GetMyLowerHouseConstituency @Inject constructor(
10 | dispatcherProvider: DispatcherProvider,
11 | private val locationRepository: LocationRepository
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: Unit): Constituency {
15 | return locationRepository.getUserWard()!!.lowerHouseConstituency
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/usecase/GetMyStateRegionConstituency.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
6 | import com.popstack.mvoter2015.domain.location.LocationRepository
7 | import javax.inject.Inject
8 |
9 | class GetMyStateRegionConstituency @Inject constructor(
10 | dispatcherProvider: DispatcherProvider,
11 | private val locationRepository: LocationRepository
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: Unit): Constituency? {
15 | return locationRepository.getUserWard()!!.stateRegionConstituency
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/constituency/usecase/GetMyUpperHouseConstituency.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.constituency.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
6 | import com.popstack.mvoter2015.domain.location.LocationRepository
7 | import javax.inject.Inject
8 |
9 | class GetMyUpperHouseConstituency @Inject constructor(
10 | dispatcherProvider: DispatcherProvider,
11 | private val locationRepository: LocationRepository
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: Unit): Constituency {
15 | return locationRepository.getUserWard()!!.upperHouseConstituency
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/exception/NetworkException.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.exception
2 |
3 | import java.io.IOException
4 |
5 | data class NetworkException constructor(
6 | val errorBody: String? = null,
7 | var errorCode: Int = 0
8 | ) : IOException()
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/FaqRepository.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq
2 |
3 | import com.popstack.mvoter2015.domain.faq.model.BallotExample
4 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory
5 | import com.popstack.mvoter2015.domain.faq.model.Faq
6 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
7 |
8 | interface FaqRepository {
9 |
10 | fun getFaq(
11 | page: Int,
12 | itemPerPage: Int,
13 | category: FaqCategory
14 | ): List
15 |
16 | fun getBallotExample(category: BallotExampleCategory): List
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/BallotExample.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | data class BallotExample(
4 | val id: BallotExampleId,
5 | val image: String,
6 | val isValid: Boolean,
7 | val reason: String?,
8 | val category: BallotExampleCategory
9 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/BallotExampleCategory.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | enum class BallotExampleCategory {
4 | NORMAL,
5 | ADVANCED
6 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/BallotExampleId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | inline class BallotExampleId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/Faq.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | data class Faq(
4 | val id: FaqId,
5 | val question: String,
6 | val answer: String,
7 | val lawSource: String?,
8 | val articleSource: String?,
9 | val category: FaqCategory
10 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/FaqCategory.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | enum class FaqCategory {
4 | VOTER_LIST,
5 | DIPLOMATIC,
6 | INTERNATIONAL_OBSERVER,
7 | CANDIDATE,
8 | CONFLICT_RESOLUTION,
9 | MEDIATION_COMMITTEES
10 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/model/FaqId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.model
2 |
3 | inline class FaqId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/usecase/GetBallotExampleList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.faq.FaqRepository
6 | import com.popstack.mvoter2015.domain.faq.model.BallotExample
7 | import com.popstack.mvoter2015.domain.faq.model.BallotExampleCategory
8 | import javax.inject.Inject
9 |
10 | class GetBallotExampleList @Inject constructor(
11 | dispatcherProvider: DispatcherProvider,
12 | private val faqRepository: FaqRepository
13 | ) :
14 | CoroutineUseCase>(dispatcherProvider) {
15 |
16 | override suspend fun provide(input: BallotExampleCategory): List {
17 | return faqRepository.getBallotExample(input)
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/usecase/GetFaq.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.faq.model.Faq
6 | import com.popstack.mvoter2015.domain.faq.model.FaqId
7 | import javax.inject.Inject
8 |
9 | class GetFaq @Inject constructor(
10 | dispatcherProvider: DispatcherProvider
11 | ) :
12 | CoroutineUseCase(
13 | dispatcherProvider
14 | ) {
15 |
16 | data class Params(
17 | val faqId: FaqId
18 | )
19 |
20 | override suspend fun provide(input: Params): Faq {
21 | TODO()
22 | }
23 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/usecase/GetFaqCategories.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
6 | import javax.inject.Inject
7 |
8 | class GetFaqCategories @Inject constructor(dispatcherProvider: DispatcherProvider) :
9 | CoroutineUseCase>(dispatcherProvider) {
10 |
11 | override suspend fun provide(input: Unit): List {
12 | return FaqCategory.values().toList()
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/faq/usecase/GetFaqList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.faq.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.faq.FaqRepository
6 | import com.popstack.mvoter2015.domain.faq.model.Faq
7 | import com.popstack.mvoter2015.domain.faq.model.FaqCategory
8 | import javax.inject.Inject
9 |
10 | class GetFaqList @Inject constructor(
11 | dispatcherProvider: DispatcherProvider,
12 | private val faqRepository: FaqRepository
13 | ) :
14 | CoroutineUseCase>(
15 | dispatcherProvider
16 | ) {
17 |
18 | data class Params(
19 | val page: Int,
20 | val itemPerPage: Int,
21 | val category: FaqCategory
22 | )
23 |
24 | override suspend fun provide(input: Params): List {
25 | return faqRepository.getFaq(
26 | input.page,
27 | input.itemPerPage,
28 | input.category
29 | )
30 | }
31 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/infra/AppUpdateManager.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.infra
2 |
3 | interface AppUpdateManager {
4 |
5 | sealed class UpdateResult {
6 |
7 | data class ForcedUpdate(val updateLink: String) : UpdateResult()
8 |
9 | data class RelaxedUpdate(val updateLink: String, val isSkipped: Boolean) : UpdateResult()
10 |
11 | object NotRequired : UpdateResult()
12 | }
13 |
14 | suspend fun checkForUpdate(): UpdateResult
15 |
16 | suspend fun skipCurrentUpdate()
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/infra/AppVersionProvider.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.infra
2 |
3 | interface AppVersionProvider {
4 |
5 | fun versionCode(): Long
6 |
7 | fun versionName(): String
8 |
9 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/FlowUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location
2 |
3 | import com.popstack.mvoter2015.domain.DispatcherProvider
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.flowOn
6 |
7 | abstract class FlowUseCase constructor(
8 | protected val dispatcherProvider: DispatcherProvider
9 | ) {
10 |
11 | fun execute(params: I): Flow {
12 | return provide(params)
13 | .flowOn(dispatcherProvider.io())
14 | }
15 |
16 | protected abstract fun provide(
17 | params: I
18 | ): Flow
19 |
20 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/CombinedLocation.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | data class CombinedLocation(
4 | val stateRegion: String,
5 | val township: String,
6 | val ward: String?
7 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/LatLng.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | data class LatLng(
4 | val latitude: Double,
5 | val longitude: Double
6 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/StateRegion.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | data class StateRegion(
4 | val name: String,
5 | val type: StateRegionType
6 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/StateRegionPCode.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | inline class StateRegionPCode(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/StateRegionTownship.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | data class StateRegionTownship(
4 | val stateRegion: String,
5 | val township: String
6 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/StateRegionType.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | enum class StateRegionType {
4 | REGION,
5 | STATE
6 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/Township.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | data class Township(
4 | val pCode: TownshipPCode,
5 | val name: String
6 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/TownshipPCode.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | inline class TownshipPCode(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/Ward.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | import com.popstack.mvoter2015.domain.constituency.model.Constituency
4 |
5 | data class Ward(
6 | val id: WardId,
7 | val name: String,
8 | val lowerHouseConstituency: Constituency,
9 | val upperHouseConstituency: Constituency,
10 | val stateRegionConstituency: Constituency?
11 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/model/WardId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.model
2 |
3 | inline class WardId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetStateRegionList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import javax.inject.Inject
7 |
8 | class GetStateRegionList @Inject constructor(
9 | private val locationRepository: LocationRepository,
10 | dispatcherProvider: DispatcherProvider
11 | ) : CoroutineUseCase>(dispatcherProvider) {
12 |
13 | override suspend fun provide(input: Unit): List {
14 | return locationRepository.getStateRegionList()
15 | }
16 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetTownshipsForStateRegion.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.Township
7 | import javax.inject.Inject
8 |
9 | class GetTownshipsForStateRegion @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase>(dispatcherProvider) {
13 |
14 | // TODO: Change params according to API changes
15 | data class Params(
16 | val stateRegionIdentifier: String
17 | )
18 |
19 | override suspend fun provide(input: Params): List {
20 | return locationRepository.getTownshipsListForStateRegion(input.stateRegionIdentifier)
21 | }
22 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetUserSelectedLocation.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.DispatcherProvider
4 | import com.popstack.mvoter2015.domain.location.FlowUseCase
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.CombinedLocation
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class GetUserSelectedLocation @Inject constructor(
11 | dispatcherProvider: DispatcherProvider,
12 | private val locationRepository: LocationRepository
13 | ) : FlowUseCase(dispatcherProvider) {
14 |
15 | override fun provide(params: Unit): Flow {
16 | return locationRepository.selectedLocationFlow()
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetUserStateRegion.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.model.StateRegion
6 | import com.popstack.mvoter2015.domain.location.model.StateRegionType
7 | import javax.inject.Inject
8 |
9 | class GetUserStateRegion @Inject constructor(dispatcherProvider: DispatcherProvider) :
10 | CoroutineUseCase(dispatcherProvider) {
11 |
12 | override suspend fun provide(input: Unit): StateRegion {
13 | return StateRegion(
14 | name = "Hello",
15 | type = StateRegionType.REGION
16 | )
17 | }
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetUserStateRegionTownship.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.StateRegionTownship
7 | import javax.inject.Inject
8 |
9 | class GetUserStateRegionTownship @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: Unit): StateRegionTownship? {
15 | return locationRepository.getUserStateRegionTownship()
16 | }
17 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetUserTownship.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.model.Township
6 | import javax.inject.Inject
7 |
8 | class GetUserTownship @Inject constructor(dispatcherProvider: DispatcherProvider) :
9 | CoroutineUseCase(dispatcherProvider) {
10 |
11 | override suspend fun provide(input: Unit): Township {
12 | TODO("Not yet implemented")
13 | }
14 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetUserWard.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.Ward
7 | import javax.inject.Inject
8 |
9 | class GetUserWard @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: Unit): Ward? {
15 | return locationRepository.getUserWard()
16 | }
17 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetWardDetails.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.Ward
7 | import javax.inject.Inject
8 |
9 | class GetWardDetails @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | // TODO: Change params according to API changes
15 | data class Params(
16 | val stateRegion: String,
17 | val township: String,
18 | val ward: String
19 | )
20 |
21 | override suspend fun provide(input: Params): Ward {
22 | return locationRepository.getWardDetails(input.stateRegion, input.township, input.ward)
23 | }
24 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/GetWardsForTownship.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import javax.inject.Inject
7 |
8 | class GetWardsForTownship @Inject constructor(
9 | private val locationRepository: LocationRepository,
10 | dispatcherProvider: DispatcherProvider
11 | ) : CoroutineUseCase>(dispatcherProvider) {
12 |
13 | // TODO: Change params according to API changes
14 | data class Params(
15 | val stateRegion: String,
16 | val township: String
17 | )
18 |
19 | override suspend fun provide(input: Params): List {
20 | return locationRepository.getWardsForTownship(input.stateRegion, input.township)
21 | }
22 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/SaveUserStateRegionTownship.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.StateRegionTownship
7 | import javax.inject.Inject
8 |
9 | class SaveUserStateRegionTownship @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | // TODO: Change params according to API changes
15 | data class Params(
16 | val stateRegionTownship: StateRegionTownship
17 | )
18 |
19 | override suspend fun provide(input: Params) {
20 | locationRepository.saveUserStateRegionTownship(
21 | input.stateRegionTownship
22 | )
23 | }
24 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/SaveUserWard.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import com.popstack.mvoter2015.domain.location.model.Ward
7 | import javax.inject.Inject
8 |
9 | class SaveUserWard @Inject constructor(
10 | private val locationRepository: LocationRepository,
11 | dispatcherProvider: DispatcherProvider
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | // TODO: Change params according to API changes
15 | data class Params(
16 | val ward: Ward
17 | )
18 |
19 | override suspend fun provide(input: Params) {
20 | locationRepository.saveUserWard(input.ward)
21 | }
22 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/location/usecase/UpdateWardDetails.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.location.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.location.LocationRepository
6 | import javax.inject.Inject
7 |
8 | class UpdateWardDetails @Inject constructor(
9 | dispatcherProvider: DispatcherProvider,
10 | private val locationRepository: LocationRepository
11 | ) : CoroutineUseCase(dispatcherProvider) {
12 |
13 | override suspend fun provide(input: Unit) {
14 | val stateRegionTownship = locationRepository.getUserStateRegionTownship() ?: return
15 | val ward = locationRepository.getUserWard() ?: return
16 |
17 | val updatedWard = locationRepository.getWardDetails(
18 | stateRegionTownship.stateRegion,
19 | stateRegionTownship.township,
20 | ward.name
21 | )
22 |
23 | locationRepository.saveUserWard(updatedWard)
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/news/NewsRepository.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.news
2 |
3 | import com.popstack.mvoter2015.domain.news.model.News
4 |
5 | interface NewsRepository {
6 |
7 | fun getNewsList(page: Int, itemPerPage: Int): List
8 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/news/model/News.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.news.model
2 |
3 | import java.time.LocalDate
4 |
5 | data class News(
6 | val id: NewsId,
7 | val title: String,
8 | val summary: String,
9 | val body: String?,
10 | val imageUrl: String?,
11 | val publishedDate: LocalDate,
12 | val url: String
13 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/news/model/NewsId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.news.model
2 |
3 | inline class NewsId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/news/usecase/GetNewsList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.news.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.news.model.News
6 | import javax.inject.Inject
7 |
8 | class GetNewsList @Inject constructor(
9 | dispatcherProvider: DispatcherProvider
10 | ) :
11 | CoroutineUseCase>(
12 | dispatcherProvider
13 | ) {
14 |
15 | data class Params(
16 | val page: Int,
17 | val itemPerPage: Int
18 | )
19 |
20 | override suspend fun provide(input: Params): List {
21 | return emptyList()
22 | // return partyRepository.getPartyList(input.page, input.itemPerPage)
23 | }
24 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/model/Party.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.model
2 |
3 | import java.time.LocalDate
4 |
5 | data class Party(
6 | val id: PartyId,
7 | val registeredNumber: Int,
8 | val nameBurmese: String,
9 | val nameEnglish: String?,
10 | val abbreviation: String?,
11 | val flagImage: String,
12 | val sealImage: String,
13 | val region: String,
14 | val leadersAndChairmenList: List,
15 | val memberCount: String?,
16 | val headquarterLocation: String,
17 | val policy: String,
18 | val contacts: List,
19 | val isEstablishedDueToArticle25: Boolean, //See https://www.constitutionaltribunal.gov.mm/lawdatabase/my/law/273 article 25
20 | val establishmentApplicationDate: LocalDate?,
21 | val establishmentApprovalDate: LocalDate?,
22 | val registrationApplicationDate: LocalDate?,
23 | val registrationApprovalDate: LocalDate?
24 | )
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/model/PartyId.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.model
2 |
3 | inline class PartyId(val value: String)
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/usecase/GetParty.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.party.model.Party
6 | import com.popstack.mvoter2015.domain.party.model.PartyId
7 | import javax.inject.Inject
8 |
9 | class GetParty @Inject constructor(
10 | dispatcherProvider: DispatcherProvider,
11 | private val partyRepository: PartyRepository
12 | ) : CoroutineUseCase(dispatcherProvider) {
13 |
14 | override suspend fun provide(input: PartyId): Party {
15 | return partyRepository.getParty(input)
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/usecase/GetPartyList.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.party.model.Party
6 | import javax.inject.Inject
7 |
8 | class GetPartyList @Inject constructor(
9 | dispatcherProvider: DispatcherProvider,
10 | private val partyRepository: PartyRepository
11 | ) :
12 | CoroutineUseCase>(
13 | dispatcherProvider
14 | ) {
15 |
16 | data class Params(
17 | val page: Int,
18 | val itemPerPage: Int
19 | )
20 |
21 | override suspend fun provide(input: Params): List {
22 | return partyRepository.getPartyList(input.page, input.itemPerPage)
23 | }
24 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/usecase/PartyRepository.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.usecase
2 |
3 | import com.popstack.mvoter2015.domain.party.model.Party
4 | import com.popstack.mvoter2015.domain.party.model.PartyId
5 |
6 | interface PartyRepository {
7 |
8 | fun getPartyList(page: Int, itemPerPage: Int): List
9 |
10 | suspend fun getParty(partyId: PartyId): Party
11 |
12 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/party/usecase/SearchParty.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.party.usecase
2 |
3 | import com.popstack.mvoter2015.domain.CoroutineUseCase
4 | import com.popstack.mvoter2015.domain.DispatcherProvider
5 | import com.popstack.mvoter2015.domain.party.model.Party
6 | import javax.inject.Inject
7 |
8 | class SearchParty @Inject constructor(
9 | dispatcherProvider: DispatcherProvider,
10 | private val partyRepository: PartyRepository
11 | ) :
12 | CoroutineUseCase>(
13 | dispatcherProvider
14 | ) {
15 |
16 | data class Params(
17 | val query: String,
18 | val page: Int,
19 | val itemPerPage: Int
20 | )
21 |
22 | override suspend fun provide(input: Params): List {
23 | return partyRepository.getPartyList(input.page, input.itemPerPage)
24 | }
25 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/utils/InputSanitizer.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.utils
2 |
3 | /**
4 | * Sanitizer that handles quirkiness of Burmese inputs.
5 | */
6 | object InputSanitizer {
7 |
8 | fun sanitizeInput(input: String): String {
9 | return input.replace("\u200B", "") //Bagan keyboard uses zero-width whitespace to do some voodoo stuffs, remove these
10 | }
11 | }
--------------------------------------------------------------------------------
/domain/src/main/java/com/popstack/mvoter2015/domain/utils/ZeroOneRule.kt:
--------------------------------------------------------------------------------
1 | package com.popstack.mvoter2015.domain.utils
2 |
3 | import com.aungkyawpaing.mmphonenumber.normalizer.Rule
4 |
5 | class ZeroOneRule : Rule {
6 |
7 | private val possibleCases = Regex("(01-)|(\\+951)|(01\\s)|(951)|(01\\.)")
8 |
9 | override fun convert(input: String): String {
10 |
11 | if (possibleCases.containsMatchIn(input)) {
12 | return input.replaceFirst(possibleCases, "01")
13 | }
14 |
15 | return input
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Sat Apr 11 14:45:42 MMT 2020
14 | android.useAndroidX=true
15 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
16 | org.gradle.parallel=true
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Mar 29 11:30:29 MMT 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-6.5.1-all.zip
7 |
--------------------------------------------------------------------------------
/release.properties:
--------------------------------------------------------------------------------
1 | forceUpdate=true
2 | url=https://us-central1-maepaysoh2020.cloudfunctions.net/deploy/android
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | include(":data:fakenetwork")
2 | include(":simplespinneradapter")
3 | include(":coroutinetestrule")
4 | include(":data:android")
5 | include(":data:network")
6 | include(":data:common")
7 | include(":data:cache")
8 | rootProject.name = "mVoter"
9 | include(":app")
10 | include(":domain")
11 |
12 | pluginManagement {
13 | repositories {
14 | google()
15 | mavenCentral()
16 | jcenter()
17 | gradlePluginPortal()
18 | }
19 | }
--------------------------------------------------------------------------------
/simplespinneradapter/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/simplespinneradapter/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PopStackHack/mVoterAndroid/724d76a8f061b633f260c262a4426b1b6b643e42/simplespinneradapter/consumer-rules.pro
--------------------------------------------------------------------------------
/simplespinneradapter/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.kts.
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
--------------------------------------------------------------------------------
/simplespinneradapter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | /
5 |
--------------------------------------------------------------------------------
/simplespinneradapter/src/main/java/com/aungkyawpaing/simplespinneradapter/StringSimpleSpinnerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.aungkyawpaing.simplespinneradapter
2 |
3 | import android.content.Context
4 |
5 | class StringSimpleSpinnerAdapter(itemList: List = listOf()) :
6 | SimpleSpinnerAdapter(itemList) {
7 |
8 | override fun getDisplayString(position: Int, context: Context): String {
9 | return getItem(position)
10 | }
11 |
12 | override fun getItemId(position: Int): Long {
13 | return position.toLong()
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/simplespinneradapter/src/main/res/layout/spinner_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------