Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Recipes Favorites #8

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.devtools.ksp'
id 'dagger.hilt.android.plugin'
id 'androidx.navigation.safeargs'
}

android {
Expand All @@ -15,6 +19,7 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "BASE_URL", "\"https://api.npoint.io/\""
}

buildTypes {
Expand All @@ -24,21 +29,58 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
kapt {
generateStubs = true
}
dataBinding {
enabled = true
}
}

dependencies {

implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
// sdp & ssp library for support all sizes on the screen
implementation "com.intuit.sdp:sdp-android:$sdp_version"
implementation "com.intuit.ssp:ssp-android:$ssp_version"
// media3-exoplayer library for playing videos
implementation "androidx.media3:media3-exoplayer:$media3_version"
implementation "androidx.media3:media3-ui:$media3_version"
// dagger hilt dependencies for dependency injection
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
// datastore for saving data in the local storage
implementation "androidx.datastore:datastore-preferences:$datastore_version"
// mockito for testing purposes
testImplementation "org.mockito:mockito-core:$mockito_version"
// lifecycle scope for observing data
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
// ktx for kotlin extensions
implementation 'androidx.activity:activity-ktx:1.7.2'
implementation 'androidx.fragment:fragment-ktx:1.6.1'
// navigation component for navigating between fragments
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
// retrofit for networking calls
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
// coil library for loading images
implementation "io.coil-kt:coil:$coil_version"
// room database
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
ksp "androidx.room:room-compiler:$room_version"
}
12 changes: 11 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:name=".app.PhoodApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -12,15 +17,20 @@
android:supportsRtl="true"
android:theme="@style/Theme.Phood"
tools:targetApi="31">

<activity
android:name=".MainActivity"
android:name=".identity.ui.view.LoginActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".app.main.MainActivity"
android:exported="true" />
</application>

</manifest>
Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 0 additions & 11 deletions app/src/main/java/com/moataz/phood/MainActivity.kt

This file was deleted.

7 changes: 7 additions & 0 deletions app/src/main/java/com/moataz/phood/app/PhoodApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.moataz.phood.app

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class PhoodApplication : Application()
20 changes: 20 additions & 0 deletions app/src/main/java/com/moataz/phood/app/di/DataSourceModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.moataz.phood.app.di

import com.moataz.phood.identity.data.local.IdentityLocalDataSourceImpl
import com.moataz.phood.identity.data.repositories.datasources.IdentityLocalDataSource
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class DataSourceModule {

@Singleton
@Binds
abstract fun bindIdentityDataSource(
identityLocalDataSourceImpl: IdentityLocalDataSourceImpl,
): IdentityLocalDataSource
}
49 changes: 49 additions & 0 deletions app/src/main/java/com/moataz/phood/app/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.moataz.phood.app.di

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.room.Room
import com.moataz.phood.app.di.utils.Constant.DATABASE_NAME
import com.moataz.phood.app.di.utils.Constant.PREFERENCE_NAME
import com.moataz.phood.recipes.data.local.RecipesDao
import com.moataz.phood.recipes.data.local.RecipesDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

@Singleton
@Provides
fun provideDatabase(@ApplicationContext appContext: Context): RecipesDatabase {
return Room.databaseBuilder(
appContext,
RecipesDatabase::class.java,
DATABASE_NAME,
).build()
}

@Singleton
@Provides
fun provideRecipesDao(database: RecipesDatabase): RecipesDao {
return database.recipesDao()
}

@Provides
@Singleton
fun provideIdentityDataStorePreferences(
@ApplicationContext applicationContext: Context,
): DataStore<Preferences> {
return PreferenceDataStoreFactory.create {
applicationContext.preferencesDataStoreFile(PREFERENCE_NAME)
}
}
}
63 changes: 63 additions & 0 deletions app/src/main/java/com/moataz/phood/app/di/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.moataz.phood.app.di

