diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..296046a --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Topten \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7643783..3cc336b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,10 +1,25 @@ + + - diff --git a/.idea/misc.xml b/.idea/misc.xml index d5d35ec..4700283 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 6026007..b42d3a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,7 +72,7 @@ android { dependencies { - implementation 'androidx.work:work-runtime:2.3.4' + implementation 'androidx.work:work-runtime:2.4.0' def work_version = "2.4.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" @@ -95,9 +95,12 @@ dependencies { // Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:2.2.5" + // LiveData + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" + // Coroutines - 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-core:1.3.9' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.6' // Navigation Components @@ -112,11 +115,13 @@ dependencies { implementation 'io.coil-kt:coil:0.11.0' // JSoup for HTML parsing - implementation 'org.jsoup:jsoup:1.12.1' + implementation 'org.jsoup:jsoup:1.13.1' // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" + // Preferences DataStore + implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01" } \ No newline at end of file diff --git a/app/src/main/java/www/thecodemonks/techbytes/NYTimes.kt b/app/src/main/java/www/thecodemonks/techbytes/NYTimes.kt index 8232bed..5adf471 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/NYTimes.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/NYTimes.kt @@ -27,21 +27,5 @@ package www.thecodemonks.techbytes import android.app.Application -import androidx.appcompat.app.AppCompatDelegate -import www.thecodemonks.techbytes.utils.isNight -class NYTimes : Application() { - - override fun onCreate() { - super.onCreate() - - // check for ui mode & set accordingly - val mode = if (isNight()) { - AppCompatDelegate.MODE_NIGHT_YES - } else { - AppCompatDelegate.MODE_NIGHT_NO - } - - AppCompatDelegate.setDefaultNightMode(mode) - } -} \ No newline at end of file +class NYTimes : Application() \ No newline at end of file diff --git a/app/src/main/java/www/thecodemonks/techbytes/utils/TimeUtils.kt b/app/src/main/java/www/thecodemonks/techbytes/datastore/UIModePreference.kt similarity index 55% rename from app/src/main/java/www/thecodemonks/techbytes/utils/TimeUtils.kt rename to app/src/main/java/www/thecodemonks/techbytes/datastore/UIModePreference.kt index daaa7ba..b303abb 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/utils/TimeUtils.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/datastore/UIModePreference.kt @@ -24,11 +24,36 @@ * */ -package www.thecodemonks.techbytes.utils +package www.thecodemonks.techbytes.datastore -import java.util.* +import android.content.Context +import androidx.datastore.DataStore +import androidx.datastore.preferences.Preferences +import androidx.datastore.preferences.createDataStore +import androidx.datastore.preferences.edit +import androidx.datastore.preferences.preferencesKey +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map -fun isNight(): Boolean { - val currentHour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) - return (currentHour <= 7 || currentHour >= 18) +class UIModePreference(context: Context) { + + companion object { + private val UI_MODE_KEY = preferencesKey("ui_mode") + } + + private val dataStore: DataStore = context.createDataStore( + name = "ui_mode_preference" + ) + + suspend fun saveToDataStore(isNightMode: Boolean) { + dataStore.edit { preferences -> + preferences[UI_MODE_KEY] = isNightMode + } + } + + val uiMode: Flow = dataStore.data + .map { preferences -> + val uiMode = preferences[UI_MODE_KEY] ?: false + uiMode + } } \ No newline at end of file diff --git a/app/src/main/java/www/thecodemonks/techbytes/ui/articles/ArticlesFragment.kt b/app/src/main/java/www/thecodemonks/techbytes/ui/articles/ArticlesFragment.kt index 6327b28..4449c33 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/ui/articles/ArticlesFragment.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/ui/articles/ArticlesFragment.kt @@ -30,11 +30,15 @@ import android.os.Bundle import android.view.Menu import android.view.MenuInflater import android.view.MenuItem +import androidx.appcompat.app.AppCompatDelegate import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import kotlinx.android.synthetic.main.fragment_articles.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import www.thecodemonks.techbytes.R import www.thecodemonks.techbytes.model.Category import www.thecodemonks.techbytes.ui.adapter.CategoryAdapter @@ -132,19 +136,43 @@ class ArticlesFragment : Fragment(R.layout.fragment_articles) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { // Inflate the menu; this adds items to the action bar if it is present. inflater.inflate(R.menu.menu, menu) + + // Set the item state + lifecycleScope.launch { + val isChecked = viewModel.readDataStore.first() + val item = menu.findItem(R.id.action_night_mode) + item.isChecked = isChecked + setUIMode(isChecked) + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { // Handle action bar item clicks here. return when (item.itemId) { - R.id.action_one -> { + R.id.action_bookmark -> { findNavController().navigate(R.id.action_articlesFragment_to_bookmarksFragment) true } + + R.id.action_night_mode -> { + item.isChecked = !item.isChecked + setUIMode(item.isChecked) + true + } else -> super.onOptionsItemSelected(item) } } + private fun setUIMode(isChecked: Boolean) { + if (isChecked) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + viewModel.saveToDataStore(true) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + viewModel.saveToDataStore(false) + } + } + private fun setUpArticleRV() { newsAdapter = NewsAdapter() article_rv.apply { diff --git a/app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseActivity.kt b/app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseActivity.kt index 3d37085..17e036a 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseActivity.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/ui/base/BaseActivity.kt @@ -60,10 +60,10 @@ class BaseActivity : AppCompatActivity() { val repo = Repo( ArticleDatabase(this) ) - val viewModelProviderFactory = - NewsViewModelProviderFactory( - repo - ) + + // Passing application to ViewModel for DataStore + val viewModelProviderFactory = NewsViewModelProviderFactory(this.application, repo) + viewModel = ViewModelProvider(this, viewModelProviderFactory).get(ArticleViewModel::class.java) diff --git a/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ArticleViewModel.kt b/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ArticleViewModel.kt index 7db1f26..8502966 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ArticleViewModel.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ArticleViewModel.kt @@ -26,23 +26,29 @@ package www.thecodemonks.techbytes.ui.viewmodel +import android.app.Application +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch +import www.thecodemonks.techbytes.datastore.UIModePreference import www.thecodemonks.techbytes.model.Article import www.thecodemonks.techbytes.repo.Repo import www.thecodemonks.techbytes.utils.Constants -class ArticleViewModel(private val repo: Repo) : ViewModel() { +class ArticleViewModel(application: Application, private val repo: Repo) : + AndroidViewModel(application) { private val _articles = MutableLiveData>() val articles: LiveData> get() = _articles + // DataStore + private val uiDataStore = UIModePreference(application) + val currentTopic: MutableLiveData by lazy { MutableLiveData().defaultTopic(Constants.NY_TECH) } @@ -67,6 +73,16 @@ class ArticleViewModel(private val repo: Repo) : ViewModel() { } } + // Get From DataStore + val readDataStore = uiDataStore.uiMode + + // Save to DataStore + fun saveToDataStore(isNightMode: Boolean) { + viewModelScope.launch(IO) { + uiDataStore.saveToDataStore(isNightMode) + } + } + private fun MutableLiveData.defaultTopic(initialValue: T) = apply { setValue(initialValue) } diff --git a/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ViewModelProviderFactory.kt b/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ViewModelProviderFactory.kt index 901d19f..8ba34e9 100644 --- a/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ViewModelProviderFactory.kt +++ b/app/src/main/java/www/thecodemonks/techbytes/ui/viewmodel/ViewModelProviderFactory.kt @@ -26,15 +26,16 @@ package www.thecodemonks.techbytes.ui.viewmodel +import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import www.thecodemonks.techbytes.repo.Repo -class NewsViewModelProviderFactory(private val repo: Repo) : +class NewsViewModelProviderFactory(private val application: Application, private val repo: Repo) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return ArticleViewModel(repo) as T + return ArticleViewModel(application, repo) as T } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_outline_dark.xml b/app/src/main/res/drawable/ic_outline_dark.xml new file mode 100644 index 0000000..5652851 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_dark.xml @@ -0,0 +1,36 @@ + + + + + diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml index fffc197..8b66a6e 100644 --- a/app/src/main/res/menu/menu.xml +++ b/app/src/main/res/menu/menu.xml @@ -2,9 +2,15 @@ + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9d30b41..a5acb1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,7 +29,11 @@ Facebook Said Not to Consider Banning Political Ads The social network has been under intense pressure for allowing misinformation and hate speech to spread on its site. By Sanju S + + Bookmark + Night Mode Switch + Bookmarks No bookmarks yet Hello blank fragment diff --git a/build.gradle b/build.gradle index 6fb8a5d..7595777 100644 --- a/build.gradle +++ b/build.gradle @@ -26,13 +26,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.3.72" + ext.kotlin_version = "1.4.10" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.0-alpha09' + classpath 'com.android.tools.build:gradle:4.2.0-alpha12' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b9ad6d7..4522e33 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -29,4 +29,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-rc-6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-6.6.1-bin.zip