├── 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 extends ViewModel> 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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
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 |
--------------------------------------------------------------------------------
/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 extends ViewModel> 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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
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 |
5 |
45 |
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 | [](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 | 
50 | 
51 | 
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 |
108 |
109 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/sridharjajoo/newsapp/core/Search/SearchFragment.java:
--------------------------------------------------------------------------------
1 | package com.example.sridharjajoo.newsapp.core.Search;
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.graphics.Color;
8 | import android.graphics.drawable.Drawable;
9 | import android.os.Bundle;
10 | import android.support.annotation.NonNull;
11 | import android.support.annotation.Nullable;
12 | import android.support.v4.app.Fragment;
13 | import android.support.v4.content.ContextCompat;
14 | import android.support.v7.widget.DividerItemDecoration;
15 | import android.support.v7.widget.LinearLayoutManager;
16 | import android.view.LayoutInflater;
17 | import android.view.Menu;
18 | import android.view.MenuInflater;
19 | import android.view.MenuItem;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.widget.SearchView;
23 | import android.widget.Toast;
24 |
25 | import com.example.sridharjajoo.newsapp.R;
26 | import com.example.sridharjajoo.newsapp.Utils.Utils;
27 | import com.example.sridharjajoo.newsapp.data.AppDatabase;
28 | import com.example.sridharjajoo.newsapp.data.Headline.Articles;
29 | import com.example.sridharjajoo.newsapp.data.Headline.HeadlineService;
30 | import com.example.sridharjajoo.newsapp.databinding.FragmentSearchBinding;
31 | import com.example.sridharjajoo.newsapp.di.Injectable;
32 |
33 | import java.util.List;
34 |
35 | import javax.inject.Inject;
36 |
37 | public class SearchFragment extends Fragment implements Injectable {
38 |
39 | @Inject
40 | HeadlineService headlineService;
41 |
42 | @Inject
43 | ViewModelProvider.Factory viewModelFactory;
44 |
45 | private SearchAdapter searchAdapter;
46 | private AppDatabase db;
47 | private SearchViewModel searchViewModel;
48 | private FragmentSearchBinding binding;
49 |
50 | @Nullable
51 | @Override
52 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
53 | binding = DataBindingUtil.inflate(inflater, R.layout.fragment_search, container, false);
54 | setHasOptionsMenu(true);
55 | db = AppDatabase.getAppDatabase(getActivity());
56 | searchViewModel = ViewModelProviders.of(this, viewModelFactory).get(SearchViewModel.class);
57 | return binding.getRoot();
58 | }
59 |
60 | private void setRecyclerView(List articlesList) {
61 | binding.cutomSearchRecycler.setLayoutManager(new LinearLayoutManager(getContext()));
62 | searchAdapter = new SearchAdapter(articlesList, getActivity(), db);
63 | binding.cutomSearchRecycler.setAdapter(searchAdapter);
64 | loadArticles(articlesList);
65 | DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
66 | itemDecorator.setDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.divider));
67 | binding.cutomSearchRecycler.addItemDecoration(itemDecorator);
68 | }
69 |
70 | private void loadArticles(List articlesList) {
71 | searchAdapter.setArticlesList(articlesList);
72 | }
73 |
74 | @Override
75 | public void onStart() {
76 | super.onStart();
77 | if (!Utils.hasNetwork()) {
78 | Toast.makeText(getActivity(), "Network not available!", Toast.LENGTH_SHORT).show();
79 | }
80 | searchViewModel.getProgress().observe(this, binding.progressBar::setVisibility);
81 | }
82 |
83 |
84 | @Override
85 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
86 | super.onCreateOptionsMenu(menu, inflater);
87 | inflater.inflate(R.menu.search, menu);
88 | MenuItem item = menu.findItem(R.id.action_search);
89 | Drawable drawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_search_black_24dp);
90 | drawable.setTint(ContextCompat.getColor(getActivity(), R.color.white));
91 | item.setIcon(drawable);
92 |
93 | item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
94 | @Override
95 | public boolean onMenuItemActionExpand(MenuItem item) {
96 | Utils.showKeyboard(getView());
97 | return true;
98 | }
99 |
100 | @Override
101 | public boolean onMenuItemActionCollapse(MenuItem item) {
102 | return true;
103 | }
104 | });
105 |
106 | SearchView searchView = new SearchView(getActivity());
107 | searchView.setBackgroundColor(Color.WHITE);
108 | searchView.setIconified(false);
109 | searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
110 | @Override
111 | public boolean onQueryTextSubmit(String query) {
112 | if (Utils.hasNetwork()) {
113 | handleSearch(query);
114 | Utils.hideKeyboard(getView());
115 | } else {
116 | Toast.makeText(getActivity(), "Network not available!", Toast.LENGTH_SHORT).show();
117 | }
118 | return true;
119 | }
120 | @Override
121 | public boolean onQueryTextChange(String newText) {
122 | return false;
123 | }
124 | });
125 | item.setActionView(searchView);
126 | }
127 |
128 | private void handleSearch(String query) {
129 | binding.noSearch.setVisibility(View.INVISIBLE);
130 | searchViewModel.customSearch(query).observe(this, this::showResult);
131 | }
132 |
133 | private void showResult(List articles) {
134 | if (articles == null || (articles.size()==0)) {
135 | searchAdapter.clearRecyclerView();
136 | binding.noSearch.setVisibility(View.VISIBLE);
137 | }else {
138 | setRecyclerView(articles);
139 | binding.noSearch.setVisibility(View.INVISIBLE);
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/sridharjajoo/newsapp/core/Search/SearchAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.sridharjajoo.newsapp.core.Search;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.graphics.Bitmap;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.FragmentActivity;
10 | import android.support.v4.content.ContextCompat;
11 | import android.support.v7.util.DiffUtil;
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.bumptech.glide.Glide;
19 | import com.bumptech.glide.load.DataSource;
20 | import com.bumptech.glide.load.engine.GlideException;
21 | import com.bumptech.glide.request.RequestListener;
22 | import com.bumptech.glide.request.RequestOptions;
23 | import com.bumptech.glide.request.target.Target;
24 | import com.example.sridharjajoo.newsapp.R;
25 | import com.example.sridharjajoo.newsapp.Utils.Utils;
26 | import com.example.sridharjajoo.newsapp.core.Headline.NewsDetailActivity;
27 | import com.example.sridharjajoo.newsapp.data.AppDatabase;
28 | import com.example.sridharjajoo.newsapp.data.Favourite.Favourite;
29 | import com.example.sridharjajoo.newsapp.data.Headline.Articles;
30 |
31 | import java.util.List;
32 |
33 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
34 |
35 | public class SearchAdapter extends RecyclerView.Adapter {
36 |
37 | private final List articlesList;
38 | private final Context context;
39 | private List articles;
40 | private final AppDatabase db;
41 |
42 | public SearchAdapter(List articlesList, FragmentActivity activity, AppDatabase db) {
43 | this.articlesList = articlesList;
44 | this.context = activity;
45 | this.db = db;
46 | }
47 |
48 | @NonNull
49 | @Override
50 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
51 | LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
52 | View view = layoutInflater.inflate(R.layout.item_headline, parent, false);
53 | return new SearchViewHolder(view);
54 | }
55 |
56 | @Override
57 | public int getItemViewType(int position) {
58 | return position;
59 | }
60 |
61 | @Override
62 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
63 | Articles currentItem = articlesList.get(position);
64 | SearchViewHolder viewHolder = (SearchViewHolder) holder;
65 | configureViewHolder(viewHolder, currentItem);
66 | }
67 |
68 | private void configureViewHolder(SearchViewHolder viewHolder, Articles currentItem) {
69 | viewHolder.newsImage.setOnClickListener(view -> {
70 | Intent intent = new Intent(context, NewsDetailActivity.class);
71 | intent.putExtra("pos", viewHolder.getAdapterPosition() + 1);
72 | intent.putExtra("title", currentItem.title);
73 | context.startActivity(intent);
74 | });
75 |
76 | Favourite favourite = new Favourite();
77 | favourite.articles = currentItem;
78 | int count = db.favoriteDao().isPresent(currentItem.title);
79 | viewHolder.favouriteCheckbox.setChecked(count == 1);
80 | viewHolder.favouriteCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
81 | if (isChecked)
82 | db.favoriteDao().insertFavourite(favourite);
83 | else
84 | db.favoriteDao().unfavouriteNews(currentItem.title);
85 | });
86 | viewHolder.description.setText(currentItem.title);
87 | viewHolder.shareButton.setOnClickListener(view -> {
88 | Utils.shareNews(context, currentItem.url);
89 | });
90 | viewHolder.newsSource.setText(currentItem.source.name);
91 | if (currentItem.publishedAt != null) {
92 | Log.e("TAG","error: "+currentItem.publishedAt);
93 | viewHolder.newsTime.setText(Utils.formattedDate(currentItem.publishedAt));
94 | }
95 |
96 | viewHolder.cardView.setElevation(0);
97 |
98 | Glide.with(context)
99 | .asBitmap()
100 | .load(currentItem.urlToImage)
101 | .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(50, 10, RoundedCornersTransformation.CornerType.ALL)))
102 | .listener(new RequestListener() {
103 | @Override
104 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
105 | viewHolder.newsImage.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_insert_photo_black_24dp));
106 | return false;
107 | }
108 |
109 | @Override
110 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
111 | viewHolder.newsImage.setImageBitmap(resource);
112 | return true;
113 | }
114 | }).submit();
115 | }
116 |
117 | public void setArticlesList(final List newList) {
118 | if (articles == null) {
119 | articles = newList;
120 | notifyItemRangeInserted(0, newList.size());
121 | } else {
122 | DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
123 | @Override
124 | public int getOldListSize() {
125 | return articles.size();
126 | }
127 |
128 | @Override
129 | public int getNewListSize() {
130 | return newList.size();
131 | }
132 |
133 | @Override
134 | public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
135 | return articles.get(oldItemPosition).urlToImage
136 | .equals(newList.get(newItemPosition).urlToImage);
137 | }
138 |
139 | @Override
140 | public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
141 | return articles.get(oldItemPosition)
142 | .equals(newList.get(newItemPosition));
143 | }
144 | });
145 | articles = newList;
146 | result.dispatchUpdatesTo(this);
147 | }
148 | }
149 |
150 | public void clearRecyclerView(){
151 | articlesList.clear();
152 | notifyDataSetChanged();
153 | }
154 |
155 | @Override
156 | public int getItemCount() {
157 | if (articles == null)
158 | return 0;
159 | return articles.size();
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/sridharjajoo/newsapp/core/Headline/HeadlineAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.sridharjajoo.newsapp.core.Headline;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.graphics.Bitmap;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.FragmentActivity;
10 | import android.support.v4.content.ContextCompat;
11 | import android.support.v7.util.DiffUtil;
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.bumptech.glide.Glide;
19 | import com.bumptech.glide.load.DataSource;
20 | import com.bumptech.glide.load.engine.GlideException;
21 | import com.bumptech.glide.request.RequestListener;
22 | import com.bumptech.glide.request.RequestOptions;
23 | import com.bumptech.glide.request.target.Target;
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.Favourite.Favourite;
28 | import com.example.sridharjajoo.newsapp.data.Headline.Articles;
29 |
30 | import java.util.List;
31 |
32 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
33 |
34 | public class HeadlineAdapter extends RecyclerView.Adapter {
35 |
36 | private final List articlesList;
37 | private final Context context;
38 | private List articles;
39 | private final AppDatabase db;
40 |
41 | public HeadlineAdapter(List articlesList, FragmentActivity activity, AppDatabase db) {
42 | this.articlesList = articlesList;
43 | this.context = activity;
44 | this.db = db;
45 | }
46 |
47 | @NonNull
48 | @Override
49 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
50 | LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
51 |
52 | switch (viewType) {
53 | case 0:
54 | View viewTop = layoutInflater.inflate(R.layout.item_headline_top, parent, false);
55 | return new HeadlineViewHolderTop(viewTop);
56 | default:
57 | View view = layoutInflater.inflate(R.layout.item_headline, parent, false);
58 | return new HeadlineViewHolder(view);
59 | }
60 | }
61 |
62 | @Override
63 | public int getItemViewType(int position) {
64 | return position;
65 | }
66 |
67 | @Override
68 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
69 | Articles currentItem = articlesList.get(position);
70 | switch (holder.getItemViewType()) {
71 | case 0:
72 | HeadlineViewHolderTop viewHolderTop = (HeadlineViewHolderTop) holder;
73 | configureViewHolderTop(viewHolderTop, currentItem);
74 | break;
75 | default:
76 | HeadlineViewHolder viewHolder = (HeadlineViewHolder) holder;
77 | configureViewHolder(viewHolder, currentItem);
78 | break;
79 | }
80 | }
81 |
82 | @SuppressLint("CheckResult")
83 | private void configureViewHolderTop(HeadlineViewHolderTop viewHolderTop, Articles currentItem) {
84 | viewHolderTop.itemView.setOnClickListener(view -> {
85 | Intent intent = new Intent(context, NewsDetailActivity.class);
86 | intent.putExtra("pos", viewHolderTop.getAdapterPosition() + 1);
87 | intent.putExtra("title", currentItem.title);
88 | context.startActivity(intent);
89 | });
90 | viewHolderTop.headlineTitle.setText(currentItem.title);
91 | viewHolderTop.cardView.setElevation(0);
92 | Glide.with(context)
93 | .asBitmap()
94 | .load(currentItem.urlToImage)
95 | .listener(new RequestListener() {
96 | @Override
97 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
98 | viewHolderTop.kenBurnsView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_insert_photo_black_24dp));
99 | return false;
100 | }
101 |
102 | @Override
103 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
104 | viewHolderTop.kenBurnsView.setImageBitmap(resource);
105 | return true;
106 | }
107 | }).submit();
108 | }
109 |
110 | private void configureViewHolder(HeadlineViewHolder viewHolder, Articles currentItem) {
111 | viewHolder.itemView.setOnClickListener(view -> {
112 | Intent intent = new Intent(context, NewsDetailActivity.class);
113 | intent.putExtra("pos", viewHolder.getAdapterPosition() + 1);
114 | intent.putExtra("title", currentItem.title);
115 | context.startActivity(intent);
116 | });
117 | Favourite favourite = new Favourite();
118 | favourite.articles = currentItem;
119 | int count = db.favoriteDao().isPresent(currentItem.title);
120 | viewHolder.favouriteCheckbox.setChecked(count == 1);
121 | viewHolder.favouriteCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
122 | if (isChecked)
123 | db.favoriteDao().insertFavourite(favourite);
124 | else
125 | db.favoriteDao().unfavouriteNews(currentItem.title);
126 | });
127 | viewHolder.description.setText(currentItem.title);
128 | viewHolder.newsSource.setText(currentItem.source.name);
129 | viewHolder.shareButton.setOnClickListener(view -> {
130 | Utils.shareNews(context, currentItem.url);
131 | });
132 | if (!currentItem.source.name.equals("Google News (India)"))
133 | viewHolder.newsTime.setText(Utils.formattedDate(currentItem.publishedAt));
134 |
135 | viewHolder.cardView.setElevation(0);
136 |
137 | Glide.with(context)
138 | .asBitmap()
139 | .load(currentItem.urlToImage)
140 | .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(50, 10, RoundedCornersTransformation.CornerType.ALL)))
141 | .listener(new RequestListener() {
142 | @Override
143 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
144 | viewHolder.newsImage.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_insert_photo_black_24dp));
145 | return false;
146 | }
147 |
148 | @Override
149 | public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
150 | viewHolder.newsImage.setImageBitmap(resource);
151 | return true;
152 | }
153 | }).submit();
154 | }
155 |
156 | public void setArticlesList(final List newList) {
157 | if (articles == null) {
158 | articles = newList;
159 | notifyItemRangeInserted(0, newList.size());
160 | } else {
161 | DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
162 | @Override
163 | public int getOldListSize() {
164 | return articles.size();
165 | }
166 |
167 | @Override
168 | public int getNewListSize() {
169 | return newList.size();
170 | }
171 |
172 | @Override
173 | public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
174 | return articles.get(oldItemPosition).urlToImage
175 | .equals(newList.get(newItemPosition).urlToImage);
176 | }
177 |
178 | @Override
179 | public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
180 | return articles.get(oldItemPosition)
181 | .equals(newList.get(newItemPosition));
182 | }
183 | });
184 | articles = newList;
185 | result.dispatchUpdatesTo(this);
186 | }
187 | }
188 |
189 | @Override
190 | public int getItemCount() {
191 | if (articles == null)
192 | return 0;
193 | return articles.size();
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/sridharjajoo/newsapp/NewsMainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.sridharjajoo.newsapp;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.AlarmManager;
5 | import android.app.PendingIntent;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.databinding.DataBindingUtil;
9 | import android.support.annotation.Nullable;
10 | import android.support.design.internal.BottomNavigationItemView;
11 | import android.support.design.internal.BottomNavigationMenuView;
12 | import android.support.design.widget.BottomNavigationView;
13 | import android.support.design.widget.TabLayout;
14 | import android.support.v4.app.Fragment;
15 | import android.support.v4.app.FragmentManager;
16 | import android.support.v4.app.FragmentPagerAdapter;
17 | import android.support.v4.view.ViewPager;
18 | import android.support.v7.app.ActionBar;
19 | import android.support.v7.app.AppCompatActivity;
20 | import android.os.Bundle;
21 | import android.util.Log;
22 | import android.view.Menu;
23 | import android.view.MenuInflater;
24 | import android.view.MenuItem;
25 | import android.view.View;
26 |
27 | import com.example.sridharjajoo.newsapp.core.Category.CategoryFragment;
28 | import com.example.sridharjajoo.newsapp.core.Favourite.FavouriteFragment;
29 | import com.example.sridharjajoo.newsapp.core.Headline.HeadlineFragment;
30 | import com.example.sridharjajoo.newsapp.core.Search.SearchFragment;
31 | import com.example.sridharjajoo.newsapp.core.Setttings.SettingsActivity;
32 | import com.example.sridharjajoo.newsapp.databinding.ActivityNewsMainBinding;
33 |
34 | import java.lang.reflect.Field;
35 | import java.util.Calendar;
36 |
37 | import javax.inject.Inject;
38 |
39 | import dagger.android.AndroidInjector;
40 | import dagger.android.DispatchingAndroidInjector;
41 | import dagger.android.support.HasSupportFragmentInjector;
42 |
43 | public class NewsMainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
44 |
45 | @Inject
46 | DispatchingAndroidInjector dispatchingAndroidInjector;
47 |
48 | private ActionBar actionBar;
49 | private ActivityNewsMainBinding binding;
50 | private int TOTALCATEGORY = 3;
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | binding = DataBindingUtil.setContentView(this, R.layout.activity_news_main);
56 | actionBar = getSupportActionBar();
57 | showNavigation();
58 | setAlarm();
59 | showHeadlines();
60 | BottomNavigationViewHelper.disableShiftMode(binding.navigation);
61 | }
62 |
63 | private void setAlarm() {
64 | // Retrieve a PendingIntent that will perform a broadcast
65 | Intent alarmIntent = new Intent(NewsMainActivity.this,
66 | AlarmReceiver.class);
67 | PendingIntent pendingIntent = PendingIntent.getBroadcast(
68 | NewsMainActivity.this, 0, alarmIntent, 0);
69 |
70 | AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
71 |
72 | // Set the alarm to start at 10:00 AM
73 | Calendar calendar = Calendar.getInstance();
74 | calendar.setTimeInMillis(System.currentTimeMillis());
75 | calendar.set(Calendar.HOUR_OF_DAY, 8);
76 | calendar.set(Calendar.MINUTE, 0);
77 | calendar.set(Calendar.SECOND, 0);
78 |
79 | manager.setRepeating(AlarmManager.RTC_WAKEUP,
80 | calendar.getTimeInMillis(), 86400000, // for repeating
81 | pendingIntent);
82 | }
83 |
84 | private void showHeadlines() {
85 | HeadlineFragment navigationFragment = new HeadlineFragment();
86 | actionBar.setTitle("Headlines");
87 | loadFragment(navigationFragment);
88 | }
89 |
90 | private void showNavigation() {
91 | binding.navigation.setOnNavigationItemSelectedListener(item -> {
92 | switch (item.getItemId()) {
93 | case R.id.navigation_headline:
94 | binding.frameContainer.setVisibility(View.VISIBLE);
95 | binding.vp.setVisibility(View.GONE);
96 | binding.tabLayout.setVisibility(View.GONE);
97 | HeadlineFragment navigationFragment = new HeadlineFragment();
98 | actionBar.setTitle("Headlines");
99 | loadFragment(navigationFragment);
100 | return true;
101 |
102 | case R.id.navigation_search:
103 | actionBar.setTitle("Search");
104 | binding.frameContainer.setVisibility(View.VISIBLE);
105 | binding.vp.setVisibility(View.GONE);
106 | binding.tabLayout.setVisibility(View.GONE);
107 | SearchFragment searchFragment = new SearchFragment();
108 | loadFragment(searchFragment);
109 | return true;
110 |
111 | case R.id.navigation_category:
112 | actionBar.setTitle("Category");
113 | binding.frameContainer.setVisibility(View.GONE);
114 | binding.vp.setVisibility(View.VISIBLE);
115 | binding.tabLayout.setVisibility(View.VISIBLE);
116 | binding.vp.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
117 | binding.tabLayout.setupWithViewPager(binding.vp);
118 | return true;
119 |
120 | case R.id.navigation_favourite:
121 | binding.frameContainer.setVisibility(View.VISIBLE);
122 | binding.vp.setVisibility(View.GONE);
123 | binding.tabLayout.setVisibility(View.GONE);
124 | FavouriteFragment favouriteFragment = new FavouriteFragment();
125 | actionBar.setTitle("Favourite");
126 | loadFragment(favouriteFragment);
127 | return true;
128 |
129 | default:
130 | binding.frameContainer.setVisibility(View.VISIBLE);
131 | binding.vp.setVisibility(View.GONE);
132 | binding.tabLayout.setVisibility(View.GONE);
133 | HeadlineFragment navigationFragmentDefault = new HeadlineFragment();
134 | actionBar.setTitle("Headlines");
135 | loadFragment(navigationFragmentDefault);
136 | return true;
137 | }
138 | });
139 | }
140 |
141 | private void loadFragment(Fragment fragment) {
142 | getSupportFragmentManager().beginTransaction()
143 | .replace(R.id.frame_container, fragment)
144 | .addToBackStack(null)
145 | .commit();
146 | }
147 |
148 | @Override
149 | public void onBackPressed() {
150 | finish();
151 | }
152 |
153 | @Override
154 | public AndroidInjector supportFragmentInjector() {
155 | return dispatchingAndroidInjector;
156 | }
157 |
158 | @Override
159 | public boolean onCreateOptionsMenu(Menu menu) {
160 | MenuInflater menuInflater = getMenuInflater();
161 | menuInflater.inflate(R.menu.settings, menu);
162 | return true;
163 | }
164 |
165 | @Override
166 | public boolean onOptionsItemSelected(MenuItem item) {
167 | switch (item.getItemId()) {
168 | case R.id.settings:
169 | startActivity(new Intent(NewsMainActivity.this, SettingsActivity.class));
170 | break;
171 | }
172 | return super.onOptionsItemSelected(item);
173 | }
174 |
175 | class MyPagerAdapter extends FragmentPagerAdapter {
176 |
177 | public MyPagerAdapter(FragmentManager fm) {
178 | super(fm);
179 | }
180 |
181 | @Nullable
182 | @Override
183 | public CharSequence getPageTitle(int position) {
184 | switch (position) {
185 | case 0:
186 | return "Science";
187 | case 1:
188 | return "Sports";
189 | case 2:
190 | return "Entertainment";
191 | }
192 | return " ";
193 |
194 | }
195 |
196 | @Override
197 | public Fragment getItem(int position) {
198 | Bundle bundle = new Bundle();
199 | bundle.putInt("search", position);
200 | CategoryFragment categoryFragment = new CategoryFragment();
201 | categoryFragment.setArguments(bundle);
202 | switch (position) {
203 | case 0:
204 | return categoryFragment;
205 | case 1:
206 | return categoryFragment;
207 | case 2:
208 | return categoryFragment;
209 | }
210 | return null;
211 | }
212 |
213 | @Override
214 | public int getCount() {
215 | return TOTALCATEGORY;
216 | }
217 | }
218 |
219 | public static class BottomNavigationViewHelper {
220 | @SuppressLint("RestrictedApi")
221 | public static void disableShiftMode(BottomNavigationView view) {
222 | BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
223 | try {
224 | Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
225 | shiftingMode.setAccessible(true);
226 | shiftingMode.setBoolean(menuView, false);
227 | shiftingMode.setAccessible(false);
228 | for (int i = 0; i < menuView.getChildCount(); i++) {
229 | BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
230 | //noinspection RestrictedApi
231 | item.setShiftingMode(false);
232 | // set once again checked value, so view will be updated
233 | //noinspection RestrictedApi
234 | item.setChecked(item.getItemData().isChecked());
235 | }
236 | } catch (NoSuchFieldException e) {
237 | Log.e("BNVHelper", "Unable to get shift mode field", e);
238 | } catch (IllegalAccessException e) {
239 | Log.e("BNVHelper", "Unable to change value of shift mode", e);
240 | }
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------