import com.moataz.phood.BuildConfig
import com.moataz.phood.identity.data.remote.IdentityService
import com.moataz.phood.identity.data.remote.IdentityServiceImpl
import com.moataz.phood.recipes.data.remote.RecipesService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

@Provides
@Singleton
fun provideIdentityService(): IdentityService {
return IdentityServiceImpl()
}

@Singleton
@Provides
fun provideRecipesNetworkService(
retrofit: Retrofit,
): RecipesService {
return retrofit.create(RecipesService::class.java)
}

@Singleton
@Provides
fun provideOkHttpClient(): OkHttpClient {
val builder = OkHttpClient()
.newBuilder()
.callTimeout(25, TimeUnit.SECONDS)
.connectTimeout(25, TimeUnit.SECONDS)
return builder.build()
}

@Singleton
@Provides
fun provideGsonConverterFactory(): GsonConverterFactory {
return GsonConverterFactory.create()
}

@Singleton
@Provides
fun provideRetrofit(
okHttpClient: OkHttpClient,
gsonConverterFactory: GsonConverterFactory,
): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(gsonConverterFactory)
.build()
}
}
28 changes: 28 additions & 0 deletions app/src/main/java/com/moataz/phood/app/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.moataz.phood.app.di

import com.moataz.phood.identity.data.repositories.IdentityRepositoryImpl
import com.moataz.phood.identity.domain.repository.IdentityRepository
import com.moataz.phood.recipes.data.repositories.RecipesRepositoryImpl
import com.moataz.phood.recipes.domain.repository.RecipesRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@Module
@InstallIn(ViewModelComponent::class)
abstract class RepositoryModule {

@ViewModelScoped
@Binds
abstract fun bindIdentityRepository(
identityRepositoryImpl: IdentityRepositoryImpl,
): IdentityRepository

@ViewModelScoped
@Binds
abstract fun bindRecipesRepository(
recipesRepositoryImpl: RecipesRepositoryImpl,
): RecipesRepository
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/moataz/phood/app/di/utils/Constant.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.moataz.phood.app.di.utils

object Constant {
const val DATABASE_NAME = "phood_recipes.db"
const val PREFERENCE_NAME = "com.moataz.phood.app_preferences"
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/moataz/phood/app/main/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.moataz.phood.app.main

import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.moataz.phood.R
import com.moataz.phood.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
initView()
}

private fun initView() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
window.statusBarColor = resources.getColor(R.color.main_color)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.moataz.phood.identity.data.local

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import com.moataz.phood.identity.data.local.utils.DataStorePreferencesKeys.LOGIN_STATE
import com.moataz.phood.identity.data.repositories.datasources.IdentityLocalDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import javax.inject.Inject

class IdentityLocalDataSourceImpl @Inject constructor(
private val userDataStore: DataStore<Preferences>,
) : IdentityLocalDataSource {
override suspend fun saveLoggedInStatus(status: Boolean) {
withContext(Dispatchers.IO) {
userDataStore.edit { preferences ->
preferences[booleanPreferencesKey(LOGIN_STATE)] = status
}
}
}

override suspend fun getLoggedInStatus(): Boolean? {
return withContext(Dispatchers.IO) {
userDataStore.data.map { preferences ->
preferences[booleanPreferencesKey(LOGIN_STATE)]
}.first()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.moataz.phood.identity.data.local.utils

object DataStorePreferencesKeys {
const val LOGIN_STATE = "login_state"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.moataz.phood.identity.data.remote

import com.moataz.phood.identity.data.remote.dto.UserDTO

interface IdentityService {
suspend fun signIn(email: String, password: String): UserDTO
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moataz.phood.identity.data.remote

import com.moataz.phood.identity.data.remote.dto.UserDTO

class IdentityServiceImpl : IdentityService {
override suspend fun signIn(email: String, password: String): UserDTO {
return UserDTO()
}
}
Loading