├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── android_ci.yml │ └── lint.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ ├── Spikey_Sanju.xml │ └── profiles_settings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── kotlinc.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTION.md ├── LICENSE ├── README.md ├── apk └── nytimes.apk ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── www │ │ └── thecodemonks │ │ └── techbytes │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── www │ │ │ └── thecodemonks │ │ │ └── techbytes │ │ │ ├── app │ │ │ └── NYTimes.kt │ │ │ ├── datastore │ │ │ └── UIModeDataStore.kt │ │ │ ├── db │ │ │ ├── AppDatabase.kt │ │ │ └── ArticleDao.kt │ │ │ ├── di │ │ │ ├── DataSourceResolver.kt │ │ │ ├── DomainResolver.kt │ │ │ └── FrameworkResolver.kt │ │ │ ├── model │ │ │ ├── Article.kt │ │ │ └── Category.kt │ │ │ ├── repo │ │ │ └── Repo.kt │ │ │ ├── ui │ │ │ ├── about │ │ │ │ ├── AboutFragment.kt │ │ │ │ └── AboutViewModel.kt │ │ │ ├── adapter │ │ │ │ ├── CategoryAdapter.kt │ │ │ │ └── NewsAdapter.kt │ │ │ ├── articles │ │ │ │ └── ArticlesFragment.kt │ │ │ ├── base │ │ │ │ ├── BaseActivity.kt │ │ │ │ └── BaseFragment.kt │ │ │ ├── bookmarks │ │ │ │ └── BookmarksFragment.kt │ │ │ ├── details │ │ │ │ └── ArticleDetailsFragment.kt │ │ │ └── viewmodel │ │ │ │ └── ArticleViewModel.kt │ │ │ ├── utils │ │ │ ├── Constants.kt │ │ │ ├── NetworkManager.kt │ │ │ ├── NetworkUtils.kt │ │ │ ├── SpacesItemDecorator.kt │ │ │ └── ViewExt.kt │ │ │ └── worker │ │ │ └── MyWorker.kt │ └── res │ │ ├── anim │ │ ├── slide_in_left.xml │ │ ├── slide_in_right.xml │ │ ├── slide_out_left.xml │ │ └── slide_out_right.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_baseline_bookmark.xml │ │ ├── ic_bookmarks.xml │ │ ├── ic_day.xml │ │ ├── ic_empty.xml │ │ ├── ic_internet_off.xml │ │ ├── ic_internet_on.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_night.xml │ │ ├── ic_ny_notification_icon.xml │ │ ├── ic_outline_dark.xml │ │ ├── ic_share.xml │ │ ├── nytimes.png │ │ └── text_chip_view.xml │ │ ├── font │ │ └── gilroybold.ttf │ │ ├── layout │ │ ├── activity_base.xml │ │ ├── content_empty_state_layout.xml │ │ ├── fragment_about.xml │ │ ├── fragment_article_details.xml │ │ ├── fragment_articles.xml │ │ ├── fragment_bookmarks.xml │ │ ├── item_post_article.xml │ │ └── item_post_category.xml │ │ ├── menu │ │ ├── menu.xml │ │ └── share_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values-night │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── fragment_articles_scene.xml │ │ └── fragment_bookmarks_scene.xml │ └── test │ └── java │ └── www │ └── thecodemonks │ └── techbytes │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── articledetails.png ├── articles.png ├── bookmarks.png ├── darkmode.png ├── lightmode.png ├── nytimes_card.jpg └── techbytes.png └── settings.gradle /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://www.paypal.com/paypalme2/spikeysanju"] 2 | -------------------------------------------------------------------------------- /.github/workflows/android_ci.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v1 17 | 18 | - name: Set Up JDK 19 | uses: actions/setup-java@v1 20 | with: 21 | java-version: 1.8 22 | 23 | - name: Run Tests 24 | run: ./gradlew test 25 | 26 | - name: Build Project 27 | run: ./gradlew assemble 28 | 29 | - name: Build with Gradle 30 | run: ./gradlew build -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: KT Lint 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | lint: 7 | name: Lint Check 🕵🏻‍♂️ 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout Code 👨🏻‍💻 12 | uses: actions/checkout@v2 13 | 14 | - name: Lint Code Base 🧪 15 | uses: docker://github/super-linter:v2.2.0 16 | env: 17 | VALIDATE_ALL_CODEBASE: true 18 | VALIDATE_MD: false 19 | VALIDATE_XML: true 20 | VALIDATE_KOTLIN: true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Topten -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 24 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | xmlns:android 33 | 34 | ^$ 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | xmlns:.* 44 | 45 | ^$ 46 | 47 | 48 | BY_NAME 49 | 50 |
51 |
52 | 53 | 54 | 55 | .*:id 56 | 57 | http://schemas.android.com/apk/res/android 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | .*:name 67 | 68 | http://schemas.android.com/apk/res/android 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | name 78 | 79 | ^$ 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 | 88 | style 89 | 90 | ^$ 91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 | .* 100 | 101 | ^$ 102 | 103 | 104 | BY_NAME 105 | 106 |
107 |
108 | 109 | 110 | 111 | .* 112 | 113 | http://schemas.android.com/apk/res/android 114 | 115 | 116 | ANDROID_ATTRIBUTE_ORDER 117 | 118 |
119 |
120 | 121 | 122 | 123 | .* 124 | 125 | .* 126 | 127 | 128 | BY_NAME 129 | 130 |
131 |
132 |
133 |
134 | 135 | 137 |
138 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/Spikey_Sanju.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This code of conduct outlines our expectations for participants within the [@TheCodeMonks](https://github.com/TheCodeMonks) community, as well as steps to reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. 4 | 5 | Our open source community strives to: 6 | 7 | * **Be friendly and patient.** 8 | * **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. 9 | 10 | * **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. 11 | 12 | * **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. 13 | 14 | * **Be careful in the words that you choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. This includes, but is not limited to: 15 | * Violent threats or language directed against another person. 16 | * Discriminatory jokes and language. 17 | * Posting sexually explicit or violent material. 18 | * Posting (or threatening to post) other people's personally identifying information ("doxing"). 19 | * Personal insults, especially those using racist or sexist terms. 20 | * Unwelcome sexual attention. 21 | * Advocating for, or encouraging, any of the above behavior. 22 | * Repeated harassment of others. In general, if someone asks you to stop, then stop. 23 | 24 | * **When we disagree, try to understand why**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. 25 | 26 | This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter. 27 | 28 | ### Diversity Statement 29 | 30 | We encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. 31 | 32 | Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected 33 | characteristics above, including participants with disabilities. 34 | 35 | ### Reporting Issues 36 | 37 | If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via [thecodemonksorg@gmail.com](mailto:thecodemonksorg@gmail.com). All reports will be handled with discretion. In your report please include: 38 | 39 | - Your contact information. 40 | - Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please 41 | include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. 42 | - Any additional information that may be helpful. 43 | 44 | After filing a report, a representative will contact you personally. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. A representative will then review the incident, follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality requests for the purpose of protecting victims of abuse. 45 | 46 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning. 47 | 48 | ## Thanks 49 | 50 | This code of conduct is based on the [Open Code of Conduct](https://github.com/todogroup/opencodeofconduct) from the [TODOGroup](http://todogroup.org). 51 | 52 | We are thankful for their work and all the communities who have paved the way with code of conducts. 53 | -------------------------------------------------------------------------------- /CONTRIBUTION.md: -------------------------------------------------------------------------------- 1 | ## Welcome Monk!, Thanks for making our community great. 2 | 3 | ### What you can do 4 | You can contribute us by filing issues, bugs and PRs. 5 | 6 | ### Contributing guidelines: 7 | - Open issue regarding proposed change. 8 | - Repo owner will contact you there. 9 | - If your proposed change is approved, Fork this repo and do changes. 10 | - Open PR against latest `dev` branch. Add nice description in PR. 11 | - You're done! 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 The Code Monks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![GitHub Cards Preview](https://github.com/TheCodeMonks/TechBytes/blob/master/screenshots/nytimes_card.jpg?raw=true) 4 | 5 | # 🗞 NY Times 6 | **NY Times** is an Minimal News 🗞 Android application built to describe the use of JSoup with Modern Android development tools. *Made with love ❤️ by [Spikeysanju](https://github.com/Spikeysanju)* 7 | 8 | ***Try latest NY Times app apk from below 👇*** 9 | 10 | [![NY Times](https://img.shields.io/badge/NYTimes🌈-APK-black.svg?style=for-the-badge&logo=android)](https://github.com/TheCodeMonks/NYTimes-App/releases/download/v1.4.3/nytimes.apk) 11 | 12 | 13 | ## Built With 🛠 14 | - [Kotlin](https://kotlinlang.org/) - First class and official programming language for Android development. 15 | - [JSoup](https://jsoup.org/) - Open source Java HTML parser, with the best of HTML5 DOM methods and CSS selectors, for easy data extraction. 16 | - [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) - For asynchronous and more.. 17 | - [Android Architecture Components](https://developer.android.com/topic/libraries/architecture) - Collection of libraries that help you design robust, testable, and maintainable apps. 18 | - [Flow](https://kotlinlang.org/docs/reference/coroutines/flow.html) - A flow is an asynchronous version of a Sequence, a type of collection whose values are lazily produced. 19 | - [Jetpack DataStore](https://developer.android.com/topic/libraries/architecture/datastore) - Jetpack DataStore is a data storage solution that allows you to store key-value pairs or typed objects with protocol buffers. DataStore uses Kotlin coroutines and Flow to store data asynchronously, consistently, and transactionally 20 | - [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) - Data objects that notify views when the underlying database changes. 21 | - [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Stores UI-related data that isn't destroyed on UI changes. 22 | - [Room](https://developer.android.com/topic/libraries/architecture/room) - SQLite object mapping library. 23 | - [Jetpack Navigation](https://developer.android.com/guide/navigation) - Navigation refers to the interactions that allow users to navigate across, into, and back out from the different pieces of content within your app 24 | - [Material Components for Android](https://github.com/material-components/material-components-android) - Modular and customizable Material Design UI components for Android. 25 | 26 | 27 | # Package Structure 28 | 29 | www.thecodemonks.techbytes # Root Package 30 | . 31 | ├── data # For data handling. 32 | │ ├── db # Local Persistence Database. Room (SQLite) database 33 | | │ ├── dao # Data Access Object for Room 34 | | | |── database # Datbase Instance 35 | | 36 | ├── model # Model classes 37 | | 38 | | 39 | ├── ui # Activity/View layer 40 | │ ├── |── base # Base Activity 41 | | │ ├── adapter # Adapter for RecyclerView 42 | | │ └── viewmodel # Viewmodels for Articles 43 | | │ ├── articles # Articles Fragment 44 | | │ ├── details # Details Fragment 45 | | │ ├── bookmarks # Bookmarks Fragment 46 | | 47 | | 48 | |── utils # Utils for URls 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | ## Architecture 57 | 58 | This app uses [***MVVM (Model View View-Model)***](https://developer.android.com/jetpack/docs/guide#recommended-app-arch) architecture. 59 | 60 | ![](https://github.com/TheCodeMonks/Notes-App/blob/master/screenshots/ANDROID%20ROOM%20DB%20DIAGRAM.jpg) 61 | 62 | 63 | ## Contribute 64 | If you want to contribute to this library, you're always welcome! 65 | See [Contributing Guidelines](https://github.com/TheCodeMonks/Notzz-App/blob/master/CONTRIBUTION.md). 66 | 67 | ## Contact 68 | Have an project? DM us at 👇 69 | 70 | Drop a mail to:- thecodemonksorg@gmail.com 71 | 72 | # Donation 73 | If this project help you reduce time to develop, you can give me a cup of coffee :) 74 | 75 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/paypalme2/spikeysanju) 76 | 77 | 78 | ## License 79 | ``` 80 | MIT License 81 | 82 | Copyright (c) 2020 TheCodeMonks 83 | 84 | Permission is hereby granted, free of charge, to any person obtaining a copy 85 | of this software and associated documentation files (the "Software"), to deal 86 | in the Software without restriction, including without limitation the rights 87 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 88 | copies of the Software, and to permit persons to whom the Software is 89 | furnished to do so, subject to the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be included in all 92 | copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 95 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 96 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 97 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 98 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 99 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 100 | SOFTWARE. 101 | ``` 102 | -------------------------------------------------------------------------------- /apk/nytimes.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/apk/nytimes.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | plugins { 28 | id 'com.android.application' 29 | id 'kotlin-android' 30 | id 'kotlin-kapt' 31 | id 'androidx.navigation.safeargs.kotlin' 32 | id 'dagger.hilt.android.plugin' 33 | } 34 | 35 | 36 | android { 37 | compileSdkVersion 30 38 | 39 | defaultConfig { 40 | applicationId "www.thecodemonks.techbytes" 41 | minSdkVersion 21 42 | targetSdkVersion 30 43 | versionCode 1 44 | versionName "1.1.1" 45 | 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | javaCompileOptions { 48 | annotationProcessorOptions { 49 | arguments += [ 50 | "room.schemaLocation" : "$projectDir/schemas".toString(), 51 | "room.incremental" : "true", 52 | "room.expandProjection": "true"] 53 | } 54 | } 55 | } 56 | 57 | buildFeatures { 58 | viewBinding true 59 | } 60 | 61 | buildTypes { 62 | release { 63 | minifyEnabled false 64 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 65 | } 66 | } 67 | 68 | compileOptions { 69 | sourceCompatibility JavaVersion.VERSION_1_8 70 | targetCompatibility JavaVersion.VERSION_1_8 71 | } 72 | 73 | kotlinOptions { 74 | jvmTarget = JavaVersion.VERSION_1_8.toString() 75 | } 76 | } 77 | 78 | dependencies { 79 | 80 | implementation 'androidx.work:work-runtime:2.5.0' 81 | def work_version = "2.5.0" 82 | 83 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 84 | implementation 'androidx.core:core-ktx:1.3.2' 85 | implementation 'androidx.appcompat:appcompat:1.2.0' 86 | implementation 'com.google.android.material:material:1.3.0' 87 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 88 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 89 | testImplementation 'junit:junit:4.13.2' 90 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 91 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 92 | 93 | // Architectural Components 94 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0" 95 | 96 | // Room 97 | implementation "androidx.room:room-runtime:2.2.6" 98 | kapt "androidx.room:room-compiler:2.2.6" 99 | 100 | // Kotlin Extensions and Coroutines support for Room 101 | implementation "androidx.room:room-ktx:2.2.6" 102 | 103 | // LiveData 104 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0" 105 | 106 | // Coroutines 107 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' 108 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2' 109 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.4.1' 110 | 111 | // Navigation Components 112 | implementation "androidx.navigation:navigation-fragment-ktx:2.3.3" 113 | implementation "androidx.navigation:navigation-ui-ktx:2.3.3" 114 | 115 | // Coroutine Lifecycle Scopes 116 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0" 117 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0" 118 | 119 | // Coil-kt 120 | implementation 'io.coil-kt:coil:1.1.0' 121 | 122 | // JSoup for HTML parsing 123 | implementation 'org.jsoup:jsoup:1.13.1' 124 | 125 | // Kotlin + coroutines 126 | implementation "androidx.work:work-runtime-ktx:$work_version" 127 | 128 | // Preferences DataStore 129 | implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06" 130 | 131 | // Hilt dependency injection 132 | implementation "com.google.dagger:hilt-android:$hilt_version" 133 | kapt "com.google.dagger:hilt-android-compiler:$hilt_version" 134 | 135 | // Hilt support for lifecycle + viewModels + workManager 136 | implementation "androidx.hilt:hilt-lifecycle-viewmodel:$hilt_support" 137 | implementation "androidx.hilt:hilt-work:$hilt_support" 138 | kapt "androidx.hilt:hilt-compiler:$hilt_support" 139 | } 140 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/www/thecodemonks/techbytes/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package www.thecodemonks.techbytes 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("www.thecodemonks.topten", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/app/NYTimes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.app 28 | 29 | import android.app.Application 30 | import dagger.hilt.android.HiltAndroidApp 31 | 32 | @HiltAndroidApp 33 | class NYTimes : Application() 34 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/datastore/UIModeDataStore.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.datastore 28 | 29 | import android.content.Context 30 | import androidx.datastore.core.DataStore 31 | import androidx.datastore.preferences.core.Preferences 32 | import androidx.datastore.preferences.core.booleanPreferencesKey 33 | import androidx.datastore.preferences.core.edit 34 | import androidx.datastore.preferences.createDataStore 35 | import kotlinx.coroutines.flow.Flow 36 | import kotlinx.coroutines.flow.map 37 | 38 | abstract class PrefsDataStore(context: Context, fileName: String) { 39 | 40 | internal val dataStore: DataStore = context.createDataStore( 41 | name = fileName 42 | ) 43 | } 44 | 45 | class UIModeDataStore(context: Context) : 46 | PrefsDataStore( 47 | context, 48 | PREF_FILE_UI_MODE 49 | ), 50 | UIModeMutableStore, 51 | UIModeReadStore { 52 | 53 | override suspend fun saveToDataStore(isNightMode: Boolean) { 54 | dataStore.edit { preferences -> 55 | preferences[UI_MODE_KEY] = isNightMode 56 | } 57 | } 58 | 59 | override val uiMode: Flow = dataStore.data 60 | .map { preferences -> 61 | val uiMode = preferences[UI_MODE_KEY] ?: false 62 | uiMode 63 | } 64 | 65 | companion object { 66 | private const val PREF_FILE_UI_MODE = "ui_mode_preference" 67 | private val UI_MODE_KEY = booleanPreferencesKey("ui_mode") 68 | } 69 | } 70 | 71 | interface UIModeMutableStore { 72 | suspend fun saveToDataStore(isNightMode: Boolean) 73 | } 74 | 75 | interface UIModeReadStore { 76 | val uiMode: Flow 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/db/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.db 28 | 29 | import android.content.Context 30 | import androidx.room.Database 31 | import androidx.room.Room 32 | import androidx.room.RoomDatabase 33 | import www.thecodemonks.techbytes.model.Article 34 | 35 | @Database( 36 | entities = [Article::class], 37 | version = 1, 38 | exportSchema = false 39 | ) 40 | abstract class AppDatabase : RoomDatabase(), ArticleDatabase { 41 | 42 | abstract override fun getArticleDao(): ArticleDao 43 | 44 | companion object { 45 | private const val DatabaseName = "articles_db.db" 46 | 47 | @Volatile 48 | private var instance: AppDatabase? = null 49 | 50 | // Check for DB instance if not null then get or insert or else create new DB Instance 51 | operator fun invoke(context: Context) = instance ?: synchronized(this) { 52 | instance ?: createDatabase(context) 53 | .also { instance = it } 54 | } 55 | 56 | // create db instance 57 | private fun createDatabase(context: Context) = Room.databaseBuilder( 58 | context.applicationContext, 59 | AppDatabase::class.java, 60 | DatabaseName 61 | ).build() 62 | } 63 | } 64 | 65 | interface ArticleDatabase { 66 | fun getArticleDao(): ArticleDao 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/db/ArticleDao.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.db 28 | 29 | import androidx.room.* 30 | import kotlinx.coroutines.flow.Flow 31 | import www.thecodemonks.techbytes.model.Article 32 | 33 | @Dao 34 | interface ArticleDao { 35 | 36 | // insert or update article 37 | @Insert(onConflict = OnConflictStrategy.REPLACE) 38 | suspend fun upsert(article: Article) 39 | 40 | // get all article from db 41 | @Query("SELECT * FROM article") 42 | fun getSavedArticle(): Flow> 43 | 44 | // delete article from db 45 | @Delete 46 | suspend fun deleteArticle(article: Article) 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/di/DataSourceResolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.di 28 | 29 | import dagger.Module 30 | import dagger.Provides 31 | import dagger.hilt.InstallIn 32 | import dagger.hilt.components.SingletonComponent 33 | import www.thecodemonks.techbytes.db.ArticleDao 34 | import www.thecodemonks.techbytes.db.ArticleDatabase 35 | import www.thecodemonks.techbytes.repo.ArticleRepository 36 | import www.thecodemonks.techbytes.repo.Repo 37 | import javax.inject.Singleton 38 | 39 | // these modules components are android framework free 40 | 41 | @InstallIn(SingletonComponent::class) 42 | @Module 43 | object DataSourceResolver { 44 | 45 | @Provides 46 | @Singleton 47 | fun provideArticleDao(articleDatabase: ArticleDatabase): ArticleDao { 48 | return articleDatabase.getArticleDao() 49 | } 50 | 51 | // todo check scope 52 | @Provides 53 | @Singleton 54 | fun provideArticleRepository(articleDatabase: ArticleDatabase): ArticleRepository { 55 | return Repo(articleDatabase) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/di/DomainResolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.di 28 | 29 | import dagger.Binds 30 | import dagger.Module 31 | import dagger.hilt.InstallIn 32 | import dagger.hilt.components.SingletonComponent 33 | import www.thecodemonks.techbytes.datastore.UIModeDataStore 34 | import www.thecodemonks.techbytes.datastore.UIModeMutableStore 35 | import www.thecodemonks.techbytes.datastore.UIModeReadStore 36 | import www.thecodemonks.techbytes.db.AppDatabase 37 | import www.thecodemonks.techbytes.db.ArticleDatabase 38 | import javax.inject.Singleton 39 | 40 | // this resolver transforms hard android framework dependencies to android free logic objects 41 | 42 | @Module 43 | @InstallIn(SingletonComponent::class) 44 | abstract class DomainResolver { 45 | 46 | @Binds 47 | @Singleton 48 | abstract fun bindArticleDatabase(appDatabase: AppDatabase): ArticleDatabase 49 | 50 | @Binds 51 | @Singleton 52 | abstract fun bindUIModeMutableStore(uiModeDataStore: UIModeDataStore): UIModeMutableStore 53 | 54 | @Binds 55 | @Singleton 56 | abstract fun bindUIModeReadStore(uiModeDataStore: UIModeDataStore): UIModeReadStore 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/di/FrameworkResolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.di 28 | 29 | import android.content.Context 30 | import dagger.Module 31 | import dagger.Provides 32 | import dagger.hilt.InstallIn 33 | import dagger.hilt.android.qualifiers.ApplicationContext 34 | import dagger.hilt.components.SingletonComponent 35 | import www.thecodemonks.techbytes.datastore.UIModeDataStore 36 | import www.thecodemonks.techbytes.db.AppDatabase 37 | import www.thecodemonks.techbytes.utils.NetworkManager 38 | import javax.inject.Singleton 39 | 40 | // this module resolve all the hard android framework dependent objects 41 | 42 | @Module 43 | @InstallIn(SingletonComponent::class) 44 | object FrameworkResolver { 45 | 46 | @Singleton 47 | @Provides 48 | fun providesAppDatabase(@ApplicationContext context: Context): AppDatabase { 49 | return AppDatabase.invoke(context) 50 | } 51 | 52 | @Singleton 53 | @Provides 54 | fun providesUIModelDataStore(@ApplicationContext context: Context): UIModeDataStore { 55 | return UIModeDataStore(context) 56 | } 57 | 58 | @Singleton 59 | @Provides 60 | fun providesNetworkManager(@ApplicationContext context: Context): NetworkManager { 61 | return NetworkManager(context) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/model/Article.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.model 28 | 29 | import androidx.room.ColumnInfo 30 | import androidx.room.Entity 31 | import androidx.room.PrimaryKey 32 | import java.io.Serializable 33 | 34 | @Entity( 35 | tableName = "article" 36 | ) 37 | data class Article( 38 | @PrimaryKey 39 | @ColumnInfo(name = "title") 40 | val title: String = " ", 41 | @ColumnInfo(name = "description") 42 | val description: String? = null, 43 | @ColumnInfo(name = "image") 44 | val image: String? = null, 45 | @ColumnInfo(name = "author") 46 | val author: String? = null, 47 | @ColumnInfo(name = "source") 48 | val source: String? = null 49 | ) : Serializable 50 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/model/Category.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.model 28 | 29 | data class Category( 30 | val title: String? = null, 31 | val source: String? = null 32 | ) 33 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/repo/Repo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.repo 28 | 29 | import kotlinx.coroutines.flow.Flow 30 | import org.jsoup.Jsoup 31 | import www.thecodemonks.techbytes.db.ArticleDatabase 32 | import www.thecodemonks.techbytes.model.Article 33 | 34 | class Repo(private val db: ArticleDatabase) : ArticleRepository { 35 | 36 | // insert or update article 37 | override suspend fun upsertArticle(article: Article) = db.getArticleDao().upsert(article) 38 | 39 | // get saved article 40 | override fun getSavedArticle(): Flow> = db.getArticleDao().getSavedArticle() 41 | 42 | // delete article 43 | override suspend fun deleteArticle(article: Article) = db.getArticleDao().deleteArticle(article) 44 | 45 | // crawl data from ny times by selecting Xpath elements 46 | override fun crawlFromNYTimes(url: String): List
{ 47 | 48 | val document = Jsoup.connect(url).get() 49 | val articles: MutableList
= mutableListOf() 50 | 51 | // Path of articles present in the web 52 | val articleHTML = document.getElementById("stream-panel") 53 | .select("div").select("ol") 54 | .select("div").select("div").select("a") 55 | 56 | // iterate each article to get content 57 | articleHTML.forEach { item -> 58 | val image = item.select("div").select("figure") 59 | .select("div").select("img").attr("src") 60 | val title = item.select("h2").text() 61 | val description = item.select("p").text() 62 | val author = item.select("div").select("p").select("span").text() 63 | val source = item.attr("href") 64 | 65 | // check for null content 66 | if (!image.isNullOrEmpty() || !title.isNullOrEmpty() || 67 | !description.isNullOrEmpty() || !author.isNullOrEmpty() || 68 | !source.isNullOrEmpty() 69 | ) { 70 | val article = Article( 71 | title, 72 | description, 73 | image, 74 | author, 75 | source 76 | ) 77 | // add iterated articles to list 78 | articles.add(article) 79 | } 80 | } 81 | return articles 82 | } 83 | } 84 | 85 | interface ArticleRepository { 86 | suspend fun upsertArticle(article: Article) 87 | fun getSavedArticle(): Flow> 88 | suspend fun deleteArticle(article: Article) 89 | fun crawlFromNYTimes(url: String): List
90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/about/AboutFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.about 28 | 29 | import android.content.Intent 30 | import android.net.Uri 31 | import android.os.Bundle 32 | import android.view.LayoutInflater 33 | import android.view.View 34 | import android.view.ViewGroup 35 | import androidx.fragment.app.activityViewModels 36 | import dagger.hilt.android.AndroidEntryPoint 37 | import www.thecodemonks.techbytes.BuildConfig 38 | import www.thecodemonks.techbytes.R 39 | import www.thecodemonks.techbytes.databinding.FragmentAboutBinding 40 | import www.thecodemonks.techbytes.ui.base.BaseFragment 41 | 42 | @AndroidEntryPoint 43 | class AboutFragment : BaseFragment() { 44 | override val viewModel: AboutViewModel by activityViewModels() 45 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 46 | super.onViewCreated(view, savedInstanceState) 47 | initViews() 48 | } 49 | 50 | private fun initViews() = with(binding) { 51 | appVersion.text = getString( 52 | R.string.text_app_version, 53 | BuildConfig.VERSION_NAME, 54 | BuildConfig.VERSION_CODE 55 | ) 56 | 57 | license.setOnClickListener { 58 | launchBrowser(REPO_LICENSE) 59 | } 60 | 61 | visitURL.setOnClickListener { 62 | launchBrowser(REPO_URL) 63 | } 64 | } 65 | 66 | private fun launchBrowser(url: String) = Intent(Intent.ACTION_VIEW, Uri.parse(url)).also { 67 | startActivity(it) 68 | } 69 | 70 | override fun getViewBinding( 71 | inflater: LayoutInflater, 72 | container: ViewGroup? 73 | ) = FragmentAboutBinding.inflate(inflater, container, false) 74 | 75 | companion object { 76 | const val REPO_URL = "https://github.com/TheCodeMonks/NYTimes-App" 77 | const val REPO_LICENSE = "https://github.com/TheCodeMonks/NYTimes-App/blob/master/LICENSE" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/about/AboutViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.about 28 | 29 | import androidx.lifecycle.ViewModel 30 | 31 | class AboutViewModel : ViewModel() 32 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/adapter/CategoryAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.adapter 28 | 29 | import android.graphics.BlendMode 30 | import android.graphics.BlendModeColorFilter 31 | import android.graphics.PorterDuff 32 | import android.graphics.drawable.Drawable 33 | import android.os.Build 34 | import android.view.LayoutInflater 35 | import android.view.ViewGroup 36 | import androidx.core.content.ContextCompat 37 | import androidx.recyclerview.widget.RecyclerView 38 | import www.thecodemonks.techbytes.R 39 | import www.thecodemonks.techbytes.databinding.ItemPostCategoryBinding 40 | import www.thecodemonks.techbytes.model.Category 41 | 42 | class CategoryAdapter(private val category: MutableList) : 43 | RecyclerView.Adapter() { 44 | 45 | private var selectedItem: Int = -1 46 | 47 | inner class CategoryViewHolder(val binding: ItemPostCategoryBinding) : 48 | RecyclerView.ViewHolder(binding.root) 49 | 50 | override fun onCreateViewHolder( 51 | parent: ViewGroup, 52 | viewType: Int 53 | ): CategoryViewHolder { 54 | val binding = 55 | ItemPostCategoryBinding.inflate(LayoutInflater.from(parent.context), parent, false) 56 | return CategoryViewHolder(binding) 57 | } 58 | 59 | override fun getItemCount(): Int { 60 | return category.size 61 | } 62 | 63 | override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) { 64 | holder.binding.apply { 65 | 66 | itemCategoryTitle.text = category[position].title 67 | 68 | // on item click 69 | holder.itemView.setOnClickListener { 70 | onItemClickListener?.let { 71 | it(category[position]) 72 | 73 | if (selectedItem == position) { 74 | notifyItemChanged(position) 75 | return@setOnClickListener 76 | } 77 | 78 | selectedItem = position 79 | notifyDataSetChanged() 80 | } 81 | } 82 | 83 | // if item selected then change it's state color 84 | when (selectedItem) { 85 | position -> { 86 | itemCategoryTitle.setTextColor( 87 | ContextCompat.getColor( 88 | itemCategoryTitle.context, 89 | R.color.white 90 | ) 91 | ) 92 | 93 | MyDrawableCompat.setColorFilter( 94 | itemCategoryTitle.background, 95 | ContextCompat.getColor(root.context, R.color.design_default_color_primary) 96 | ) 97 | } 98 | else -> { 99 | itemCategoryTitle.setTextColor( 100 | ContextCompat.getColor( 101 | itemCategoryTitle.context, 102 | R.color.categoryText 103 | ) 104 | ) 105 | MyDrawableCompat.setColorFilter( 106 | itemCategoryTitle.background, 107 | ContextCompat.getColor(root.context, R.color.blue_smoke) 108 | ) 109 | } 110 | } 111 | } 112 | } 113 | 114 | // on item click listener 115 | private var onItemClickListener: ((Category) -> Unit)? = null 116 | fun setOnItemClickListener(listener: (Category) -> Unit) { 117 | onItemClickListener = listener 118 | } 119 | 120 | // check if android version is greater than Q -> color filter else use set color filter 121 | object MyDrawableCompat { 122 | fun setColorFilter(drawable: Drawable, color: Int) { 123 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 124 | drawable.colorFilter = BlendModeColorFilter(color, BlendMode.SRC_ATOP) 125 | } else { 126 | drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP) 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/adapter/NewsAdapter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.adapter 28 | 29 | import android.view.LayoutInflater 30 | import android.view.View 31 | import android.view.ViewGroup 32 | import androidx.recyclerview.widget.AsyncListDiffer 33 | import androidx.recyclerview.widget.DiffUtil 34 | import androidx.recyclerview.widget.RecyclerView 35 | import coil.load 36 | import coil.transform.RoundedCornersTransformation 37 | import www.thecodemonks.techbytes.databinding.ItemPostArticleBinding 38 | import www.thecodemonks.techbytes.model.Article 39 | 40 | class NewsAdapter : RecyclerView.Adapter() { 41 | 42 | private val differCallback = object : DiffUtil.ItemCallback
() { 43 | override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean { 44 | return oldItem.source == newItem.source 45 | } 46 | 47 | override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean { 48 | return oldItem == newItem 49 | } 50 | } 51 | 52 | val differ = AsyncListDiffer(this, differCallback) 53 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsVH { 54 | val binding = 55 | ItemPostArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false) 56 | return NewsVH(binding) 57 | } 58 | 59 | override fun getItemCount(): Int { 60 | return differ.currentList.size 61 | } 62 | 63 | override fun onBindViewHolder(holder: NewsVH, position: Int) { 64 | 65 | val item = differ.currentList[position] 66 | holder.binding.apply { 67 | 68 | // TODO clean logic 69 | if (item.title.isBlank() || item.description.isNullOrBlank() || 70 | item.image.isNullOrBlank() 71 | ) { 72 | itemArticleTitle.visibility = View.GONE 73 | itemPostDescription.visibility = View.GONE 74 | itemPostAuthor.visibility = View.GONE 75 | itemArticleImage.visibility = View.GONE 76 | } else { 77 | itemArticleTitle.text = item.title 78 | itemPostDescription.text = item.description 79 | itemPostAuthor.text = item.author.toString().ifBlank { "Unknown" } 80 | itemArticleImage.load(item.image) { 81 | crossfade(true) 82 | crossfade(200) 83 | transformations( 84 | RoundedCornersTransformation( 85 | 12f, 86 | 12f, 87 | 12f, 88 | 12f 89 | ) 90 | ) 91 | } 92 | } 93 | 94 | // on item click 95 | holder.itemView.setOnClickListener { 96 | onItemClickListener?.let { it(item) } 97 | } 98 | } 99 | } 100 | 101 | inner class NewsVH(val binding: ItemPostArticleBinding) : RecyclerView.ViewHolder(binding.root) 102 | 103 | // on item click listener 104 | private var onItemClickListener: ((Article) -> Unit)? = null 105 | fun setOnItemClickListener(listener: (Article) -> Unit) { 106 | onItemClickListener = listener 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.base 28 | 29 | import android.os.Bundle 30 | import androidx.activity.viewModels 31 | import androidx.appcompat.app.AppCompatActivity 32 | import androidx.navigation.NavController 33 | import androidx.navigation.fragment.NavHostFragment 34 | import androidx.navigation.ui.NavigationUI 35 | import androidx.work.Constraints 36 | import androidx.work.ExistingPeriodicWorkPolicy.KEEP 37 | import androidx.work.NetworkType 38 | import androidx.work.PeriodicWorkRequestBuilder 39 | import androidx.work.WorkManager 40 | import dagger.hilt.android.AndroidEntryPoint 41 | import www.thecodemonks.techbytes.R 42 | import www.thecodemonks.techbytes.databinding.ActivityBaseBinding 43 | import www.thecodemonks.techbytes.ui.viewmodel.ArticleViewModel 44 | import www.thecodemonks.techbytes.worker.MyWorker 45 | import java.util.concurrent.TimeUnit 46 | 47 | @AndroidEntryPoint 48 | class BaseActivity : AppCompatActivity() { 49 | 50 | // todo test scope and reference 51 | private val viewModel: ArticleViewModel by viewModels() 52 | 53 | private lateinit var navController: NavController 54 | 55 | override fun onCreate(savedInstanceState: Bundle?) { 56 | super.onCreate(savedInstanceState) 57 | 58 | val binding = ActivityBaseBinding.inflate(layoutInflater) 59 | 60 | setContentView(binding.root) 61 | setSupportActionBar(binding.toolbar) 62 | 63 | // todo integrate with Hilt 64 | // setup workManager 65 | initWorker() 66 | 67 | // init nav controller with back action button 68 | val navHostFragment = 69 | supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment 70 | navController = navHostFragment.navController 71 | NavigationUI.setupActionBarWithNavController(this, navController) 72 | } 73 | 74 | // todo integrate with Hilt 75 | private fun initWorker() { 76 | // worker constraints 77 | val constraints = Constraints.Builder() 78 | .setRequiredNetworkType(NetworkType.CONNECTED) 79 | .build() 80 | 81 | // periodic worker for every 15 minutes 82 | val notificationWorkRequest = 83 | PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) 84 | .setConstraints(constraints) 85 | .build() 86 | 87 | // enqueue work 88 | val workManager = WorkManager.getInstance(applicationContext) 89 | workManager.enqueueUniquePeriodicWork(JOB_TAG, KEEP, notificationWorkRequest) 90 | } 91 | 92 | override fun onSupportNavigateUp(): Boolean { 93 | navController.navigateUp() 94 | return super.onSupportNavigateUp() 95 | } 96 | 97 | companion object { 98 | const val JOB_TAG = "articlesWorkerTag" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.base 28 | 29 | import android.content.Context 30 | import android.os.Bundle 31 | import android.view.LayoutInflater 32 | import android.view.View 33 | import android.view.ViewGroup 34 | import android.widget.Toast 35 | import androidx.fragment.app.Fragment 36 | import androidx.lifecycle.ViewModel 37 | import androidx.viewbinding.ViewBinding 38 | 39 | abstract class BaseFragment : Fragment() { 40 | 41 | private var _binding: VB? = null 42 | protected val binding get() = _binding!! 43 | 44 | protected abstract val viewModel: VM 45 | 46 | override fun onCreateView( 47 | inflater: LayoutInflater, 48 | container: ViewGroup?, 49 | savedInstanceState: Bundle? 50 | ): View? { 51 | _binding = getViewBinding(inflater, container) 52 | return binding.root 53 | } 54 | 55 | protected abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB 56 | 57 | fun toast(message: String) { 58 | Toast.makeText(activity, message, Toast.LENGTH_SHORT).show() 59 | } 60 | 61 | fun applicationContext(): Context = requireActivity().applicationContext 62 | 63 | override fun onDestroy() { 64 | super.onDestroy() 65 | _binding = null 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/details/ArticleDetailsFragment.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.details 28 | 29 | import android.os.Build 30 | import android.os.Bundle 31 | import android.view.LayoutInflater 32 | import android.view.Menu 33 | import android.view.MenuInflater 34 | import android.view.MenuItem 35 | import android.view.View 36 | import android.view.ViewGroup 37 | import androidx.core.app.ShareCompat 38 | import androidx.fragment.app.activityViewModels 39 | import androidx.navigation.fragment.navArgs 40 | import dagger.hilt.android.AndroidEntryPoint 41 | import www.thecodemonks.techbytes.R 42 | import www.thecodemonks.techbytes.databinding.FragmentArticleDetailsBinding 43 | import www.thecodemonks.techbytes.model.Article 44 | import www.thecodemonks.techbytes.ui.base.BaseFragment 45 | import www.thecodemonks.techbytes.ui.viewmodel.ArticleViewModel 46 | import www.thecodemonks.techbytes.utils.Constants 47 | 48 | @AndroidEntryPoint 49 | class ArticleDetailsFragment : BaseFragment() { 50 | 51 | override val viewModel: ArticleViewModel by activityViewModels() 52 | private val args: ArticleDetailsFragmentArgs by navArgs() 53 | private var completeUrl: String? = null 54 | 55 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 56 | super.onViewCreated(view, savedInstanceState) 57 | 58 | setHasOptionsMenu(true) 59 | 60 | // receive bundle here 61 | val bundle = args.article 62 | completeUrl = Constants.URL.plus(bundle.source) 63 | 64 | // webView with url has param 65 | binding.webView.apply { 66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 67 | webViewClient = webViewClient 68 | } 69 | loadUrl(completeUrl!!) 70 | } 71 | 72 | binding.btnSavedArticle.setOnClickListener { 73 | val article = Article( 74 | bundle.title, 75 | bundle.description, 76 | bundle.image, 77 | bundle.author, 78 | bundle.source 79 | ) 80 | viewModel.upsertArticle(article).also { 81 | toast(getString(R.string.successfully_saved)) 82 | } 83 | } 84 | } 85 | 86 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 87 | // Inflate the menu; this adds items to the action bar if it is present. 88 | inflater.inflate(R.menu.share_menu, menu) 89 | } 90 | 91 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 92 | // Handle action bar item clicks here. 93 | return when (item.itemId) { 94 | R.id.action_share -> { 95 | val shareMsg = getString( 96 | R.string.share_message, 97 | args.article.title, 98 | completeUrl 99 | ) 100 | 101 | val intent = ShareCompat.IntentBuilder.from(requireActivity()) 102 | .setType("text/plain") 103 | .setText(shareMsg) 104 | .intent 105 | 106 | if (intent.resolveActivity(requireActivity().packageManager) != null) { 107 | startActivity(intent) 108 | } 109 | true 110 | } 111 | else -> super.onOptionsItemSelected(item) 112 | } 113 | } 114 | 115 | override fun getViewBinding( 116 | inflater: LayoutInflater, 117 | container: ViewGroup? 118 | ) = FragmentArticleDetailsBinding.inflate(inflater, container, false) 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ArticleViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.ui.viewmodel 28 | 29 | import android.app.Application 30 | import androidx.hilt.lifecycle.ViewModelInject 31 | import androidx.lifecycle.AndroidViewModel 32 | import androidx.lifecycle.LiveData 33 | import androidx.lifecycle.MutableLiveData 34 | import androidx.lifecycle.asLiveData 35 | import androidx.lifecycle.viewModelScope 36 | import kotlinx.coroutines.Dispatchers.IO 37 | import kotlinx.coroutines.launch 38 | import www.thecodemonks.techbytes.datastore.UIModeMutableStore 39 | import www.thecodemonks.techbytes.datastore.UIModeReadStore 40 | import www.thecodemonks.techbytes.model.Article 41 | import www.thecodemonks.techbytes.repo.ArticleRepository 42 | import www.thecodemonks.techbytes.utils.Constants 43 | import www.thecodemonks.techbytes.utils.NetworkManager 44 | 45 | class ArticleViewModel @ViewModelInject constructor( 46 | application: Application, 47 | private val repo: ArticleRepository, 48 | private val uiModeEdit: UIModeMutableStore, 49 | val uiModeRead: UIModeReadStore, 50 | private val networkManager: NetworkManager, 51 | ) : AndroidViewModel(application) { 52 | 53 | private val _articles = MutableLiveData>() 54 | val articles: LiveData> 55 | get() = _articles 56 | 57 | val networkObserver = networkManager.observeConnectionStatus 58 | 59 | val currentTopic: MutableLiveData by lazy { 60 | MutableLiveData().defaultTopic(Constants.NY_TECH) 61 | } 62 | 63 | // save article 64 | fun upsertArticle(article: Article) = viewModelScope.launch { 65 | repo.upsertArticle(article) 66 | } 67 | 68 | // get saved article 69 | fun getSavedArticle() = repo.getSavedArticle().asLiveData() 70 | 71 | // save article 72 | fun deleteArticle(article: Article) = viewModelScope.launch { 73 | repo.deleteArticle(article) 74 | } 75 | 76 | private var currentQueryUrl = "" 77 | 78 | // crawl data from NY times 79 | fun crawlFromNYTimes(url: String) { 80 | currentQueryUrl = url 81 | if (networkObserver.value == true) { 82 | viewModelScope.launch(IO) { 83 | _articles.postValue(repo.crawlFromNYTimes(url)) 84 | } 85 | } 86 | } 87 | 88 | fun reCrawlFromNYTimes(refreshFailed: () -> Unit = {}) { 89 | if (networkObserver.value == true) { 90 | viewModelScope.launch(IO) { 91 | _articles.postValue(repo.crawlFromNYTimes(currentQueryUrl)) 92 | } 93 | } else { 94 | refreshFailed.invoke() 95 | } 96 | } 97 | 98 | // Save to DataStore 99 | fun saveToDataStore(isNightMode: Boolean) = viewModelScope.launch(IO) { 100 | uiModeEdit.saveToDataStore(isNightMode) 101 | } 102 | 103 | // set default topic when opening 104 | private fun MutableLiveData.defaultTopic(initialValue: T) = 105 | apply { setValue(initialValue) } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.utils 28 | 29 | object Constants { 30 | const val URL = "https://www.nytimes.com" 31 | const val NY_SCIENCE = "https://www.nytimes.com/section/science" 32 | const val NY_TECH = "https://www.nytimes.com/section/technology" 33 | const val NY_BUSINESS = "https://www.nytimes.com/section/business/smallbusiness" 34 | const val NY_YOURMONEY = "https://www.nytimes.com/section/your-money" 35 | const val NY_EDUCATION = "https://www.nytimes.com/section/education" 36 | const val NY_SPORTS = "https://www.nytimes.com/section/sports/soccer" 37 | const val NY_SPACE = "https://www.nytimes.com/section/science/space" 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/utils/NetworkManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.utils 28 | 29 | import android.content.Context 30 | import android.net.ConnectivityManager 31 | import android.net.Network 32 | import android.net.NetworkCapabilities 33 | import android.net.NetworkRequest 34 | import android.os.Build 35 | import androidx.core.content.getSystemService 36 | import androidx.lifecycle.LiveData 37 | import androidx.lifecycle.MutableLiveData 38 | 39 | /** 40 | * Network Manager, extends capabilities of ConnectivityManager#NetworkCallback() 41 | * by providing a observable callback on network status 42 | * 43 | * Author : [https://github.com/ch8n] 44 | * website : [https://chetangupta.net] 45 | * Creation Date : 4-08-2020 46 | */ 47 | class NetworkManager(context: Context) : ConnectivityManager.NetworkCallback() { 48 | 49 | private val _connectionStatusLiveData: MutableLiveData = MutableLiveData() 50 | val observeConnectionStatus: LiveData = _connectionStatusLiveData 51 | 52 | private val appContext: Context = context.applicationContext 53 | 54 | init { 55 | val connectivityManager = appContext.getSystemService() 56 | 57 | if (connectivityManager != null) { 58 | connectivityManager.registerNetworkCallbackCompact(this) 59 | 60 | val connectionStatus = connectivityManager.allNetworks.any { network -> 61 | val networkCapabilities = connectivityManager.getNetworkCapabilities(network) 62 | return@any networkCapabilities?.hasCapability( 63 | NetworkCapabilities.NET_CAPABILITY_INTERNET 64 | ) == true 65 | } 66 | 67 | _connectionStatusLiveData.value = connectionStatus 68 | } 69 | } 70 | 71 | override fun onAvailable(network: Network) { 72 | super.onAvailable(network) 73 | _connectionStatusLiveData.postValue(true) 74 | } 75 | 76 | override fun onLost(network: Network) { 77 | super.onLost(network) 78 | _connectionStatusLiveData.postValue(false) 79 | } 80 | 81 | private fun ConnectivityManager.registerNetworkCallbackCompact(networkManager: NetworkManager) { 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 83 | registerDefaultNetworkCallback(networkManager) 84 | } else { 85 | val builder = NetworkRequest.Builder() 86 | registerNetworkCallback(builder.build(), networkManager) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/utils/NetworkUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.utils 28 | 29 | import android.content.Context 30 | import android.net.ConnectivityManager 31 | import android.net.Network 32 | import android.net.NetworkCapabilities 33 | import android.net.NetworkRequest 34 | import android.os.Build 35 | import androidx.lifecycle.LiveData 36 | import androidx.lifecycle.MutableLiveData 37 | 38 | /** 39 | * Network Utility to detect availability or unavailability of Internet connection 40 | */ 41 | object NetworkUtils : ConnectivityManager.NetworkCallback() { 42 | 43 | private val networkLiveData: MutableLiveData = MutableLiveData() 44 | 45 | /** 46 | * Returns instance of [LiveData] which can be observed for connectivity changes. 47 | */ 48 | fun observeConnectivity(context: Context): LiveData { 49 | val connectivityManager = 50 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 51 | 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 53 | connectivityManager.registerDefaultNetworkCallback(this) 54 | } else { 55 | val builder = NetworkRequest.Builder() 56 | connectivityManager.registerNetworkCallback(builder.build(), this) 57 | } 58 | 59 | // Retrieve current status of connectivity 60 | val isConnected = isConnected(connectivityManager) 61 | 62 | networkLiveData.postValue(isConnected) 63 | 64 | return networkLiveData 65 | } 66 | 67 | override fun onAvailable(network: Network) { 68 | networkLiveData.postValue(true) 69 | } 70 | 71 | override fun onLost(network: Network) { 72 | networkLiveData.postValue(false) 73 | } 74 | } 75 | 76 | private fun isConnected(connectivityManager: ConnectivityManager): Boolean { 77 | var isConnected = false 78 | 79 | // Retrieve current status of connectivity 80 | connectivityManager.allNetworks.forEach { network -> 81 | val networkCapability = connectivityManager.getNetworkCapabilities(network) 82 | 83 | networkCapability?.let { 84 | if (it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 85 | isConnected = true 86 | return@forEach 87 | } 88 | } 89 | } 90 | return isConnected 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/utils/SpacesItemDecorator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.utils 28 | 29 | import android.content.res.Resources 30 | import android.graphics.Rect 31 | import android.view.View 32 | import androidx.recyclerview.widget.LinearLayoutManager 33 | import androidx.recyclerview.widget.RecyclerView 34 | 35 | class SpacesItemDecorator(space: Int) : RecyclerView.ItemDecoration() { 36 | 37 | private val space: Int = space.dp 38 | 39 | override fun getItemOffsets( 40 | outRect: Rect, 41 | view: View, 42 | parent: RecyclerView, 43 | state: RecyclerView.State 44 | ) { 45 | val orientation = (parent.layoutManager as? LinearLayoutManager)?.orientation 46 | ?: LinearLayoutManager.VERTICAL 47 | if (orientation == LinearLayoutManager.HORIZONTAL) { 48 | when (parent.getChildLayoutPosition(view)) { 49 | 0 -> { 50 | outRect.left = space 51 | outRect.right = space 52 | } 53 | else -> outRect.right = space 54 | } 55 | } else { 56 | when (parent.getChildLayoutPosition(view)) { 57 | 0 -> { 58 | outRect.top = space 59 | outRect.left = space 60 | outRect.right = space 61 | outRect.bottom = space 62 | } 63 | else -> { 64 | outRect.left = space 65 | outRect.right = space 66 | outRect.bottom = space 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | val Int.dp: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt() 74 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/utils/ViewExt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.utils 28 | 29 | import android.content.Context 30 | import android.view.View 31 | import android.widget.TextView 32 | import androidx.annotation.DrawableRes 33 | import androidx.core.content.ContextCompat 34 | 35 | fun View.show() { 36 | visibility = View.VISIBLE 37 | } 38 | 39 | fun View.hide() { 40 | visibility = View.GONE 41 | } 42 | 43 | fun View.setVisible(isVisible: Boolean) { 44 | if (isVisible) show() else hide() 45 | } 46 | 47 | fun TextView.setDrawableLeft(@DrawableRes id: Int = 0) { 48 | this.setCompoundDrawablesWithIntrinsicBounds(id, 0, 0, 0) 49 | } 50 | 51 | fun Context.getColorCompat(color: Int) = ContextCompat.getColor(this, color) 52 | -------------------------------------------------------------------------------- /app/src/main/java/www/thecodemonks/techbytes/worker/MyWorker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | package www.thecodemonks.techbytes.worker 28 | 29 | import android.app.NotificationChannel 30 | import android.app.NotificationManager 31 | import android.app.PendingIntent 32 | import android.content.Context 33 | import android.content.Intent 34 | import android.media.RingtoneManager 35 | import android.os.Build 36 | import android.util.Log 37 | import androidx.core.app.NotificationCompat 38 | import androidx.core.content.ContextCompat 39 | import androidx.work.Worker 40 | import androidx.work.WorkerParameters 41 | import www.thecodemonks.techbytes.R 42 | import www.thecodemonks.techbytes.db.AppDatabase 43 | import www.thecodemonks.techbytes.repo.Repo 44 | import www.thecodemonks.techbytes.ui.base.BaseActivity 45 | import www.thecodemonks.techbytes.utils.Constants 46 | 47 | class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) { 48 | 49 | private fun showNotification(title: String, description: String) { 50 | 51 | val intent = Intent(applicationContext, BaseActivity::class.java) 52 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 53 | val pendingIntent = PendingIntent.getActivity( 54 | applicationContext, 55 | 0, 56 | intent, 57 | PendingIntent.FLAG_ONE_SHOT 58 | ) 59 | 60 | val channelId = applicationContext.getString(R.string.default_notification_channel_id) 61 | val channelName = applicationContext.getString(R.string.default_notification_channel_name) 62 | val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) 63 | val notificationBuilder = NotificationCompat.Builder(applicationContext, channelId) 64 | .setColor(ContextCompat.getColor(applicationContext, R.color.black)) 65 | .setSmallIcon(R.drawable.ic_ny_notification_icon) 66 | .setContentTitle(title) 67 | .setContentText(description) 68 | .setAutoCancel(true) 69 | .setPriority(NotificationCompat.PRIORITY_DEFAULT) 70 | .setSound(defaultSoundUri) 71 | .setContentIntent(pendingIntent) 72 | 73 | val notificationManager = 74 | applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 75 | 76 | // For android OREO notification channel is needed 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 78 | val channel = NotificationChannel( 79 | channelId, 80 | channelName, 81 | NotificationManager.IMPORTANCE_HIGH 82 | ) 83 | notificationManager.createNotificationChannel(channel) 84 | } 85 | 86 | notificationManager.notify(0, notificationBuilder.build()) 87 | } 88 | 89 | override fun doWork(): Result { 90 | 91 | val repository = Repo(AppDatabase(applicationContext)) 92 | val result = repository.crawlFromNYTimes(Constants.NY_TECH).toList() 93 | 94 | return if (result.isNullOrEmpty()) { 95 | Log.d(javaClass.simpleName, "Work Failed. Retrying...") 96 | Result.retry() 97 | } else { 98 | val title = result[0].title 99 | val description = result[0].description.toString() 100 | showNotification(title, description) 101 | Log.d(javaClass.simpleName, "Notification Displayed!") 102 | Log.d(javaClass.simpleName, "Work Succeed...") 103 | 104 | Result.success() 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_bookmark.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bookmarks.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 39 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_day.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 39 | 46 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_empty.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 98 | 101 | 104 | 105 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_internet_off.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_internet_on.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_night.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 39 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ny_notification_icon.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_dark.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 39 | 46 | 53 | 60 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/nytimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/drawable/nytimes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_chip_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/font/gilroybold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/font/gilroybold.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_base.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 35 | 36 | 42 | 43 | 47 | 48 | 49 | 50 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_empty_state_layout.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 33 | 39 | 40 | 52 | 53 | 54 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_about.xml: -------------------------------------------------------------------------------- 1 | 26 | 32 | 33 | 43 | 44 | 55 | 56 | 57 | 66 | 67 | 68 | 79 | 80 | 81 | 91 | 92 | 93 | 104 | 105 | 106 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_article_details.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 32 | 33 | 37 | 38 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_articles.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 34 | 35 | 45 | 46 | 54 | 55 | 56 | 60 | 61 | 64 | 65 | 73 | 74 | 86 | 87 | 96 | 97 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_bookmarks.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 34 | 35 | 39 | 40 | 47 | 48 | 49 | 50 | 51 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_post_article.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 34 | 35 | 44 | 45 | 52 | 53 | 64 | 65 | 74 | 75 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_post_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 33 | 34 | 35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 35 | 41 | 42 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/menu/share_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 30 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 33 | 34 | 39 | 46 | 53 | 56 | 57 | 62 | 65 | 66 | 71 | 74 | 75 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | @color/black 30 | @color/purple_200 31 | @color/white 32 | #121212 33 | #ffffff 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | Share Article 30 | Article saved successfully! 31 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | #FFBB86FC 30 | #FF6200EE 31 | #FF3700B3 32 | #FF03DAC5 33 | #FF018786 34 | #FF000000 35 | #FFFFFFFF 36 | #f3f7f9 37 | #000000 38 | 39 | 40 | @color/white 41 | @color/purple_500 42 | @color/purple_700 43 | @color/white 44 | @color/teal_200 45 | @color/teal_700 46 | @color/black 47 | 48 | #6fcf97 49 | #eb5757 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimen.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | 0dp 30 | 4dp 31 | 8dp 32 | 12dp 33 | 16dp 34 | 24dp 35 | 32dp 36 | 48dp 37 | 64dp 38 | 250dp 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | NY Times 29 | Facebook Said Not to Consider Banning Political Ads 30 | The social network has been under intense pressure for allowing misinformation and hate speech to spread on its site. 31 | By Sanju S 32 | 33 | 34 | \"%s\"\n\n source: %s. \n\nVisit: https://github.com/TheCodeMonks/NYTimes-App 35 | 36 | 37 | Bookmark 38 | Night Mode Switch 39 | 40 | Bookmarks 41 | No bookmarks yet 42 | Hello blank fragment 43 | 44 | 45 | 1 46 | Ny Times 47 | 48 | No internet connection 49 | You\'re back online 50 | Share Article 51 | Article saved successfully! 52 | Oops!, It\'s Empty 53 | There are no bookmarks found right now 54 | About 55 | 56 | 57 | 58 | Licensed Under MIT License 59 | Visit 60 | https://github.com/TheCodeMonks/NYTimes-App 61 | v%s (%d) 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | 33 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | true 30 | 31 | 51 | 52 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fragment_articles_scene.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 29 | 30 | 31 | 38 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fragment_bookmarks_scene.xml: -------------------------------------------------------------------------------- 1 | 26 | 27 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/test/java/www/thecodemonks/techbytes/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package www.thecodemonks.techbytes 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * MIT License 4 | * * 5 | * * Copyright (c) 2020 Spikey Sanju 6 | * * 7 | * * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * * of this software and associated documentation files (the "Software"), to deal 9 | * * in the Software without restriction, including without limitation the rights 10 | * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * * copies of the Software, and to permit persons to whom the Software is 12 | * * furnished to do so, subject to the following conditions: 13 | * * 14 | * * The above copyright notice and this permission notice shall be included in all 15 | * * copies or substantial portions of the Software. 16 | * * 17 | * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * * SOFTWARE. 24 | * 25 | */ 26 | 27 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 28 | buildscript { 29 | 30 | //versions 31 | ext.kotlin_version = "1.4.21" 32 | ext.hilt_version = "2.30.1-alpha" 33 | ext.hilt_support = "1.0.0-alpha02" 34 | 35 | repositories { 36 | google() 37 | jcenter() 38 | } 39 | dependencies { 40 | classpath 'com.android.tools.build:gradle:4.2.0-beta05' 41 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 42 | classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.2" 43 | classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" 44 | } 45 | } 46 | 47 | plugins { 48 | id "org.jlleitschuh.gradle.ktlint" version "9.4.1" 49 | } 50 | 51 | allprojects { 52 | repositories { 53 | google() 54 | jcenter() 55 | } 56 | apply plugin: "org.jlleitschuh.gradle.ktlint" 57 | } 58 | 59 | task clean(type: Delete) { 60 | delete rootProject.buildDir 61 | } 62 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # /* 3 | # * MIT License 4 | # * 5 | # * Copyright (c) 2020 Spikey Sanju 6 | # * 7 | # * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # * of this software and associated documentation files (the "Software"), to deal 9 | # * in the Software without restriction, including without limitation the rights 10 | # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # * copies of the Software, and to permit persons to whom the Software is 12 | # * furnished to do so, subject to the following conditions: 13 | # * 14 | # * The above copyright notice and this permission notice shall be included in all 15 | # * copies or substantial portions of the Software. 16 | # * 17 | # * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # * SOFTWARE. 24 | # */ 25 | # 26 | 27 | #Sat Jan 09 01:55:26 IST 2021 28 | distributionBase=GRADLE_USER_HOME 29 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip 30 | distributionPath=wrapper/dists 31 | zipStorePath=wrapper/dists 32 | zipStoreBase=GRADLE_USER_HOME 33 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /screenshots/articledetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/articledetails.png -------------------------------------------------------------------------------- /screenshots/articles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/articles.png -------------------------------------------------------------------------------- /screenshots/bookmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/bookmarks.png -------------------------------------------------------------------------------- /screenshots/darkmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/darkmode.png -------------------------------------------------------------------------------- /screenshots/lightmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/lightmode.png -------------------------------------------------------------------------------- /screenshots/nytimes_card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/nytimes_card.jpg -------------------------------------------------------------------------------- /screenshots/techbytes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheCodeMonks/NYTimes-App/f74e3820d68e29f9ac7feecb5fdf58725272480f/screenshots/techbytes.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "Topten" 2 | include ':app' 3 | --------------------------------------------------------------------------------