Skip to content

Commit

Permalink
+ Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mina Mikhail committed Aug 17, 2021
0 parents commit 000721f
Show file tree
Hide file tree
Showing 117 changed files with 4,316 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Built application files
*.apk
*.ap_

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# Intellij
*.iml
.idea

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Tech stack & News App libraries

- Navigation component - navigation graph for navigating and replacing screens/fragments
- ViewBinding - allows to more easily write code that interacts with views and replaces ```findViewById```.
- ViewModel - UI related data holder, lifecycle aware.
- LiveData - Build data objects that notify views when the underlying database changes.
- Dagger-Hilt for dependency injection. Object creation and scoping is handled by Hilt.
- Kotlin Coroutines + Flow - for managing background threads with simplified code and reducing needs for callbacks
- Retrofit2 & OkHttp3 - to make REST requests to the web service integrated.
- Room for database
- Recyclerview animation
- Coil - for image loading

## Architecture:

- MVVM Architecture
- Repository pattern
- Applying SOLID principles, each class has a single job with separation of concerns by making classes independent
of each other and communicating with interfaces.

## Features

+ Get breaking news
+ Save news to local
+ Search for specific news

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
87 changes: 87 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'androidx.navigation.safeargs'
id 'dagger.hilt.android.plugin'
}

android {
compileSdk compile_sdk_version

defaultConfig {
applicationId "com.mina_mikhail.newsapp"
minSdk min_sdk_version
targetSdk compile_sdk_version
versionCode version_code
versionName version_name

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
debug {
buildConfigField("String", "API_BASE_URL", "\"https://newsapi.org/v2/\"")
buildConfigField("String", "API_KEY", "\"5c203f74fdcc4265bca981fd059fee2c\"")
}

release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

buildConfigField("String", "API_BASE_URL", "\"https://newsapi.org/v2/\"")
buildConfigField("String", "API_KEY", "\"5c203f74fdcc4265bca981fd059fee2c\"")
}
}

buildFeatures {
viewBinding true
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

// Support
implementation "androidx.appcompat:appcompat:$appcompat"
implementation "androidx.core:core-ktx:$core_ktx"
implementation "androidx.legacy:legacy-support-v4:$support_version"

// Arch Components
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

// Kotlin Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines"

// Room
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"

// Networking
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
implementation "com.google.code.gson:gson:$gson"
implementation "com.squareup.okhttp3:logging-interceptor:$interceptor"
implementation "com.mocklets:pluto:$pluto"

// UI
implementation "com.google.android.material:material:$material_design"
implementation "androidx.navigation:navigation-fragment-ktx:$android_navigation"
implementation "androidx.navigation:navigation-ui-ktx:$android_navigation"
implementation "com.github.ybq:Android-SpinKit:$loading_animations"
implementation "com.tapadoo.android:alerter:$alerter"
implementation "io.coil-kt:coil:$coil"

// Utils
implementation "androidx.datastore:datastore-preferences:$datastore_preferences"

// Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
30 changes: 30 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mina_mikhail.newsapp">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name="com.mina_mikhail.newsapp.core.MyApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NewsApp"
android:usesCleartextTraffic="true">

<activity android:name=".features.splash.presentation.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".features.news.presentation.NewsActivity" />

</application>

</manifest>
Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions app/src/main/java/com/mina_mikhail/newsapp/core/MyApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.mina_mikhail.newsapp.core

import android.app.Application
import com.mina_mikhail.newsapp.BuildConfig
import com.mocklets.pluto.Pluto
import com.mocklets.pluto.PlutoLog
import com.mocklets.pluto.modules.exceptions.ANRException
import com.mocklets.pluto.modules.exceptions.ANRListener
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MyApplication : Application() {

override
fun onCreate() {
super.onCreate()

initPlutoNetworkInspection()

initPlutoANRInspection()
}

private fun initPlutoNetworkInspection() {
if (BuildConfig.DEBUG) {
Pluto.initialize(this)
}
}

private fun initPlutoANRInspection() {
if (BuildConfig.DEBUG) {
Pluto.setANRListener(object : ANRListener {
override
fun onAppNotResponding(exception: ANRException) {
exception.printStackTrace()
PlutoLog.e("ANR", exception.threadStateMap)
}
})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mina_mikhail.newsapp.core.data_source

import com.mina_mikhail.newsapp.core.network.FailureStatus.API_FAIL
import com.mina_mikhail.newsapp.core.network.FailureStatus.NO_INTERNET
import com.mina_mikhail.newsapp.core.network.FailureStatus.OTHER
import com.mina_mikhail.newsapp.core.network.FailureStatus.SERVER_SIDE_EXCEPTION
import com.mina_mikhail.newsapp.core.network.FailureStatus.TOKEN_EXPIRED
import com.mina_mikhail.newsapp.core.network.Resource
import com.mina_mikhail.newsapp.core.network.ResponseStatus
import com.mina_mikhail.newsapp.features.news.domain.entity.response.NewsResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import retrofit2.HttpException
import java.net.UnknownHostException
import javax.inject.Inject

open class BaseRemoteDataSource @Inject constructor() {

companion object {
const val LIST_PAGE_SIZE = 10
}

suspend fun <T> safeApiCall(apiCall: suspend () -> T): Resource<T> {
return withContext(Dispatchers.IO) {
try {
val apiResponse: T = apiCall.invoke()

if ((apiResponse as NewsResponse).status == ResponseStatus.SUCCESS) {
if ((apiResponse as NewsResponse).totalResults == 0) {
Resource.Empty
} else {
Resource.Success(apiResponse)
}
} else {
Resource.Failure(API_FAIL, 200, "Error from api")
}
} catch (throwable: Throwable) {
when (throwable) {
is HttpException -> {
if (throwable.code() == 401) {
Resource.Failure(TOKEN_EXPIRED, throwable.code(), throwable.response()?.errorBody().toString())
} else {
Resource.Failure(SERVER_SIDE_EXCEPTION, throwable.code(), throwable.response()?.errorBody().toString())
}
}

is UnknownHostException -> {
Resource.Failure(NO_INTERNET, null, null)
}

else -> {
Resource.Failure(OTHER, null, null)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mina_mikhail.newsapp.core.di.module

import com.mina_mikhail.newsapp.features.news.data.data_source.remote.NewsServices
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import javax.inject.Singleton

@Module(includes = [RetrofitModule::class])
@InstallIn(SingletonComponent::class)
object NetworkServicesModule {

@Provides
@Singleton
fun provideNewsServices(retrofit: Retrofit): NewsServices {
return retrofit.create(NewsServices::class.java)
}
}
Loading

0 comments on commit 000721f

Please sign in to comment.