├── .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 | 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 | 4 | 5 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_candidate_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_faq.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_image_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_news.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_party.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_party_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------