├── app ├── .gitignore ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── nature.jpg │ │ │ │ ├── splash_icon.png │ │ │ │ ├── divider.xml │ │ │ │ ├── favourite_icon.xml │ │ │ │ ├── splash_layer.xml │ │ │ │ ├── ic_category.xml │ │ │ │ ├── ic_insert_photo_black_24dp.xml │ │ │ │ ├── favourite_red.xml │ │ │ │ ├── ic_rounded_corner.xml │ │ │ │ ├── ic_favorite_black_24dp.xml │ │ │ │ ├── ic_search_black_24dp.xml │ │ │ │ ├── ic_favorite_border_black_24dp.xml │ │ │ │ ├── ic_share_black_24dp.xml │ │ │ │ ├── read_more_button.xml │ │ │ │ ├── ic_settings_black_24dp.xml │ │ │ │ ├── ic_language_black_24dp.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── layout │ │ │ │ ├── fragment_headline_detail.xml │ │ │ │ ├── activity_splash_screen.xml │ │ │ │ ├── fragment_favourite.xml │ │ │ │ ├── item_headline_top.xml │ │ │ │ ├── fragment_category.xml │ │ │ │ ├── fragment_search.xml │ │ │ │ ├── fragment_headline.xml │ │ │ │ ├── activity_settings.xml │ │ │ │ ├── activity_news_main.xml │ │ │ │ ├── item_settings.xml │ │ │ │ ├── item_headline.xml │ │ │ │ └── activity_news_detail.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── menu │ │ │ │ ├── search.xml │ │ │ │ ├── settings.xml │ │ │ │ └── navigation.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── animator │ │ │ │ └── scale.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── sridharjajoo │ │ │ │ └── newsapp │ │ │ │ ├── di │ │ │ │ ├── Injectable.java │ │ │ │ ├── modules │ │ │ │ │ ├── AppModule.java │ │ │ │ │ ├── AndroidModule.java │ │ │ │ │ ├── ApiModule.java │ │ │ │ │ ├── ModelModule.java │ │ │ │ │ ├── ViewModelKey.java │ │ │ │ │ ├── ViewModelModule.java │ │ │ │ │ ├── ActivityBuildersModule.java │ │ │ │ │ └── NetworkModule.java │ │ │ │ ├── AppComponent.java │ │ │ │ ├── NewsViewModelFactory.java │ │ │ │ └── AppInjector.java │ │ │ │ ├── data │ │ │ │ ├── SourceConverter.java │ │ │ │ ├── Settings │ │ │ │ │ ├── SettingsResponse.java │ │ │ │ │ └── SourceSettings.java │ │ │ │ ├── Headline │ │ │ │ │ ├── Source.java │ │ │ │ │ ├── HeadlineResponse.java │ │ │ │ │ ├── HeadlineService.java │ │ │ │ │ ├── NewsDao.java │ │ │ │ │ ├── Articles.java │ │ │ │ │ ├── HeadlineApi.java │ │ │ │ │ └── HeadlineServiceImpl.java │ │ │ │ ├── CustomSearch │ │ │ │ │ └── CustomSearchResponse.java │ │ │ │ ├── Favourite │ │ │ │ │ ├── Favourite.java │ │ │ │ │ └── FavoriteDao.java │ │ │ │ └── AppDatabase.java │ │ │ │ ├── SplashScreen.java │ │ │ │ ├── core │ │ │ │ ├── Headline │ │ │ │ │ ├── HeadlineViewHolderTop.java │ │ │ │ │ ├── HeadlineViewHolder.java │ │ │ │ │ ├── HeadlineViewModel.java │ │ │ │ │ ├── NewsDetailActivity.java │ │ │ │ │ ├── HeadlineFragment.java │ │ │ │ │ └── HeadlineAdapter.java │ │ │ │ ├── Search │ │ │ │ │ ├── SearchViewHolder.java │ │ │ │ │ ├── SearchViewModel.java │ │ │ │ │ ├── SearchFragment.java │ │ │ │ │ └── SearchAdapter.java │ │ │ │ ├── Category │ │ │ │ │ ├── CategoryViewModel.java │ │ │ │ │ └── CategoryFragment.java │ │ │ │ ├── Setttings │ │ │ │ │ └── SettingsActivity.java │ │ │ │ └── Favourite │ │ │ │ │ └── FavouriteFragment.java │ │ │ │ ├── NewsMainApplication.java │ │ │ │ ├── AlarmReceiver.java │ │ │ │ ├── Utils │ │ │ │ └── Utils.java │ │ │ │ └── NewsMainActivity.java │ │ └── AndroidManifest.xml │ ├── lombok.config │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── sridharjajoo │ │ │ └── newsapp │ │ │ ├── ExampleUnitTest.java │ │ │ └── core │ │ │ └── Headline │ │ │ └── HeadlineViewModelTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── sridharjajoo │ │ └── newsapp │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── Screenshots ├── img2.png ├── img3.png └── img_1.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── caches │ └── build_file_checksums.ser ├── vcs.xml ├── runConfigurations.xml ├── gradle.xml ├── misc.xml ├── codeStyles │ └── Project.xml └── assetWizardSettings.xml ├── .gitignore ├── .travis.yml ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /Screenshots/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/Screenshots/img2.png -------------------------------------------------------------------------------- /Screenshots/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/Screenshots/img3.png -------------------------------------------------------------------------------- /Screenshots/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/Screenshots/img_1.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/drawable/nature.jpg -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/drawable/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24sp 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/Injectable.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di; 2 | 3 | public interface Injectable { 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/lombok.config: -------------------------------------------------------------------------------- 1 | lombok.anyConstructor.suppressConstructorProperties = true 2 | lombok.addGeneratedAnnotation = false 3 | lombok.addLombokGeneratedAnnotation = true 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sridharjajoo/NewsApp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0288D1 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/SourceConverter.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data; 2 | 3 | import android.arch.persistence.room.TypeConverter; 4 | import android.arch.persistence.room.TypeConverters; 5 | 6 | public class SourceConverter { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 09 08:20:58 IST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #FFF 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_headline_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/AppModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import dagger.Module; 4 | 5 | @Module(includes = {ModelModule.class, NetworkModule.class, AndroidModule.class, ViewModelModule.class 6 | }) 7 | public class AppModule { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /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/drawable/favourite_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Settings/SettingsResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Settings; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class SettingsResponse { 9 | 10 | public String status; 11 | public List sources; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/search.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_layer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_category.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/menu/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/Source.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | 4 | import android.arch.persistence.room.ColumnInfo; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | public class Source { 10 | @ColumnInfo(name = "source_name") 11 | public String name; 12 | 13 | @ColumnInfo(name = "source_id") 14 | public String id; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Settings/SourceSettings.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Settings; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class SourceSettings { 7 | 8 | public String id; 9 | public String name; 10 | public String description; 11 | public String url; 12 | public String category; 13 | public String language; 14 | public String country; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_insert_photo_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/CustomSearch/CustomSearchResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.CustomSearch; 2 | 3 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | 7 | import java.util.List; 8 | 9 | import lombok.Data; 10 | 11 | @Data 12 | public class CustomSearchResponse { 13 | 14 | public List articles; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/sridharjajoo/newsapp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/favourite_red.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_rounded_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_favourite.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/AndroidModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import android.content.Context; 4 | 5 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | 12 | @Module 13 | public class AndroidModule { 14 | 15 | @Provides 16 | @Singleton 17 | Context providesContext() { 18 | return NewsMainApplication.context; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/ApiModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineApi; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | import retrofit2.Retrofit; 10 | 11 | @Module 12 | public class ApiModule { 13 | 14 | @Provides 15 | @Singleton 16 | public HeadlineApi providesHeadlineApi(Retrofit retrofit) { 17 | return retrofit.create(HeadlineApi.class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/SplashScreen.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | 7 | public class SplashScreen extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | Intent intent = new Intent(this, NewsMainActivity.class); 13 | startActivity(intent); 14 | finish(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/ModelModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 4 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineServiceImpl; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Binds; 9 | import dagger.Module; 10 | 11 | @Module 12 | public abstract class ModelModule { 13 | 14 | @Binds 15 | @Singleton 16 | abstract HeadlineService bindsHeadlineServiceModule(HeadlineServiceImpl headlineService); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/HeadlineResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 4 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 5 | 6 | import java.util.List; 7 | 8 | import lombok.AllArgsConstructor; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | @Data 14 | public class HeadlineResponse { 15 | 16 | public long totalResults; 17 | public String status; 18 | public List articles; 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | sudo: false 3 | jdk: 4 | - oraclejdk8 5 | android: 6 | components: 7 | - tools 8 | - android-27 9 | - build-tools-27.0.3 10 | - platform-tools 11 | before_cache: 12 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 13 | cache: 14 | directories: 15 | - "${TRAVIS_BUILD_DIR}/gradle/caches/" 16 | - "${TRAVIS_BUILD_DIR}/gradle/wrapper/dists/" 17 | - "$HOME/.gradle/caches/" 18 | - "$HOME/.gradle/wrapper/" 19 | install: 20 | - echo yes | ${ANDROID_HOME}/tools/bin/sdkmanager "platforms;android-27" 21 | script: 22 | - ./gradlew build -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/ViewModelKey.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | import dagger.MapKey; 12 | 13 | @Documented 14 | @Target({ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @MapKey 17 | @interface ViewModelKey { 18 | Class value(); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_border_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Favourite/Favourite.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Favourite; 2 | 3 | import android.arch.persistence.room.ColumnInfo; 4 | import android.arch.persistence.room.Embedded; 5 | import android.arch.persistence.room.Entity; 6 | import android.arch.persistence.room.PrimaryKey; 7 | 8 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 9 | import com.example.sridharjajoo.newsapp.data.Headline.Source; 10 | 11 | import lombok.Data; 12 | 13 | @Data 14 | @Entity(tableName = "favourites") 15 | public class Favourite { 16 | 17 | @PrimaryKey(autoGenerate = true) 18 | public int idFavourite; 19 | 20 | @Embedded(prefix = "article") 21 | public Articles articles; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/read_more_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/HeadlineService.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | import com.example.sridharjajoo.newsapp.data.CustomSearch.CustomSearchResponse; 4 | import com.example.sridharjajoo.newsapp.data.Settings.SettingsResponse; 5 | 6 | import io.reactivex.Observable; 7 | import io.reactivex.annotations.NonNull; 8 | import retrofit2.Response; 9 | 10 | public interface HeadlineService { 11 | 12 | @NonNull 13 | Observable getHeadline(String headlineRequest); 14 | 15 | @NonNull 16 | Observable getCustomSearchReponse(String query); 17 | 18 | @NonNull 19 | Observable getCustomHeadline(String category); 20 | 21 | @NonNull 22 | Observable getSources(); 23 | } 24 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Favourite/FavoriteDao.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Favourite; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Insert; 5 | import android.arch.persistence.room.Query; 6 | 7 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 8 | 9 | import java.util.List; 10 | 11 | @Dao 12 | public interface FavoriteDao { 13 | 14 | @Query("SELECT * FROM favourites") 15 | List getFavourites(); 16 | 17 | @Query("SELECT COUNT(*) FROM favourites WHERE articlenews_title = :title") 18 | int isPresent(String title); 19 | 20 | @Query("DELETE FROM favourites WHERE articlenews_title = :title") 21 | void unfavouriteNews(String title); 22 | 23 | @Insert 24 | void insertFavourite(Favourite... favourites); 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/NewsDao.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | import android.arch.persistence.room.Dao; 4 | import android.arch.persistence.room.Insert; 5 | import android.arch.persistence.room.Query; 6 | 7 | import java.util.List; 8 | 9 | @Dao 10 | public interface NewsDao { 11 | 12 | @Query("SELECT * FROM articles") 13 | List newsArticles(); 14 | 15 | @Query("SELECT * FROM articles WHERE id = :pos ") 16 | Articles getArticle(int pos); 17 | 18 | @Query("SELECT * FROM articles WHERE news_title = :currentTitle") 19 | Articles getArticleString(String currentTitle); 20 | 21 | @Query("SELECT COUNT(*) FROM articles") 22 | int getCount(); 23 | 24 | @Query("DELETE FROM articles") 25 | void deleteTable(); 26 | 27 | @Insert 28 | void insertAt(Articles... articles); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/AppComponent.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di; 2 | 3 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 4 | import com.example.sridharjajoo.newsapp.di.modules.ActivityBuildersModule; 5 | import com.example.sridharjajoo.newsapp.di.modules.AppModule; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Component; 10 | import dagger.android.AndroidInjectionModule; 11 | import dagger.android.AndroidInjector; 12 | import dagger.android.support.AndroidSupportInjectionModule; 13 | 14 | @Singleton 15 | @Component(modules = {AndroidInjectionModule.class, 16 | ActivityBuildersModule.class, 17 | AndroidSupportInjectionModule.class, 18 | AppModule.class}) 19 | 20 | public interface AppComponent extends AndroidInjector { 21 | 22 | void inject(NewsMainApplication newsMainApplication); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/sridharjajoo/newsapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.sridharjajoo.newsapp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineViewHolderTop.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.support.v7.widget.CardView; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.example.sridharjajoo.newsapp.R; 9 | import com.flaviofaria.kenburnsview.KenBurnsView; 10 | 11 | public class HeadlineViewHolderTop extends RecyclerView.ViewHolder { 12 | 13 | public TextView headlineTitle; 14 | public KenBurnsView kenBurnsView; 15 | public CardView cardView; 16 | 17 | public HeadlineViewHolderTop(View itemView) { 18 | super(itemView); 19 | headlineTitle = (TextView) itemView.findViewById(R.id.headline_title_top); 20 | kenBurnsView = (KenBurnsView) itemView.findViewById(R.id.kenburns_view); 21 | cardView = (CardView) itemView.findViewById(R.id.item_headline_top_card); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Taaza Khabar 3 | Headlines 4 | Search 5 | Favourite 6 | Published At: 7 | Source 8 | Read full story 9 | Google News (India) 10 | The Hindu 11 | The Times of India 12 | Choose the News Sources! 13 | Order By Time? 14 | No Search Results 15 | Category 16 | 17 | 18 | Hello blank fragment 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/NewsMainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.support.multidex.MultiDexApplication; 6 | 7 | import com.example.sridharjajoo.newsapp.di.AppInjector; 8 | 9 | import javax.inject.Inject; 10 | 11 | import dagger.android.AndroidInjector; 12 | import dagger.android.DispatchingAndroidInjector; 13 | import dagger.android.HasActivityInjector; 14 | 15 | public class NewsMainApplication extends MultiDexApplication implements HasActivityInjector { 16 | 17 | public static volatile Context context; 18 | 19 | @Inject 20 | DispatchingAndroidInjector dispatchingAndroidInjector; 21 | 22 | @Override 23 | public AndroidInjector activityInjector() { 24 | return dispatchingAndroidInjector; 25 | } 26 | 27 | @Override 28 | public void onCreate() { 29 | super.onCreate(); 30 | context = getApplicationContext(); 31 | AppInjector.init(this); 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data; 2 | 3 | import android.arch.persistence.room.Database; 4 | import android.arch.persistence.room.Room; 5 | import android.arch.persistence.room.RoomDatabase; 6 | import android.content.Context; 7 | 8 | import com.example.sridharjajoo.newsapp.data.Favourite.FavoriteDao; 9 | import com.example.sridharjajoo.newsapp.data.Favourite.Favourite; 10 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 11 | import com.example.sridharjajoo.newsapp.data.Headline.NewsDao; 12 | 13 | 14 | @Database(entities = {Articles.class, Favourite.class}, version = 10) 15 | public abstract class AppDatabase extends RoomDatabase { 16 | 17 | public abstract NewsDao newsDao(); 18 | 19 | public abstract FavoriteDao favoriteDao(); 20 | 21 | public static AppDatabase getAppDatabase(Context context) { 22 | return Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "news-database") 23 | .allowMainThreadQueries() 24 | .fallbackToDestructiveMigration() 25 | .build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/Articles.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | import android.arch.persistence.room.ColumnInfo; 4 | import android.arch.persistence.room.Embedded; 5 | import android.arch.persistence.room.Entity; 6 | import android.arch.persistence.room.PrimaryKey; 7 | 8 | import com.google.gson.annotations.Expose; 9 | 10 | import lombok.Builder; 11 | import lombok.Data; 12 | 13 | @Data 14 | @Entity(tableName = "articles") 15 | public class Articles { 16 | 17 | @PrimaryKey(autoGenerate = true) 18 | @ColumnInfo(name = "id") 19 | @Expose(serialize = false, deserialize = false) 20 | public long idArticle; 21 | 22 | @ColumnInfo(name = "news_description") 23 | public String description; 24 | 25 | @ColumnInfo(name = "news_content") 26 | public String content; 27 | 28 | @ColumnInfo(name = "news_image_url") 29 | public String urlToImage; 30 | 31 | @ColumnInfo(name = "news_title") 32 | public String title; 33 | 34 | @ColumnInfo(name = "news_date") 35 | public String publishedAt; 36 | 37 | @ColumnInfo(name = "news_url") 38 | public String url; 39 | 40 | @Embedded 41 | public Source source; 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_headline_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 18 | 19 | 20 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_language_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/ViewModelModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | 6 | import com.example.sridharjajoo.newsapp.core.Category.CategoryViewModel; 7 | import com.example.sridharjajoo.newsapp.core.Headline.HeadlineViewModel; 8 | import com.example.sridharjajoo.newsapp.core.Search.SearchViewModel; 9 | import com.example.sridharjajoo.newsapp.di.NewsViewModelFactory; 10 | 11 | import javax.inject.Singleton; 12 | 13 | import dagger.Binds; 14 | import dagger.Module; 15 | import dagger.multibindings.IntoMap; 16 | 17 | @Module 18 | public abstract class ViewModelModule { 19 | 20 | @Binds 21 | @IntoMap 22 | @ViewModelKey(SearchViewModel.class) 23 | public abstract ViewModel bindsSearchViewModel(SearchViewModel searchViewModel); 24 | 25 | @Binds 26 | @IntoMap 27 | @ViewModelKey(CategoryViewModel.class) 28 | public abstract ViewModel bindsCategoryViewModel(CategoryViewModel categoryViewModel); 29 | 30 | @Binds 31 | @IntoMap 32 | @ViewModelKey(HeadlineViewModel.class) 33 | public abstract ViewModel bindsHeadlineViewModel(HeadlineViewModel headlineViewModel); 34 | 35 | @Binds 36 | public abstract ViewModelProvider.Factory bindViewModelFactory(NewsViewModelFactory factory); 37 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 12 | 13 | 18 | 19 | 25 | 26 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Search/SearchViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Search; 2 | 3 | import android.support.v7.widget.CardView; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.widget.CheckBox; 7 | import android.widget.ImageButton; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.example.sridharjajoo.newsapp.R; 12 | 13 | public class SearchViewHolder extends RecyclerView.ViewHolder { 14 | 15 | public TextView description; 16 | public ImageView newsImage; 17 | public CardView cardView; 18 | public TextView newsTime; 19 | public TextView newsSource; 20 | public CheckBox favouriteCheckbox; 21 | public ImageButton shareButton; 22 | 23 | public SearchViewHolder(View itemView) { 24 | super(itemView); 25 | description = (TextView) itemView.findViewById(R.id.description); 26 | newsImage = (ImageView) itemView.findViewById(R.id.image_news); 27 | cardView = (CardView) itemView.findViewById(R.id.item_headline_card); 28 | newsTime = (TextView) itemView.findViewById(R.id.news_time); 29 | newsSource = (TextView) itemView.findViewById(R.id.news_source); 30 | favouriteCheckbox = (CheckBox) itemView.findViewById(R.id.favourite_icon); 31 | shareButton = (ImageButton) itemView.findViewById(R.id.share_button); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 12 | 13 | 18 | 19 | 25 | 26 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.support.v7.widget.CardView; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.widget.CheckBox; 7 | import android.widget.ImageButton; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.example.sridharjajoo.newsapp.R; 12 | 13 | public class HeadlineViewHolder extends RecyclerView.ViewHolder { 14 | 15 | public TextView description; 16 | public ImageView newsImage; 17 | public CardView cardView; 18 | public TextView newsTime; 19 | public TextView newsSource; 20 | public CheckBox favouriteCheckbox; 21 | public ImageButton shareButton; 22 | 23 | public HeadlineViewHolder(View itemView) { 24 | super(itemView); 25 | description = (TextView) itemView.findViewById(R.id.description); 26 | newsImage = (ImageView) itemView.findViewById(R.id.image_news); 27 | cardView = (CardView) itemView.findViewById(R.id.item_headline_card); 28 | newsTime = (TextView) itemView.findViewById(R.id.news_time); 29 | newsSource = (TextView) itemView.findViewById(R.id.news_source); 30 | favouriteCheckbox = (CheckBox) itemView.findViewById(R.id.favourite_icon); 31 | shareButton = (ImageButton) itemView.findViewById(R.id.share_button); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/ActivityBuildersModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import com.example.sridharjajoo.newsapp.NewsMainActivity; 4 | import com.example.sridharjajoo.newsapp.core.Category.CategoryFragment; 5 | import com.example.sridharjajoo.newsapp.core.Favourite.FavouriteFragment; 6 | import com.example.sridharjajoo.newsapp.core.Headline.HeadlineFragment; 7 | import com.example.sridharjajoo.newsapp.core.Headline.NewsDetailActivity; 8 | import com.example.sridharjajoo.newsapp.core.Search.SearchFragment; 9 | import com.example.sridharjajoo.newsapp.core.Setttings.SettingsActivity; 10 | 11 | import dagger.Module; 12 | import dagger.android.ContributesAndroidInjector; 13 | 14 | @Module 15 | public abstract class ActivityBuildersModule { 16 | 17 | @ContributesAndroidInjector 18 | abstract NewsMainActivity contributesNewsMainActivity(); 19 | 20 | @ContributesAndroidInjector 21 | abstract NewsDetailActivity contributesNewsDetailActivity(); 22 | 23 | @ContributesAndroidInjector 24 | abstract FavouriteFragment contributesFavouriteFragment(); 25 | 26 | @ContributesAndroidInjector 27 | abstract HeadlineFragment contributesHeadlineFragment(); 28 | 29 | @ContributesAndroidInjector 30 | abstract SearchFragment contributesSearchFragment(); 31 | 32 | @ContributesAndroidInjector 33 | abstract CategoryFragment contributesCategoryFragment(); 34 | 35 | @ContributesAndroidInjector 36 | abstract SettingsActivity contributesSettingsActivity(); 37 | } 38 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineViewModelTest.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.arch.core.executor.testing.InstantTaskExecutorRule; 4 | 5 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 6 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineResponse; 7 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 8 | 9 | import org.junit.Before; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.junit.rules.TestRule; 13 | import org.junit.runner.RunWith; 14 | import org.junit.runners.JUnit4; 15 | import org.mockito.Mock; 16 | import org.mockito.Mockito; 17 | import org.mockito.junit.MockitoJUnit; 18 | import org.mockito.junit.MockitoRule; 19 | 20 | import java.lang.reflect.Array; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | import io.reactivex.Observable; 25 | 26 | 27 | @RunWith(JUnit4.class) 28 | public class HeadlineViewModelTest { 29 | 30 | @Rule 31 | public MockitoRule mockitoRule = MockitoJUnit.rule(); 32 | @Rule 33 | public TestRule rule = new InstantTaskExecutorRule(); 34 | 35 | @Mock 36 | HeadlineService headlineService; 37 | @Mock 38 | android.arch.lifecycle.Observer progress; 39 | 40 | private HeadlineViewModel headlineViewModel; 41 | 42 | @Before 43 | public void setUp() { 44 | headlineViewModel = new HeadlineViewModel(headlineService); 45 | } 46 | 47 | @Test 48 | public void shoudlLoadArticlesSUccessfully() { 49 | //TODO 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/HeadlineApi.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | 4 | import com.example.sridharjajoo.newsapp.data.CustomSearch.CustomSearchResponse; 5 | import com.example.sridharjajoo.newsapp.data.Settings.SettingsResponse; 6 | 7 | import io.reactivex.Observable; 8 | import retrofit2.Response; 9 | import retrofit2.http.GET; 10 | import retrofit2.http.Query; 11 | 12 | public interface HeadlineApi { 13 | 14 | @GET("top-headlines") 15 | Observable getHeadlines(@Query(value = "country") String country, 16 | @Query(value = "apiKey") String apiKey); 17 | 18 | @GET("top-headlines") 19 | Observable getHeadlinesFiltered( 20 | @Query(value = "sources") String sources, 21 | @Query(value = "apiKey") String apiKey); 22 | 23 | 24 | @GET("top-headlines") 25 | Observable getHeadlinesFilteredByCategory( 26 | @Query(value = "category") String category, 27 | @Query(value = "apiKey") String apiKey, 28 | @Query(value = "country") String country); 29 | 30 | 31 | @GET("top-headlines") 32 | Observable getSearchResponse(@Query(value = "q") String query, 33 | @Query(value = "apiKey") String apiKey); 34 | @GET("sources") 35 | Observable getSourcesResponse(@Query(value = "apiKey") String apiKey, 36 | @Query(value = "country") String country); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/AlarmReceiver.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp; 2 | 3 | import android.app.NotificationManager; 4 | import android.app.PendingIntent; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.support.v4.app.NotificationCompat; 9 | 10 | public class AlarmReceiver extends BroadcastReceiver { 11 | 12 | @Override 13 | public void onReceive(Context context, Intent intent) { 14 | NotificationManager notificationManager = (NotificationManager) 15 | context.getSystemService(Context.NOTIFICATION_SERVICE); 16 | 17 | //Create the content intent for the notification, which launches this activity 18 | Intent contentIntent = new Intent(context, NewsMainActivity.class); 19 | PendingIntent contentPendingIntent = PendingIntent.getActivity 20 | (context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); 21 | 22 | //Build the notification 23 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "news") 24 | .setSmallIcon(R.mipmap.ic_launcher) 25 | .setContentTitle("News App") 26 | .setContentText("Catch the latest Headlines!") 27 | .setContentIntent(contentPendingIntent) 28 | .setAutoCancel(true) 29 | .setPriority(NotificationCompat.PRIORITY_HIGH) 30 | .setDefaults(NotificationCompat.DEFAULT_ALL); 31 | 32 | //Deliver the notification 33 | notificationManager.notify(0, builder.build()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Search/SearchViewModel.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Search; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.arch.lifecycle.LiveData; 5 | import android.arch.lifecycle.MutableLiveData; 6 | import android.arch.lifecycle.ViewModel; 7 | import android.util.Log; 8 | import android.view.View; 9 | 10 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 11 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 12 | 13 | import java.util.List; 14 | 15 | import javax.inject.Inject; 16 | 17 | public class SearchViewModel extends ViewModel { 18 | 19 | private final HeadlineService headlineService; 20 | private MutableLiveData progress = new MutableLiveData<>(); 21 | private MutableLiveData> articles = new MutableLiveData<>(); 22 | 23 | @Inject 24 | public SearchViewModel(HeadlineService headlineService) { 25 | this.headlineService = headlineService; 26 | } 27 | 28 | @SuppressLint("CheckResult") 29 | public LiveData> customSearch(String query) { 30 | headlineService.getCustomSearchReponse(query) 31 | .doOnSubscribe(disposable -> progress.setValue(View.VISIBLE)) 32 | .doFinally(() -> progress.setValue(View.GONE)) 33 | .subscribe(response -> { 34 | articles.setValue(response.articles); 35 | } 36 | , error -> Log.i("SearchViewModel.class", "customSearch: " + error)); 37 | return articles; 38 | } 39 | 40 | public LiveData getProgress() { 41 | return progress; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/NewsViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | import android.support.annotation.NonNull; 6 | 7 | import java.util.Map; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Provider; 11 | 12 | public class NewsViewModelFactory implements ViewModelProvider.Factory { 13 | 14 | private final Map, Provider> creators; 15 | 16 | @Inject 17 | public NewsViewModelFactory(Map, Provider> creators) { 18 | this.creators = creators; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | @SuppressWarnings({"unchecked", "PMD.AvoidThrowingRawExceptionTypes", "PMD.AvoidCatchingGenericException"}) 24 | public T create(@NonNull Class modelClass) { 25 | Provider creator = creators.get(modelClass); 26 | if (creator == null) { 27 | for (Map.Entry, Provider> entry : creators.entrySet()) { 28 | if (modelClass.isAssignableFrom(entry.getKey())) { 29 | creator = entry.getValue(); 30 | break; 31 | } 32 | } 33 | } 34 | if (creator == null) { 35 | throw new IllegalArgumentException("unknown model class " + modelClass); 36 | } 37 | try { 38 | return (T) creator.get(); 39 | } catch (Exception e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_headline.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 16 | 17 | 21 | 22 | 26 | 27 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Category/CategoryViewModel.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Category; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.arch.lifecycle.LiveData; 5 | import android.arch.lifecycle.MutableLiveData; 6 | import android.arch.lifecycle.ViewModel; 7 | import android.util.Log; 8 | import android.view.View; 9 | 10 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 11 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 12 | 13 | import java.util.List; 14 | 15 | import javax.inject.Inject; 16 | 17 | public class CategoryViewModel extends ViewModel { 18 | 19 | private final HeadlineService headlineService; 20 | private MutableLiveData progress = new MutableLiveData<>(); 21 | private MutableLiveData> articles = new MutableLiveData<>(); 22 | 23 | @Inject 24 | public CategoryViewModel(HeadlineService headlineService) { 25 | this.headlineService = headlineService; 26 | } 27 | 28 | @SuppressLint("CheckResult") 29 | public LiveData> headlineByCategory(String category) { 30 | headlineService.getCustomHeadline(category) 31 | .doOnSubscribe(disposable -> progress.setValue(View.VISIBLE)) 32 | .doFinally(() -> progress.setValue(View.GONE)) 33 | .subscribe(response -> { 34 | articles.setValue(response.articles); 35 | } 36 | , error -> Log.i("SearchViewModel.class", "HeadlineByCategory: " + error)); 37 | return articles; 38 | } 39 | 40 | public LiveData getProgress() { 41 | return progress; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 12 | 13 | 18 | 19 | 25 | 26 | 32 | 33 | 39 | 40 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineViewModel.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.arch.lifecycle.LiveData; 5 | import android.arch.lifecycle.MutableLiveData; 6 | import android.arch.lifecycle.ViewModel; 7 | import android.content.Context; 8 | import android.location.Address; 9 | import android.location.Geocoder; 10 | import android.location.Location; 11 | import android.location.LocationManager; 12 | import android.util.Log; 13 | 14 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 15 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 16 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 17 | 18 | import java.io.IOException; 19 | import java.util.List; 20 | 21 | import javax.inject.Inject; 22 | 23 | public class HeadlineViewModel extends ViewModel { 24 | 25 | private final HeadlineService headlineService; 26 | private List articlesList; 27 | private final MutableLiveData progress = new MutableLiveData<>(); 28 | private final MutableLiveData> articles = new MutableLiveData<>(); 29 | 30 | @Inject 31 | public HeadlineViewModel(HeadlineService headlineService) { 32 | this.headlineService = headlineService; 33 | } 34 | 35 | @SuppressLint("CheckResult") 36 | public LiveData> getNewsArticles() { 37 | headlineService.getHeadline("in") 38 | .doOnSubscribe(disposable -> progress.setValue(0)) 39 | .doFinally(() -> progress.setValue(8)) 40 | .subscribe(status -> { 41 | articles.setValue(status.articles); 42 | }, error -> { 43 | Log.i("HeadlineFragment.class", "onStart: " + error); 44 | }); 45 | return articles; 46 | } 47 | 48 | public LiveData getProgress() { 49 | return progress; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/assetWizardSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_news_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | 15 | 16 | 24 | 25 | 31 | 32 | 38 | 39 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/NewsDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.net.Uri; 5 | import android.support.customtabs.CustomTabsIntent; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.bumptech.glide.Glide; 15 | import com.example.sridharjajoo.newsapp.R; 16 | import com.example.sridharjajoo.newsapp.Utils.Utils; 17 | import com.example.sridharjajoo.newsapp.data.AppDatabase; 18 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 19 | import com.example.sridharjajoo.newsapp.databinding.ActivityNewsDetailBinding; 20 | 21 | 22 | public class NewsDetailActivity extends AppCompatActivity { 23 | 24 | private ActivityNewsDetailBinding binding; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | binding = DataBindingUtil.setContentView(this,R.layout.activity_news_detail); 30 | Bundle bundle = getIntent().getExtras(); 31 | assert bundle != null; 32 | if (bundle.getString("title") != null) { 33 | Articles currentArticle = AppDatabase.getAppDatabase(this).newsDao().getArticleString(bundle.getString("title")); 34 | binding.tvNewsDesc.setText(Utils.truncateExtra(currentArticle.content)); 35 | Glide.with(this).load(currentArticle.urlToImage).into(binding.ivNewsImage); 36 | binding.tvNewsSource.setText(currentArticle.source.name); 37 | binding.tvNewsTitle.setText(currentArticle.title); 38 | binding.tvTime.setText(Utils.formattedDate(currentArticle.publishedAt)); 39 | binding.btnReadFull.setOnClickListener(view -> { 40 | String url = currentArticle.url; 41 | CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); 42 | CustomTabsIntent customTabsIntent = builder.build(); 43 | customTabsIntent.launchUrl(this, Uri.parse(url)); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 29 | 30 | 40 | 41 | 52 | 53 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Setttings/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Setttings; 2 | 3 | import android.content.SharedPreferences; 4 | import android.databinding.DataBindingUtil; 5 | import android.preference.PreferenceManager; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.widget.CheckBox; 11 | 12 | import com.example.sridharjajoo.newsapp.R; 13 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 14 | import com.example.sridharjajoo.newsapp.data.Settings.SourceSettings; 15 | import com.example.sridharjajoo.newsapp.databinding.ActivityNewsMainBinding; 16 | import com.example.sridharjajoo.newsapp.databinding.ActivitySettingsBinding; 17 | 18 | import java.util.List; 19 | 20 | import javax.inject.Inject; 21 | 22 | import dagger.android.AndroidInjector; 23 | import dagger.android.DispatchingAndroidInjector; 24 | import dagger.android.support.HasSupportFragmentInjector; 25 | 26 | public class SettingsActivity extends AppCompatActivity implements HasSupportFragmentInjector { 27 | 28 | @Inject 29 | HeadlineService headlineService; 30 | 31 | @Inject 32 | DispatchingAndroidInjector dispatchingAndroidInjector; 33 | 34 | private List sourcesList; 35 | private ActivitySettingsBinding binding; 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | binding = DataBindingUtil.setContentView(this,R.layout.activity_settings); 40 | sharedPreference(); 41 | } 42 | 43 | private void sharedPreference() { 44 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 45 | SharedPreferences.Editor editor = sharedPreferences.edit(); 46 | binding.timesOfIndia.setChecked(sharedPreferences.getBoolean("time", false)); 47 | binding.theHindu.setChecked(sharedPreferences.getBoolean("hindu", false)); 48 | binding.googleNewsIndia.setChecked(sharedPreferences.getBoolean("google", false)); 49 | 50 | binding.timesOfIndia.setOnCheckedChangeListener((compoundButton, b) -> { 51 | editor.putBoolean("time", b); 52 | editor.commit(); 53 | }); 54 | 55 | binding.theHindu.setOnCheckedChangeListener((compoundButton, b) -> { 56 | editor.putBoolean("hindu", b); 57 | editor.commit(); 58 | }); 59 | 60 | binding.googleNewsIndia.setOnCheckedChangeListener((compoundButton, b) -> { 61 | editor.putBoolean("google", b); 62 | editor.commit(); 63 | }); 64 | } 65 | 66 | @Override 67 | public AndroidInjector supportFragmentInjector() { 68 | return dispatchingAndroidInjector; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Favourite/FavouriteFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Favourite; 2 | 3 | import android.arch.persistence.room.RoomDatabase; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v4.content.ContextCompat; 10 | import android.support.v7.widget.DividerItemDecoration; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | 18 | import com.example.sridharjajoo.newsapp.R; 19 | import com.example.sridharjajoo.newsapp.core.Headline.HeadlineAdapter; 20 | import com.example.sridharjajoo.newsapp.core.Search.SearchAdapter; 21 | import com.example.sridharjajoo.newsapp.data.AppDatabase; 22 | import com.example.sridharjajoo.newsapp.data.Favourite.Favourite; 23 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 24 | import com.example.sridharjajoo.newsapp.data.Headline.Source; 25 | import com.example.sridharjajoo.newsapp.databinding.FragmentFavouriteBinding; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Objects; 30 | import dagger.Binds; 31 | 32 | public class FavouriteFragment extends Fragment { 33 | 34 | private AppDatabase db; 35 | private SearchAdapter searchAdapter; 36 | private List listArticles = new ArrayList<>(); 37 | private FragmentFavouriteBinding binding; 38 | 39 | @Nullable 40 | @Override 41 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 42 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_favourite, container, false); 43 | getSavedNews(); 44 | return binding.getRoot(); 45 | } 46 | 47 | 48 | private void setRecyclerView(List articlesList) { 49 | binding.favouriteRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 50 | searchAdapter = new SearchAdapter(articlesList, getActivity(), db); 51 | binding.favouriteRecyclerView.setAdapter(searchAdapter); 52 | loadArticles(articlesList); 53 | DividerItemDecoration itemDecorator = new DividerItemDecoration(Objects.requireNonNull(getContext()), DividerItemDecoration.VERTICAL); 54 | itemDecorator.setDrawable(Objects.requireNonNull(ContextCompat.getDrawable(Objects.requireNonNull(getActivity()), R.drawable.divider))); 55 | binding.favouriteRecyclerView.addItemDecoration(itemDecorator); 56 | } 57 | 58 | private void loadArticles(List articlesList) { 59 | searchAdapter.setArticlesList(articlesList); 60 | } 61 | 62 | private void getSavedNews(){ 63 | db = AppDatabase.getAppDatabase(getActivity()); 64 | List favouriteList = db.favoriteDao().getFavourites(); 65 | for (int i = 0; i < favouriteList.size(); i++) { 66 | listArticles.add(favouriteList.get(i).articles); 67 | } 68 | setRecyclerView(listArticles); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gitter](https://img.shields.io/badge/chat-on%20gitter-ff006f.svg)](https://gitter.im/news_app_kwoc) 2 | 3 | # NewsApp 4 | An app that fetches latest news and headlines. It also allows the users to favourite their news. The user also has the option to choose the source of news. They can also search for any particular trending keyword. The following libraries were used in this project. 5 | 6 | - [RxJava 2](https://github.com/ReactiveX/RxJava) 7 | - [Dagger 2](https://github.com/google/dagger) 8 | - [Jackson](https://github.com/FasterXML/jackson) 9 | - [jsonapi-converter](https://github.com/jasminb/jsonapi-converter) 10 | - [ButterKnife](https://github.com/JakeWharton/butterknife) 11 | - [Lombok](https://projectlombok.org/) 12 | - [Glide](https://github.com/bumptech/glide) 13 | - [Retrofit](https://github.com/square/retrofit) + [OkHttp](https://github.com/square/okhttp) 14 | 15 | Also Room was used for adding offline support. 16 | 17 | # Steps of Installation 18 | 1. Fork the github repo. 19 | 2. Install the repo locally. 20 | 3. Generate your own API key from newsapi.org. 21 | 4. In your system level .gradle file create a file with the name gradle.properties and add the api key to it in the following manner : 22 | 23 | MY_API_KEY = "you_api_key" 24 | 25 | 5. Save the above file. 26 | 6. Run the app. 27 | 28 | # Project Conventions 29 | ## MVVM Architecture 30 | The project follows the MVVM design pattern.
31 | MVVM(Model–view–viewmodel) is one of the architectural patterns which enhances separation of concerns, it allows separating the user interface logic from the business (or the back-end) logic.

32 | MVVM has mainly the following layers: 33 | - Model
34 | Model represents the data and business logic of the app. One of the recommended implementation strategies of this layer, is to expose its data through observables to be decoupled completely from ViewModel or any other observer. 35 | - ViewModel
36 | ViewModel interacts with model and also prepares observable(s) that can be observed by a View. 37 | - View
38 | Finally, the view role in this pattern is to observe a ViewModel observable to get data in order to update UI elements accordingly. 39 | 40 | 41 | ## Roadmap 42 | 43 | - Adding different sections for Science, Technology and other types of news. 44 | - Improving the offline support. 45 | - Adding more filters 46 | - Adding unit testing 47 | 48 | ## App Screenshots 49 | ![](https://raw.githubusercontent.com/rob729/NewsApp/master/Screenshots/img_1.png) 50 | ![](https://raw.githubusercontent.com/rob729/NewsApp/master/Screenshots/img2.png) 51 | ![](https://raw.githubusercontent.com/rob729/NewsApp/master/Screenshots/img3.png) 52 | 53 | ## License 54 | 55 | >(c) 2019 Sridhar Jajoo 56 | 57 | >This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 58 | 59 | >This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 60 | 61 | >You should have received a copy of the GNU General Public License along with this app. If not, see . 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/AppInjector.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.support.v4.app.FragmentManager; 9 | 10 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 11 | 12 | import dagger.android.AndroidInjection; 13 | import dagger.android.support.AndroidSupportInjection; 14 | import dagger.android.support.HasSupportFragmentInjector; 15 | 16 | public final class AppInjector { 17 | 18 | //empty constructor 19 | public AppInjector() { 20 | 21 | } 22 | 23 | public static void init(NewsMainApplication newsMainApplication) { 24 | DaggerAppComponent 25 | .create() 26 | .inject(newsMainApplication); 27 | 28 | newsMainApplication 29 | .registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 30 | @Override 31 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 32 | handleActivity(activity); 33 | } 34 | 35 | @Override 36 | public void onActivityStarted(Activity activity) { 37 | // Do Nothing 38 | } 39 | 40 | @Override 41 | public void onActivityResumed(Activity activity) { 42 | // Do Nothing 43 | } 44 | 45 | @Override 46 | public void onActivityPaused(Activity activity) { 47 | // Do Nothing 48 | } 49 | 50 | @Override 51 | public void onActivityStopped(Activity activity) { 52 | // Do Nothing 53 | } 54 | 55 | @Override 56 | public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { 57 | // Do Nothing 58 | } 59 | 60 | @Override 61 | public void onActivityDestroyed(Activity activity) { 62 | // Do Nothing 63 | } 64 | }); 65 | } 66 | 67 | private static void handleActivity(Activity activity) { 68 | if (activity instanceof HasSupportFragmentInjector) { 69 | AndroidInjection.inject(activity); 70 | } 71 | if (activity instanceof FragmentActivity) { 72 | ((FragmentActivity) activity).getSupportFragmentManager() 73 | .registerFragmentLifecycleCallbacks( 74 | new FragmentManager.FragmentLifecycleCallbacks() { 75 | @Override 76 | public void onFragmentCreated(FragmentManager fm, Fragment f, 77 | Bundle savedInstanceState) { 78 | if (f instanceof Injectable) { 79 | AndroidSupportInjection.inject(f); 80 | } 81 | } 82 | }, true); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/res/animator/scale.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 15 | 20 | 21 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 51 | 56 | 61 | 62 | 68 | 74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/di/modules/NetworkModule.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.di.modules; 2 | 3 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 4 | import com.example.sridharjajoo.newsapp.Utils.Utils; 5 | import com.example.sridharjajoo.newsapp.data.CustomSearch.CustomSearchResponse; 6 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineResponse; 7 | import com.example.sridharjajoo.newsapp.data.Settings.SettingsResponse; 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | import com.fasterxml.jackson.databind.DeserializationFeature; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | import com.fasterxml.jackson.databind.SerializationFeature; 12 | import com.github.jasminb.jsonapi.retrofit.JSONAPIConverterFactory; 13 | 14 | import java.util.concurrent.TimeUnit; 15 | 16 | import javax.inject.Named; 17 | import javax.inject.Singleton; 18 | 19 | import dagger.Module; 20 | import dagger.Provides; 21 | import okhttp3.Cache; 22 | import okhttp3.CacheControl; 23 | import okhttp3.OkHttpClient; 24 | import okhttp3.Response; 25 | import retrofit2.CallAdapter; 26 | import retrofit2.Converter; 27 | import retrofit2.Retrofit; 28 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 29 | import retrofit2.converter.jackson.JacksonConverterFactory; 30 | 31 | @Module(includes = ApiModule.class) 32 | public class NetworkModule { 33 | 34 | @Provides 35 | @Singleton 36 | ObjectMapper providesObjectMapper() { 37 | return new ObjectMapper() 38 | .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) 39 | .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) 40 | .setSerializationInclusion(JsonInclude.Include.NON_ABSENT); 41 | } 42 | 43 | @Provides 44 | Class[] providesMappedClasses() { 45 | return new Class[]{HeadlineResponse.class, SettingsResponse.class, CustomSearchResponse.class}; 46 | } 47 | 48 | @Provides 49 | @Singleton 50 | @Named("jsonapi") 51 | Converter.Factory providesJsonApiFactory(ObjectMapper objectMapper, Class... mappedClasses) { 52 | return new JSONAPIConverterFactory(objectMapper, mappedClasses); 53 | } 54 | 55 | @Provides 56 | @Singleton 57 | @Named("jackson") 58 | Converter.Factory providesJacksonFactory(ObjectMapper objectMapper) { 59 | return JacksonConverterFactory.create(objectMapper); 60 | } 61 | 62 | @Provides 63 | @Singleton 64 | Cache providesCache() { 65 | int cacheSize = 10 * 1024 * 1024; // 10 MB 66 | return new Cache(NewsMainApplication.context.getCacheDir(), cacheSize); 67 | } 68 | 69 | @Provides 70 | @Singleton 71 | OkHttpClient providesOkHttpClient(Cache cache) { 72 | return new OkHttpClient.Builder() 73 | .cache(cache) 74 | .addNetworkInterceptor(chain -> { 75 | Response response = chain.proceed(chain.request()); 76 | CacheControl cacheControl; 77 | cacheControl = new CacheControl.Builder().maxAge(1, TimeUnit.MINUTES).build(); 78 | return response.newBuilder() 79 | .removeHeader("pragma") 80 | .removeHeader("Cache-Control") 81 | .header("Cache-Control", cacheControl.toString()) 82 | .build(); 83 | }) 84 | .build(); 85 | } 86 | 87 | @Provides 88 | @Singleton 89 | CallAdapter.Factory providesCallAdapterFactory() { 90 | return RxJava2CallAdapterFactory.create(); 91 | } 92 | 93 | @Provides 94 | @Singleton 95 | Retrofit providesRetrofitBuilder(CallAdapter.Factory callAdapterFactory, 96 | OkHttpClient client, @Named("jackson") Converter.Factory factory) { 97 | return new Retrofit.Builder() 98 | .client(client) 99 | .addConverterFactory(factory) 100 | .addCallAdapterFactory(callAdapterFactory) 101 | .baseUrl("https://newsapi.org/v2/") 102 | .build(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/data/Headline/HeadlineServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.data.Headline; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.preference.PreferenceManager; 7 | 8 | import com.example.sridharjajoo.newsapp.Utils.Utils; 9 | import com.example.sridharjajoo.newsapp.data.AppDatabase; 10 | import com.example.sridharjajoo.newsapp.data.CustomSearch.CustomSearchResponse; 11 | import com.example.sridharjajoo.newsapp.data.Settings.SettingsResponse; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import javax.inject.Inject; 17 | 18 | import io.reactivex.Observable; 19 | import io.reactivex.schedulers.Schedulers; 20 | import retrofit2.Response; 21 | 22 | import static io.reactivex.android.schedulers.AndroidSchedulers.mainThread; 23 | 24 | public class HeadlineServiceImpl implements HeadlineService { 25 | 26 | private final HeadlineApi headlineApi; 27 | private final Context context; 28 | private AppDatabase appDatabase; 29 | 30 | @Inject 31 | public HeadlineServiceImpl(HeadlineApi headlineApi, Context context) { 32 | this.headlineApi = headlineApi; 33 | this.context = context; 34 | } 35 | 36 | @Override 37 | public Observable getHeadline(String headlineRequest) { 38 | List list = sharedPreferenceHandle(); 39 | String csvString = Utils.getCSVString(list); 40 | if (csvString.isEmpty()) { 41 | return headlineApi.getHeadlines("in", Utils.apiKey) 42 | .doOnNext(this::syncSaveHeadlines) 43 | .subscribeOn(Schedulers.io()) 44 | .observeOn(mainThread()); 45 | } else { 46 | return headlineApi.getHeadlinesFiltered(csvString, Utils.apiKey) 47 | .doOnNext(this::syncSaveHeadlines) 48 | .subscribeOn(Schedulers.io()) 49 | .observeOn(mainThread()); 50 | } 51 | } 52 | 53 | @Override 54 | public Observable getCustomSearchReponse(String query) { 55 | return headlineApi.getSearchResponse(query, Utils.apiKey) 56 | .doOnNext(this::syncSaveCustom) 57 | .subscribeOn(Schedulers.io()) 58 | .observeOn(mainThread()); 59 | } 60 | 61 | @Override 62 | public Observable getCustomHeadline(String category) { 63 | return headlineApi.getHeadlinesFilteredByCategory(category, Utils.apiKey, "in") 64 | .doOnNext(this::syncSaveHeadlines) 65 | .subscribeOn(Schedulers.io()) 66 | .observeOn(mainThread()); 67 | } 68 | 69 | private void syncSaveCustom(CustomSearchResponse response) { 70 | appDatabase = AppDatabase.getAppDatabase(context); 71 | List articles = response.articles; 72 | for (Articles newsArticle : articles) { 73 | appDatabase.newsDao().insertAt(newsArticle); 74 | } 75 | } 76 | 77 | @Override 78 | public Observable getSources() { 79 | return headlineApi.getSourcesResponse(Utils.apiKey, "in") 80 | .subscribeOn(Schedulers.io()) 81 | .observeOn(mainThread()); 82 | } 83 | 84 | //store the articles list to DB 85 | public void syncSaveHeadlines(HeadlineResponse headlineResponse) { 86 | appDatabase = AppDatabase.getAppDatabase(context); 87 | List articles = headlineResponse.articles; 88 | for (Articles newsArticle : articles) { 89 | appDatabase.newsDao().insertAt(newsArticle); 90 | } 91 | } 92 | 93 | private List sharedPreferenceHandle() { 94 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 95 | boolean times = sharedPreferences.getBoolean("time", false); 96 | boolean hindu = sharedPreferences.getBoolean("hindu", false); 97 | boolean google = sharedPreferences.getBoolean("google", false); 98 | String query = times ? "the-times-of-india" : ""; 99 | String query2 = hindu ? "the-hindu" : ""; 100 | String query3 = google ? "google-news-in" : ""; 101 | List list = new ArrayList<>(); 102 | list.add(query); 103 | list.add(query2); 104 | list.add(query3); 105 | return list; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Category/CategoryFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Category; 2 | 3 | 4 | import android.arch.lifecycle.ViewModelProvider; 5 | import android.arch.lifecycle.ViewModelProviders; 6 | import android.databinding.DataBindingUtil; 7 | import android.os.Bundle; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v4.content.ContextCompat; 10 | import android.support.v7.widget.DividerItemDecoration; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.util.Log; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.Toast; 17 | 18 | import com.example.sridharjajoo.newsapp.R; 19 | import com.example.sridharjajoo.newsapp.Utils.Utils; 20 | import com.example.sridharjajoo.newsapp.core.Search.SearchAdapter; 21 | import com.example.sridharjajoo.newsapp.core.Search.SearchViewModel; 22 | import com.example.sridharjajoo.newsapp.data.AppDatabase; 23 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 24 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 25 | import com.example.sridharjajoo.newsapp.databinding.FragmentCategoryBinding; 26 | import com.example.sridharjajoo.newsapp.di.Injectable; 27 | 28 | import java.util.List; 29 | 30 | import javax.inject.Inject; 31 | 32 | 33 | public class CategoryFragment extends Fragment implements Injectable { 34 | 35 | @Inject 36 | HeadlineService headlineService; 37 | 38 | @Inject 39 | ViewModelProvider.Factory viewModelFactory; 40 | 41 | private FragmentCategoryBinding binding; 42 | private CategoryViewModel categoryViewModel; 43 | private SearchAdapter searchAdapter; 44 | private AppDatabase db; 45 | private String category; 46 | private int selectedCategory=0; 47 | 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 50 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_category, container, false); 51 | db = AppDatabase.getAppDatabase(getActivity()); 52 | categoryViewModel = ViewModelProviders.of(this, viewModelFactory).get(CategoryViewModel.class); 53 | return binding.getRoot(); 54 | } 55 | 56 | @Override 57 | public void onStart() { 58 | super.onStart(); 59 | if (!Utils.hasNetwork()) { 60 | Toast.makeText(getActivity(), "Network not available!", Toast.LENGTH_SHORT).show(); 61 | } 62 | categoryViewModel.getProgress().observe(this, binding.progressBar::setVisibility); 63 | selectedCategory = getArguments().getInt("search"); 64 | category = findCategory(selectedCategory); 65 | showHeadlineByCategory(category); 66 | } 67 | 68 | private void setRecyclerView(List articlesList) { 69 | binding.categoryRecycler.setLayoutManager(new LinearLayoutManager(getContext())); 70 | searchAdapter = new SearchAdapter(articlesList, getActivity(), db); 71 | binding.categoryRecycler.setAdapter(searchAdapter); 72 | loadArticles(articlesList); 73 | DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL); 74 | itemDecorator.setDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.divider)); 75 | binding.categoryRecycler.addItemDecoration(itemDecorator); 76 | } 77 | 78 | private void loadArticles(List articlesList) { 79 | searchAdapter.setArticlesList(articlesList); 80 | } 81 | 82 | private void showHeadlineByCategory(String category){ 83 | binding.noSearch.setVisibility(View.INVISIBLE); 84 | categoryViewModel.headlineByCategory(category).observe(this, this::showResult); 85 | } 86 | 87 | 88 | private String findCategory(int selectedCategory){ 89 | switch(selectedCategory){ 90 | case 0 : return "science"; 91 | case 1 : return "sports"; 92 | case 2 : return "entertainment"; 93 | default: return "science"; 94 | } 95 | } 96 | 97 | private void showResult(List articles) { 98 | if (articles == null || (articles.size()==0)) { 99 | searchAdapter.clearRecyclerView(); 100 | binding.noSearch.setVisibility(View.VISIBLE); 101 | } else{ 102 | setRecyclerView(articles); 103 | binding.noSearch.setVisibility(View.INVISIBLE); 104 | } 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.example.sridharjajoo.news_app" 7 | minSdkVersion 21 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | vectorDrawables.useSupportLibrary = true 13 | multiDexEnabled true 14 | } 15 | buildTypes { 16 | debug { 17 | buildConfigField 'String', "API_KEY", MY_API_KEY 18 | } 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | buildConfigField 'String', "API_KEY", MY_API_KEY 23 | } 24 | } 25 | compileOptions { 26 | targetCompatibility 1.8 27 | sourceCompatibility 1.8 28 | } 29 | dataBinding { 30 | enabled true 31 | } 32 | } 33 | 34 | dependencies { 35 | def room_version = "1.1.1" 36 | 37 | implementation "android.arch.persistence.room:runtime:$room_version" 38 | annotationProcessor "android.arch.persistence.room:compiler:$room_version" 39 | // use kapt for Kotlin 40 | 41 | // optional - RxJava support for Room 42 | implementation "android.arch.persistence.room:rxjava2:$room_version" 43 | 44 | implementation fileTree(dir: 'libs', include: ['*.jar']) 45 | implementation 'com.android.support:appcompat-v7:27.1.1' 46 | implementation 'com.android.support:design:27.1.1' 47 | implementation 'com.android.support:multidex:1.0.3' 48 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 51 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 52 | implementation 'com.android.support:cardview-v7:27.1.1' 53 | implementation 'com.android.support:support-v4:27.1.1' 54 | 55 | // Dagger 56 | implementation "com.google.dagger:dagger:2.14.1" 57 | implementation "com.google.dagger:dagger-android:2.14.1" 58 | implementation "com.google.dagger:dagger-android-support:2.14.1" 59 | annotationProcessor "com.google.dagger:dagger-compiler:2.14.1" 60 | annotationProcessor "com.google.dagger:dagger-android-processor:2.14.1" 61 | 62 | // Retrofit 63 | implementation "com.squareup.okhttp3:okhttp:3.10.0" 64 | implementation "com.squareup.retrofit2:retrofit:2.3.0" 65 | implementation "com.squareup.retrofit2:converter-jackson:2.3.0" 66 | implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0" 67 | implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' 68 | implementation 'com.github.jasminb:jsonapi-converter:0.9' 69 | implementation "com.squareup.retrofit2:adapter-rxjava:2.3.0" 70 | implementation "com.squareup.retrofit2:converter-gson:2.3.0" 71 | 72 | // Lombok 73 | compileOnly "org.projectlombok:lombok:1.16.18" 74 | testCompileOnly "org.projectlombok:lombok:1.16.18" 75 | annotationProcessor "org.projectlombok:lombok:1.16.18" 76 | testAnnotationProcessor "org.projectlombok:lombok:1.16.18" 77 | 78 | // Butterknife 79 | implementation "com.jakewharton:butterknife:8.8.1" 80 | annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1" 81 | 82 | // RxJava 83 | implementation "io.reactivex.rxjava2:rxandroid:2.0.2" 84 | implementation "io.reactivex.rxjava2:rxjava:2.1.10" 85 | implementation 'com.f2prateek.rx.preferences2:rx-preferences:2.0.0' 86 | 87 | //glide 88 | implementation 'com.github.bumptech.glide:glide:4.7.1' 89 | annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' 90 | implementation 'jp.wasabeef:glide-transformations:3.3.0' 91 | 92 | implementation 'com.flaviofaria:kenburnsview:1.0.7' 93 | implementation 'com.android.support:customtabs:27.1.1' 94 | 95 | implementation "android.arch.lifecycle:runtime:1.1.1" 96 | implementation "android.arch.lifecycle:extensions:1.1.1" 97 | implementation "android.arch.lifecycle:common-java8:1.1.1" 98 | annotationProcessor "android.arch.lifecycle:compiler:1.1.1" 99 | 100 | testImplementation 'junit:junit:4.12' 101 | testImplementation 'org.mockito:mockito-inline:2.23.0' 102 | testImplementation("android.arch.core:core-testing:1.1.1", { 103 | exclude group: 'com.android.support', module: 'support-compat' 104 | exclude group: 'com.android.support', module: 'support-annotations' 105 | exclude group: 'com.android.support', module: 'support-core-utils' 106 | }) 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.core.Headline; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | import android.arch.lifecycle.ViewModelProviders; 6 | import android.databinding.DataBindingUtil; 7 | import android.location.LocationManager; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.app.Fragment; 12 | import android.support.v4.content.ContextCompat; 13 | import android.support.v4.widget.SwipeRefreshLayout; 14 | import android.support.v7.widget.DividerItemDecoration; 15 | import android.support.v7.widget.LinearLayoutManager; 16 | import android.support.v7.widget.RecyclerView; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.widget.ProgressBar; 21 | import android.widget.SearchView; 22 | import android.widget.Toast; 23 | 24 | import com.example.sridharjajoo.newsapp.R; 25 | import com.example.sridharjajoo.newsapp.Utils.Utils; 26 | import com.example.sridharjajoo.newsapp.data.AppDatabase; 27 | import com.example.sridharjajoo.newsapp.data.Headline.Articles; 28 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService; 29 | import com.example.sridharjajoo.newsapp.databinding.FragmentHeadlineBinding; 30 | import com.example.sridharjajoo.newsapp.di.Injectable; 31 | 32 | import java.util.List; 33 | import java.util.Objects; 34 | 35 | import javax.inject.Inject; 36 | 37 | public class HeadlineFragment extends Fragment implements Injectable { 38 | 39 | @Inject 40 | ViewModelProvider.Factory viewModelFactory; 41 | 42 | @Inject 43 | HeadlineService headlineService; 44 | 45 | private List articlesList; 46 | private HeadlineAdapter headlineAdapter; 47 | private AppDatabase db; 48 | protected LocationManager locationManager; 49 | private HeadlineViewModel headlineViewModel; 50 | private FragmentHeadlineBinding binding; 51 | 52 | @Nullable 53 | @Override 54 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 55 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_headline, container, false); 56 | db = AppDatabase.getAppDatabase(Objects.requireNonNull(getActivity())); 57 | headlineViewModel = ViewModelProviders.of(this, viewModelFactory).get(HeadlineViewModel.class); 58 | return binding.getRoot(); 59 | } 60 | 61 | @SuppressLint("CheckResult") 62 | @Override 63 | public void onStart() { 64 | super.onStart(); 65 | if (!Utils.hasNetwork()) { 66 | Toast.makeText(getActivity(), "Network not available!", Toast.LENGTH_SHORT).show(); 67 | } 68 | headlineViewModel.getProgress().observe(this, binding.progressBar::setVisibility); 69 | loadNewsArticles(); 70 | 71 | binding.pullToRefresh.setOnRefreshListener(() -> { 72 | if (!Utils.hasNetwork()) { 73 | Toast.makeText(getActivity(), "Network not available!", Toast.LENGTH_SHORT).show(); 74 | binding.pullToRefresh.setRefreshing(false); 75 | return; 76 | } 77 | headlineViewModel.getProgress().observe(getActivity(), binding.progressBar::setVisibility); 78 | loadNewsArticles(); 79 | binding.pullToRefresh.setRefreshing(false); 80 | }); 81 | } 82 | 83 | private void loadNewsArticles() { 84 | headlineViewModel.getNewsArticles().observe(this, list -> { 85 | articlesList = list; 86 | if (articlesList != null) { 87 | setRecyclerView(articlesList); 88 | } 89 | }); 90 | 91 | } 92 | 93 | private void setRecyclerView(List articlesList) { 94 | binding.recyclerHeadline.setLayoutManager(new LinearLayoutManager(getContext())); 95 | headlineAdapter = new HeadlineAdapter(articlesList, getActivity(), db); 96 | binding.recyclerHeadline.setAdapter(headlineAdapter); 97 | loadArticles(articlesList); 98 | DividerItemDecoration itemDecorator = new DividerItemDecoration(Objects.requireNonNull(getContext()), DividerItemDecoration.VERTICAL); 99 | itemDecorator.setDrawable(Objects.requireNonNull(ContextCompat.getDrawable(Objects.requireNonNull(getActivity()), R.drawable.divider))); 100 | binding.recyclerHeadline.addItemDecoration(itemDecorator); 101 | } 102 | 103 | private void loadArticles(List articlesList) { 104 | headlineAdapter.setArticlesList(articlesList); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/sridharjajoo/newsapp/Utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.example.sridharjajoo.newsapp.Utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.location.Address; 8 | import android.location.Geocoder; 9 | import android.location.Location; 10 | import android.location.LocationManager; 11 | import android.net.ConnectivityManager; 12 | import android.net.NetworkInfo; 13 | import android.preference.PreferenceManager; 14 | import android.view.View; 15 | import android.view.inputmethod.InputMethodManager; 16 | 17 | import com.example.sridharjajoo.newsapp.BuildConfig; 18 | import com.example.sridharjajoo.newsapp.NewsMainApplication; 19 | 20 | import java.io.IOException; 21 | import java.text.DateFormat; 22 | import java.text.ParseException; 23 | import java.text.SimpleDateFormat; 24 | import java.util.ArrayList; 25 | import java.util.Date; 26 | import java.util.List; 27 | 28 | import javax.inject.Inject; 29 | 30 | public class Utils { 31 | 32 | public static String apiKey = BuildConfig.API_KEY; 33 | 34 | public static void hideKeyboard(View view) { 35 | if (view != null) { 36 | InputMethodManager manager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 37 | view.clearFocus(); 38 | if (manager != null) { 39 | manager.hideSoftInputFromWindow(view.getWindowToken(), 0); 40 | } 41 | } 42 | } 43 | 44 | public static void showKeyboard(View view){ 45 | if(view != null){ 46 | InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 47 | if (imm != null) { 48 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,0); 49 | } 50 | } 51 | } 52 | 53 | public static void shareNews(Context context, String url){ 54 | Intent intent = new Intent(Intent.ACTION_SEND); 55 | intent.setType("text/plain"); 56 | intent.putExtra(Intent.EXTRA_TEXT, url); 57 | context.startActivity(Intent.createChooser(intent, "Share News")); 58 | } 59 | 60 | public static String formattedDate(String dateUTC) { 61 | DateFormat targetFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm"); 62 | Date sd = null; 63 | try { 64 | sd = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(dateUTC); 65 | } catch (ParseException e) { 66 | e.printStackTrace(); 67 | } 68 | 69 | if(sd != null) 70 | return targetFormat.format(sd); 71 | else 72 | return " "; 73 | } 74 | 75 | public static String getCSVString(List sourcesList) { 76 | StringBuilder csvBuilder = new StringBuilder(); 77 | for (String newsPaper : sourcesList) { 78 | if (!newsPaper.isEmpty()) { 79 | csvBuilder.append(newsPaper); 80 | csvBuilder.append(","); 81 | } 82 | } 83 | return csvBuilder.toString(); 84 | } 85 | 86 | public static String truncateExtra(String content) { 87 | if (content == null) 88 | return ""; 89 | return content.replaceAll("(\\[\\+\\d+ chars])", ""); 90 | } 91 | 92 | public static boolean hasNetwork() { 93 | ConnectivityManager connectivityManager 94 | = (ConnectivityManager) NewsMainApplication.context.getSystemService(Context.CONNECTIVITY_SERVICE); 95 | NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); 96 | return activeNetworkInfo != null && activeNetworkInfo.isConnected(); 97 | } 98 | 99 | public String getCountry() { 100 | String country_name = null; 101 | LocationManager lm = (LocationManager)(NewsMainApplication.context).getSystemService(Context.LOCATION_SERVICE); 102 | Geocoder geocoder = new Geocoder(NewsMainApplication.context); 103 | for(String provider: lm.getAllProviders()) { 104 | @SuppressWarnings("ResourceType") Location location = lm.getLastKnownLocation(provider); 105 | if(location!=null) { 106 | try { 107 | List
addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); 108 | if(addresses != null && addresses.size() > 0) { 109 | country_name = addresses.get(0).getCountryName(); 110 | break; 111 | } 112 | } catch (IOException e) { 113 | e.printStackTrace(); 114 | } 115 | } 116 | } 117 | return country_name; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_headline.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 27 | 28 | 32 | 33 | 47 | 48 | 56 | 57 | 58 | 59 | 65 | 66 | 73 | 74 | 81 | 82 | 87 | 88 | 96 | 97 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_news_detail.xml: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 | 20 | 21 | 29 | 37 | 38 | 48 | 49 | 60 | 61 | 72 | 84 | 85 | 95 | 96 |