diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 45b5654..3cc336b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,9 +1,22 @@ - - + + diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/app/build.gradle b/app/build.gradle index c339f69..6476c0b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,19 +5,23 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: "kotlin-kapt" + +apply plugin: 'com.google.gms.google-services' + android { - compileSdkVersion 29 - buildToolsVersion "29.0.2" + compileSdkVersion 30 defaultConfig { applicationId "com.androiddevs.newsflash" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "API_KEY", API_KEY) } - + testOptions { + unitTests.returnDefaultValues = true + } buildTypes { release { @@ -25,40 +29,54 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - - dataBinding { - enabled = true + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + buildFeatures { + dataBinding = true + } + configurations.all { + resolutionStrategy { + exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug" + } + } + sourceSets { + debug { + assets { + srcDirs 'src/debug/assets' + } + } } } dependencies { - def retrofitVersion = "2.6.1" - def rxAndroidVersion = "2.1.1" - def okHttpVersion = "4.2.2" - def navVersion = "2.1.0" + def retrofitVersion = "2.9.0" + def okHttpVersion = '4.8.1' + def navVersion = "2.3.0" + def lifecycle_version = "2.2.0" + def room_version = "2.2.5" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.cardview:cardview:1.0.0' // ROOM + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" // NAVIGATION COMPONENT implementation "androidx.navigation:navigation-fragment-ktx:$navVersion" implementation "androidx.navigation:navigation-ui-ktx:$navVersion" - // RX ANDROID - implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion" - - // RETROFIT - implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" - implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // OK HTTP implementation "com.squareup.okhttp3:okhttp:$okHttpVersion" @@ -66,7 +84,28 @@ dependencies { // TESTING implementation 'androidx.legacy:legacy-support-v4:1.0.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + testImplementation 'junit:junit:4.13' + testImplementation 'org.mockito:mockito-core:3.0.0' + testImplementation "androidx.arch.core:core-testing:2.1.0" + + // DAGGER + implementation 'com.google.dagger:dagger:2.28.3' + kapt 'com.google.dagger:dagger-compiler:2.28.3' + kaptTest 'com.google.dagger:dagger-compiler:2.28.3' + annotationProcessor 'com.google.dagger:dagger-compiler:2.28.3' + + + //VIEW-MODEL AND LIVE DATA + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" + + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.8" + + //FIREBASE AUTH + implementation 'com.google.firebase:firebase-auth:19.3.2' + implementation 'com.google.android.gms:play-services-auth:18.1.0' } diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json new file mode 100644 index 0000000..0261de4 --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,48 @@ +{ + "project_info": { + "project_number": "346374230003", + "firebase_url": "https://newsflash-d9f16.firebaseio.com", + "project_id": "newsflash-d9f16", + "storage_bucket": "newsflash-d9f16.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:346374230003:android:7a60be2dafbcbab4784c86", + "android_client_info": { + "package_name": "com.androiddevs.newsflash" + } + }, + "oauth_client": [ + { + "client_id": "346374230003-43hiooahke19pg4oj04bgj28cst8hbv4.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.androiddevs.newsflash", + "certificate_hash": "ee06eced07e8378ceeb12f98b6c0318cf7489cbc" + } + }, + { + "client_id": "346374230003-34cu38csb8ni5gh7jtni7dl7gp2ebbus.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyB4XWpA-Ok3AOz5V4hMv1ipVF_c-Tm4pB0" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "346374230003-34cu38csb8ni5gh7jtni7dl7gp2ebbus.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6e4903e..e8682dc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,8 +10,9 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:name=".NewsFlashApplication" android:theme="@style/AppTheme"> - + diff --git a/app/src/main/java/com/androiddevs/newsflash/NewsFlashApplication.kt b/app/src/main/java/com/androiddevs/newsflash/NewsFlashApplication.kt new file mode 100644 index 0000000..3c65ab5 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/NewsFlashApplication.kt @@ -0,0 +1,15 @@ +package com.androiddevs.newsflash + +import android.app.Application +import com.androiddevs.newsflash.di.CoreInjector +import com.androiddevs.newsflash.di.components.DaggerAppComponent + + +class NewsFlashApplication : Application() { + + override fun onCreate() { + super.onCreate() + CoreInjector.injector = DaggerAppComponent.builder() + .application(this).build() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/constants/NewsConstants.kt b/app/src/main/java/com/androiddevs/newsflash/constants/NewsConstants.kt new file mode 100644 index 0000000..0a15372 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/constants/NewsConstants.kt @@ -0,0 +1,4 @@ +package com.androiddevs.newsflash.constants + +const val API_KEY = "" +const val RC_SIGN_IN = 123 \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/NewsError.kt b/app/src/main/java/com/androiddevs/newsflash/data/NewsError.kt deleted file mode 100644 index 257a110..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/data/NewsError.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.androiddevs.newsflash.data - -import com.google.gson.annotations.SerializedName - -object NewsError -{ - data class Error( - @SerializedName("status") - val status: String, - @SerializedName("code") - val code: String, - @SerializedName("message") - val message: String - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/NewsResult.kt b/app/src/main/java/com/androiddevs/newsflash/data/NewsResult.kt deleted file mode 100644 index 7418dc8..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/data/NewsResult.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.androiddevs.newsflash.data - -import com.google.gson.annotations.SerializedName - -object NewsResult -{ - - data class News( - @SerializedName("status") - val status: String, - @SerializedName("totalResults") - val totalResults: Int, - @SerializedName("articles") - val articles: List
- ) - { - data class Article( - @SerializedName("source") - val source: Source, - @SerializedName("author") - val author: String, - @SerializedName("title") - val title: String, - @SerializedName("description") - val description: String, - @SerializedName("url") - val url: String, - @SerializedName("urlToImage") - val urlToImage: String, - @SerializedName("publishedAt") - val publishedAt: String, - @SerializedName("content") - val content: String - ) - { - data class Source( - @SerializedName("id") - val id: String, - @SerializedName("name") - val name: String - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/NewsSources.kt b/app/src/main/java/com/androiddevs/newsflash/data/NewsSources.kt deleted file mode 100644 index a3b34b8..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/data/NewsSources.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.androiddevs.newsflash.data - -import com.google.gson.annotations.SerializedName - -object NewsSources -{ - - data class Sources( - @SerializedName("status") - val status: String, - @SerializedName("sources") - val sources: List - ) - { - data class Source( - @SerializedName("id") - val id: String, - @SerializedName("name") - val name: String, - @SerializedName("description") - val description: String, - @SerializedName("url") - val url: String, - @SerializedName("category") - val category: String, - @SerializedName("language") - val language: String, - @SerializedName("country") - val country: String - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDao.kt b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDao.kt new file mode 100644 index 0000000..7f80931 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDao.kt @@ -0,0 +1,16 @@ +package com.androiddevs.newsflash.data.database + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy + +@Dao +interface NewsDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert() + + @Delete + suspend fun delete() +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDatabase.kt b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDatabase.kt new file mode 100644 index 0000000..9fb8307 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsDatabase.kt @@ -0,0 +1,6 @@ +package com.androiddevs.newsflash.data.database + +import androidx.room.RoomDatabase + +abstract class NewsDatabase: RoomDatabase() { +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/database/NewsEntity.kt b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsEntity.kt new file mode 100644 index 0000000..9f1bd6e --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/database/NewsEntity.kt @@ -0,0 +1,10 @@ +package com.androiddevs.newsflash.data.database + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class NewsEntity( + @PrimaryKey(autoGenerate = true) + val id: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelper.kt b/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelper.kt new file mode 100644 index 0000000..8264e59 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelper.kt @@ -0,0 +1,10 @@ +package com.androiddevs.newsflash.data.local.shared_preferences + +interface PreferencesHelper { + + fun setLoggedIn() + + fun logout() + + fun getLoggedIn():Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelperImpl.kt b/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelperImpl.kt new file mode 100644 index 0000000..4826090 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/local/shared_preferences/PreferencesHelperImpl.kt @@ -0,0 +1,25 @@ +package com.androiddevs.newsflash.data.local.shared_preferences + +import android.content.SharedPreferences +import javax.inject.Inject + +class PreferencesHelperImpl @Inject constructor(private val sharedPreferences: SharedPreferences) : + PreferencesHelper { + + companion object { + const val LOGGEDIN = "LOGGED_IN" + } + + override fun setLoggedIn() { + sharedPreferences.edit().putBoolean(LOGGEDIN, true).apply() + } + + override fun logout() { + sharedPreferences.edit().clear().apply() + } + + override fun getLoggedIn(): Boolean { + return sharedPreferences.getBoolean(LOGGEDIN, false) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/NewsAPILayerImpl.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/NewsAPILayerImpl.kt new file mode 100644 index 0000000..f336972 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/NewsAPILayerImpl.kt @@ -0,0 +1,32 @@ +package com.androiddevs.newsflash.data.network + +import com.androiddevs.newsflash.constants.API_KEY +import com.androiddevs.newsflash.data.network.apiwrapper.Resource +import com.androiddevs.newsflash.data.network.apiwrapper.handleResponse +import com.androiddevs.newsflash.data.network.contract.NewsAPILayer +import com.androiddevs.newsflash.data.network.models.News +import com.androiddevs.newsflash.data.network.models.TopHeadlinesRequest +import retrofit2.Retrofit +import javax.inject.Inject + +class NewsAPILayerImpl @Inject constructor(retrofit: Retrofit) : + NewsAPILayer { + + private val newsApiService by lazy { + retrofit.create(NewsApiService::class.java) + } + + override suspend fun getBusinessNews(topHeadlinesRequest: TopHeadlinesRequest): Resource { + return handleResponse { + newsApiService.getTopHeadlines( + topHeadlinesRequest.country, + topHeadlinesRequest.category, + topHeadlinesRequest.sources, + topHeadlinesRequest.keyword, + topHeadlinesRequest.pageSize, + topHeadlinesRequest.page, + API_KEY + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/NewsApiService.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/NewsApiService.kt new file mode 100644 index 0000000..68a1351 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/NewsApiService.kt @@ -0,0 +1,41 @@ +package com.androiddevs.newsflash.data.network + +import com.androiddevs.newsflash.data.network.models.News +import com.androiddevs.newsflash.data.network.models.Sources +import retrofit2.http.GET +import retrofit2.http.Query + +interface NewsApiService { + @GET("sources") + fun getNewsSources( + @Query("category") category: String, @Query("language") language: String, + @Query("country") country: String + ): Sources + + @GET("top-headlines") + fun getTopHeadlines( + @Query("country") country: String?, + @Query("category") category: String?, + @Query("sources") sources: String?, + @Query("q") keyword: String?, + @Query("pageSize") pageSize: Int, + @Query("page") page: Int, + @Query("apiKey") apiKey: String? + ): News + + @GET("everything") + fun getEverything( + @Query("q") keyword: String, + @Query("qInTitle") keyWordInArtcile: String, + @Query("sources") sources: String, + @Query("domains") domain: String, + @Query("excludeDomains") excludeDomains: String, + @Query("from") from: String, + @Query("to") to: String, + @Query("language") language: String, + @Query("sortBy") sortBy: String, + @Query("pageSize") pageSize: Int, + @Query("page") page: Int + ): News + +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/apiwrapper/RetrofitAPIWrapper.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/apiwrapper/RetrofitAPIWrapper.kt new file mode 100644 index 0000000..2c2227a --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/apiwrapper/RetrofitAPIWrapper.kt @@ -0,0 +1,40 @@ +package com.androiddevs.newsflash.data.network.apiwrapper + +suspend fun handleResponse(apiBlock: suspend () -> T): Resource { + return try { + val response = apiBlock() + if (response != null) { + Resource.success(response) + } else { + Resource.getDefaultError() + } + } catch (exception: java.lang.Exception) { + Resource.error("Something went wrong. Please try again later", exception) + } +} + +data class Resource( + val status: Status, + val data: T? = null, + val message: String? = null, + val errorException: Exception? = null +) { + + companion object { + fun success(data: T?): Resource { + return Resource(Status.SUCCESS, data) + } + + fun error(errorMessage: String? = null, exception: Exception? = null): Resource { + return Resource(Status.ERROR, message = errorMessage, errorException = exception) + } + + fun getDefaultError(): Resource { + return error("Something went wrong. Please try again later") + } + } +} + +enum class Status { + SUCCESS, ERROR +} diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/contract/NewsAPILayer.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/contract/NewsAPILayer.kt new file mode 100644 index 0000000..b77931e --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/contract/NewsAPILayer.kt @@ -0,0 +1,9 @@ +package com.androiddevs.newsflash.data.network.contract + +import com.androiddevs.newsflash.data.network.apiwrapper.Resource +import com.androiddevs.newsflash.data.network.models.News +import com.androiddevs.newsflash.data.network.models.TopHeadlinesRequest + +interface NewsAPILayer { + suspend fun getBusinessNews(topHeadlinesRequest: TopHeadlinesRequest): Resource +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/models/News.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/models/News.kt new file mode 100644 index 0000000..68a9608 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/models/News.kt @@ -0,0 +1,23 @@ +package com.androiddevs.newsflash.data.network.models + +data class News( + val status: String? = null, + val totalResults: Int = 0, + val articles: List
= listOf() +) { + data class Article( + val source: Source? = null, + val author: String, + val title: String, + val description: String, + val url: String, + val urlToImage: String, + val publishedAt: String? = null, + val content: String? = null + ) { + data class Source( + val id: String? = null, + val name: String? = null + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/models/NewsError.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/models/NewsError.kt new file mode 100644 index 0000000..7866d35 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/models/NewsError.kt @@ -0,0 +1,7 @@ +package com.androiddevs.newsflash.data.network.models + +data class Error( + val status: String? = null, + val code: String? = null, + val message: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/models/Sources.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/models/Sources.kt new file mode 100644 index 0000000..d9979ee --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/models/Sources.kt @@ -0,0 +1,16 @@ +package com.androiddevs.newsflash.data.network.models + +data class Sources( + val status: String? = null, + val sources: List = listOf() +) { + data class Source( + val id: String? = null, + val name: String? = null, + val description: String? = null, + val url: String? = null, + val category: String? = null, + val language: String? = null, + val country: String? = null + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/network/models/TopHeadlinesRequest.kt b/app/src/main/java/com/androiddevs/newsflash/data/network/models/TopHeadlinesRequest.kt new file mode 100644 index 0000000..683dd07 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/network/models/TopHeadlinesRequest.kt @@ -0,0 +1,15 @@ +package com.androiddevs.newsflash.data.network.models + +data class TopHeadlinesRequest( + var country: String? = null, + var category: String? = null, + var sources: String? = null, + var keyword: String? = null, + var pageSize: Int = 0, + var page: Int = 0 +) { + companion object { + fun getDefaultParams(): TopHeadlinesRequest = + TopHeadlinesRequest("in", pageSize = 10, page = 0) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/repository/NewsRepositoryImpl.kt b/app/src/main/java/com/androiddevs/newsflash/data/repository/NewsRepositoryImpl.kt new file mode 100644 index 0000000..dceffe8 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/repository/NewsRepositoryImpl.kt @@ -0,0 +1,27 @@ +package com.androiddevs.newsflash.data.repository + +import com.androiddevs.newsflash.data.network.apiwrapper.Resource +import com.androiddevs.newsflash.data.network.apiwrapper.Status +import com.androiddevs.newsflash.data.network.contract.NewsAPILayer +import com.androiddevs.newsflash.data.network.models.TopHeadlinesRequest +import com.androiddevs.newsflash.data.repository.contract.NewsRepository +import com.androiddevs.newsflash.data.repository.mapper.toNewsArticle +import com.androiddevs.newsflash.data.repository.models.NewsArticle +import javax.inject.Inject + +class NewsRepositoryImpl @Inject constructor(private val apiLayer: NewsAPILayer) : + NewsRepository { + override suspend fun getBusinessNews(topHeadlinesRequest: TopHeadlinesRequest): Resource> { + val response = apiLayer.getBusinessNews(topHeadlinesRequest) + if (response.status == Status.SUCCESS) { + response.data?.let { + val articles = it.articles.map { it.toNewsArticle() } + return Resource.success(articles) + } ?: run { + return Resource.getDefaultError() + } + } else { + return Resource.error(response.message, response.errorException) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/repository/contract/NewsRepository.kt b/app/src/main/java/com/androiddevs/newsflash/data/repository/contract/NewsRepository.kt new file mode 100644 index 0000000..f4df7e4 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/repository/contract/NewsRepository.kt @@ -0,0 +1,9 @@ +package com.androiddevs.newsflash.data.repository.contract + +import com.androiddevs.newsflash.data.network.apiwrapper.Resource +import com.androiddevs.newsflash.data.network.models.TopHeadlinesRequest +import com.androiddevs.newsflash.data.repository.models.NewsArticle + +interface NewsRepository { + suspend fun getBusinessNews(topHeadlinesRequest: TopHeadlinesRequest): Resource> +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/repository/mapper/NewsArticleMapper.kt b/app/src/main/java/com/androiddevs/newsflash/data/repository/mapper/NewsArticleMapper.kt new file mode 100644 index 0000000..c4246e0 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/repository/mapper/NewsArticleMapper.kt @@ -0,0 +1,16 @@ +package com.androiddevs.newsflash.data.repository.mapper + +import com.androiddevs.newsflash.data.network.models.News +import com.androiddevs.newsflash.data.repository.models.NewsArticle + +fun News.Article.toNewsArticle(): NewsArticle { + return NewsArticle( + source?.name ?: "", + author, + title, + description, + url, + urlToImage, + publishedAt ?: "" + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/data/repository/models/NewsArticle.kt b/app/src/main/java/com/androiddevs/newsflash/data/repository/models/NewsArticle.kt new file mode 100644 index 0000000..68cc0fa --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/data/repository/models/NewsArticle.kt @@ -0,0 +1,13 @@ +package com.androiddevs.newsflash.data.repository.models + + +data class NewsArticle( + val sourceName: String, + val authorName: String = "", + val articleTitle: String, + val description: String, + val articleUrl: String, + val imageUrl: String, + val publishedDate: String = "" +) + diff --git a/app/src/main/java/com/androiddevs/newsflash/di/CoreInjector.kt b/app/src/main/java/com/androiddevs/newsflash/di/CoreInjector.kt new file mode 100644 index 0000000..656f604 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/di/CoreInjector.kt @@ -0,0 +1,8 @@ +package com.androiddevs.newsflash.di + +import com.androiddevs.newsflash.di.components.AppComponent + +object CoreInjector { + @JvmStatic + lateinit var injector: AppComponent +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/di/components/AppComponent.kt b/app/src/main/java/com/androiddevs/newsflash/di/components/AppComponent.kt new file mode 100644 index 0000000..33a8fea --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/di/components/AppComponent.kt @@ -0,0 +1,23 @@ +package com.androiddevs.newsflash.di.components + +import android.app.Application +import com.androiddevs.newsflash.di.modules.CoreBinder +import com.androiddevs.newsflash.di.modules.CoreModule +import dagger.BindsInstance +import dagger.Component +import javax.inject.Singleton + + +@Singleton +@Component(modules = [CoreBinder::class, CoreModule::class]) +interface AppComponent { + + @Component.Builder + interface Builder { + + @BindsInstance + fun application(application: Application): Builder + + fun build(): AppComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/di/modules/APIBinder.kt b/app/src/main/java/com/androiddevs/newsflash/di/modules/APIBinder.kt new file mode 100644 index 0000000..c2b2c0c --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/di/modules/APIBinder.kt @@ -0,0 +1,36 @@ +package com.androiddevs.newsflash.di.modules + +import com.androiddevs.newsflash.data.network.NewsAPILayerImpl +import com.androiddevs.newsflash.data.network.contract.NewsAPILayer +import dagger.Binds +import dagger.Module +import dagger.Provides +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +@Module(includes = [APIBinder.APIModule::class]) +abstract class APIBinder { + + companion object { + const val PREF_NAME = "com.androiddevs.newsflash" + const val BASE_URL = "https://newsapi.org/v2/" + } + + @Binds + abstract fun bindsAPILayer(newsApiLayerImpl: NewsAPILayerImpl): NewsAPILayer + + @Module + internal object APIModule { + @Provides + fun provideRetrofitInstance(gsonConverterFactory: GsonConverterFactory): Retrofit = + Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(gsonConverterFactory) + .build() + + + @Provides + fun providesGsonConverterFactory(): GsonConverterFactory = GsonConverterFactory.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreBinder.kt b/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreBinder.kt new file mode 100644 index 0000000..6669ed6 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreBinder.kt @@ -0,0 +1,24 @@ +package com.androiddevs.newsflash.di.modules + +import com.androiddevs.newsflash.data.local.shared_preferences.PreferencesHelper +import com.androiddevs.newsflash.data.local.shared_preferences.PreferencesHelperImpl +import com.androiddevs.newsflash.data.network.NewsAPILayerImpl +import com.androiddevs.newsflash.data.network.contract.NewsAPILayer +import com.androiddevs.newsflash.utils.AppDispatchers +import com.androiddevs.newsflash.utils.DispatcherProvider +import dagger.Binds +import dagger.Module + + +@Module +abstract class CoreBinder { + + @Binds + abstract fun bindsSharedPreferenceHelper(preferencesHelperImpl: PreferencesHelperImpl): PreferencesHelper + + @Binds + abstract fun bindsDispatchers(appDispatchers: AppDispatchers): DispatcherProvider + + @Binds + abstract fun bindsNewsRepo(newsAPILayerImpl: NewsAPILayerImpl): NewsAPILayer +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreModule.kt b/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreModule.kt new file mode 100644 index 0000000..f4b2d36 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/di/modules/CoreModule.kt @@ -0,0 +1,27 @@ +package com.androiddevs.newsflash.di.modules + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import dagger.Module +import dagger.Provides +import javax.inject.Singleton + + +@Module(includes = [APIBinder::class]) +class CoreModule { + + companion object { + const val PREF_NAME = "com.androiddevs.newsflash" + const val BASE_URL = "com.androiddevs.newsflash" + } + + @Singleton + @Provides + fun providesSharedPreferences(context: Application): SharedPreferences = + context.applicationContext.getSharedPreferences( + PREF_NAME, + Context.MODE_PRIVATE + ) + +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/repository/NewsApiService.kt b/app/src/main/java/com/androiddevs/newsflash/repository/NewsApiService.kt deleted file mode 100644 index d7ef33f..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/repository/NewsApiService.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.androiddevs.newsflash.repository - - -import com.androiddevs.newsflash.BuildConfig -import com.androiddevs.newsflash.data.NewsResult -import com.androiddevs.newsflash.data.NewsSources -import io.reactivex.Observable -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.Response -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import retrofit2.converter.gson.GsonConverterFactory -import retrofit2.http.GET -import retrofit2.http.Query -import java.util.concurrent.TimeUnit - -interface NewsApiService { - @GET("sources") - fun getNewsSources( - @Query("category") category: String, - @Query("language") language: String, - @Query("country") country: String - ): Observable - - @GET("top-headlines") - fun getTopHeadlines( - @Query("country") country: String, - @Query("category") category: String, - @Query("sources") sources: String, - @Query("q") keyword: String, - @Query("pageSize") pageSize: Int, - @Query("page") page: Int - ): Observable - - @GET("everything") - fun getEverything( - @Query("q") keyword: String, - @Query("qInTitle") keyWordInArtcile : String, - @Query("sources") sources: String, - @Query("domains") domain: String, - @Query("excludeDomains") excludeDomains: String, - @Query("from") from: String, - @Query("to") to: String, - @Query("language") language:String, - @Query("sortBy") sortBy:String, - @Query("pageSize") pageSize: Int, - @Query("page") page: Int):Observable - - - companion object { - fun create(): NewsApiService { - - val httpLoggingInterceptor = HttpLoggingInterceptor() - httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY - - val okHttpClient = OkHttpClient.Builder() - .addInterceptor(httpLoggingInterceptor) - .addInterceptor(object : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val original = chain.request() - - val request = original.newBuilder() - .addHeader("X-Api-Key", BuildConfig.API_KEY) - .method(original.method, original.body) - .build() - - return chain.proceed(request) - } - }) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) - .build() - - val retrofit = Retrofit.Builder() - .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) - .addConverterFactory(GsonConverterFactory.create()) - .baseUrl("https://newsapi.org/v2/") - .client(okHttpClient) - .build() - - - return retrofit.create(NewsApiService::class.java) - - } - - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/ui/MainActivity.kt b/app/src/main/java/com/androiddevs/newsflash/ui/MainActivity.kt new file mode 100644 index 0000000..7ab9506 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/ui/MainActivity.kt @@ -0,0 +1,23 @@ +package com.androiddevs.newsflash.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentTransaction +import com.androiddevs.newsflash.R +import com.androiddevs.newsflash.ui.fragments.LoginFragment + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + openLoginFragment() + } + + private fun openLoginFragment() { + val transaction: FragmentTransaction = supportFragmentManager.beginTransaction() + transaction.add(R.id.fragment_main, LoginFragment()) + transaction.addToBackStack(null) + transaction.commit() + } +} diff --git a/app/src/main/java/com/androiddevs/newsflash/view/adapter/HomeNewsRecyelerAdapter.kt b/app/src/main/java/com/androiddevs/newsflash/ui/adapter/HomeNewsRecyelerAdapter.kt similarity index 63% rename from app/src/main/java/com/androiddevs/newsflash/view/adapter/HomeNewsRecyelerAdapter.kt rename to app/src/main/java/com/androiddevs/newsflash/ui/adapter/HomeNewsRecyelerAdapter.kt index 8d473c4..b398319 100644 --- a/app/src/main/java/com/androiddevs/newsflash/view/adapter/HomeNewsRecyelerAdapter.kt +++ b/app/src/main/java/com/androiddevs/newsflash/ui/adapter/HomeNewsRecyelerAdapter.kt @@ -1,4 +1,4 @@ -package com.androiddevs.newsflash.view.adapter +package com.androiddevs.newsflash.ui.adapter import android.view.LayoutInflater @@ -6,12 +6,12 @@ import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.androiddevs.newsflash.R -import com.androiddevs.newsflash.data.NewsResult +import com.androiddevs.newsflash.data.network.models.News import com.androiddevs.newsflash.databinding.HomeNewsRowBinding -class HomeNewsRecyelerAdapter(private var newsList: ArrayList) : - RecyclerView.Adapter() { +class HomeNewsRecyclerAdapter(private var newsList: ArrayList) : + RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val layoutInflater = LayoutInflater.from(parent.context) @@ -33,11 +33,11 @@ class HomeNewsRecyelerAdapter(private var newsList: ArrayList + if (task.isSuccessful) { + //TODO: Navigate to homeScreen + Log.e(TAG, "Google Sign In SuccessFull") + } else { + Log.e(TAG, "Google Sign In failed") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/HomeFragmentViewModel.kt b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/HomeFragmentViewModel.kt new file mode 100644 index 0000000..e763b23 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/HomeFragmentViewModel.kt @@ -0,0 +1,50 @@ +package com.androiddevs.newsflash.ui.viewModel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.androiddevs.newsflash.data.network.apiwrapper.Status +import com.androiddevs.newsflash.data.network.models.TopHeadlinesRequest +import com.androiddevs.newsflash.data.repository.contract.NewsRepository +import com.androiddevs.newsflash.data.repository.models.NewsArticle +import com.androiddevs.newsflash.utils.DispatcherProvider +import kotlinx.coroutines.launch + +class HomeFragmentViewModel constructor( + private val newsRepository: NewsRepository, + private val appDispatchers: DispatcherProvider +) : ViewModel() { + + private val screenStates: MutableLiveData = + MutableLiveData(HomeScreenStates.Loading) + + fun subscribeToUIState(): LiveData = screenStates + + init { + + } + + fun getTopHeadlines(headlinesRequest: TopHeadlinesRequest) { + viewModelScope.launch(appDispatchers.ioDispatcher) { + val response = newsRepository.getBusinessNews(headlinesRequest) + if (response.status == Status.SUCCESS) { + response.data?.let { + screenStates.postValue(HomeScreenStates.TopHeadlinesReceived(it)) + } ?: kotlin.run { + screenStates.postValue(HomeScreenStates.ErrorState) + } + } else { + screenStates.postValue(HomeScreenStates.ErrorState) + } + } + } + +} + +sealed class HomeScreenStates { + object Loading : HomeScreenStates() + object ErrorState : HomeScreenStates() + data class TopHeadlinesReceived(val articleList: List) : + HomeScreenStates() +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/LoginViewModel.kt b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/LoginViewModel.kt new file mode 100644 index 0000000..8ad220c --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/LoginViewModel.kt @@ -0,0 +1,19 @@ +package com.androiddevs.newsflash.ui.viewModel + +import android.app.Activity +import androidx.lifecycle.ViewModel +import com.androiddevs.newsflash.R +import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.auth.api.signin.GoogleSignInClient +import com.google.android.gms.auth.api.signin.GoogleSignInOptions + +class LoginViewModel(private val activity: Activity): ViewModel() { + fun createGoogleSignInRequest(): GoogleSignInClient { + val googleSignInOption = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(activity.getString(R.string.default_web_client_id)) + .requestEmail() + .build() + + return activity.let { GoogleSignIn.getClient(it,googleSignInOption) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/viewModeFactory/ViewModelFactory.kt b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/viewModeFactory/ViewModelFactory.kt new file mode 100644 index 0000000..c9abec7 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/ui/viewModel/viewModeFactory/ViewModelFactory.kt @@ -0,0 +1,25 @@ +package com.androiddevs.newsflash.ui.viewModel.viewModeFactory + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider + +@Suppress("UNCHECKED_CAST") +class ViewModelFactory +@Inject constructor( + private val creators: Map, @JvmSuppressWildcards Provider> +) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + val creator = creators[modelClass] ?: creators.asIterable() + .firstOrNull { modelClass.isAssignableFrom(it.key) }?.value + ?: throw IllegalArgumentException("unknown model class $modelClass") + return try { + creator.get() as T + } catch (e: Exception) { + throw RuntimeException(e) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/androiddevs/newsflash/utils/AppDispatchers.kt b/app/src/main/java/com/androiddevs/newsflash/utils/AppDispatchers.kt new file mode 100644 index 0000000..85f4472 --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/utils/AppDispatchers.kt @@ -0,0 +1,16 @@ +package com.androiddevs.newsflash.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import javax.inject.Inject + +interface DispatcherProvider { + val mainDispatchers: CoroutineDispatcher + val ioDispatcher: CoroutineDispatcher +} + +class AppDispatchers @Inject constructor() : DispatcherProvider { + override val mainDispatchers: CoroutineDispatcher = Dispatchers.Main + override val ioDispatcher: CoroutineDispatcher = Dispatchers.IO + +} diff --git a/app/src/main/java/com/androiddevs/newsflash/view/MainActivity.kt b/app/src/main/java/com/androiddevs/newsflash/view/MainActivity.kt deleted file mode 100644 index 85370ea..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/view/MainActivity.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.androiddevs.newsflash.view - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle -import android.util.Log -import com.androiddevs.newsflash.R -import com.androiddevs.newsflash.repository.NewsApiService -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class MainActivity : AppCompatActivity() -{ - - override fun onCreate(savedInstanceState: Bundle?) - { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - - - - - } -} diff --git a/app/src/main/java/com/androiddevs/newsflash/view/fragments/HomeFragment.kt b/app/src/main/java/com/androiddevs/newsflash/view/fragments/HomeFragment.kt deleted file mode 100644 index 90d45f3..0000000 --- a/app/src/main/java/com/androiddevs/newsflash/view/fragments/HomeFragment.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.androiddevs.newsflash.view.fragments - - -import android.os.Bundle -import android.util.Log -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import androidx.recyclerview.widget.LinearLayoutManager - -import com.androiddevs.newsflash.R -import com.androiddevs.newsflash.data.NewsResult -import com.androiddevs.newsflash.databinding.FragmentHomeBinding -import com.androiddevs.newsflash.databinding.FragmentHomeBinding.inflate -import com.androiddevs.newsflash.databinding.HomeNewsRowBinding -import com.androiddevs.newsflash.repository.NewsApiService -import com.androiddevs.newsflash.view.adapter.HomeNewsRecyelerAdapter -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import kotlinx.android.synthetic.main.fragment_home.* - -/** - * A simple [Fragment] subclass. - */ -class HomeFragment : Fragment() { - private val newsApiService by lazy { NewsApiService.create() } - private lateinit var disposable: Disposable - private lateinit var newsList: ArrayList - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val homeBinding = DataBindingUtil.inflate( - inflater, - R.layout.fragment_home, - container, - false - ) - newsList = ArrayList() - disposable = newsApiService.getTopHeadlines("us", "", "", "", 10, 0) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ result -> - for (article in result.articles) { - newsList.add(article) - } - homeRecyclerView.adapter = HomeNewsRecyelerAdapter(newsList) - homeRecyclerView.layoutManager = LinearLayoutManager(context) - }, { error -> Log.v("Error", error.message.toString()) }) - return homeBinding.root - } - - -} diff --git a/app/src/main/java/com/androiddevs/newsflash/viewModelproviders/LoginViewModelProvider.kt b/app/src/main/java/com/androiddevs/newsflash/viewModelproviders/LoginViewModelProvider.kt new file mode 100644 index 0000000..f109a2b --- /dev/null +++ b/app/src/main/java/com/androiddevs/newsflash/viewModelproviders/LoginViewModelProvider.kt @@ -0,0 +1,12 @@ +package com.androiddevs.newsflash.viewModelproviders + +import android.app.Activity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.androiddevs.newsflash.ui.viewModel.LoginViewModel + +class LoginViewModelProvider(private val activity: Activity): ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return LoginViewModel(activity) as T + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/button_background.xml b/app/src/main/res/drawable/button_background.xml new file mode 100644 index 0000000..4dc0f08 --- /dev/null +++ b/app/src/main/res/drawable/button_background.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_facebook.xml b/app/src/main/res/drawable/ic_facebook.xml new file mode 100644 index 0000000..581a952 --- /dev/null +++ b/app/src/main/res/drawable/ic_facebook.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_google.xml b/app/src/main/res/drawable/ic_google.xml new file mode 100644 index 0000000..27a5daf --- /dev/null +++ b/app/src/main/res/drawable/ic_google.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_welcome.xml b/app/src/main/res/drawable/ic_welcome.xml new file mode 100644 index 0000000..7790377 --- /dev/null +++ b/app/src/main/res/drawable/ic_welcome.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/round_button.xml b/app/src/main/res/drawable/round_button.xml new file mode 100644 index 0000000..f3b2d55 --- /dev/null +++ b/app/src/main/res/drawable/round_button.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3c27338..ef2cafa 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ + tools:context=".ui.MainActivity"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index fb5ab35..904cc78 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,13 +1,16 @@ + - + tools:context=".ui.fragments.HomeFragment"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml new file mode 100644 index 0000000..47fee6f --- /dev/null +++ b/app/src/main/res/layout/fragment_login.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + +