diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..603b140
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..88e9dba
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Newzz-Compose
\ No newline at end of file
diff --git a/.idea/codeStyles b/.idea/codeStyles
new file mode 100644
index 0000000..ad98bfd
--- /dev/null
+++ b/.idea/codeStyles
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/nbt762.xml b/.idea/dictionaries/nbt762.xml
new file mode 100644
index 0000000..6ba15ee
--- /dev/null
+++ b/.idea/dictionaries/nbt762.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..b9f8a5e
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
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/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7bfef59
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/render.experimental.xml b/.idea/render.experimental.xml
new file mode 100644
index 0000000..8ec256a
--- /dev/null
+++ b/.idea/render.experimental.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..3fb6130
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,94 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+
+android {
+ compileSdkVersion 29
+
+ defaultConfig {
+ applicationId "com.akash.newzz_compose"
+ minSdkVersion 21
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ Properties properties = new Properties()
+ properties.load(project.rootProject.file('local.properties').newDataInputStream())
+ def apiKey = properties.getProperty("api_key")
+ buildConfigField("String", "API_KEY", apiKey)
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ buildFeatures {
+ compose true
+ }
+
+ composeOptions {
+ kotlinCompilerVersion "1.3.70-dev-withExperimentalGoogleExtensions-20200424"
+ kotlinCompilerExtensionVersion "0.1.0-dev12"
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.0'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+
+ /* ---------------------JetPack Compose-----------------------------*/
+ implementation("androidx.compose:compose-runtime:$compose_version")
+ implementation("androidx.ui:ui-core:$compose_version")
+ implementation "androidx.ui:ui-layout:$compose_version"
+ implementation "androidx.ui:ui-material:$compose_version"
+ implementation "androidx.ui:ui-tooling:$compose_version"
+ implementation "androidx.ui:ui-livedata:$compose_version"
+
+ /* ---------------------JetPack Compose-----------------------------*/
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+
+
+ /* ---------------------Kotlin-coroutines---------------------------*/
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
+
+ /*---------------------Network and Moshi----------------------------*/
+ implementation 'com.squareup.retrofit2:retrofit:2.6.2'
+ implementation 'com.squareup.okhttp3:okhttp:4.2.1'
+ implementation 'com.squareup.retrofit2:converter-moshi:2.6.2'
+ implementation 'com.squareup.moshi:moshi:1.9.1'
+ kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.9.1'
+
+
+ /*-----------------------Koin-------------------------------------*/
+ implementation "org.koin:koin-android:$koin_version"
+ implementation "org.koin:koin-android-viewmodel:$koin_version"
+
+ /*-----------------------Glide-----------------------------*/
+ implementation 'com.github.bumptech.glide:glide:4.11.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
+
+ /*---------------------ChromeCustomTab------------------------------*/
+ implementation 'androidx.browser:browser:1.3.0-alpha01'
+
+ /*---------------------CoilImageLoader------------------------------*/
+ implementation "dev.chrisbanes.accompanist:accompanist-coil:0.1.3"
+
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/akash/newzz_compose/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/akash/newzz_compose/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..b7c1975
--- /dev/null
+++ b/app/src/androidTest/java/com/akash/newzz_compose/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.akash.newzz_compose
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.akash.newzz_compose", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..490acac
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/Category.kt b/app/src/main/java/com/akash/newzz_compose/Category.kt
new file mode 100644
index 0000000..55c7e99
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/Category.kt
@@ -0,0 +1,17 @@
+package com.akash.newzz_compose
+
+import androidx.annotation.StringDef
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+@Retention(AnnotationRetention.SOURCE)
+@StringDef(value = [Category.GENERAL, Category.BUSINESS, Category.TECH])
+internal annotation class Category {
+ companion object {
+ const val GENERAL = "general"
+ const val BUSINESS = "business"
+ const val TECH = "technology"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/Constants.kt b/app/src/main/java/com/akash/newzz_compose/Constants.kt
new file mode 100644
index 0000000..933582c
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/Constants.kt
@@ -0,0 +1,11 @@
+package com.akash.newzz_compose
+
+/**
+ * Created by Akash on 06/06/20
+ */
+object Constants {
+ val API_KEY: String
+ get() = BuildConfig.API_KEY
+ val COUNTRY: String
+ get() = "in"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/NewsApplication.kt b/app/src/main/java/com/akash/newzz_compose/NewsApplication.kt
new file mode 100644
index 0000000..f092d72
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/NewsApplication.kt
@@ -0,0 +1,38 @@
+package com.akash.newzz_compose
+
+import android.app.Application
+import android.content.Context
+import android.net.ConnectivityManager
+import com.akash.newzz_compose.di.appModule
+import org.koin.android.ext.koin.androidContext
+import org.koin.android.ext.koin.androidLogger
+import org.koin.core.context.startKoin
+
+/**
+ * Created by Akash on 06/06/20
+ */
+class NewsApplication : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ instances = this
+ startKoin{
+ androidLogger()
+ androidContext(this@NewsApplication)
+ modules(appModule)
+ }
+ }
+
+
+ companion object {
+ lateinit var instances: NewsApplication
+
+
+ fun isNetworkConnected(): Boolean {
+ val connectivityManager =
+ instances.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val networkInfo = connectivityManager.activeNetworkInfo
+ return networkInfo != null && networkInfo.isConnected
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/apiService/NewsApiService.kt b/app/src/main/java/com/akash/newzz_compose/data/apiService/NewsApiService.kt
new file mode 100644
index 0000000..7f233a6
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/apiService/NewsApiService.kt
@@ -0,0 +1,109 @@
+package com.akash.newzz_compose.data.apiService
+
+/**
+ * Created by Akash on 06/06/20
+ */
+import com.akash.newzz_compose.Constants
+import com.akash.newzz_compose.NewsApplication
+import com.akash.newzz_compose.data.response.NewsResponse
+import okhttp3.Cache
+import okhttp3.CacheControl
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import retrofit2.Response
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.GET
+import retrofit2.http.Query
+import java.io.File
+import java.util.concurrent.TimeUnit
+
+interface NewsApiService {
+
+
+ @GET("top-headlines?sortBy=publishedAt&pageSize=25")
+ suspend fun getArticlesByCateGoryAsync(
+ @Query("category") category: String,
+ @Query("country") country: String = Constants.COUNTRY
+ ): Response
+
+
+ companion object {
+
+ private const val HEADER_CACHE_CONTROL = "Cache-Control"
+ private const val HEADER_PRAGMA = "Pragma"
+ private const val cacheSize = (5 * 1024 * 1024).toLong() // 5 MB
+
+
+ operator fun invoke(): NewsApiService {
+ val requestInterceptor = Interceptor { chain ->
+
+ val response = chain.proceed(chain.request())
+
+ val cacheControl = CacheControl.Builder()
+ .maxAge(5, TimeUnit.SECONDS)
+ .build()
+
+ return@Interceptor response.newBuilder()
+ .removeHeader(HEADER_PRAGMA)
+ .removeHeader(HEADER_CACHE_CONTROL)
+ .header(HEADER_CACHE_CONTROL, cacheControl.toString())
+ .build()
+ }
+
+ val cache = Cache(
+ File(NewsApplication.instances.cacheDir, "networkCache"),
+ cacheSize
+ )
+
+ val offlineInterceptor = Interceptor { chain ->
+
+
+ val url = chain.request()
+ .url
+ .newBuilder()
+ .addQueryParameter("apiKey", Constants.API_KEY)
+ .build()
+
+ var request = chain.request()
+
+ if (NewsApplication.isNetworkConnected()) {
+
+ request = request
+ .newBuilder()
+ .url(url)
+ .build()
+ } else {
+
+ val cacheControl = CacheControl.Builder()
+ .maxStale(7, TimeUnit.DAYS)
+ .build()
+
+ request = request
+ .newBuilder()
+ .url(url)
+ .removeHeader(HEADER_PRAGMA)
+ .removeHeader(HEADER_CACHE_CONTROL)
+ .cacheControl(cacheControl)
+ .build()
+
+ }
+
+ return@Interceptor chain.proceed(request)
+ }
+
+ val okHttpClient = OkHttpClient.Builder()
+ .cache(cache)
+ .addInterceptor(offlineInterceptor)
+ .addNetworkInterceptor(requestInterceptor)
+ .build()
+
+ return Retrofit.Builder()
+ .baseUrl("https://newsapi.org/v2/")
+ .client(okHttpClient)
+ .addConverterFactory(MoshiConverterFactory.create())
+ .build()
+ .create(NewsApiService::class.java)
+ }
+ }
+}
diff --git a/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepository.kt b/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepository.kt
new file mode 100644
index 0000000..a1ca2de
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepository.kt
@@ -0,0 +1,11 @@
+package com.akash.newzz_compose.data.repository
+
+import com.akash.newzz_compose.data.response.NewsResponse
+import com.akash.newzz_compose.utils.Result
+
+/**
+ * Created by Akash on 06/06/20
+ */
+interface NewsRepository {
+ suspend fun getArticlesByCategoryAsync(category: String, page: Int): Result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepositoryImpl.kt b/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepositoryImpl.kt
new file mode 100644
index 0000000..9c0e198
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/repository/NewsRepositoryImpl.kt
@@ -0,0 +1,57 @@
+package com.akash.newzz_compose.data.repository
+
+import com.akash.newzz_compose.data.apiService.NewsApiService
+import com.akash.newzz_compose.data.response.NewsError
+import com.akash.newzz_compose.data.response.NewsResponse
+import com.akash.newzz_compose.utils.Result
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+class NewsRepositoryImpl constructor(
+ private val newsApiService: NewsApiService,
+ private val moshi: Moshi
+) : NewsRepository {
+ override suspend fun getArticlesByCategoryAsync(category: String, page: Int): Result {
+ try {
+ val response = newsApiService.getArticlesByCateGoryAsync(category)
+ return if (response.isSuccessful) {
+ if (response.body() != null) {
+ Result.Success(response.body()!!)
+ } else {
+ Result.Error("No Data found")
+ }
+ } else {
+ val jsonAdapter: JsonAdapter = moshi.adapter(
+ NewsError::class.java
+ )
+ withContext(Dispatchers.IO) {
+ val newsError = jsonAdapter.fromJson(response.errorBody()?.string()!!)
+ Result.Error(
+ newsError!!.message,
+ showRetry(newsError.code)
+ )
+ }
+ }
+
+ } catch (e: Exception) {
+ e.printStackTrace()
+ var errorMessage = e.localizedMessage
+ if (e.localizedMessage!!.contains("Unable to resolve host")) {
+ errorMessage = "No internet connection"
+ }
+ return Result.Error(errorMessage ?: "Something went wrong")
+ }
+ }
+
+ private fun showRetry(code: String): Boolean = when (code) {
+ "apiKeyDisabled", "apiKeyExhausted", "apiKeyInvalid", "apiKeyMissing" -> false
+ else -> true
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/response/NewsArticle.kt b/app/src/main/java/com/akash/newzz_compose/data/response/NewsArticle.kt
new file mode 100644
index 0000000..c7df98a
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/response/NewsArticle.kt
@@ -0,0 +1,22 @@
+package com.akash.newzz_compose.data.response
+
+import com.squareup.moshi.JsonClass
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+@JsonClass(generateAdapter = true)
+data class NewsArticle(
+ val publishedAt: String,
+
+ val source: NewsSource,
+
+ val title: String,
+
+ val description: String? = null,
+
+ val url: String? = null,
+
+ val urlToImage: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/response/NewsError.kt b/app/src/main/java/com/akash/newzz_compose/data/response/NewsError.kt
new file mode 100644
index 0000000..9db5321
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/response/NewsError.kt
@@ -0,0 +1,16 @@
+package com.akash.newzz_compose.data.response
+
+import com.squareup.moshi.JsonClass
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+@JsonClass(generateAdapter = true)
+data class NewsError(
+ val code: String,
+
+ val message: String,
+
+ val status: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/response/NewsResponse.kt b/app/src/main/java/com/akash/newzz_compose/data/response/NewsResponse.kt
new file mode 100644
index 0000000..2183491
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/response/NewsResponse.kt
@@ -0,0 +1,15 @@
+package com.akash.newzz_compose.data.response
+
+import com.squareup.moshi.JsonClass
+
+/**
+ * Created by Akash on 06/06/20
+ */
+@JsonClass(generateAdapter = true)
+data class NewsResponse(
+ val articles: List,
+
+ var status: String,
+
+ val totalResults: Int
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/data/response/NewsSource.kt b/app/src/main/java/com/akash/newzz_compose/data/response/NewsSource.kt
new file mode 100644
index 0000000..944fe86
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/data/response/NewsSource.kt
@@ -0,0 +1,12 @@
+package com.akash.newzz_compose.data.response
+
+import com.squareup.moshi.JsonClass
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+@JsonClass(generateAdapter = true)
+data class NewsSource(
+ val name: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/di/module.kt b/app/src/main/java/com/akash/newzz_compose/di/module.kt
new file mode 100644
index 0000000..4800981
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/di/module.kt
@@ -0,0 +1,20 @@
+package com.akash.newzz_compose.di
+
+import com.akash.newzz_compose.viewmodel.NewzzViewModel
+import com.akash.newzz_compose.data.apiService.NewsApiService
+import com.akash.newzz_compose.data.repository.NewsRepository
+import com.akash.newzz_compose.data.repository.NewsRepositoryImpl
+import com.squareup.moshi.Moshi
+import org.koin.android.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+val appModule = module {
+ viewModel { NewzzViewModel(get()) }
+ single { NewsRepositoryImpl(get(), get()) as NewsRepository }
+ single { NewsApiService() }
+ single { Moshi.Builder().build() }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/style/Color.kt b/app/src/main/java/com/akash/newzz_compose/style/Color.kt
new file mode 100644
index 0000000..06344f7
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/style/Color.kt
@@ -0,0 +1,34 @@
+package com.akash.newzz_compose.style
+
+import androidx.ui.graphics.Color
+
+/**
+ * Created by Akash on 07/06/20
+ */
+
+val deepPurple = Color(0xFF4a148c)
+val darkColor = Color(0xFF121212)
+
+val sourceTextColor = Color(0xFF474646)
+val sourceTextColorDark = Color(0xFF868080)
+
+val titleColor = Color.Black
+val titleColorDark = Color(0xB3DCDCDC)
+
+val listBackgroundColor = Color.White
+val listBackgroundColorDark = Color(0xFF1E1E1E)
+
+val bottomNavBackground = Color.White
+val bottomNavBackgroundDark = Color(0xFF222222)
+
+val bottomNavIconActiveColor = Color(0xFF4a148c)
+val bottomNavIconInActiveColor = Color.Black
+
+val bottomNavIconActiveColorDark = Color(0xB3DCDCDC)
+val bottomNavIconInActiveColorDark = Color(0xFF5C5757)
+
+val circularLoaderColor = deepPurple
+val circularLoaderColorDark = Color.White
+
+val dividerColor = Color(0xFFDCDCDC)
+val dividerColorDark = Color(0xFF2B2929)
diff --git a/app/src/main/java/com/akash/newzz_compose/style/TextStyle.kt b/app/src/main/java/com/akash/newzz_compose/style/TextStyle.kt
new file mode 100644
index 0000000..7c42485
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/style/TextStyle.kt
@@ -0,0 +1,34 @@
+package com.akash.newzz_compose.style
+
+import androidx.ui.graphics.Color
+import androidx.ui.text.TextStyle
+import androidx.ui.text.font.FontWeight
+import androidx.ui.unit.sp
+
+/**
+ * Created by Akash on 07/06/20
+ */
+
+
+val categoryTitleStyle = TextStyle(
+ fontWeight = FontWeight.Bold,
+ fontSize = 20.sp,
+ color = Color.White
+)
+
+val articleTitleStyle = TextStyle(
+ fontWeight = FontWeight.Medium,
+ fontSize = 16.sp,
+ color = titleColor
+)
+
+val sourceTextStyle = TextStyle(
+ fontSize = 14.sp,
+ color = sourceTextColor
+)
+
+val dateTextStyle = TextStyle(
+ fontWeight = FontWeight.ExtraLight,
+ fontSize = 12.sp,
+ color = sourceTextColor
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/style/Theme.kt b/app/src/main/java/com/akash/newzz_compose/style/Theme.kt
new file mode 100644
index 0000000..202953d
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/style/Theme.kt
@@ -0,0 +1,25 @@
+package com.akash.newzz_compose.style
+
+import androidx.ui.material.darkColorPalette
+import androidx.ui.material.lightColorPalette
+
+/**
+ * Created by Akash on 05/06/20
+ */
+
+val themeColor = lightColorPalette(
+ primary = deepPurple,
+ primaryVariant = deepPurple,
+ secondary = deepPurple,
+ secondaryVariant = deepPurple,
+ surface = deepPurple,
+ background = deepPurple
+)
+
+val darkThemeColor = darkColorPalette(
+ primary = darkColor,
+ primaryVariant = darkColor,
+ secondary = darkColor,
+ surface = darkColor
+)
+
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/NewzzActivity.kt b/app/src/main/java/com/akash/newzz_compose/ui/NewzzActivity.kt
new file mode 100644
index 0000000..219adaf
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/NewzzActivity.kt
@@ -0,0 +1,28 @@
+package com.akash.newzz_compose.ui
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.Model
+import androidx.ui.core.setContent
+import androidx.ui.livedata.observeAsState
+import androidx.ui.material.MaterialTheme
+import com.akash.newzz_compose.style.darkThemeColor
+import com.akash.newzz_compose.style.themeColor
+import com.akash.newzz_compose.viewmodel.NewzzViewModel
+import com.akash.newzz_compose.ui.home.Home
+import org.koin.android.viewmodel.ext.android.viewModel
+
+
+class NewzzActivity : AppCompatActivity() {
+ private val viewModel: NewzzViewModel by viewModel()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ val isDark = viewModel.isDarkTheme.observeAsState(false)
+ MaterialTheme(colors = if(isDark.value) darkThemeColor else themeColor) {
+ Home(viewModel)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/articleListPage/ArticleListScreen.kt b/app/src/main/java/com/akash/newzz_compose/ui/articleListPage/ArticleListScreen.kt
new file mode 100644
index 0000000..cca53cc
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/articleListPage/ArticleListScreen.kt
@@ -0,0 +1,104 @@
+package com.akash.newzz_compose.ui.articleListPage
+
+import androidx.compose.Composable
+import androidx.compose.State
+import androidx.ui.core.Alignment
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.Modifier
+import androidx.ui.foundation.Text
+import androidx.ui.foundation.VerticalScroller
+import androidx.ui.foundation.clickable
+import androidx.ui.layout.*
+import androidx.ui.material.Divider
+import androidx.ui.text.style.TextOverflow
+import androidx.ui.unit.dp
+import com.akash.newzz_compose.data.response.NewsArticle
+import com.akash.newzz_compose.style.*
+import com.akash.newzz_compose.ui.common.HeightSpacer
+import com.akash.newzz_compose.ui.common.RemoteImage
+import com.akash.newzz_compose.ui.common.WidthSpacer
+import com.akash.newzz_compose.utils.CustomTabUtil
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+@Composable
+fun ArticleRow(article: NewsArticle, isDark: State, onClick: () -> Unit) {
+ Column(modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp)) {
+ Row(
+ /* onClick with more than one modifier was not working hence wrapping it with Column
+ * Tried modifier = Modifier.clickable(
+ onClick = {
+ onClick()
+ }
+ ).plus(Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp))
+ */
+ modifier = Modifier.clickable(
+ onClick = {
+ onClick()
+ }
+ ),
+ verticalGravity = Alignment.CenterVertically
+ ) {
+ RemoteImage(
+ url = article.urlToImage,
+ modifier = Modifier.preferredHeight(100.dp)
+ .plus(Modifier.preferredWidth(100.dp))
+ )
+ WidthSpacer(value = 10.dp)
+ Column {
+ if (!article.source.name.isNullOrEmpty()) {
+ Text(
+ text = article.source.name,
+ style = if (isDark.value) sourceTextStyle.copy(color = sourceTextColorDark) else sourceTextStyle
+ )
+ HeightSpacer(value = 4.dp)
+ }
+ Text(
+ text = article.title,
+ style = if (isDark.value) articleTitleStyle.copy(color = titleColorDark) else articleTitleStyle,
+ maxLines = 3,
+ overflow = TextOverflow.Ellipsis
+ )
+ HeightSpacer(value = 4.dp)
+ Text(
+ text = article.publishedAt.substring(0, 10),
+ style = if (isDark.value) dateTextStyle.copy(color = sourceTextColorDark) else dateTextStyle
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ArticleList(articles: List, isDark: State) {
+ /**
+ * App crashing while scrolling the AdapterList hence verticalScroller with Columns used.
+ * @see Issue tracker
+ */
+ /*AdapterList(data = articles) { article ->
+ ArticleRow(article = article)
+ Divider(
+ color = Color(0xFFDCDCDC)
+ )
+ }*/
+ val context = ContextAmbient.current
+ VerticalScroller {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ articles.forEach { article ->
+ ArticleRow(
+ article = article,
+ isDark = isDark,
+ onClick = {
+ CustomTabUtil.launch(context, article.url.toString(), isDark.value)
+ }
+ )
+ HeightSpacer(value = 10.dp)
+ Divider(
+ color = if (isDark.value) dividerColorDark else dividerColor
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/common/Common.kt b/app/src/main/java/com/akash/newzz_compose/ui/common/Common.kt
new file mode 100644
index 0000000..7e018d5
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/common/Common.kt
@@ -0,0 +1,66 @@
+package com.akash.newzz_compose.ui.common
+
+import androidx.compose.Composable
+import androidx.ui.core.ContentScale
+import androidx.ui.core.Modifier
+import androidx.ui.foundation.Box
+import androidx.ui.foundation.Image
+import androidx.ui.foundation.shape.corner.RoundedCornerShape
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.Shape
+import androidx.ui.graphics.vector.VectorAsset
+import androidx.ui.layout.Spacer
+import androidx.ui.layout.fillMaxSize
+import androidx.ui.layout.preferredHeight
+import androidx.ui.layout.preferredWidth
+import androidx.ui.material.Surface
+import androidx.ui.res.vectorResource
+import androidx.ui.unit.Dp
+import androidx.ui.unit.dp
+import com.akash.newzz_compose.R
+import dev.chrisbanes.accompanist.coil.CoilImageWithCrossfade
+
+/**
+ * Created by Akash on 07/06/20
+ */
+
+@Composable
+fun HeightSpacer(value: Dp) {
+ Spacer(modifier = Modifier.preferredHeight(value))
+}
+
+@Composable
+fun WidthSpacer(value: Dp) {
+ Spacer(modifier = Modifier.preferredWidth(value))
+}
+
+@Composable
+fun RemoteImage(
+ url: String?,
+ modifier: Modifier,
+ errorImage: VectorAsset = vectorResource(id = R.drawable.ic_newzz_error),
+ contentScale: ContentScale = ContentScale.Crop,
+ shape : Shape = RoundedCornerShape(5.dp)
+) {
+ Box(
+ modifier = modifier
+ ){
+ if (url.isNullOrEmpty()) {
+ Image(
+ modifier = Modifier.fillMaxSize(),
+ asset = errorImage
+ )
+ } else {
+ Surface(
+ color = Color.Transparent,
+ shape = shape
+ ) {
+ CoilImageWithCrossfade(
+ data = url,
+ contentScale = contentScale
+ )
+ }
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/home/BodyContent.kt b/app/src/main/java/com/akash/newzz_compose/ui/home/BodyContent.kt
new file mode 100644
index 0000000..0932eea
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/home/BodyContent.kt
@@ -0,0 +1,176 @@
+package com.akash.newzz_compose.ui.home
+
+import androidx.compose.Composable
+import androidx.compose.State
+import androidx.ui.core.Alignment
+import androidx.ui.core.Modifier
+import androidx.ui.foundation.Box
+import androidx.ui.foundation.Icon
+import androidx.ui.foundation.Text
+import androidx.ui.foundation.shape.corner.RoundedCornerShape
+import androidx.ui.graphics.Color
+import androidx.ui.layout.*
+import androidx.ui.livedata.observeAsState
+import androidx.ui.material.CircularProgressIndicator
+import androidx.ui.material.IconButton
+import androidx.ui.material.Surface
+import androidx.ui.material.TextButton
+import androidx.ui.res.vectorResource
+import androidx.ui.text.TextStyle
+import androidx.ui.unit.dp
+import com.akash.newzz_compose.Category
+import com.akash.newzz_compose.R
+import com.akash.newzz_compose.style.*
+import com.akash.newzz_compose.ui.articleListPage.ArticleList
+import com.akash.newzz_compose.viewmodel.NewzzViewModel
+
+
+/**
+ * Created by Akash on 05/06/20
+ */
+
+@Composable
+fun BodyContent(viewModel: NewzzViewModel) {
+ val page = viewModel.pageNumber.observeAsState(NewzzViewModel.General)
+ val generalState = viewModel.generalState.observeAsState(NewzzViewModel.ArticleState())
+ val businessState = viewModel.businessState.observeAsState(NewzzViewModel.ArticleState())
+ val techState = viewModel.techState.observeAsState(NewzzViewModel.ArticleState())
+ val isDark = viewModel.isDarkTheme.observeAsState(false)
+ Surface(modifier = Modifier.fillMaxSize()) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ TopAppBar(viewModel)
+ Surface(
+ color = if (isDark.value) listBackgroundColorDark else listBackgroundColor,
+ shape = RoundedCornerShape(topLeft = 5.dp, topRight = 5.dp),
+ modifier = Modifier.fillMaxSize().padding(
+ start = 10.dp,
+ end = 10.dp,
+ bottom = 54.dp
+ )
+ ) {
+ when (page.value) {
+ NewzzViewModel.General -> ArticleStateWidget(
+ state = generalState,
+ isDark = isDark,
+ onClick = {
+ viewModel.performAction(NewzzViewModel.Action.FetchArticles(Category.GENERAL))
+ }
+ )
+ NewzzViewModel.Business -> ArticleStateWidget(
+ state = businessState,
+ isDark = isDark,
+ onClick = {
+ viewModel.performAction(NewzzViewModel.Action.FetchArticles(Category.BUSINESS))
+ }
+ )
+ NewzzViewModel.Technology -> ArticleStateWidget(
+ state = techState,
+ isDark = isDark,
+ onClick = {
+ viewModel.performAction(NewzzViewModel.Action.FetchArticles(Category.TECH))
+ }
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun TopAppBar(viewModel: NewzzViewModel) {
+ val page = viewModel.pageNumber.observeAsState(NewzzViewModel.General)
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalGravity = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.padding(top = 40.dp, start = 16.dp, end = 16.dp, bottom = 16.dp),
+ text = getTitle(page.value),
+ style = categoryTitleStyle
+ )
+ Box(modifier = Modifier.padding(top = 24.dp, end = 8.dp)) {
+ ThemeSwitcher(viewModel = viewModel)
+ }
+ }
+}
+
+@Composable
+fun ArticleStateWidget(state: State, isDark: State, onClick: () -> Unit) {
+ when {
+ state.value.isLoading -> {
+ Loading(isDark)
+ }
+ state.value.list != null -> {
+ ArticleList(articles = state.value.list!!, isDark = isDark)
+ }
+ else -> {
+ ErrorView(
+ errorMessage = state.value.error!!.errorMessage,
+ showRetry = state.value.error!!.showRetry,
+ onClick = onClick,
+ isDark = isDark
+ )
+ }
+ }
+}
+
+
+@Composable
+fun ThemeSwitcher(viewModel: NewzzViewModel) {
+ val isDark = viewModel.isDarkTheme.observeAsState(false)
+ val light = vectorResource(id = R.drawable.ic_light)
+ val dark = vectorResource(id = R.drawable.ic_dark)
+ IconButton(onClick = {
+ viewModel.performAction(NewzzViewModel.Action.SwitchTheme)
+ }) {
+ Icon(
+ asset = if (isDark.value) light else dark,
+ tint = Color.White
+ )
+ }
+}
+
+
+@Composable
+fun Loading(isDark: State) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalGravity = Alignment.CenterHorizontally
+ ) {
+ CircularProgressIndicator(
+ color = if (isDark.value) circularLoaderColorDark else circularLoaderColor
+ )
+ }
+}
+
+@Composable
+fun ErrorView(errorMessage: String, showRetry: Boolean, isDark: State, onClick: () -> Unit) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalGravity = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = errorMessage,
+ style = if(isDark.value) articleTitleStyle.copy(color = titleColorDark) else articleTitleStyle
+ )
+ if (showRetry) {
+ TextButton(onClick = onClick) {
+ Text(
+ text = "Retry",
+ style = TextStyle(
+ color = if(isDark.value) sourceTextColorDark else deepPurple
+ )
+ )
+ }
+ }
+ }
+}
+
+
+private fun getTitle(pageNumber: Int): String = when (pageNumber) {
+ 1 -> "General"
+ 2 -> "Business"
+ 3 -> "Technology"
+ else -> throw IllegalAccessException("Page number is invalid")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/home/BottomBar.kt b/app/src/main/java/com/akash/newzz_compose/ui/home/BottomBar.kt
new file mode 100644
index 0000000..857b34e
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/home/BottomBar.kt
@@ -0,0 +1,88 @@
+package com.akash.newzz_compose.ui.home
+
+import androidx.compose.Composable
+import androidx.compose.State
+import androidx.ui.core.Modifier
+import androidx.ui.foundation.Icon
+import androidx.ui.layout.Arrangement
+import androidx.ui.layout.Row
+import androidx.ui.livedata.observeAsState
+import androidx.ui.material.BottomAppBar
+import androidx.ui.material.IconButton
+import androidx.ui.res.vectorResource
+import com.akash.newzz_compose.R
+import com.akash.newzz_compose.style.*
+import com.akash.newzz_compose.viewmodel.NewzzViewModel
+
+/**
+ * Created by Akash on 05/06/20
+ */
+
+@Composable
+fun BottomBar(viewModel: NewzzViewModel) {
+ val isDark = viewModel.isDarkTheme.observeAsState(false)
+ BottomAppBar(backgroundColor = if (isDark.value) bottomNavBackgroundDark else bottomNavBackground) {
+ val page = viewModel.pageNumber.observeAsState(NewzzViewModel.General)
+ Row(
+ modifier = Modifier.weight(1f),
+ horizontalArrangement = Arrangement.SpaceAround
+ ) {
+ BottomNavItem(
+ asset = R.drawable.ic_general,
+ isSelected = page.value == NewzzViewModel.General,
+ isDark = isDark,
+ onClick = {
+ if (page.value != NewzzViewModel.General) {
+ viewModel.performAction(
+ NewzzViewModel.Action.ChangePageTo(
+ NewzzViewModel.General
+ )
+ )
+ }
+ }
+ )
+ BottomNavItem(
+ asset = R.drawable.ic_business,
+ isSelected = page.value == NewzzViewModel.Business,
+ isDark = isDark,
+ onClick = {
+ if (page.value != NewzzViewModel.Business) {
+ viewModel.performAction(
+ NewzzViewModel.Action.ChangePageTo(
+ NewzzViewModel.Business
+ )
+ )
+ }
+ }
+ )
+ BottomNavItem(
+ asset = R.drawable.ic_tech,
+ isSelected = page.value == NewzzViewModel.Technology,
+ isDark = isDark,
+ onClick = {
+ if (page.value != NewzzViewModel.Technology) {
+ viewModel.performAction(
+ NewzzViewModel.Action.ChangePageTo(
+ NewzzViewModel.Technology
+ )
+ )
+ }
+ }
+ )
+ }
+ }
+}
+
+@Composable
+fun BottomNavItem(asset: Int, isDark: State, onClick: () -> Unit, isSelected: Boolean = false) {
+ IconButton(onClick = onClick) {
+ Icon(
+ asset = vectorResource(id = asset),
+ tint = if (isDark.value) {
+ if (isSelected) bottomNavIconActiveColorDark else bottomNavIconInActiveColorDark
+ } else {
+ if (isSelected) bottomNavIconActiveColor else bottomNavIconInActiveColor
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/ui/home/Home.kt b/app/src/main/java/com/akash/newzz_compose/ui/home/Home.kt
new file mode 100644
index 0000000..60b32c2
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/ui/home/Home.kt
@@ -0,0 +1,19 @@
+package com.akash.newzz_compose.ui.home
+
+import androidx.compose.Composable
+import androidx.ui.material.Scaffold
+import com.akash.newzz_compose.viewmodel.NewzzViewModel
+
+/**
+ * Created by Akash on 05/06/20
+ */
+
+@Composable
+fun Home(
+ viewModel: NewzzViewModel
+) {
+ Scaffold(
+ bodyContent = { BodyContent(viewModel) },
+ bottomAppBar = { BottomBar(viewModel) }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/utils/CustomTabUtil.kt b/app/src/main/java/com/akash/newzz_compose/utils/CustomTabUtil.kt
new file mode 100644
index 0000000..ef58d15
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/utils/CustomTabUtil.kt
@@ -0,0 +1,60 @@
+package com.akash.newzz_compose.utils
+
+import android.content.Context
+import android.net.Uri
+import androidx.browser.customtabs.CustomTabsIntent
+import androidx.core.content.ContextCompat
+import com.akash.newzz_compose.R
+
+/**
+ * Created by Akash on 07/06/20
+ */
+object CustomTabUtil {
+ private var builder: CustomTabsIntent? = null
+ private var builderDark: CustomTabsIntent? = null
+
+ fun launch(context: Context, url: String, isDark: Boolean) {
+ if (isDark) {
+ if (builderDark == null) {
+ builderDark = CustomTabsIntent.Builder()
+ .setToolbarColor(
+ ContextCompat.getColor(context, R.color.darkTheme)
+ )
+ .setShowTitle(true)
+ .setStartAnimations(
+ context,
+ R.anim.slide_in_right,
+ R.anim.slide_out_left
+ )
+ .setExitAnimations(
+ context,
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right
+ )
+ .build()
+ }
+ builderDark?.launchUrl(context, Uri.parse(url))
+ } else {
+ if (builder == null) {
+ builder = CustomTabsIntent.Builder()
+ .setToolbarColor(
+ ContextCompat.getColor(context, R.color.lightTheme)
+ )
+ .setShowTitle(true)
+ .setStartAnimations(
+ context,
+ R.anim.slide_in_right,
+ R.anim.slide_out_left
+ )
+ .setExitAnimations(
+ context,
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right
+ )
+ .build()
+ }
+ builder?.launchUrl(context, Uri.parse(url))
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/akash/newzz_compose/utils/Result.kt b/app/src/main/java/com/akash/newzz_compose/utils/Result.kt
new file mode 100644
index 0000000..c4328a5
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/utils/Result.kt
@@ -0,0 +1,10 @@
+package com.akash.newzz_compose.utils
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+sealed class Result {
+ data class Success(val data: T) : Result()
+ data class Error(val errorMessage: String, val showRetry: Boolean = true) : Result()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/akash/newzz_compose/viewmodel/NewzzViewModel.kt b/app/src/main/java/com/akash/newzz_compose/viewmodel/NewzzViewModel.kt
new file mode 100644
index 0000000..83f61ac
--- /dev/null
+++ b/app/src/main/java/com/akash/newzz_compose/viewmodel/NewzzViewModel.kt
@@ -0,0 +1,207 @@
+package com.akash.newzz_compose.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.akash.newzz_compose.Category
+import com.akash.newzz_compose.data.repository.NewsRepository
+import com.akash.newzz_compose.data.response.NewsArticle
+import com.akash.newzz_compose.utils.Result
+import kotlinx.coroutines.*
+
+/**
+ * Created by Akash on 06/06/20
+ */
+
+class NewzzViewModel(private val repo: NewsRepository) : ViewModel() {
+
+ private val job = SupervisorJob()
+ private val viewModelScope = CoroutineScope(Dispatchers.Main) + job
+
+ private val _pageNumber = MutableLiveData().apply {
+ value =
+ General
+ }
+ val pageNumber: LiveData = _pageNumber
+
+ private val _isDarkTheme = MutableLiveData().apply { value = false }
+ val isDarkTheme: LiveData = _isDarkTheme
+
+ private val generalArticlesListState = ArticleState()
+ private val businessArticlesListState = ArticleState()
+ private val techArticlesListState = ArticleState()
+
+ private val _generalState = MutableLiveData().apply {
+ value = generalArticlesListState
+ }
+ val generalState: LiveData = _generalState
+
+ private val _businessState = MutableLiveData().apply {
+ value = businessArticlesListState
+ }
+ val businessState: LiveData = _businessState
+
+ private val _techState = MutableLiveData().apply {
+ value = techArticlesListState
+ }
+ val techState: LiveData = _techState
+
+ init {
+ performAction(Action.FetchArticles(Category.GENERAL))
+ }
+
+ fun performAction(action: Action) {
+ when (action) {
+ is Action.ChangePageTo -> {
+ _pageNumber.value = action.page
+ when (action.page) {
+ General -> performAction(
+ Action.FetchArticles(Category.GENERAL)
+ )
+ Business -> performAction(
+ Action.FetchArticles(Category.BUSINESS)
+ )
+ Technology -> performAction(
+ Action.FetchArticles(Category.TECH)
+ )
+ }
+ }
+ is Action.FetchArticles -> viewModelScope.launch {
+ withContext(Dispatchers.IO) {
+ fetArticles(action.category)
+ }
+ }
+ is Action.SwitchTheme -> {
+ _isDarkTheme.value = !_isDarkTheme.value!!
+ }
+ }
+ }
+
+ private suspend fun fetArticles(category: String) {
+ when (category) {
+ Category.GENERAL -> fetchGeneralArticles(category)
+ Category.BUSINESS -> fetchBusinessArticles(category)
+ Category.TECH -> fetchTechArticles(category)
+ }
+ }
+
+ private suspend fun fetchGeneralArticles(category: String) {
+ val state = generalState.value!!
+ if (state.list == null || state.list.isEmpty()) {
+ withContext(Dispatchers.Main) {
+ val articleState = ArticleState()
+ _generalState.value = articleState
+ }
+ when (val result = repo.getArticlesByCategoryAsync(category, 1)) {
+ is Result.Success -> {
+ withContext(Dispatchers.Main) {
+ val articleState = generalArticlesListState.copy(
+ isLoading = false,
+ list = result.data.articles,
+ error = null
+ )
+ _generalState.value = articleState
+ }
+ }
+ is Result.Error -> {
+ withContext(Dispatchers.Main) {
+ val articleState = generalArticlesListState.copy(
+ isLoading = false,
+ list = null,
+ error = Error(result.errorMessage, result.showRetry)
+ )
+ _generalState.value = articleState
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun fetchBusinessArticles(category: String) {
+ val state = businessState.value!!
+ if (state.list == null || state.list.isEmpty()) {
+ withContext(Dispatchers.Main){
+ val articleState = ArticleState()
+ _businessState.value = articleState
+ }
+ when (val result = repo.getArticlesByCategoryAsync(category, 1)) {
+ is Result.Success -> {
+ withContext(Dispatchers.Main) {
+ val articleState = businessArticlesListState.copy(
+ isLoading = false,
+ list = result.data.articles,
+ error = null
+ )
+ _businessState.value = articleState
+ }
+ }
+ is Result.Error -> {
+ withContext(Dispatchers.Main) {
+ val articleState = businessArticlesListState.copy(
+ isLoading = false,
+ list = null,
+ error = Error(result.errorMessage, result.showRetry)
+ )
+ _businessState.value = articleState
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun fetchTechArticles(category: String) {
+ val state = techState.value!!
+ if (state.list == null || state.list.isEmpty()) {
+ withContext(Dispatchers.Main){
+ val articleState = ArticleState()
+ _techState.value = articleState
+ }
+ when (val result = repo.getArticlesByCategoryAsync(category, 1)) {
+ is Result.Success -> {
+ withContext(Dispatchers.Main) {
+ val articleState = techArticlesListState.copy(
+ isLoading = false,
+ list = result.data.articles,
+ error = null
+ )
+ _techState.value = articleState
+ }
+ }
+ is Result.Error -> {
+ withContext(Dispatchers.Main) {
+ val articleState = techArticlesListState.copy(
+ isLoading = false,
+ list = null,
+ error = Error(result.errorMessage, result.showRetry)
+ )
+ _techState.value = articleState
+ }
+ }
+ }
+ }
+ }
+
+ sealed class Action {
+ data class ChangePageTo(val page: Int) : Action()
+ data class FetchArticles(val category: String) : Action()
+ object SwitchTheme : Action()
+ }
+
+ data class ArticleState(
+ val isLoading: Boolean = true,
+ val list: List? = emptyList(),
+ val error: Error? = null
+ )
+
+ data class Error(
+ val errorMessage : String,
+ val showRetry : Boolean = true
+ )
+
+ companion object {
+ const val General = 1
+ const val Business = 2
+ const val Technology = 3
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml
new file mode 100644
index 0000000..8f18d59
--- /dev/null
+++ b/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml
new file mode 100644
index 0000000..0a5f875
--- /dev/null
+++ b/app/src/main/res/anim/slide_out_left.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_business.xml b/app/src/main/res/drawable/ic_business.xml
new file mode 100644
index 0000000..26d1a50
--- /dev/null
+++ b/app/src/main/res/drawable/ic_business.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dark.xml b/app/src/main/res/drawable/ic_dark.xml
new file mode 100644
index 0000000..c097af7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dark.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_general.xml b/app/src/main/res/drawable/ic_general.xml
new file mode 100644
index 0000000..afbda8d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_general.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_light.xml b/app/src/main/res/drawable/ic_light.xml
new file mode 100644
index 0000000..f674100
--- /dev/null
+++ b/app/src/main/res/drawable/ic_light.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_newzz_error.xml b/app/src/main/res/drawable/ic_newzz_error.xml
new file mode 100644
index 0000000..945e792
--- /dev/null
+++ b/app/src/main/res/drawable/ic_newzz_error.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_tech.xml b/app/src/main/res/drawable/ic_tech.xml
new file mode 100644
index 0000000..ffb5131
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tech.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a571e60
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..61da551
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c41dd28
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..db5080a
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..6dba46d
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..da31a87
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..15ac681
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..b216f2d
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f25a419
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..e96783c
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d0bfae3
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,8 @@
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+ #FF121212
+ #FF4a148c
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..5577ecb
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Newzz-Compose
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..f5ff934
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/akash/newzz_compose/ExampleUnitTest.kt b/app/src/test/java/com/akash/newzz_compose/ExampleUnitTest.kt
new file mode 100644
index 0000000..ec004de
--- /dev/null
+++ b/app/src/test/java/com/akash/newzz_compose/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.akash.newzz_compose
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..5502686
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,29 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.3.72"
+ ext.compose_version = "0.1.0-dev12"
+ ext.coroutines_version = "1.3.6"
+ ext.koin_version = "2.1.5"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.0.0-alpha09"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..4d15d01
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..6b6c668
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 05 18:29:18 IST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..dd98e84
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Newzz-Compose"
\ No newline at end of file