├── .github └── workflows │ ├── android.yml │ ├── androidTest.yml │ └── task_for_library_update.yml ├── .gitignore ├── .idea ├── assetWizardSettings.xml ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── saveactions_settings.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── README.md ├── action_article_list ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── action │ └── articlelist │ ├── ReadAllArticlesAction.kt │ ├── ReadArticleAction.kt │ └── UnReadArticleAction.kt ├── admob ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── admob │ │ ├── AdmobFragment.kt │ │ ├── AdmobProvider.kt │ │ └── AdmobViewHolder.kt │ └── res │ ├── layout │ ├── fragment_admob.xml │ └── item_list_ad.xml │ └── values │ └── strings.xml ├── advertisement ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── advertisement │ │ ├── AdFragment.kt │ │ ├── AdProvider.kt │ │ └── AdViewHolder.kt │ └── res │ └── values │ └── strings.xml ├── android-lint.xml ├── app ├── build.gradle ├── dummy-release-key ├── lint.xml └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── phicdy │ │ └── mycuration │ │ ├── DatabaseUtil.kt │ │ ├── data │ │ └── repository │ │ │ ├── ArticleRepositoryTest.kt │ │ │ ├── CurationRepositoryTest.kt │ │ │ ├── FilterRepositoryTest.kt │ │ │ └── RssRepositoryTest.kt │ │ ├── domain │ │ ├── rss │ │ │ ├── Atom.kt │ │ │ ├── AtomAndroidDeveloperBlog.kt │ │ │ ├── FeedBurnerAndroidDeveloperBlog.kt │ │ │ ├── IconParserTest.kt │ │ │ ├── RssParserTest.kt │ │ │ ├── RssV1.kt │ │ │ └── RssV2.kt │ │ └── task │ │ │ ├── GetFeedIconTaskTest.kt │ │ │ └── HatenaBookmarkApiTest.kt │ │ ├── uitest │ │ ├── AddFeedTest.kt │ │ ├── EditFeedTitleTest.kt │ │ ├── FeedUrlHookTest.kt │ │ ├── FilterListTest.kt │ │ ├── SearchArticleTest.kt │ │ ├── SettingTest.kt │ │ ├── TopActivityControl.kt │ │ └── UiTest.kt │ │ └── util │ │ └── test │ │ └── FileUtilTest.kt │ ├── benchmark │ └── java │ │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── FlipperInitializer.kt │ ├── debug │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── FlipperInitializer.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── license.html │ ├── baseline-prof.txt │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ ├── DefaultWorkerFactory.kt │ │ │ ├── LaunchActivity.kt │ │ │ ├── MyApplication.kt │ │ │ ├── di │ │ │ ├── ActivityModule.kt │ │ │ ├── ApplicationModule.kt │ │ │ ├── RepositoryModule.kt │ │ │ └── ViewModelModule.kt │ │ │ └── util │ │ │ └── log │ │ │ └── TimberTree.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ │ ├── layout │ │ └── activity_launch.xml │ │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── xml │ │ ├── activity_launch_scene.xml │ │ ├── backup_rules.xml │ │ ├── network_security_config.xml │ │ └── searchable_feed.xml │ └── release │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── FlipperInitializer.kt ├── baselineprofile ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── baselineprofile │ ├── BaselineProfileGenerator.kt │ └── StartupBaselineProfileBenchmarks.kt ├── benchmark ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── benchmark │ ├── ColdStartupBenchmark.kt │ └── StartupBenchmark.kt ├── build.gradle ├── core ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── core │ │ ├── Action.kt │ │ ├── ActionCreator.kt │ │ ├── ActionCreator1.kt │ │ ├── ActionCreator2.kt │ │ ├── ActionCreator3.kt │ │ ├── ActionCreator4.kt │ │ ├── CoroutineDispatcherProvider.kt │ │ ├── DefaultCoroutineDispatcherProvider.kt │ │ ├── Dispatcher.kt │ │ ├── Reducer.kt │ │ └── Store.kt │ └── res │ └── values │ └── strings.xml ├── di_common ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── di │ └── common │ └── Qualifiers.kt ├── domain ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── domain │ │ │ ├── alarm │ │ │ ├── AlarmManagerTaskManager.kt │ │ │ ├── AutoUpdateBroadcastReciever.kt │ │ │ └── BootReceiver.kt │ │ │ ├── rss │ │ │ ├── IconParser.kt │ │ │ ├── RssParseExecutor.kt │ │ │ ├── RssParseResult.kt │ │ │ ├── RssParser.kt │ │ │ └── RssUrlHookIntentData.kt │ │ │ ├── setting │ │ │ └── SettingInitialData.kt │ │ │ ├── task │ │ │ ├── FilterTask.kt │ │ │ ├── GetFeedIconTask.kt │ │ │ └── NetworkTaskManager.kt │ │ │ └── util │ │ │ ├── DateParser.kt │ │ │ ├── NetworkUtil.kt │ │ │ └── TextUtil.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── domain │ └── util │ ├── DateParserTest.kt │ └── TextUtilTest.kt ├── entity ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── entity │ │ │ ├── Article.kt │ │ │ ├── Curation.kt │ │ │ ├── CurationCondition.kt │ │ │ ├── CurationSelection.kt │ │ │ ├── FaivoriteArticle.kt │ │ │ ├── FavoritableArticle.kt │ │ │ ├── FavoriteArticleItem.kt │ │ │ ├── Feed.kt │ │ │ ├── Filter.kt │ │ │ ├── FilterFeedRegistration.kt │ │ │ ├── ReadAllArticles.kt │ │ │ ├── ReadArticle.kt │ │ │ ├── RssListMode.kt │ │ │ ├── RssUpdateIntervalCheckDate.kt │ │ │ └── UnReadArticle.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── entity │ └── FilterTest.kt ├── feature_add_curation ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── feature │ └── addcuration │ ├── AddCurationActivity.kt │ ├── AddCurationEvent.kt │ ├── AddCurationState.kt │ ├── AddCurationStateStore.kt │ ├── AddCurationTextFieldType.kt │ ├── AddCurationWordAction.kt │ ├── AddCurationWordActionCreator.kt │ ├── DeleteCurationWordAction.kt │ ├── DeleteCurationWordActionCreator.kt │ ├── InitializeAddCurationAction.kt │ ├── InitializeAddCurationActionCreator.kt │ ├── MyProgressDialogFragment.kt │ ├── StoreCurationAction.kt │ ├── StoreCurationActionCreator.kt │ ├── StoreCurationState.kt │ ├── StoreCurationStateStore.kt │ ├── UpdateTextFieldAction.kt │ ├── UpdateTextFieldActionCreator.kt │ └── UpdateTextFieldEvent.kt ├── feature_article_list ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── articlelist │ │ │ ├── ArticleListAdapter.kt │ │ │ ├── ArticleListReducer.kt │ │ │ ├── ArticleListUiBinding.kt │ │ │ ├── ArticleListViewModel.kt │ │ │ ├── ArticleRecyclerView.kt │ │ │ ├── ArticleSearchResultActivity.kt │ │ │ ├── ArticlesListActivity.kt │ │ │ ├── ArticlesListFragment.kt │ │ │ ├── FavoriteArticlesListActivity.kt │ │ │ ├── FavoriteArticlesListFragment.kt │ │ │ ├── Interation.kt │ │ │ ├── action │ │ │ ├── ArticleListAction.kt │ │ │ ├── FetchArticleListActionCreator.kt │ │ │ ├── FetchArticleListOfRssActionCreator.kt │ │ │ ├── FetchFavoriteArticleListActionCreator.kt │ │ │ ├── FinishStateActionCreator.kt │ │ │ ├── OpenUrlActionCreator.kt │ │ │ ├── ReadAllArticlesActionCreator.kt │ │ │ ├── ReadAllFavoriteArticlesActionCreator.kt │ │ │ ├── ReadArticleActionCreator.kt │ │ │ ├── ScrollActionCreator.kt │ │ │ ├── SearchArticleListActionCreator.kt │ │ │ ├── ShareUrlActionCreator.kt │ │ │ ├── SwipeActionCreator.kt │ │ │ └── UpdateFavoriteStatusActionCreator.kt │ │ │ ├── store │ │ │ ├── ArticleListStore.kt │ │ │ ├── FinishStateStore.kt │ │ │ ├── OpenExternalWebBrowserStateStore.kt │ │ │ ├── OpenInternalWebBrowserStateStore.kt │ │ │ ├── ReadAllArticlesStateStore.kt │ │ │ ├── ReadArticlePositionStore.kt │ │ │ ├── ScrollPositionStore.kt │ │ │ ├── SearchResultStore.kt │ │ │ ├── ShareUrlStore.kt │ │ │ └── SwipePositionStore.kt │ │ │ └── util │ │ │ └── VectorDrawableUtil.kt │ └── res │ │ ├── layout │ │ ├── activity_article_search_result.xml │ │ ├── activity_articles_list.xml │ │ ├── activity_favorite_articles_list.xml │ │ ├── articles_list.xml │ │ ├── footer_article_list_activity.xml │ │ └── fragment_articles_list.xml │ │ ├── menu │ │ └── menu_article.xml │ │ └── values │ │ └── strings.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── articlelist │ │ └── action │ │ ├── OpenUrlActionCreatorTest.kt │ │ ├── ReadArticlePositionActionCreatorTest.kt │ │ └── ShareUrlActionCreatorTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_curated_article_list ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── curatedarticlelist │ │ ├── CuratedArticleListAdapter.kt │ │ ├── CuratedArticleRecyclerView.kt │ │ ├── CuratedArticlesListActivity.kt │ │ ├── CuratedArticlesListFragment.kt │ │ ├── action │ │ ├── CuratedArticleListAction.kt │ │ ├── FetchCuratedArticleListActionCreator.kt │ │ ├── FinishStateActionCreator.kt │ │ ├── OpenUrlActionCreator.kt │ │ ├── ReadAllCuratedArticlesActionCreator.kt │ │ ├── ReadCuratedArticleActionCreator.kt │ │ ├── ScrollActionCreator.kt │ │ ├── ShareUrlActionCreator.kt │ │ └── SwipeActionCreator.kt │ │ ├── store │ │ ├── CuratedArticleListStore.kt │ │ ├── FinishCuratedArticleStateStore.kt │ │ ├── OpenCuratedArticleWithExternalWebBrowserStateStore.kt │ │ ├── OpenCuratedArticleWithInternalWebBrowserStateStore.kt │ │ ├── ReadAllCuratedArticlesStateStore.kt │ │ ├── ReadCuratedArticlePositionStore.kt │ │ ├── ScrollCuratedArticlePositionStore.kt │ │ ├── ShareCuratedArticleUrlStore.kt │ │ └── SwipeCuratedArticlePositionStore.kt │ │ └── util │ │ └── VectorDrawableUtil.kt │ └── res │ ├── layout │ ├── activity_curated_articles_list.xml │ ├── footer_curated_article_list.xml │ ├── fragment_curated_articles_list.xml │ └── item_curated_articles_list.xml │ ├── menu │ └── menu_curated_articles_list.xml │ └── values │ └── strings.xml ├── feature_curation_list ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── curationlist │ │ │ ├── CurationItem.kt │ │ │ ├── CurationListFragment.kt │ │ │ ├── CurationListPresenter.kt │ │ │ └── CurationListView.kt │ └── res │ │ └── layout │ │ ├── curation_list.xml │ │ └── fragment_curation_list.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── curationlist │ │ └── CurationListPresenterTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_feed_search ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── feedsearch │ │ │ ├── FeedSearchActivity.kt │ │ │ ├── FeedSearchPresenter.kt │ │ │ └── FeedSearchView.kt │ └── res │ │ ├── layout │ │ └── activity_feed_search.xml │ │ └── menu │ │ └── menu_feed_search.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── feedsearch │ │ └── FeedSearchPresenterTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_feed_url_hook ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── feedurlhook │ │ │ ├── FeedUrlHookActivity.kt │ │ │ ├── FeedUrlHookPresenter.kt │ │ │ └── FeedUrlHookView.kt │ └── res │ │ └── layout │ │ └── activity_feed_url_hook.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── feedurlhook │ │ └── FeedUrlHookPresenterTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_filter_list ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── filterlist │ │ │ ├── FilterListFragment.kt │ │ │ ├── FilterListPresenter.kt │ │ │ └── FilterListView.kt │ └── res │ │ └── layout │ │ ├── filters_list.xml │ │ └── fragment_filter_list.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── feature_filter_list │ │ └── FilterListPresenterTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_license ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── license │ │ └── LicenseActivity.kt │ └── res │ └── layout │ └── activity_license.xml ├── feature_register_filter ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── feature_register_filter │ │ │ ├── RegisterFilterActivity.kt │ │ │ ├── RegisterFilterPresenter.kt │ │ │ ├── RegisterFilterView.kt │ │ │ ├── SelectFilterTargetRssActivity.kt │ │ │ ├── SelectFilterTargetRssFragment.kt │ │ │ ├── SelectFilterTargetRssPresenter.kt │ │ │ └── SelectTargetRssView.kt │ └── res │ │ ├── drawable │ │ ├── bg_filter_target_rss.xml │ │ ├── ic_description.xml │ │ ├── ic_filter.xml │ │ ├── ic_rss_black.xml │ │ └── ic_web.xml │ │ ├── layout │ │ ├── activity_register_filter.xml │ │ ├── activity_select_filter_target_rss.xml │ │ ├── content_select_filter_target_rss.xml │ │ ├── filter_target_rss_list.xml │ │ └── fragment_select_filter_target_rss.xml │ │ └── menu │ │ ├── menu_register_filter.xml │ │ └── menu_select_filter_rss.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── feature_register_filter │ │ └── RegisterFilterPresenterTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_rss_list ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── rss │ │ │ ├── ChangeRssListModeActionCreator.kt │ │ │ ├── ConsumeRssListMessageAction.kt │ │ │ ├── ConsumeRssListMessageActionCreator.kt │ │ │ ├── DeleteRssAction.kt │ │ │ ├── DeleteRssActionCreator.kt │ │ │ ├── DeleteRssFailedAction.kt │ │ │ ├── EditRssTitleActionCreator.kt │ │ │ ├── EditRssTitleErrorAction.kt │ │ │ ├── EditRssTitleSuccessAction.kt │ │ │ ├── EditRssTitleValue.kt │ │ │ ├── FetchAllRssListActionCreator.kt │ │ │ ├── HideDeleteRssAlertDialogAction.kt │ │ │ ├── HideDeleteRssAlertDialogActionCreator.kt │ │ │ ├── HideDropdownMenuAction.kt │ │ │ ├── HideDropdownMenuActionCreator.kt │ │ │ ├── HideEditRssTitleAlertDialogAction.kt │ │ │ ├── HideEditRssTitleAlertDialogActionCreator.kt │ │ │ ├── IconFetchWorker.kt │ │ │ ├── LaunchUpdateAllRssActionCreator.kt │ │ │ ├── NewRssTitleChangeAction.kt │ │ │ ├── NewRssTitleChangeActionCreator.kt │ │ │ ├── RSSListStateStore.kt │ │ │ ├── RssItemView.kt │ │ │ ├── RssListAction.kt │ │ │ ├── RssListFragment.kt │ │ │ ├── RssListItem.kt │ │ │ ├── RssListItemFactory.kt │ │ │ ├── RssListMessage.kt │ │ │ ├── RssListState.kt │ │ │ ├── RssListUpdateAction.kt │ │ │ ├── RssListUpdateState.kt │ │ │ ├── ShowDeleteRssAlertDialogAction.kt │ │ │ ├── ShowDeleteRssAlertDialogActionCreator.kt │ │ │ ├── ShowDropdownMenuAction.kt │ │ │ ├── ShowDropdownMenuActionCreator.kt │ │ │ ├── ShowEditRssTitleAlertDialogAction.kt │ │ │ ├── ShowEditRssTitleAlertDialogActionCreator.kt │ │ │ └── UpdateAllRssActionCreator.kt │ └── res │ │ ├── drawable │ │ └── ic_view_headline_black_24dp.xml │ │ └── values │ │ └── strings.xml │ └── test │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── rss │ │ └── EditRssTitleActionCreatorTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_setting ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── setting │ │ │ ├── SettingActivity.kt │ │ │ ├── SettingFragment.kt │ │ │ ├── SettingPresenter.kt │ │ │ └── SettingView.kt │ └── res │ │ ├── layout │ │ └── activity_setting.xml │ │ └── xml │ │ ├── setting_fragment.xml │ │ └── setting_fragment_debug.xml │ └── test │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── setting │ └── SettingPresenterTest.kt ├── feature_top ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── top │ │ │ ├── CheckReviewRequestActionCreator.kt │ │ │ ├── CloseRateDialogAction.kt │ │ │ ├── CloseRateDialogActionCreator.kt │ │ │ ├── InitializeTopAction.kt │ │ │ ├── InitializeTopActionCreator.kt │ │ │ ├── InitializeTopValue.kt │ │ │ ├── ShowRateDialogAction.kt │ │ │ ├── TopActivity.kt │ │ │ ├── TopState.kt │ │ │ └── TopStateStore.kt │ └── res │ │ ├── anim │ │ ├── fab_fadein_curation.xml │ │ ├── fab_fadein_filter.xml │ │ ├── fab_fadein_rss.xml │ │ ├── fab_fadeout_curation.xml │ │ ├── fab_fadeout_filter.xml │ │ ├── fab_fadeout_rss.xml │ │ ├── fab_rotation.xml │ │ └── fab_rotation_back.xml │ │ ├── color │ │ └── item_bottom_navigation.xml │ │ ├── drawable-hdpi │ │ ├── tab_curation.png │ │ ├── tab_feed.png │ │ └── tab_filter.png │ │ ├── drawable-mdpi │ │ ├── tab_curation.png │ │ ├── tab_feed.png │ │ └── tab_filter.png │ │ ├── drawable-xhdpi │ │ ├── tab_curation.png │ │ ├── tab_feed.png │ │ └── tab_filter.png │ │ ├── drawable-xxhdpi │ │ ├── tab_curation.png │ │ ├── tab_feed.png │ │ └── tab_filter.png │ │ ├── drawable │ │ └── ic_settings.xml │ │ ├── layout │ │ └── activity_top.xml │ │ └── menu │ │ ├── main.xml │ │ └── menu_top_bottom_navigation.xml │ └── test │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── feature_util ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── feature │ │ └── util │ │ └── ActivityExtension.kt │ └── res │ └── values │ └── strings.xml ├── glide ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── glide │ │ └── MyCurationAppGlideModule.kt │ └── res │ └── values │ └── strings.xml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt ├── jacoco.gradle ├── modules.plantuml ├── modules.png ├── privacy-policy └── privacy-policy.html ├── renovate.json ├── repository ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── debug │ └── java │ │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── data │ │ └── repository │ │ └── AdditionalSettingRepository.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── data │ │ │ ├── network │ │ │ └── HatenaBookmarkApi.kt │ │ │ ├── preference │ │ │ └── PreferenceHelper.kt │ │ │ └── repository │ │ │ ├── AdditionalSettingApi.kt │ │ │ ├── ArticleRepository.kt │ │ │ ├── CurationRepository.kt │ │ │ ├── FavoriteRepository.kt │ │ │ ├── FilterRepository.kt │ │ │ └── RssRepository.kt │ ├── res │ │ └── values │ │ │ └── strings.xml │ └── sqldelight │ │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── data │ │ ├── 1.sqm │ │ ├── 2.sqm │ │ ├── 3.sqm │ │ ├── 4.sqm │ │ ├── Article.sq │ │ ├── Curation.sq │ │ ├── CurationCondition.sq │ │ ├── CurationSelection.sq │ │ ├── FavoriteArticle.sq │ │ ├── Feed.sq │ │ ├── FilterFeedRegistration.sq │ │ └── Filters.sq │ └── release │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── data │ └── repository │ └── AdditionalSettingRepository.kt ├── resource ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── resource │ │ ├── Color.kt │ │ └── Theme.kt │ └── res │ ├── drawable-hdpi │ └── hatena.png │ ├── drawable-mdpi │ └── hatena.png │ ├── drawable-xhdpi │ └── hatena.png │ ├── drawable-xxhdpi │ └── hatena.png │ ├── drawable │ ├── ic_add.xml │ ├── ic_check.xml │ ├── ic_down.xml │ ├── ic_favorite_off.xml │ ├── ic_favorite_on.xml │ ├── ic_rss.xml │ ├── ic_search.xml │ └── ic_share.xml │ ├── values-night │ └── colors.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── array.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── scripts └── dependency_diff.sh ├── settings.gradle ├── test_util ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── phicdy │ └── test │ └── util │ ├── CoroutineTestRule.kt │ └── TestCoroutineDispatcherProvider.kt ├── tracker ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── phicdy │ │ └── mycuration │ │ └── tracker │ │ └── TrackerHelper.kt │ └── res │ └── values │ └── strings.xml ├── util ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── phicdy │ │ │ └── mycuration │ │ │ └── util │ │ │ ├── FileUtil.kt │ │ │ ├── ToastHelper.kt │ │ │ └── UrlUtil.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── phicdy │ └── mycuration │ └── util │ └── UrlUtilTest.kt └── whatsnews ├── whatsnew-en-AU ├── whatsnew-en-GB ├── whatsnew-en-US └── whatsnew-ja-JP /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # eclipse settings 19 | .classpath 20 | .project 21 | .settings/ 22 | .metadata/ 23 | FilFeed/build/ 24 | PullToReflresh/build/ 25 | PullToReflresh/pullToReflresh.iml 26 | FilFeed/filFeed.iml 27 | FilFeed.iml 28 | main/main.iml 29 | build/ 30 | .idea/misc.xml 31 | .idea/libraries/ 32 | .gradle/ 33 | .idea/modules.xml 34 | .idea/workspace.xml 35 | google-services.json 36 | 37 | *.iml 38 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/saveactions_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /action_article_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /action_article_list/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.action.articlelist' 9 | } 10 | 11 | dependencies { 12 | implementation project(':core') 13 | implementation project(':entity') 14 | } 15 | -------------------------------------------------------------------------------- /action_article_list/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/action_article_list/consumer-rules.pro -------------------------------------------------------------------------------- /action_article_list/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /action_article_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /action_article_list/src/main/java/com/phicdy/action/articlelist/ReadAllArticlesAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.action.articlelist 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.entity.ReadAllArticles 5 | 6 | class ReadAllArticlesAction( 7 | override val value: ReadAllArticles 8 | ) : Action -------------------------------------------------------------------------------- /action_article_list/src/main/java/com/phicdy/action/articlelist/ReadArticleAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.action.articlelist 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.entity.ReadArticle 5 | 6 | data class ReadArticleAction( 7 | override val value: ReadArticle 8 | ) : Action 9 | -------------------------------------------------------------------------------- /action_article_list/src/main/java/com/phicdy/action/articlelist/UnReadArticleAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.action.articlelist 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.entity.UnReadArticle 5 | 6 | data class UnReadArticleAction( 7 | override val value: UnReadArticle 8 | ) : Action -------------------------------------------------------------------------------- /admob/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /admob/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | defaultConfig { 3 | manifestPlaceholders = [ 4 | ad_app_id: "$ADMOB_ID_MYCURATION" 5 | ] 6 | 7 | buildConfigField "String", "AD_APP_ID", "\"$ADMOB_ID_MYCURATION\"" 8 | resValue "string", "ad_unit_id_list", "$ADMOB_UNIT_ID_MYCURATION_LIST" 9 | resValue "string", "ad_unit_id_fragment", "$ADMOB_UNIT_ID_MYCURATION_FRAGMENT" 10 | } 11 | 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | namespace 'com.phicdy.mycuration.admob' 19 | } 20 | 21 | dependencies { 22 | implementation project(':advertisement') 23 | 24 | implementation libs.firebase.ads 25 | 26 | implementation libs.recyclerview 27 | implementation libs.constraintlayout 28 | 29 | implementation libs.javax.inject 30 | } 31 | -------------------------------------------------------------------------------- /admob/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /admob/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /admob/src/main/java/com/phicdy/mycuration/admob/AdmobFragment.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.admob 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.google.android.gms.ads.AdRequest 8 | import com.google.android.gms.ads.AdView 9 | import com.phicdy.mycuration.advertisement.AdFragment 10 | 11 | class AdmobFragment : AdFragment() { 12 | 13 | override fun onCreateView( 14 | inflater: LayoutInflater, 15 | container: ViewGroup?, 16 | savedInstanceState: Bundle? 17 | ): View? { 18 | val rootView = inflater.inflate(R.layout.fragment_admob, container, false) 19 | 20 | val adView = rootView.findViewById(R.id.adView) 21 | adView.loadAd(AdRequest.Builder().build()) 22 | return rootView 23 | } 24 | 25 | companion object { 26 | fun newInstance() = AdmobFragment() 27 | } 28 | } -------------------------------------------------------------------------------- /admob/src/main/java/com/phicdy/mycuration/admob/AdmobProvider.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.admob 2 | 3 | import android.content.Context 4 | import android.view.ViewGroup 5 | import com.google.android.gms.ads.MobileAds 6 | import com.phicdy.mycuration.advertisement.AdFragment 7 | import com.phicdy.mycuration.advertisement.AdProvider 8 | import com.phicdy.mycuration.advertisement.AdViewHolder 9 | import javax.inject.Inject 10 | 11 | class AdmobProvider @Inject constructor() : AdProvider { 12 | 13 | override fun init(context: Context) { 14 | MobileAds.initialize(context) {} 15 | } 16 | 17 | override fun newViewHolderInstance(parent: ViewGroup): AdViewHolder = AdmobViewHolder(parent) 18 | 19 | override fun newFragmentInstance(): AdFragment = AdmobFragment.newInstance() 20 | } -------------------------------------------------------------------------------- /admob/src/main/java/com/phicdy/mycuration/admob/AdmobViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.admob 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import com.google.android.gms.ads.AdRequest 7 | import com.google.android.gms.ads.AdView 8 | import com.phicdy.mycuration.advertisement.AdViewHolder 9 | 10 | class AdmobViewHolder( 11 | parent: ViewGroup, 12 | itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.item_list_ad, parent, false) 13 | ) : AdViewHolder(itemView) { 14 | 15 | private val adView: AdView = itemView.findViewById(R.id.adView) 16 | 17 | override fun bind() { 18 | adView.loadAd(AdRequest.Builder().build()) 19 | } 20 | } -------------------------------------------------------------------------------- /admob/src/main/res/layout/fragment_admob.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /admob/src/main/res/layout/item_list_ad.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /admob/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /advertisement/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /advertisement/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.advertisement' 9 | 10 | } 11 | 12 | dependencies { 13 | implementation libs.recyclerview 14 | implementation libs.appcompat 15 | } 16 | -------------------------------------------------------------------------------- /advertisement/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /advertisement/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /advertisement/src/main/java/com/phicdy/mycuration/advertisement/AdFragment.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.advertisement 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | abstract class AdFragment : Fragment() 6 | -------------------------------------------------------------------------------- /advertisement/src/main/java/com/phicdy/mycuration/advertisement/AdProvider.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.advertisement 2 | 3 | import android.content.Context 4 | import android.view.ViewGroup 5 | 6 | interface AdProvider { 7 | fun init(context: Context) 8 | fun newViewHolderInstance(parent: ViewGroup): AdViewHolder 9 | fun newFragmentInstance(): AdFragment 10 | } -------------------------------------------------------------------------------- /advertisement/src/main/java/com/phicdy/mycuration/advertisement/AdViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.advertisement 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | abstract class AdViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 7 | abstract fun bind() 8 | } -------------------------------------------------------------------------------- /advertisement/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android-lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/dummy-release-key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/dummy-release-key -------------------------------------------------------------------------------- /app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/phicdy/mycuration/DatabaseUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration 2 | 3 | import android.database.SQLException 4 | import com.phicdy.mycuration.repository.Database 5 | import timber.log.Timber 6 | 7 | fun deleteAll(db: Database) { 8 | try { 9 | db.transaction { 10 | db.curationConditionQueries.deleteAll() 11 | db.curationSelectionQueries.deleteAll() 12 | db.articleQueries.deleteAll() 13 | db.filterFeedRegistrationQueries.deleteAll() 14 | db.filtersQueries.deleteAll() 15 | db.curationQueries.deleteAll() 16 | db.feedQueries.deleteAll() 17 | } 18 | } catch (e: SQLException) { 19 | Timber.e(e) 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/phicdy/mycuration/domain/rss/IconParserTest.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.rss 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | import org.junit.runner.RunWith 6 | import org.junit.runners.JUnit4 7 | 8 | @RunWith(JUnit4::class) 9 | class IconParserTest { 10 | 11 | @Test 12 | fun testParseXml() { 13 | val parser = IconParser() 14 | assertEquals("https://gigazine.net/favicon.ico", parser.parseHtml("https://gigazine.net")) 15 | assertEquals( 16 | "https://b.hatena.ne.jp/favicon.ico", 17 | parser.parseHtml("https://b.hatena.ne.jp") 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/phicdy/mycuration/util/test/FileUtilTest.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.util.test 2 | 3 | import androidx.test.core.app.ApplicationProvider 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | import com.phicdy.mycuration.util.FileUtil 6 | import org.junit.Assert.assertEquals 7 | import org.junit.Before 8 | import org.junit.Test 9 | import org.junit.runner.RunWith 10 | 11 | @RunWith(AndroidJUnit4::class) 12 | class FileUtilTest { 13 | 14 | @Before 15 | fun setup() { 16 | FileUtil.setUpAppPath(ApplicationProvider.getApplicationContext()) 17 | } 18 | 19 | @Test 20 | fun testGetIconSavePath() { 21 | assertEquals(FileUtil.getAppPath(ApplicationProvider.getApplicationContext()) + "icons/", FileUtil.iconSaveFolder()) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/benchmark/java/com/phicdy/mycuration/FlipperInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration 2 | 3 | import android.app.Application 4 | 5 | class FlipperInitializer { 6 | 7 | fun init(myApplication: Application) {} 8 | } -------------------------------------------------------------------------------- /app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/debug/java/com/phicdy/mycuration/FlipperInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration 2 | 3 | import android.app.Application 4 | import com.facebook.flipper.android.AndroidFlipperClient 5 | import com.facebook.flipper.android.utils.FlipperUtils 6 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin 7 | import com.facebook.flipper.plugins.inspector.DescriptorMapping 8 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin 9 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin 10 | import com.facebook.soloader.SoLoader 11 | 12 | class FlipperInitializer { 13 | 14 | fun init(application: Application) { 15 | if (FlipperUtils.shouldEnableFlipper(application)) { 16 | SoLoader.init(application, false) 17 | val client = AndroidFlipperClient.getInstance(application) 18 | client.addPlugin(InspectorFlipperPlugin(application, DescriptorMapping.withDefaults())) 19 | client.addPlugin(DatabasesFlipperPlugin(application)) 20 | client.addPlugin(SharedPreferencesFlipperPlugin(application, "FilterPref")) 21 | client.start() 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/phicdy/mycuration/DefaultWorkerFactory.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration 2 | 3 | import android.content.Context 4 | import androidx.work.ListenableWorker 5 | import androidx.work.WorkerFactory 6 | import androidx.work.WorkerParameters 7 | import com.phicdy.mycuration.data.repository.RssRepository 8 | import com.phicdy.mycuration.rss.IconFetchWorker 9 | 10 | class DefaultWorkerFactory( 11 | private val rssRepository: RssRepository 12 | ) : WorkerFactory() { 13 | 14 | override fun createWorker( 15 | appContext: Context, 16 | workerClassName: String, 17 | workerParameters: WorkerParameters 18 | ): ListenableWorker? { 19 | return when (Class.forName(workerClassName)) { 20 | IconFetchWorker::class.java -> IconFetchWorker(appContext, workerParameters, rssRepository) 21 | else -> throw IllegalArgumentException("invalid worker: $workerClassName") 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/phicdy/mycuration/di/ActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.di 2 | 3 | import android.content.Context 4 | import dagger.Module 5 | import dagger.Provides 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ActivityComponent 8 | import dagger.hilt.android.qualifiers.ActivityContext 9 | import dagger.hilt.android.scopes.ActivityScoped 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlin.coroutines.CoroutineContext 13 | 14 | @Module 15 | @InstallIn(ActivityComponent::class) 16 | object ActivityModule { 17 | @ActivityScoped 18 | @Provides 19 | fun provideCoroutineScope(@ActivityContext activity: Context): CoroutineScope = 20 | activity as CoroutineScope 21 | 22 | @ActivityScoped 23 | @Provides 24 | fun provideCoroutineContext(): CoroutineContext = Dispatchers.Main 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/phicdy/mycuration/di/ViewModelModule.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import dagger.hilt.InstallIn 6 | import dagger.hilt.android.components.ViewModelComponent 7 | import dagger.hilt.android.scopes.ViewModelScoped 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlin.coroutines.CoroutineContext 10 | 11 | @Module 12 | @InstallIn(ViewModelComponent::class) 13 | object ViewModelModule { 14 | @ViewModelScoped 15 | @Provides 16 | fun provideCoroutineContext(): CoroutineContext = Dispatchers.Main 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/drawable/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/drawable/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_launch.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/searchable_feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/release/java/com/phicdy/mycuration/FlipperInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration 2 | 3 | import android.app.Application 4 | 5 | class FlipperInitializer { 6 | 7 | fun init(myApplication: Application) {} 8 | } -------------------------------------------------------------------------------- /baselineprofile/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /baselineprofile/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /benchmark/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.core' 9 | 10 | } 11 | 12 | dependencies { 13 | implementation libs.coroutines.core 14 | implementation libs.coroutines.android 15 | 16 | implementation libs.bundles.lifecycle 17 | 18 | implementation libs.javax.inject 19 | } 20 | -------------------------------------------------------------------------------- /core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/Action.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface Action { 4 | val value: T 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/ActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface ActionCreator { 4 | suspend fun run() 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/ActionCreator1.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface ActionCreator1 { 4 | suspend fun run(arg: T) 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/ActionCreator2.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface ActionCreator2 { 4 | suspend fun run(arg1: T1, arg2: T2) 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/ActionCreator3.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface ActionCreator3 { 4 | suspend fun run(arg1: T1, arg2: T2, arg3: T3) 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/ActionCreator4.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface ActionCreator4 { 4 | suspend fun run(arg1: T1, arg2: T2, arg3: T3, arg4: T4) 5 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/CoroutineDispatcherProvider.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | 5 | interface CoroutineDispatcherProvider { 6 | fun main(): CoroutineDispatcher 7 | fun default(): CoroutineDispatcher 8 | fun io(): CoroutineDispatcher 9 | fun unconfined(): CoroutineDispatcher 10 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/DefaultCoroutineDispatcherProvider.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | import kotlinx.coroutines.CoroutineDispatcher 4 | import kotlinx.coroutines.Dispatchers 5 | 6 | class DefaultCoroutineDispatcherProvider : CoroutineDispatcherProvider { 7 | override fun main(): CoroutineDispatcher = Dispatchers.Main 8 | override fun default(): CoroutineDispatcher = Dispatchers.Default 9 | override fun io(): CoroutineDispatcher = Dispatchers.IO 10 | override fun unconfined(): CoroutineDispatcher = Dispatchers.Unconfined 11 | } -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/Reducer.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | interface Reducer { 4 | fun reduce(action: Action<*>) 5 | } 6 | -------------------------------------------------------------------------------- /core/src/main/java/com/phicdy/mycuration/core/Store.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.core 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import kotlinx.coroutines.CoroutineScope 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.Job 9 | import kotlin.coroutines.CoroutineContext 10 | 11 | abstract class Store( 12 | protected val dispatcher: Dispatcher, 13 | val context: CoroutineContext = Dispatchers.Main 14 | ) : ViewModel(), CoroutineScope { 15 | 16 | private val job = Job() 17 | override val coroutineContext: CoroutineContext 18 | get() = job + context 19 | 20 | protected val _state = MutableLiveData() 21 | val state: LiveData 22 | get() = _state 23 | 24 | abstract suspend fun notify(action: Action<*>) 25 | 26 | override fun onCleared() { 27 | job.cancel() 28 | dispatcher.unregister(this) 29 | super.onCleared() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /di_common/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /di_common/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.di.common' 9 | } 10 | 11 | dependencies { 12 | implementation libs.javax.inject 13 | } -------------------------------------------------------------------------------- /di_common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /di_common/src/main/java/com/phicdy/mycuration/di/common/Qualifiers.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.di.common 2 | 3 | import javax.inject.Qualifier 4 | 5 | @Qualifier 6 | @Retention(AnnotationRetention.BINARY) 7 | annotation class ApplicationCoroutineScope -------------------------------------------------------------------------------- /domain/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /domain/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | apply plugin: 'dagger.hilt.android.plugin' 3 | 4 | android { 5 | buildTypes { 6 | release { 7 | minifyEnabled false 8 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 9 | } 10 | } 11 | namespace 'com.phicdy.mycuration.domain' 12 | } 13 | 14 | dependencies { 15 | implementation project(':entity') 16 | implementation project(':repository') 17 | implementation project(':util') 18 | 19 | implementation libs.coroutines.core 20 | implementation libs.coroutines.android 21 | 22 | implementation libs.retrofit 23 | 24 | implementation libs.jsoup 25 | 26 | implementation libs.timber 27 | 28 | implementation libs.javax.inject 29 | 30 | implementation libs.dagger.hilt 31 | kapt libs.dagger.hilt.compiler 32 | } 33 | -------------------------------------------------------------------------------- /domain/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /domain/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/alarm/BootReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.alarm 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | 7 | import com.phicdy.mycuration.data.preference.PreferenceHelper 8 | 9 | class BootReceiver : BroadcastReceiver() { 10 | 11 | override fun onReceive(context: Context, intent: Intent?) { 12 | if (intent != null && intent.action == Intent.ACTION_BOOT_COMPLETED) { 13 | AlarmManagerTaskManager(context).run { 14 | setNewAlarm(PreferenceHelper.autoUpdateIntervalSecond) 15 | setFixUnreadCountAlarm() 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/rss/IconParser.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.rss 2 | 3 | import org.jsoup.Jsoup 4 | import java.net.URL 5 | 6 | class IconParser { 7 | 8 | fun parseHtml(urlString: String): String { 9 | if (urlString.isBlank()) return "" 10 | try { 11 | Jsoup.connect(urlString).get().getElementsByTag("link").forEach { link -> 12 | if (link.attr("rel") != "shortcut icon" && link.attr("rel") != "apple-touch-icon") return@forEach 13 | val href = link.attr("href") 14 | if (!href.startsWith("http:") && !href.startsWith("https:")) { 15 | val url = URL(urlString) 16 | // The link is /// 17 | return if (href.startsWith("//")) { 18 | url.protocol + ":" + href 19 | } else URL(url.protocol, url.host, href).toString() 20 | } 21 | return href 22 | } 23 | } catch (e: Exception) { 24 | e.printStackTrace() 25 | } 26 | return "" 27 | } 28 | } -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/rss/RssParseResult.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.rss 2 | 3 | import com.phicdy.mycuration.entity.Feed 4 | 5 | 6 | class RssParseResult(val feed: Feed? = null, 7 | val failedReason: FailedReason = FailedReason.NOT_FAILED) { 8 | 9 | enum class FailedReason { 10 | NOT_FAILED, 11 | INVALID_URL, 12 | NON_RSS_HTML, 13 | NOT_FOUND 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/rss/RssUrlHookIntentData.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.rss 2 | 3 | data class RssUrlHookIntentData( 4 | val action: String, 5 | val dataString: String, 6 | val extrasText: CharSequence 7 | ) -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/task/FilterTask.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.task 2 | 3 | import com.phicdy.mycuration.data.repository.ArticleRepository 4 | import com.phicdy.mycuration.data.repository.FilterRepository 5 | import kotlinx.coroutines.coroutineScope 6 | 7 | class FilterTask( 8 | private val articleRepository: ArticleRepository, 9 | private val filterRepository: FilterRepository 10 | ) { 11 | 12 | suspend fun applyFiltering(feedId: Int): Int = coroutineScope { 13 | val filters = filterRepository.getEnabledFiltersOfFeed(feedId) 14 | if (filters.size == 0) return@coroutineScope 0 15 | return@coroutineScope articleRepository.applyFiltersOfRss(filters, feedId) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/task/GetFeedIconTask.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.task 2 | 3 | import com.phicdy.mycuration.domain.rss.IconParser 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | 7 | class GetFeedIconTask { 8 | 9 | suspend fun execute(siteUrl: String): String = withContext(Dispatchers.IO) { 10 | return@withContext IconParser().parseHtml(siteUrl) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/util/NetworkUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.util 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | 6 | 7 | object NetworkUtil { 8 | 9 | fun isWifiConnected(context: Context): Boolean { 10 | val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 11 | val info = connectivityManager.activeNetworkInfo 12 | return info != null && info.isConnected && info.typeName == "WIFI" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /domain/src/main/java/com/phicdy/mycuration/domain/util/TextUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.util 2 | 3 | import java.util.regex.Pattern 4 | 5 | object TextUtil { 6 | 7 | fun removeLineFeed(string: String): String { 8 | return Pattern.compile("\t|\r|\n|\r\n").matcher(string).replaceAll("") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /domain/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /domain/src/test/java/com/phicdy/mycuration/domain/util/TextUtilTest.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.domain.util 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | import org.junit.runner.RunWith 6 | import org.junit.runners.JUnit4 7 | 8 | @RunWith(JUnit4::class) 9 | class TextUtilTest { 10 | 11 | @Test 12 | fun testRemoveLineFeed() { 13 | assertThat(TextUtil.removeLineFeed("aaa\r")).isEqualTo("aaa") 14 | assertThat(TextUtil.removeLineFeed("aaa\n")).isEqualTo("aaa") 15 | assertThat(TextUtil.removeLineFeed("aaa\t")).isEqualTo("aaa") 16 | assertThat(TextUtil.removeLineFeed("aaa\r\n")).isEqualTo("aaa") 17 | 18 | assertThat(TextUtil.removeLineFeed("a\ra\na")).isEqualTo("aaa") 19 | assertThat(TextUtil.removeLineFeed("a\ra\ta")).isEqualTo("aaa") 20 | assertThat(TextUtil.removeLineFeed("a\ra\r\na")).isEqualTo("aaa") 21 | assertThat(TextUtil.removeLineFeed("a\na\ta")).isEqualTo("aaa") 22 | assertThat(TextUtil.removeLineFeed("a\na\r\na")).isEqualTo("aaa") 23 | assertThat(TextUtil.removeLineFeed("a\ta\r\na")).isEqualTo("aaa") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /entity/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /entity/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.entity' 9 | } 10 | 11 | dependencies { 12 | } 13 | -------------------------------------------------------------------------------- /entity/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /entity/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/Article.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | 4 | data class Article( 5 | val id: Int, 6 | var title: String, 7 | var url: String, 8 | var status: String, 9 | val point: String, 10 | var postedDate: Long, 11 | val feedId: Int, 12 | val feedTitle: String, 13 | val feedIconPath: String 14 | ) { 15 | 16 | companion object { 17 | 18 | const val TABLE_NAME = "articles" 19 | const val ID = "_id" 20 | const val TITLE = "title" 21 | const val URL = "url" 22 | const val STATUS = "status" 23 | const val POINT = "point" 24 | const val DATE = "date" 25 | const val FEEDID = "feedId" 26 | 27 | const val UNREAD = "unread" 28 | const val READ = "read" 29 | 30 | const val DEDAULT_HATENA_POINT = "-1" 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/Curation.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | 4 | data class Curation( 5 | val id: Int, 6 | val name: String 7 | ) { 8 | 9 | companion object { 10 | const val TABLE_NAME = "curations" 11 | const val NAME = "name" 12 | const val ID = "_id" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/CurationCondition.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | object CurationCondition { 4 | const val TABLE_NAME = "curationConditions" 5 | const val CURATION_ID = "curationId" 6 | const val WORD = "word" 7 | } 8 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/CurationSelection.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | object CurationSelection { 4 | const val TABLE_NAME = "curationSelections" 5 | const val CURATION_ID = "curationId" 6 | const val ARTICLE_ID = "articleId" 7 | } 8 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/FaivoriteArticle.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class FavoriteArticle( 4 | val id: Int, 5 | val articleId: Int 6 | ) { 7 | 8 | companion object { 9 | const val TABLE_NAME = "favoriteArticles" 10 | const val ID = "_id" 11 | const val ARTICLE_ID = "articleId" 12 | 13 | const val CREATE_TABLE_SQL = "create table " + TABLE_NAME + "(" + 14 | ID + " integer primary key autoincrement," + 15 | ARTICLE_ID + " integer," + 16 | "foreign key(" + ARTICLE_ID + ") references " + Article.TABLE_NAME + "(" + Article.ID + "))" 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/FavoritableArticle.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class FavoritableArticle( 4 | val id: Int, 5 | var title: String, 6 | var url: String, 7 | var status: String, 8 | val point: String, 9 | var postedDate: Long, 10 | val feedId: Int, 11 | val feedTitle: String, 12 | val feedIconPath: String, 13 | val isFavorite: Boolean 14 | ) 15 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/FavoriteArticleItem.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class FavoriteArticleItem( 4 | val id: Int, 5 | val article: Article 6 | ) -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/FilterFeedRegistration.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | 4 | object FilterFeedRegistration { 5 | const val TABLE_NAME = "filterFeedRegistrations" 6 | const val FEED_ID = "feedId" 7 | const val FILTER_ID = "filterId" 8 | 9 | const val DROP_TABLE_SQL = "DROP TABLE $TABLE_NAME" 10 | } 11 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/ReadAllArticles.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class ReadAllArticles( 4 | val rssId: Int 5 | ) 6 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/ReadArticle.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class ReadArticle( 4 | val rssId: Int, 5 | val count: Int 6 | ) 7 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/RssListMode.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | enum class RssListMode { 4 | UNREAD_ONLY, ALL 5 | } -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/RssUpdateIntervalCheckDate.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | import java.util.Date 4 | 5 | data class RssUpdateIntervalCheckDate( 6 | private val now: Date 7 | ) { 8 | fun toTime() = now.time 9 | } 10 | -------------------------------------------------------------------------------- /entity/src/main/java/com/phicdy/mycuration/entity/UnReadArticle.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | data class UnReadArticle( 4 | val rssId: Int, 5 | val count: Int 6 | ) 7 | -------------------------------------------------------------------------------- /entity/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /entity/src/test/java/com/phicdy/mycuration/entity/FilterTest.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.entity 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | 6 | class FilterTest { 7 | @Test 8 | fun `when RSS list has a RSS then RSS title is the RSS's title`() { 9 | val filter = Filter( 10 | id = 1, 11 | feedId = 1, 12 | keyword = "aaa", 13 | title = "bbb", 14 | url = "http://www.google.com", 15 | feeds = arrayListOf(Feed(title = "title")) 16 | ) 17 | assertThat(filter.feedTitle).isEqualTo("title") 18 | } 19 | 20 | @Test 21 | fun `when RSS list has two RSSes then RSS title is combined RSS title`() { 22 | val filter = Filter( 23 | id = 1, 24 | feedId = 1, 25 | keyword = "aaa", 26 | title = "bbb", 27 | url = "http://www.google.com", 28 | feeds = arrayListOf( 29 | Feed(title = "title"), 30 | Feed(title = "title2") 31 | ) 32 | ) 33 | assertThat(filter.feedTitle).isEqualTo("title, title2") 34 | } 35 | } -------------------------------------------------------------------------------- /feature_add_curation/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_add_curation/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.compose.compiler) 3 | } 4 | 5 | apply plugin: 'kotlin-kapt' 6 | apply plugin: 'dagger.hilt.android.plugin' 7 | 8 | android { 9 | buildTypes { 10 | release { 11 | minifyEnabled false 12 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 13 | } 14 | } 15 | buildFeatures { 16 | compose true 17 | } 18 | namespace 'com.phicdy.mycuration.feature.addcuration' 19 | } 20 | 21 | dependencies { 22 | implementation project(':core') 23 | implementation project(':domain') 24 | implementation project(':feature_util') 25 | implementation project(':repository') 26 | implementation project(':resource') 27 | implementation project(':tracker') 28 | implementation project(':util') 29 | 30 | implementation libs.appcompat 31 | implementation libs.fragment 32 | implementation libs.bundles.lifecycle 33 | 34 | implementation libs.dagger.hilt 35 | kapt libs.dagger.hilt.compiler 36 | } -------------------------------------------------------------------------------- /feature_add_curation/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_add_curation/consumer-rules.pro -------------------------------------------------------------------------------- /feature_add_curation/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /feature_add_curation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/AddCurationEvent.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | sealed class AddCurationEvent { 4 | object Empty : AddCurationEvent() 5 | object Duplicated : AddCurationEvent() 6 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/AddCurationState.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | sealed class AddCurationState { 4 | object Loading : AddCurationState() 5 | data class Loaded( 6 | val words: List, 7 | val titleField: String, 8 | val wordField: String, 9 | ) : AddCurationState() 10 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/AddCurationTextFieldType.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | enum class AddCurationTextFieldType { 4 | TITLE, WORD 5 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/AddCurationWordAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class AddCurationWordAction(override val value: String) : Action -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/AddCurationWordActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class AddCurationWordActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ): ActionCreator1 { 10 | override suspend fun run(arg: String) { 11 | dispatcher.dispatch(AddCurationWordAction(arg)) 12 | } 13 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/DeleteCurationWordAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class DeleteCurationWordAction(override val value: Int) : Action -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/DeleteCurationWordActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class DeleteCurationWordActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ): ActionCreator1 { 10 | override suspend fun run(arg: Int) { 11 | dispatcher.dispatch(DeleteCurationWordAction(arg)) 12 | } 13 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/InitializeAddCurationAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class InitializeAddCurationAction(override val value: AddCurationState) : Action -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/InitializeAddCurationActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.data.repository.CurationRepository 6 | import javax.inject.Inject 7 | 8 | class InitializeAddCurationActionCreator @Inject constructor( 9 | private val repository: CurationRepository, 10 | private val dispatcher: Dispatcher 11 | ) : ActionCreator1 { 12 | override suspend fun run(arg: Int) { 13 | dispatcher.dispatch(InitializeAddCurationAction(AddCurationState.Loading)) 14 | if (arg != NOT_EDIT_CURATION_ID) { 15 | val curationName = repository.getCurationNameById(arg) 16 | val words = repository.getCurationWords(arg) 17 | dispatcher.dispatch(InitializeAddCurationAction(AddCurationState.Loaded(words, curationName, ""))) 18 | } else { 19 | dispatcher.dispatch(InitializeAddCurationAction(AddCurationState.Loaded(emptyList(), "", ""))) 20 | } 21 | } 22 | 23 | companion object { 24 | const val NOT_EDIT_CURATION_ID = -1 25 | } 26 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/StoreCurationAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class StoreCurationAction(override val value: StoreCurationState) : Action -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/StoreCurationState.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | sealed class StoreCurationState { 4 | object Loading : StoreCurationState() 5 | object SucceedToAdd: StoreCurationState() 6 | object SucceedToEdit: StoreCurationState() 7 | object EmptyNameError: StoreCurationState() 8 | object EmptyWordError: StoreCurationState() 9 | object SameNameExitError: StoreCurationState() 10 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/StoreCurationStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class StoreCurationStateStore @Inject constructor( 11 | dispatcher: Dispatcher, 12 | ) : Store(dispatcher) { 13 | 14 | init { 15 | dispatcher.register(this) 16 | } 17 | 18 | override suspend fun notify(action: Action<*>) { 19 | when (action) { 20 | is StoreCurationAction -> { 21 | _state.value = action.value 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/UpdateTextFieldAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class UpdateTextFieldAction(override val value: UpdateTextFieldEvent) : Action -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/UpdateTextFieldActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | import com.phicdy.mycuration.core.ActionCreator2 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class UpdateTextFieldActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher, 9 | ): ActionCreator2 { 10 | 11 | override suspend fun run(arg1: AddCurationTextFieldType, arg2: String) { 12 | dispatcher.dispatch(UpdateTextFieldAction(UpdateTextFieldEvent(arg1, arg2))) 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /feature_add_curation/src/main/java/com/phicdy/mycuration/feature/addcuration/UpdateTextFieldEvent.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.addcuration 2 | 3 | data class UpdateTextFieldEvent(val type: AddCurationTextFieldType, val value: String) -------------------------------------------------------------------------------- /feature_article_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_article_list/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /feature_article_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/ArticleListUiBinding.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist 2 | 3 | sealed class ArticleListUiBinding { 4 | object Init : ArticleListUiBinding() 5 | data class Loaded(val list: List) : ArticleListUiBinding() 6 | data class Searched(val list: List) : ArticleListUiBinding() 7 | } 8 | -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/ArticleRecyclerView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | 6 | class ArticleRecyclerView(context: Context, attrs: AttributeSet?) : androidx.recyclerview.widget.RecyclerView(context, attrs) { 7 | 8 | override fun performClick(): Boolean { 9 | super.performClick() 10 | return true 11 | } 12 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/Interation.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist 2 | 3 | sealed class Interation { 4 | data class Scroll(val positionAfterScroll: Int) : Interation() 5 | data class OpenInternalWebBrowser(val url: String) : Interation() 6 | data class OpenExternalWebBrowser(val url: String) : Interation() 7 | data class Share(val url: String) : Interation() 8 | data class ReadArticle(val position: Int) : Interation() 9 | data class SwipeArtilce(val position: Int) : Interation() 10 | object ReadAllOfArticles : Interation() 11 | object Finish : Interation() 12 | } 13 | -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/action/SearchArticleListActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.action 2 | 3 | import com.phicdy.mycuration.articlelist.ArticleItem 4 | import com.phicdy.mycuration.core.ActionCreator1 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.data.preference.PreferenceHelper 7 | import com.phicdy.mycuration.data.repository.ArticleRepository 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.withContext 10 | import javax.inject.Inject 11 | 12 | class SearchArticleListActionCreator @Inject constructor( 13 | private val dispatcher: Dispatcher, 14 | private val articleRepository: ArticleRepository, 15 | private val preferenceHelper: PreferenceHelper 16 | ) : ActionCreator1 { 17 | 18 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 19 | override suspend fun run(query: String) { 20 | withContext(Dispatchers.IO) { 21 | articleRepository.searchArticles(query, preferenceHelper.sortNewArticleTop) 22 | .map { ArticleItem.Content(it) } 23 | .let { dispatcher.dispatch(SearchArticleAction(it)) } 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/action/ShareUrlActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.action 2 | 3 | import com.phicdy.mycuration.articlelist.ArticleItem 4 | import com.phicdy.mycuration.core.ActionCreator2 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.withContext 8 | import javax.inject.Inject 9 | 10 | class ShareUrlActionCreator @Inject constructor( 11 | private val dispatcher: Dispatcher 12 | ) : ActionCreator2> { 13 | 14 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 15 | override suspend fun run(position: Int, items: List) { 16 | withContext(Dispatchers.IO) { 17 | if (position < 0 || position >= items.size) return@withContext 18 | when (val item = items[position]) { 19 | is ArticleItem.Content -> dispatcher.dispatch(ShareUrlAction(item.value.url)) 20 | ArticleItem.Advertisement -> {} 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/ArticleListStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.ArticleItem 4 | import com.phicdy.mycuration.articlelist.action.FetchArticleAction 5 | import com.phicdy.mycuration.articlelist.action.UpdateFavoriteAction 6 | import com.phicdy.mycuration.core.Action 7 | import com.phicdy.mycuration.core.Dispatcher 8 | import com.phicdy.mycuration.core.Store 9 | import dagger.hilt.android.lifecycle.HiltViewModel 10 | import javax.inject.Inject 11 | 12 | @HiltViewModel 13 | class ArticleListStore @Inject constructor( 14 | dispatcher: Dispatcher 15 | ) : Store>(dispatcher) { 16 | 17 | init { 18 | dispatcher.register(this) 19 | } 20 | 21 | override suspend fun notify(action: Action<*>) { 22 | when (action) { 23 | is FetchArticleAction -> _state.value = action.value 24 | is UpdateFavoriteAction -> _state.value = action.value 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/FinishStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.FinishAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.CoroutineScope 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class FinishStateStore @Inject constructor( 13 | dispatcher: Dispatcher 14 | ) : Store(dispatcher), CoroutineScope { 15 | 16 | init { 17 | dispatcher.register(this) 18 | } 19 | 20 | override suspend fun notify(action: Action<*>) { 21 | when (action) { 22 | is FinishAction -> _state.value = true 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/OpenExternalWebBrowserStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.OpenExternalBrowserAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class OpenExternalWebBrowserStateStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is OpenExternalBrowserAction -> _state.value = action.value 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/OpenInternalWebBrowserStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.OpenInternalBrowserAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class OpenInternalWebBrowserStateStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is OpenInternalBrowserAction -> _state.value = action.value 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/ReadAllArticlesStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.action.articlelist.ReadAllArticlesAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import com.phicdy.mycuration.entity.ReadAllArticles 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class ReadAllArticlesStateStore @Inject constructor( 13 | dispatcher: Dispatcher 14 | ) : Store(dispatcher) { 15 | 16 | init { 17 | dispatcher.register(this) 18 | } 19 | 20 | override suspend fun notify(action: Action<*>) { 21 | when (action) { 22 | is ReadAllArticlesAction -> _state.value = action.value 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/ReadArticlePositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.ReadArticlePositionAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ReadArticlePositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ReadArticlePositionAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/ScrollPositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.ScrollAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ScrollPositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ScrollAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/SearchResultStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.ArticleItem 4 | import com.phicdy.mycuration.articlelist.action.SearchArticleAction 5 | import com.phicdy.mycuration.core.Action 6 | import com.phicdy.mycuration.core.Dispatcher 7 | import com.phicdy.mycuration.core.Store 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class SearchResultStore @Inject constructor( 13 | dispatcher: Dispatcher 14 | ) : Store>(dispatcher) { 15 | 16 | init { 17 | dispatcher.register(this) 18 | } 19 | 20 | override suspend fun notify(action: Action<*>) { 21 | when (action) { 22 | is SearchArticleAction -> _state.value = action.value 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/ShareUrlStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.ShareUrlAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.Dispatchers 9 | import javax.inject.Inject 10 | import kotlin.coroutines.CoroutineContext 11 | 12 | @HiltViewModel 13 | class ShareUrlStore @Inject constructor( 14 | dispatcher: Dispatcher, 15 | context: CoroutineContext = Dispatchers.Main 16 | ) : Store(dispatcher, context) { 17 | 18 | init { 19 | dispatcher.register(this) 20 | } 21 | 22 | override suspend fun notify(action: Action<*>) { 23 | when (action) { 24 | is ShareUrlAction -> _state.value = action.value 25 | } 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/store/SwipePositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.store 2 | 3 | import com.phicdy.mycuration.articlelist.action.SwipeAction 4 | import com.phicdy.mycuration.core.Action 5 | import com.phicdy.mycuration.core.Dispatcher 6 | import com.phicdy.mycuration.core.Store 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class SwipePositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is SwipeAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_article_list/src/main/java/com/phicdy/mycuration/articlelist/util/VectorDrawableUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.articlelist.util 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.Canvas 6 | import androidx.core.content.ContextCompat 7 | 8 | fun bitmapFrom(context: Context, vectorDrawableId: Int): Bitmap? { 9 | val drawable = ContextCompat.getDrawable(context, vectorDrawableId) 10 | drawable?.let { 11 | val bitmap = Bitmap.createBitmap( 12 | drawable.intrinsicWidth, 13 | drawable.intrinsicHeight, 14 | Bitmap.Config.ARGB_8888 15 | ) 16 | val canvas = Canvas(bitmap) 17 | drawable.setBounds(0, 0, canvas.width, canvas.height) 18 | drawable.draw(canvas) 19 | 20 | return bitmap 21 | } 22 | return null 23 | } 24 | 25 | -------------------------------------------------------------------------------- /feature_article_list/src/main/res/layout/footer_article_list_activity.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /feature_article_list/src/main/res/layout/fragment_articles_list.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /feature_article_list/src/main/res/menu/menu_article.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /feature_article_list/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_article_list/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_curated_article_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_curated_article_list/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_curated_article_list/consumer-rules.pro -------------------------------------------------------------------------------- /feature_curated_article_list/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/CuratedArticleRecyclerView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | 6 | class CuratedArticleRecyclerView(context: Context, attrs: AttributeSet?) : androidx.recyclerview.widget.RecyclerView(context, attrs) { 7 | 8 | override fun performClick(): Boolean { 9 | super.performClick() 10 | return true 11 | } 12 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/action/ShareUrlActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.action 2 | 3 | import com.phicdy.mycuration.core.ActionCreator2 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.curatedarticlelist.CuratedArticleItem 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.withContext 8 | import javax.inject.Inject 9 | 10 | class ShareUrlActionCreator @Inject constructor( 11 | private val dispatcher: Dispatcher 12 | ) : ActionCreator2> { 13 | 14 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 15 | override suspend fun run(position: Int, items: List) { 16 | withContext(Dispatchers.IO) { 17 | if (position < 0 || position >= items.size) return@withContext 18 | when (val item = items[position]) { 19 | is CuratedArticleItem.Content -> dispatcher.dispatch(ShareUrlAction(item.value.url)) 20 | CuratedArticleItem.Advertisement -> {} 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/CuratedArticleListStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.CuratedArticleItem 7 | import com.phicdy.mycuration.curatedarticlelist.action.FetchArticleAction 8 | import dagger.hilt.android.lifecycle.HiltViewModel 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class CuratedArticleListStore @Inject constructor( 13 | dispatcher: Dispatcher 14 | ) : Store>(dispatcher) { 15 | 16 | init { 17 | dispatcher.register(this) 18 | } 19 | 20 | override suspend fun notify(action: Action<*>) { 21 | when (action) { 22 | is FetchArticleAction -> _state.value = action.value 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/FinishCuratedArticleStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.FinishAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.CoroutineScope 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class FinishCuratedArticleStateStore @Inject constructor( 13 | dispatcher: Dispatcher 14 | ) : Store(dispatcher), CoroutineScope { 15 | 16 | init { 17 | dispatcher.register(this) 18 | } 19 | 20 | override suspend fun notify(action: Action<*>) { 21 | when (action) { 22 | is FinishAction -> _state.value = true 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/OpenCuratedArticleWithExternalWebBrowserStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.OpenExternalBrowserAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class OpenCuratedArticleWithExternalWebBrowserStateStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is OpenExternalBrowserAction -> _state.value = action.value 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/OpenCuratedArticleWithInternalWebBrowserStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.OpenInternalBrowserAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class OpenCuratedArticleWithInternalWebBrowserStateStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is OpenInternalBrowserAction -> _state.value = action.value 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/ReadAllCuratedArticlesStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.ReadALlArticlesAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ReadAllCuratedArticlesStateStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ReadALlArticlesAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/ReadCuratedArticlePositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.ReadArticleAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ReadCuratedArticlePositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ReadArticleAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/ScrollCuratedArticlePositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.ScrollAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ScrollCuratedArticlePositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ScrollAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/ShareCuratedArticleUrlStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.ShareUrlAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ShareCuratedArticleUrlStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is ShareUrlAction -> _state.value = action.value 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/store/SwipeCuratedArticlePositionStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.store 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import com.phicdy.mycuration.curatedarticlelist.action.SwipeAction 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class SwipeCuratedArticlePositionStore @Inject constructor( 12 | dispatcher: Dispatcher 13 | ) : Store(dispatcher) { 14 | 15 | init { 16 | dispatcher.register(this) 17 | } 18 | 19 | override suspend fun notify(action: Action<*>) { 20 | when (action) { 21 | is SwipeAction -> _state.value = action.value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/java/com/phicdy/mycuration/curatedarticlelist/util/VectorDrawableUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curatedarticlelist.util 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.Canvas 6 | import androidx.core.content.ContextCompat 7 | 8 | fun bitmapFrom(context: Context, vectorDrawableId: Int): Bitmap? { 9 | val drawable = ContextCompat.getDrawable(context, vectorDrawableId) 10 | drawable?.let { 11 | val bitmap = Bitmap.createBitmap( 12 | drawable.intrinsicWidth, 13 | drawable.intrinsicHeight, 14 | Bitmap.Config.ARGB_8888 15 | ) 16 | val canvas = Canvas(bitmap) 17 | drawable.setBounds(0, 0, canvas.width, canvas.height) 18 | drawable.draw(canvas) 19 | 20 | return bitmap 21 | } 22 | return null 23 | } 24 | 25 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/res/layout/footer_curated_article_list.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/res/layout/fragment_curated_articles_list.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 17 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/res/menu/menu_curated_articles_list.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /feature_curated_article_list/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_curation_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_curation_list/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.mycuration.curationlist' 15 | } 16 | 17 | dependencies { 18 | implementation project(':domain') 19 | implementation project(':entity') 20 | implementation project(':repository') 21 | implementation project(':resource') 22 | 23 | implementation libs.coroutines.core 24 | implementation libs.coroutines.android 25 | 26 | implementation libs.appcompat 27 | implementation libs.recyclerview 28 | 29 | implementation libs.dagger.hilt 30 | kapt libs.dagger.hilt.compiler 31 | } -------------------------------------------------------------------------------- /feature_curation_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_curation_list/src/main/java/com/phicdy/mycuration/curationlist/CurationItem.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curationlist 2 | 3 | interface CurationItem { 4 | 5 | fun setCount(count: String) 6 | fun setName(name: String) 7 | } 8 | -------------------------------------------------------------------------------- /feature_curation_list/src/main/java/com/phicdy/mycuration/curationlist/CurationListView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.curationlist 2 | 3 | import com.phicdy.mycuration.entity.Curation 4 | 5 | interface CurationListView { 6 | fun startEditCurationActivity(editCurationId: Int) 7 | fun setNoRssTextToEmptyView() 8 | fun registerContextMenu() 9 | fun initListBy(curations: List) 10 | fun showRecyclerView() 11 | fun hideRecyclerView() 12 | fun showEmptyView() 13 | fun hideEmptyView() 14 | } 15 | -------------------------------------------------------------------------------- /feature_curation_list/src/main/res/layout/fragment_curation_list.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /feature_curation_list/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_feed_search/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_feed_search/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.mycuration.feedsearch' 15 | } 16 | 17 | dependencies { 18 | implementation project(':domain') 19 | implementation project(':entity') 20 | implementation project(':feature_util') 21 | implementation project(':feature_feed_url_hook') 22 | implementation project(':repository') 23 | implementation project(':resource') 24 | implementation project(':tracker') 25 | implementation project(':util') 26 | 27 | implementation libs.coroutines.core 28 | implementation libs.coroutines.android 29 | 30 | implementation libs.appcompat 31 | implementation libs.material 32 | 33 | implementation libs.dagger.hilt 34 | kapt libs.dagger.hilt.compiler 35 | 36 | implementation libs.materialshowcaseview 37 | } -------------------------------------------------------------------------------- /feature_feed_search/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_feed_search/src/main/java/com/phicdy/mycuration/feedsearch/FeedSearchView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feedsearch 2 | 3 | interface FeedSearchView { 4 | fun startFeedUrlHookActivity(url: String) 5 | fun showProgressBar() 6 | fun dismissProgressBar() 7 | fun load(url: String) 8 | fun showInvalidUrlErrorToast() 9 | fun showGenericErrorToast() 10 | fun showAddFeedSuccessToast() 11 | fun finishView() 12 | fun setSearchViewTextFrom(url: String) 13 | fun trackFailedUrl(url: String) 14 | } 15 | -------------------------------------------------------------------------------- /feature_feed_search/src/main/res/menu/menu_feed_search.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /feature_feed_search/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_feed_url_hook/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_feed_url_hook/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.mycuration.feedurlhook' 15 | } 16 | 17 | dependencies { 18 | implementation project(':domain') 19 | implementation project(':entity') 20 | implementation project(':feature_util') 21 | implementation project(':repository') 22 | implementation project(':resource') 23 | implementation project(':tracker') 24 | implementation project(':util') 25 | 26 | implementation libs.coroutines.core 27 | implementation libs.coroutines.android 28 | 29 | implementation libs.appcompat 30 | 31 | implementation libs.dagger.hilt 32 | kapt libs.dagger.hilt.compiler 33 | } -------------------------------------------------------------------------------- /feature_feed_url_hook/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_feed_url_hook/src/main/java/com/phicdy/mycuration/feedurlhook/FeedUrlHookView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feedurlhook 2 | 3 | interface FeedUrlHookView { 4 | fun showSuccessToast() 5 | fun showInvalidUrlErrorToast() 6 | fun showGenericErrorToast() 7 | fun finishView() 8 | fun trackFailedUrl(url: String) 9 | } 10 | -------------------------------------------------------------------------------- /feature_feed_url_hook/src/main/res/layout/activity_feed_url_hook.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature_feed_url_hook/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_filter_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_filter_list/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.mycuration.filterlist' 15 | } 16 | 17 | dependencies { 18 | implementation project(':entity') 19 | implementation project(':feature_register_filter') 20 | implementation project(':repository') 21 | implementation project(':resource') 22 | 23 | implementation libs.recyclerview 24 | implementation libs.appcompat 25 | 26 | implementation libs.dagger.hilt 27 | kapt libs.dagger.hilt.compiler 28 | } -------------------------------------------------------------------------------- /feature_filter_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_filter_list/src/main/java/com/phicdy/mycuration/filterlist/FilterListView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.filterlist 2 | 3 | import com.phicdy.mycuration.entity.Filter 4 | 5 | interface FilterListView { 6 | fun remove(position: Int) 7 | fun notifyListChanged() 8 | fun startEditActivity(filterId: Int) 9 | fun showFilterList(filters: ArrayList) 10 | fun hideFilterList() 11 | fun showEmptyView() 12 | fun hideEmptyView() 13 | fun setRssEmptyMessage() 14 | } 15 | -------------------------------------------------------------------------------- /feature_filter_list/src/main/res/layout/fragment_filter_list.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | 21 | -------------------------------------------------------------------------------- /feature_filter_list/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_license/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_license/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildFeatures { 3 | viewBinding true 4 | } 5 | 6 | buildTypes { 7 | release { 8 | minifyEnabled false 9 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 10 | } 11 | } 12 | namespace 'com.phicdy.mycuration.license' 13 | } 14 | 15 | dependencies { 16 | implementation project(':feature_util') 17 | implementation project(':resource') 18 | 19 | implementation libs.appcompat 20 | implementation libs.material 21 | } -------------------------------------------------------------------------------- /feature_license/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_license/consumer-rules.pro -------------------------------------------------------------------------------- /feature_license/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /feature_license/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_register_filter/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_register_filter/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.feature_register_filter' 15 | } 16 | 17 | dependencies { 18 | implementation project(':domain') 19 | implementation project(':entity') 20 | implementation project(':feature_util') 21 | implementation project(':glide') 22 | implementation project(':repository') 23 | implementation project(':resource') 24 | implementation project(':tracker') 25 | implementation project(':util') 26 | 27 | implementation libs.coroutines.core 28 | implementation libs.coroutines.android 29 | 30 | implementation libs.appcompat 31 | implementation libs.recyclerview 32 | implementation libs.material 33 | 34 | implementation libs.dagger.hilt 35 | kapt libs.dagger.hilt.compiler 36 | 37 | implementation libs.glide 38 | kapt libs.glide.compiler 39 | } -------------------------------------------------------------------------------- /feature_register_filter/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/java/com/phicdy/feature_register_filter/RegisterFilterView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.feature_register_filter 2 | 3 | interface RegisterFilterView { 4 | fun filterKeyword(): String 5 | fun filterUrl(): String 6 | fun filterTitle(): String 7 | fun setFilterTitle(title: String) 8 | fun setFilterTargetRss(rss: String) 9 | fun setMultipleFilterTargetRss() 10 | fun resetFilterTargetRss() 11 | fun setFilterUrl(url: String) 12 | fun setFilterKeyword(keyword: String) 13 | fun handleEmptyTitle() 14 | fun handleEmptyCondition() 15 | fun handlePercentOnly() 16 | fun finish() 17 | fun showSaveSuccessToast() 18 | fun showSaveErrorToast() 19 | fun trackEdit() 20 | fun trackRegister() 21 | fun handleEmptyFeed() 22 | } 23 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/java/com/phicdy/feature_register_filter/SelectFilterTargetRssPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.feature_register_filter 2 | 3 | import android.view.MenuItem 4 | import javax.inject.Inject 5 | 6 | class SelectFilterTargetRssPresenter @Inject constructor(private val view: SelectTargetRssView) { 7 | 8 | fun optionItemSelected(item: MenuItem) { 9 | when (item.itemId) { 10 | R.id.done_select_target_rss -> view.finishSelect() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/java/com/phicdy/feature_register_filter/SelectTargetRssView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.feature_register_filter 2 | 3 | 4 | interface SelectTargetRssView { 5 | fun finishSelect() 6 | } 7 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/drawable/bg_filter_target_rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/drawable/ic_description.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/drawable/ic_rss_black.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/drawable/ic_web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/layout/activity_select_filter_target_rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/layout/content_select_filter_target_rss.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/layout/fragment_select_filter_target_rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/menu/menu_register_filter.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /feature_register_filter/src/main/res/menu/menu_select_filter_rss.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /feature_register_filter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_rss_list/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_rss_list/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.compose.compiler) 3 | } 4 | 5 | apply plugin: 'kotlin-kapt' 6 | apply plugin: 'dagger.hilt.android.plugin' 7 | 8 | android { 9 | buildTypes { 10 | release { 11 | minifyEnabled false 12 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 13 | } 14 | } 15 | buildFeatures { 16 | compose true 17 | } 18 | namespace 'com.phicdy.mycuration.rss' 19 | } 20 | 21 | dependencies { 22 | implementation project(':core') 23 | implementation project(':domain') 24 | implementation project(':entity') 25 | implementation project(':feature_article_list') 26 | implementation project(':repository') 27 | implementation project(':resource') 28 | implementation project(':action_article_list') 29 | 30 | implementation libs.work 31 | implementation libs.fragment 32 | implementation libs.accompanist 33 | 34 | implementation libs.coroutines.core 35 | implementation libs.coroutines.android 36 | 37 | implementation libs.dagger.hilt 38 | kapt libs.dagger.hilt.compiler 39 | 40 | implementation libs.timber 41 | } 42 | -------------------------------------------------------------------------------- /feature_rss_list/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ConsumeRssListMessageAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class ConsumeRssListMessageAction(override val value: RssListMessage) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ConsumeRssListMessageActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class ConsumeRssListMessageActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher, 9 | ) : ActionCreator1 { 10 | override suspend fun run(rssListMessage: RssListMessage) { 11 | dispatcher.dispatch(ConsumeRssListMessageAction(rssListMessage)) 12 | } 13 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/DeleteRssAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class DeleteRssAction(override val value: Int) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/DeleteRssActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator3 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.data.repository.RssRepository 6 | import com.phicdy.mycuration.entity.Feed 7 | import com.phicdy.mycuration.entity.RssListMode 8 | import javax.inject.Inject 9 | 10 | class DeleteRssActionCreator @Inject constructor( 11 | private val dispatcher: Dispatcher, 12 | private val rssRepository: RssRepository 13 | ) : ActionCreator3, RssListMode> { 14 | 15 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 16 | override suspend fun run(rssId: Int, rawRssList: List, mode: RssListMode) { 17 | if (rssRepository.deleteRss(rssId)) { 18 | dispatcher.dispatch(DeleteRssAction(rssId)) 19 | } else { 20 | dispatcher.dispatch(DeleteRssFailedAction(Unit)) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/DeleteRssFailedAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class DeleteRssFailedAction(override val value: Unit) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/EditRssTitleActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator2 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.data.repository.RssRepository 6 | import javax.inject.Inject 7 | 8 | class EditRssTitleActionCreator @Inject constructor( 9 | private val dispatcher: Dispatcher, 10 | private val rssRepository: RssRepository 11 | ): ActionCreator2 { 12 | override suspend fun run(newTitle: String, rssId: Int) { 13 | if (newTitle.isBlank()) { 14 | dispatcher.dispatch(EditRssTitleErrorAction(RssListMessage.Type.ERROR_EMPTY_RSS_TITLE_EDIT)) 15 | } else { 16 | val numOfUpdate = rssRepository.saveNewTitle(rssId, newTitle) 17 | if (numOfUpdate == 1) { 18 | dispatcher.dispatch(EditRssTitleSuccessAction(EditRssTitleValue(newTitle, rssId))) 19 | } else { 20 | dispatcher.dispatch(EditRssTitleErrorAction(RssListMessage.Type.ERROR_SAVE_RSS_TITLE)) 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/EditRssTitleErrorAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class EditRssTitleErrorAction(override val value: RssListMessage.Type) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/EditRssTitleSuccessAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class EditRssTitleSuccessAction(override val value: EditRssTitleValue) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/EditRssTitleValue.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | data class EditRssTitleValue( 4 | val newTitle: String, 5 | val rssId: Int 6 | ) 7 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideDeleteRssAlertDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class HideDeleteRssAlertDialogAction(override val value: Unit) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideDeleteRssAlertDialogActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class HideDeleteRssAlertDialogActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator { 10 | 11 | override suspend fun run() { 12 | dispatcher.dispatch(HideDeleteRssAlertDialogAction(Unit)) 13 | } 14 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideDropdownMenuAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class HideDropdownMenuAction(override val value: Unit) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideDropdownMenuActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class HideDropdownMenuActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator { 10 | 11 | override suspend fun run() { 12 | dispatcher.dispatch(HideDropdownMenuAction(Unit)) 13 | } 14 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideEditRssTitleAlertDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class HideEditRssTitleAlertDialogAction(override val value: Unit) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/HideEditRssTitleAlertDialogActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class HideEditRssTitleAlertDialogActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator { 10 | 11 | override suspend fun run() { 12 | dispatcher.dispatch(HideEditRssTitleAlertDialogAction(Unit)) 13 | } 14 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/IconFetchWorker.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import android.content.Context 4 | import androidx.work.CoroutineWorker 5 | import androidx.work.WorkerParameters 6 | import com.phicdy.mycuration.data.repository.RssRepository 7 | import com.phicdy.mycuration.domain.task.GetFeedIconTask 8 | import com.phicdy.mycuration.entity.Feed 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.withContext 11 | 12 | class IconFetchWorker( 13 | appContext: Context, 14 | workerParams: WorkerParameters, 15 | private val rssRepository: RssRepository 16 | ) : CoroutineWorker(appContext, workerParams) { 17 | 18 | override suspend fun doWork(): Result = withContext(Dispatchers.IO) { 19 | rssRepository.getAllFeeds().forEach { 20 | if (it.iconPath.isEmpty() || it.iconPath == Feed.DEDAULT_ICON_PATH) { 21 | val iconUrl = GetFeedIconTask().execute(it.siteUrl) 22 | rssRepository.saveIconPath(it.siteUrl, iconUrl) 23 | } 24 | } 25 | return@withContext Result.success() 26 | } 27 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/NewRssTitleChangeAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class NewRssTitleChangeAction (override val value: String) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/NewRssTitleChangeActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class NewRssTitleChangeActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator1 { 10 | 11 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 12 | override suspend fun run(title: String) { 13 | dispatcher.dispatch(NewRssTitleChangeAction(title)) 14 | } 15 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssItemView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | interface RssItemView { 4 | interface Footer { 5 | fun showAllView() 6 | fun showHideView() 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | class RssListAction(override val value: RssListState) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListItem.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | sealed class RssListItem : Equatable { 4 | 5 | data class All(val unreadCount: Int) : RssListItem() 6 | 7 | object Favroite : RssListItem() { 8 | override fun equals(other: Any?): Boolean = other is Favroite 9 | } 10 | 11 | data class Content( 12 | val rssId: Int, 13 | val rssTitle: String, 14 | val isDefaultIcon: Boolean, 15 | val rssIconPath: String, 16 | val unreadCount: Int 17 | ) : RssListItem() 18 | 19 | data class Footer(val state: RssListFooterState) : RssListItem() 20 | } 21 | 22 | enum class RssListFooterState { 23 | ALL, 24 | UNREAD_ONLY 25 | } 26 | 27 | /** 28 | * For suppress lint of DiffUtil.ItemCallback 29 | * https://stackoverflow.com/questions/55895359/lint-error-suspicious-equality-check-equals-is-not-implemented-in-object-dif 30 | */ 31 | interface Equatable { 32 | override fun equals(other: Any?): Boolean 33 | } 34 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListMessage.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | data class RssListMessage( 4 | val id: Long, 5 | val type: Type 6 | ) { 7 | enum class Type{ 8 | SUCCEED_TO_EDIT_RSS, 9 | SUCCEED_TO_DELETE_RSS, 10 | ERROR_EMPTY_RSS_TITLE_EDIT, 11 | ERROR_SAVE_RSS_TITLE, 12 | ERROR_DELETE_RSS, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListState.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.entity.Feed 4 | import com.phicdy.mycuration.entity.RssListMode 5 | 6 | data class RssListState( 7 | val item: List, 8 | val rawRssList: List, 9 | val mode: RssListMode, 10 | val isInitializing: Boolean, 11 | val isRefreshing: Boolean, 12 | val messageList: List = emptyList(), 13 | val showDropdownMenuId: Int?, 14 | val showDeleteRssDialogId: Int?, 15 | val showEditRssTitleDialogId: Int?, 16 | val showEditRssTitleDialogTitle: String?, 17 | ) 18 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListUpdateAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | class RssListUpdateAction(override val value: RssListUpdateState) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/RssListUpdateState.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.entity.Feed 4 | 5 | sealed class RssListUpdateState { 6 | object Started : RssListUpdateState() 7 | data class Finished(val updated: List) : RssListUpdateState() 8 | object Failed : RssListUpdateState() 9 | } 10 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowDeleteRssAlertDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class ShowDeleteRssAlertDialogAction(override val value: Int) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowDeleteRssAlertDialogActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class ShowDeleteRssAlertDialogActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator1 { 10 | 11 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 12 | override suspend fun run(rssId: Int) { 13 | dispatcher.dispatch(ShowDeleteRssAlertDialogAction(rssId)) 14 | } 15 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowDropdownMenuAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class ShowDropdownMenuAction(override val value: Int) : Action 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowDropdownMenuActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator1 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class ShowDropdownMenuActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator1 { 10 | 11 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 12 | override suspend fun run(rssId: Int) { 13 | dispatcher.dispatch(ShowDropdownMenuAction(rssId)) 14 | } 15 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowEditRssTitleAlertDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class ShowEditRssTitleAlertDialogAction(override val value: Int, val title: String) : 6 | Action 7 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/java/com/phicdy/mycuration/rss/ShowEditRssTitleAlertDialogActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.rss 2 | 3 | import com.phicdy.mycuration.core.ActionCreator2 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class ShowEditRssTitleAlertDialogActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator2 { 10 | 11 | @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") 12 | override suspend fun run(rssId: Int, title: String) { 13 | dispatcher.dispatch(ShowEditRssTitleAlertDialogAction(rssId, title)) 14 | } 15 | } -------------------------------------------------------------------------------- /feature_rss_list/src/main/res/drawable/ic_view_headline_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_rss_list/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /feature_rss_list/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_setting/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_setting/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-kapt' 4 | id 'dagger.hilt.android.plugin' 5 | } 6 | 7 | android { 8 | buildTypes { 9 | release { 10 | minifyEnabled false 11 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 12 | } 13 | } 14 | namespace 'com.phicdy.mycuration.setting' 15 | } 16 | 17 | dependencies { 18 | implementation project(':domain') 19 | implementation project(':feature_license') 20 | implementation project(':feature_util') 21 | implementation project(':repository') 22 | implementation project(':resource') 23 | implementation project(':util') 24 | implementation project(':tracker') 25 | 26 | implementation libs.coroutines.core 27 | implementation libs.coroutines.android 28 | 29 | implementation libs.appcompat 30 | implementation libs.preference 31 | implementation libs.material 32 | 33 | implementation libs.dagger.hilt 34 | kapt libs.dagger.hilt.compiler 35 | } -------------------------------------------------------------------------------- /feature_setting/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_setting/src/main/java/com/phicdy/mycuration/setting/SettingView.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.setting 2 | 3 | interface SettingView { 4 | fun initView() 5 | fun initListener() 6 | fun setUpdateInterval(index: Int, summary: String) 7 | fun setAutoUpdateInMainUi(isAutoUpdateInMainUi: Boolean) 8 | fun setTheme(index: Int, theme: String, mode: Int) 9 | fun setArticleSort(isNewArticleTop: Boolean) 10 | fun setInternalBrowser(isEnabled: Boolean) 11 | fun setAllReadBehavior(index: Int, summary: String) 12 | fun setSwipeDirection(index: Int, summary: String) 13 | fun setLaunchTab(index: Int, summary: String) 14 | fun startLicenseActivity() 15 | } 16 | -------------------------------------------------------------------------------- /feature_setting/src/main/res/layout/activity_setting.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 16 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /feature_top/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature_top/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/CheckReviewRequestActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.data.preference.PreferenceHelper 6 | import javax.inject.Inject 7 | 8 | class CheckReviewRequestActionCreator @Inject constructor( 9 | private val helper: PreferenceHelper, 10 | private val dispatcher: Dispatcher 11 | ) : ActionCreator { 12 | override suspend fun run() { 13 | if (!helper.isReviewed() && helper.getReviewCount() - 1 <= 0) { 14 | dispatcher.dispatch(ShowRateDialogAction(Unit)) 15 | helper.resetReviewCount() 16 | } else { 17 | if (helper.getReviewCount() <= 0) { 18 | helper.resetReviewCount() 19 | } else { 20 | helper.decreaseReviewCount() 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/CloseRateDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class CloseRateDialogAction(override val value: Unit) : Action -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/CloseRateDialogActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import javax.inject.Inject 6 | 7 | class CloseRateDialogActionCreator @Inject constructor( 8 | private val dispatcher: Dispatcher 9 | ) : ActionCreator { 10 | override suspend fun run() { 11 | dispatcher.dispatch(CloseRateDialogAction(Unit)) 12 | } 13 | } -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/InitializeTopAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | internal data class InitializeTopAction(override val value: InitializeTopValue) : Action -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/InitializeTopActionCreator.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.ActionCreator 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.data.repository.RssRepository 6 | import javax.inject.Inject 7 | 8 | class InitializeTopActionCreator @Inject constructor( 9 | private val rssRepository: RssRepository, 10 | private val dispatcher: Dispatcher 11 | ): ActionCreator { 12 | override suspend fun run() { 13 | val numOfRss = rssRepository.getNumOfRss() 14 | dispatcher.dispatch(InitializeTopAction(InitializeTopValue(numOfRss))) 15 | } 16 | } -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/InitializeTopValue.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | internal data class InitializeTopValue( 4 | val numOfRss: Long 5 | ) -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/ShowRateDialogAction.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.Action 4 | 5 | data class ShowRateDialogAction(override val value: Unit) : Action -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/TopState.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | data class TopState( 4 | val numOfRss: Long, 5 | val showRateDialog: Boolean = false 6 | ) -------------------------------------------------------------------------------- /feature_top/src/main/java/com/phicdy/mycuration/top/TopStateStore.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.top 2 | 3 | import com.phicdy.mycuration.core.Action 4 | import com.phicdy.mycuration.core.Dispatcher 5 | import com.phicdy.mycuration.core.Store 6 | import dagger.hilt.android.lifecycle.HiltViewModel 7 | import javax.inject.Inject 8 | 9 | @HiltViewModel 10 | class TopStateStore @Inject constructor( 11 | dispatcher: Dispatcher 12 | ): Store(dispatcher) { 13 | override suspend fun notify(action: Action<*>) { 14 | when (action) { 15 | is InitializeTopAction -> _state.value = 16 | state.value?.copy(numOfRss = action.value.numOfRss) 17 | is ShowRateDialogAction -> _state.value = 18 | state.value?.copy(showRateDialog = true) 19 | is CloseRateDialogAction -> _state.value = 20 | state.value?.copy(showRateDialog = false) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadein_curation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadein_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadein_rss.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadeout_curation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadeout_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_fadeout_rss.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_rotation.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /feature_top/src/main/res/anim/fab_rotation_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /feature_top/src/main/res/color/item_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-hdpi/tab_curation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-hdpi/tab_curation.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-hdpi/tab_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-hdpi/tab_feed.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-hdpi/tab_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-hdpi/tab_filter.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-mdpi/tab_curation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-mdpi/tab_curation.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-mdpi/tab_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-mdpi/tab_feed.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-mdpi/tab_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-mdpi/tab_filter.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xhdpi/tab_curation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xhdpi/tab_curation.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xhdpi/tab_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xhdpi/tab_feed.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xhdpi/tab_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xhdpi/tab_filter.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xxhdpi/tab_curation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xxhdpi/tab_curation.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xxhdpi/tab_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xxhdpi/tab_feed.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable-xxhdpi/tab_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/feature_top/src/main/res/drawable-xxhdpi/tab_filter.png -------------------------------------------------------------------------------- /feature_top/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_top/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /feature_top/src/main/res/menu/menu_top_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /feature_top/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /feature_util/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_util/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | buildTypes { 5 | release { 6 | minifyEnabled false 7 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 8 | } 9 | } 10 | namespace 'com.phicdy.mycuration.feature.util' 11 | 12 | } 13 | 14 | dependencies { 15 | implementation project(':repository') 16 | implementation libs.appcompat 17 | } 18 | -------------------------------------------------------------------------------- /feature_util/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /feature_util/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_util/src/main/java/com/phicdy/mycuration/feature/util/ActivityExtension.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.feature.util 2 | 3 | import android.util.TypedValue 4 | import androidx.annotation.AttrRes 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.appcompat.app.AppCompatDelegate 7 | import com.phicdy.mycuration.data.preference.PreferenceHelper 8 | 9 | fun AppCompatActivity.getThemeColor(@AttrRes res: Int): Int { 10 | val typedValue = TypedValue() 11 | theme.resolveAttribute(res, typedValue, true) 12 | return typedValue.data 13 | } 14 | 15 | fun AppCompatActivity.changeTheme() { 16 | delegate.setLocalNightMode(when (PreferenceHelper.theme) { 17 | PreferenceHelper.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO 18 | PreferenceHelper.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES 19 | else -> AppCompatDelegate.MODE_NIGHT_NO 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /feature_util/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /glide/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /glide/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.glide' 9 | } 10 | 11 | dependencies { 12 | implementation libs.glide 13 | kapt libs.glide.compiler 14 | } 15 | -------------------------------------------------------------------------------- /glide/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /glide/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /glide/src/main/java/com/phicdy/mycuration/glide/MyCurationAppGlideModule.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.glide 2 | 3 | import com.bumptech.glide.annotation.GlideModule 4 | import com.bumptech.glide.module.AppGlideModule 5 | 6 | @GlideModule 7 | class MyCurationAppGlideModule : AppGlideModule() -------------------------------------------------------------------------------- /glide/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC 2 | org.gradle.caching=true 3 | org.gradle.parallel=true 4 | org.gradle.configureondemand=true 5 | org.gradle.daemon=true 6 | # properties 7 | COPY_BUILD_DESTINATION=build/outputs/apk/production/debug/ 8 | RELEASE_KEY_ALIAS=key 9 | RELEASE_STORE_FILE_PATH=dummy-release-key 10 | RELEASE_KEY_PASSWORD=testtest 11 | RELEASE_STORE_PASSWORD=testtest 12 | android.useAndroidX=true 13 | android.enableJetifier=false 14 | android.databinding.incremental=true 15 | kapt.incremental.apt=true 16 | ADMOB_ID_MYCURATION=admob_id 17 | # Test ID 18 | ADMOB_UNIT_ID_MYCURATION_LIST=ca-app-pub-3940256099942544/6300978111 19 | ADMOB_UNIT_ID_MYCURATION_FRAGMENT=ca-app-pub-3940256099942544/6300978111 20 | android.defaults.buildfeatures.buildconfig=true 21 | android.nonTransitiveRClass=false 22 | android.nonFinalResIds=false 23 | org.gradle.configuration-cache=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/modules.png -------------------------------------------------------------------------------- /privacy-policy/privacy-policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

プライバシーポリシー

4 | 5 |

MyCuration

6 | 7 |
8 |

広告について

9 | 本アプリでは、広告配信ツールとしてAdMob(Google Inc.)を使用しており、AdMobがご利用者の情報を自動取得する場合があります。取得する情報、利用目的、第三者への提供等につきましては、以下の広告配信事業者のアプリケーション・プライバシーポリシーのリンクよりご確認ください。
10 |
11 | Google 広告に関するポリシー
12 | https://policies.google.com/technologies/ads?hl=ja
13 |

利用状況解析ついて

14 | 本アプリでは、今後の開発の参考とするため、アプリの利用状況データを収集するツールとしてFirebase(Google Inc.)を使用しており、Firebaseがご利用者の情報を自動取得する場合があります。取得する情報、利用目的、第三者への提供等につきましては、以下のGoogleプライバシーポリシーのリンクよりご確認ください。
15 |
16 | Google プライバシーポリシー
17 | https://policies.google.com/privacy?hl=ja"
18 |
19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /repository/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /repository/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'dagger.hilt.android.plugin' 2 | apply plugin: 'com.squareup.sqldelight' 3 | 4 | android { 5 | namespace 'com.phicdy.mycuration.repository' 6 | 7 | buildTypes { 8 | release { 9 | minifyEnabled false 10 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 11 | } 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation project(':core') 17 | implementation project(':entity') 18 | implementation project(':di_common') 19 | implementation project(':util') 20 | 21 | implementation libs.annotation 22 | 23 | implementation libs.coroutines.core 24 | implementation libs.coroutines.android 25 | 26 | implementation libs.retrofit 27 | 28 | implementation libs.timber 29 | 30 | implementation libs.dagger.hilt 31 | kapt libs.dagger.hilt.compiler 32 | 33 | implementation libs.sqldelight 34 | implementation libs.sqldelight.coroutines 35 | 36 | implementation libs.sqlite 37 | } 38 | -------------------------------------------------------------------------------- /repository/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /repository/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /repository/src/main/java/com/phicdy/mycuration/data/repository/AdditionalSettingApi.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.data.repository 2 | 3 | import java.io.File 4 | import java.io.InputStream 5 | 6 | interface AdditionalSettingApi { 7 | 8 | suspend fun exportDb(currentDb: File): Boolean 9 | suspend fun importDb(currentDb: File, importDb: InputStream) 10 | suspend fun addDebugRss() 11 | 12 | suspend fun deleteAllArticles() 13 | } -------------------------------------------------------------------------------- /repository/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/1.sqm: -------------------------------------------------------------------------------- 1 | DROP TABLE filters; 2 | CREATE TABLE filters ( 3 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 4 | keyword TEXT, 5 | url TEXT, 6 | title TEXT, 7 | enabled INTEGER 8 | ); 9 | CREATE TABLE filterFeedRegistrations ( 10 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 11 | filterId INTEGER, 12 | feedId INTEGER, 13 | FOREIGN KEY(filterId) REFERENCES filters(_id), 14 | FOREIGN KEY(feedId) REFERENCES feeds(_id) 15 | ); 16 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/2.sqm: -------------------------------------------------------------------------------- 1 | DROP TABLE filters; 2 | CREATE TABLE filters ( 3 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 4 | keyword TEXT, 5 | url TEXT, 6 | title TEXT, 7 | enabled INTEGER 8 | ); 9 | CREATE TABLE filterFeedRegistrations ( 10 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 11 | filterId INTEGER, 12 | feedId INTEGER, 13 | FOREIGN KEY(filterId) REFERENCES filters(_id), 14 | FOREIGN KEY(feedId) REFERENCES feeds(_id) 15 | ); 16 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/3.sqm: -------------------------------------------------------------------------------- 1 | UPDATE feeds 2 | SET iconPath = "defaultIconPath"; 3 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/4.sqm: -------------------------------------------------------------------------------- 1 | CREATE TABLE favoriteArticles ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | articleId INTEGER, 4 | FOREIGN KEY(articleId) REFERENCES articles(_id) 5 | ); 6 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/Curation.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE curations ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | name TEXT NOT NULL 4 | ); 5 | 6 | insert: 7 | INSERT INTO curations(name) 8 | VALUES (?); 9 | 10 | selectLastInsertRowId: 11 | SELECT last_insert_rowid(); 12 | 13 | updateNmae: 14 | UPDATE curations 15 | SET name = ? 16 | WHERE _id = ?; 17 | 18 | getAll: 19 | SELECT * 20 | FROM curations 21 | ORDER BY name; 22 | 23 | getCountByName: 24 | SELECT COUNT(*) 25 | FROM curations 26 | WHERE name = ?; 27 | 28 | getById: 29 | SELECT * 30 | FROM curations 31 | WHERE _id = ?; 32 | 33 | getCountOfAllUnreadArticlesOfCuration: 34 | SELECT COUNT(*) 35 | FROM articles 36 | INNER JOIN curationSelections 37 | WHERE curationSelections.curationId = ? AND articles.status = "unread" AND articles._id = curationSelections.articleId 38 | ORDER BY date; 39 | 40 | getAllCurationWords: 41 | SELECT * 42 | FROM curations 43 | INNER JOIN curationConditions 44 | WHERE curations._id = curationConditions.curationId 45 | ORDER BY curations._id; 46 | 47 | delete: 48 | DELETE FROM curations 49 | WHERE _id = ?; 50 | 51 | deleteAll: 52 | DELETE FROM curations; 53 | 54 | selectChanges: 55 | SELECT changes(); 56 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/CurationCondition.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE curationConditions ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | word TEXT NOT NULL, 4 | curationId INTEGER NOT NULL, 5 | FOREIGN KEY(curationId) REFERENCES curations(_id) 6 | ); 7 | 8 | insert: 9 | INSERT INTO curationConditions(curationId, word) 10 | VALUES (?, ?); 11 | 12 | getAll: 13 | SELECT * 14 | FROM curationConditions 15 | WHERE curationId = ?; 16 | 17 | delete: 18 | DELETE FROM curationConditions 19 | WHERE curationId = ?; 20 | 21 | deleteAll: 22 | DELETE FROM curationConditions; 23 | 24 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/CurationSelection.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE curationSelections ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | curationId INTEGER, 4 | articleId INTEGER, 5 | FOREIGN KEY(curationId) REFERENCES curations(_id), 6 | FOREIGN KEY(articleId) REFERENCES articles(_id) 7 | ); 8 | 9 | insert: 10 | INSERT INTO curationSelections(articleId, curationId) 11 | VALUES (?, ?); 12 | 13 | deleteByArticleId: 14 | DELETE FROM curationSelections 15 | WHERE articleId = ?; 16 | 17 | deleteByCurationId: 18 | DELETE FROM curationSelections 19 | WHERE curationId = ?; 20 | 21 | deleteAll: 22 | DELETE FROM curationSelections; 23 | 24 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/FavoriteArticle.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE favoriteArticles ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | articleId INTEGER NOT NULL, 4 | FOREIGN KEY(articleId) REFERENCES articles(_id) 5 | ); 6 | 7 | insert: 8 | INSERT INTO favoriteArticles(articleId) 9 | VALUES (?); 10 | 11 | selectLastInsertRowId: 12 | SELECT last_insert_rowid(); 13 | 14 | getAllOrderByAsc: 15 | SELECT * 16 | FROM articles 17 | INNER JOIN feeds 18 | LEFT OUTER JOIN favoriteArticles 19 | ON (articles._id = favoriteArticles.articleId) 20 | WHERE articles.feedId = feeds._id 21 | ORDER BY date ASC; 22 | 23 | getAllOrderByDesc: 24 | SELECT * 25 | FROM articles 26 | INNER JOIN feeds 27 | LEFT OUTER JOIN favoriteArticles 28 | ON (articles._id = favoriteArticles.articleId) 29 | WHERE articles._id = favoriteArticles.articleId AND articles.feedId = feeds._id 30 | ORDER BY date DESC; 31 | 32 | delete: 33 | DELETE FROM favoriteArticles 34 | WHERE articleId = ?; 35 | 36 | selectChanges: 37 | SELECT changes(); 38 | -------------------------------------------------------------------------------- /repository/src/main/sqldelight/com/phicdy/mycuration/data/FilterFeedRegistration.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE filterFeedRegistrations ( 2 | _id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | filterId INTEGER, 4 | feedId INTEGER, 5 | FOREIGN KEY(filterId) REFERENCES filters(_id), 6 | FOREIGN KEY(feedId) REFERENCES feeds(_id) 7 | ); 8 | 9 | insert: 10 | INSERT INTO filterFeedRegistrations(filterId, feedId) 11 | VALUES (?, ?); 12 | 13 | selectLastInsertRowId: 14 | SELECT last_insert_rowid(); 15 | 16 | deleteByFeedId: 17 | DELETE FROM filterFeedRegistrations 18 | WHERE feedId = ?; 19 | 20 | deleteByFilterId: 21 | DELETE FROM filterFeedRegistrations 22 | WHERE filterId = ?; 23 | 24 | deleteAll: 25 | DELETE FROM filterFeedRegistrations; 26 | 27 | -------------------------------------------------------------------------------- /repository/src/release/java/com/phicdy/mycuration/data/repository/AdditionalSettingRepository.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.data.repository 2 | 3 | import java.io.File 4 | import java.io.InputStream 5 | 6 | class AdditionalSettingRepository( 7 | private val rssRepository: RssRepository, 8 | private val articleRepository: ArticleRepository 9 | ) : AdditionalSettingApi { 10 | override suspend fun exportDb(currentDb: File): Boolean = false 11 | 12 | override suspend fun importDb(currentDb: File, importDb: InputStream) { 13 | } 14 | 15 | override suspend fun addDebugRss() { 16 | } 17 | 18 | override suspend fun deleteAllArticles() { 19 | } 20 | } -------------------------------------------------------------------------------- /resource/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /resource/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.compose.compiler) 3 | } 4 | 5 | android { 6 | buildTypes { 7 | release { 8 | minifyEnabled false 9 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 10 | } 11 | } 12 | buildFeatures { 13 | compose true 14 | } 15 | namespace 'com.phicdy.mycuration.resource' 16 | } 17 | 18 | dependencies { 19 | implementation libs.material 20 | } 21 | -------------------------------------------------------------------------------- /resource/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /resource/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resource/src/main/java/com/phicdy/mycuration/resource/Color.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.resource 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Black900 = Color(0xff212121) 6 | val Black = Color(0xff000000) 7 | 8 | val Green700 = Color(0xff388E3C) 9 | 10 | val White = Color(0xffffffff) 11 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable-hdpi/hatena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/resource/src/main/res/drawable-hdpi/hatena.png -------------------------------------------------------------------------------- /resource/src/main/res/drawable-mdpi/hatena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/resource/src/main/res/drawable-mdpi/hatena.png -------------------------------------------------------------------------------- /resource/src/main/res/drawable-xhdpi/hatena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/resource/src/main/res/drawable-xhdpi/hatena.png -------------------------------------------------------------------------------- /resource/src/main/res/drawable-xxhdpi/hatena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/resource/src/main/res/drawable-xxhdpi/hatena.png -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_down.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_favorite_off.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_favorite_on.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_rss.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resource/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resource/src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /resource/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /resource/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 16sp 7 | 8 | 8dp 9 | 10 | -------------------------------------------------------------------------------- /scripts/dependency_diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usage: scripts/dependency_diff.sh from-branch to-branch 4 | # Example: scripts/dependency_diff.sh main update-compose 5 | 6 | echo "dependency diff" 7 | echo "branch: $1 -> $2, module: $3, configuration: $4" 8 | 9 | FROM_DEPENDENCIES_FILE="$(echo "$1" | sed 's/\//\\_/g')-dependency.txt" 10 | TO_DEPENDENCIES_FILE="$(echo "$2" | sed 's/\//\\_/g')-dependency.txt" 11 | MODULE=$3 12 | CONFIGURATION=$4 13 | 14 | git checkout -q "$1" 15 | git pull -q origin "$1" 16 | ./gradlew "$MODULE:dependencies" --configuration "$CONFIGURATION" > "$FROM_DEPENDENCIES_FILE" 17 | git checkout -q "$2" 18 | git pull -q origin "$2" 19 | ./gradlew "$MODULE:dependencies" --configuration "$CONFIGURATION" > "$TO_DEPENDENCIES_FILE" 20 | 21 | curl -L0 https://github.com/careem/dependency-diff-tldr/releases/download/v0.0.2/dependency-diff-tldr-r8.jar > dependency-diff-tldr-r8.jar 22 | 23 | CHECKSUM="$(shasum dependency-diff-tldr-r8.jar | awk '{ print $1 }')" 24 | EXPECTED_CHECKSUM=908796dc9bcf0e2fbaf340837381b24ca6dbcd94 25 | if [ "$CHECKSUM" != $EXPECTED_CHECKSUM ]; then 26 | echo "dependency-diff-tldr-r8.jar CHECKSUM is not same" 27 | exit 1 28 | fi 29 | java -jar dependency-diff-tldr-r8.jar "$FROM_DEPENDENCIES_FILE" "$TO_DEPENDENCIES_FILE" -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | include ':app', 10 | ':action_article_list', 11 | ':admob', 12 | ':advertisement', 13 | ':baselineprofile', 14 | ':core', 15 | ':di_common', 16 | ':domain', 17 | ':entity', 18 | ':feature_add_curation', 19 | ':feature_article_list', 20 | ':feature_curation_list', 21 | ':feature_curated_article_list', 22 | ':feature_feed_url_hook', 23 | ':feature_feed_search', 24 | ':feature_filter_list', 25 | ':feature_rss_list', 26 | ':feature_register_filter', 27 | ':feature_setting', 28 | ':feature_top', 29 | ':feature_util', 30 | ':feature_license', 31 | ':glide', 32 | ':benchmark', 33 | ':repository', 34 | ':resource', 35 | ':test_util', 36 | ':tracker', 37 | ':util' 38 | -------------------------------------------------------------------------------- /test_util/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /test_util/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | namespace 'com.phicdy.test.util' 7 | buildTypes { 8 | release { 9 | minifyEnabled false 10 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 11 | } 12 | } 13 | } 14 | 15 | dependencies { 16 | implementation libs.coroutines.test 17 | implementation libs.junit 18 | implementation project(':core') 19 | } -------------------------------------------------------------------------------- /test_util/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phicdy/MyCuration/927092aa59c1162d1d518e64f28938d0608a8995/test_util/consumer-rules.pro -------------------------------------------------------------------------------- /test_util/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /test_util/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test_util/src/main/java/com/phicdy/test/util/CoroutineTestRule.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.test.util 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.test.StandardTestDispatcher 6 | import kotlinx.coroutines.test.TestScope 7 | import kotlinx.coroutines.test.resetMain 8 | import kotlinx.coroutines.test.setMain 9 | import org.junit.rules.TestWatcher 10 | import org.junit.runner.Description 11 | 12 | @ExperimentalCoroutinesApi 13 | class CoroutineTestRule : TestWatcher() { 14 | private val testDispatcher = StandardTestDispatcher() 15 | val testCoroutineDispatcherProvider = 16 | TestCoroutineDispatcherProvider(testDispatcher) 17 | val testCoroutineScope = TestScope(testDispatcher) 18 | 19 | override fun starting(description: Description) { 20 | super.starting(description) 21 | Dispatchers.setMain(testDispatcher) 22 | } 23 | 24 | override fun finished(description: Description) { 25 | super.finished(description) 26 | Dispatchers.resetMain() 27 | } 28 | } -------------------------------------------------------------------------------- /test_util/src/main/java/com/phicdy/test/util/TestCoroutineDispatcherProvider.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.test.util 2 | 3 | import com.phicdy.mycuration.core.CoroutineDispatcherProvider 4 | import kotlinx.coroutines.CoroutineDispatcher 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.test.TestDispatcher 7 | 8 | @ExperimentalCoroutinesApi 9 | class TestCoroutineDispatcherProvider(private val testDispatcher: TestDispatcher) : CoroutineDispatcherProvider { 10 | override fun default(): CoroutineDispatcher = testDispatcher 11 | override fun io(): CoroutineDispatcher = testDispatcher 12 | override fun main(): CoroutineDispatcher = testDispatcher 13 | override fun unconfined(): CoroutineDispatcher = testDispatcher 14 | } -------------------------------------------------------------------------------- /tracker/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /tracker/build.gradle: -------------------------------------------------------------------------------- 1 | android { 2 | buildTypes { 3 | release { 4 | minifyEnabled false 5 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 6 | } 7 | } 8 | namespace 'com.phicdy.mycuration.tracker' 9 | 10 | } 11 | 12 | dependencies { 13 | implementation platform(libs.firebase.bom) 14 | implementation 'com.google.firebase:firebase-analytics-ktx' 15 | } 16 | -------------------------------------------------------------------------------- /tracker/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /tracker/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tracker/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /util/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /util/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | buildTypes { 5 | release { 6 | minifyEnabled false 7 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 8 | } 9 | } 10 | namespace 'com.phicdy.mycuration.util' 11 | } 12 | 13 | dependencies { 14 | } 15 | -------------------------------------------------------------------------------- /util/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /util/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /util/src/main/java/com/phicdy/mycuration/util/ToastHelper.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.util 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | 6 | object ToastHelper { 7 | 8 | fun showToast(context: Context?, text: String?, length: Int) { 9 | if (context == null || text == null || text == "" || 10 | length != Toast.LENGTH_SHORT && length != Toast.LENGTH_LONG) { 11 | return 12 | } 13 | Toast.makeText(context, text, length).show() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /util/src/main/java/com/phicdy/mycuration/util/UrlUtil.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.util 2 | 3 | import java.util.regex.Pattern 4 | 5 | 6 | object UrlUtil { 7 | 8 | fun hasParameterUrl(url: String): Boolean { 9 | val regex = "^(http|https):\\/\\/.+\\/\\?.+$" 10 | val p = Pattern.compile(regex) 11 | val m = p.matcher(url) 12 | return m.find() 13 | } 14 | 15 | fun removeUrlParameter(url: String): String { 16 | if (!isCorrectUrl(url)) { 17 | return url 18 | } 19 | return if (!url.contains("?")) { 20 | url 21 | } else url.substring(0, url.indexOf("?")) 22 | } 23 | 24 | fun isCorrectUrl(url: String?): Boolean { 25 | return url != null && (url.startsWith("http://") || url.startsWith("https://")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /util/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /util/src/test/java/com/phicdy/mycuration/util/UrlUtilTest.kt: -------------------------------------------------------------------------------- 1 | package com.phicdy.mycuration.util 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.Test 5 | import org.junit.runner.RunWith 6 | import org.junit.runners.JUnit4 7 | 8 | @RunWith(JUnit4::class) 9 | class UrlUtilTest { 10 | 11 | @Test 12 | fun testRemoveUrlParameter() { 13 | val removedUrl = UrlUtil.removeUrlParameter("http://harofree.blog.fc2.com/?ps") 14 | assertThat(removedUrl).isEqualTo("http://harofree.blog.fc2.com/") 15 | } 16 | 17 | @Test 18 | fun testHasParameterUrl() { 19 | assertThat(UrlUtil.hasParameterUrl("http://www.xxx.com/?aaa")).isTrue() 20 | assertThat(UrlUtil.hasParameterUrl("https://www.xxx.com/?aaa")).isTrue() 21 | assertThat(UrlUtil.hasParameterUrl("http://www.xxx.com/aaa/?bbb")).isTrue() 22 | assertThat(UrlUtil.hasParameterUrl("http://www.xxx.com/aaa")).isFalse() 23 | assertThat(UrlUtil.hasParameterUrl("http://www.xxx.com?/aaa")).isFalse() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /whatsnews/whatsnew-en-AU: -------------------------------------------------------------------------------- 1 | Improved -------------------------------------------------------------------------------- /whatsnews/whatsnew-en-GB: -------------------------------------------------------------------------------- 1 | Improved 2 | -------------------------------------------------------------------------------- /whatsnews/whatsnew-en-US: -------------------------------------------------------------------------------- 1 | Improved 2 | -------------------------------------------------------------------------------- /whatsnews/whatsnew-ja-JP: -------------------------------------------------------------------------------- 1 | バージョン1.12.0(2024/08/03) 2 | - RSSの更新処理をv1.11.0の処理に戻しました 3 | --------------------------------------------------------------------------------