Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • muelle45/recipeapp
1 result
Show changes
Commits on Source (2)
Showing
with 637 additions and 0 deletions
*.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
local.properties
# Default ignored files
/shelf/
/workspace.xml
My Application
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/drawable/recipe_item_background.xml" value="0.2265" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/drawable/step_background.xml" value="0.124" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/activity_cook_mode.xml" value="0.11302083333333333" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/activity_cooking_mode.xml" value="0.2" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/activity_main.xml" value="0.2" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/activity_recipe_detail.xml" value="0.264" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/activity_settings.xml" value="0.2" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/dialog_add_recipe.xml" value="0.2" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/fragment_cook_mode.xml" value="0.11302083333333333" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/fragment_dashboard.xml" value="0.2269021739130435" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/fragment_home.xml" value="0.264" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/item_recipe.xml" value="0.5117466802860061" />
<entry key="..\:/Studium/MobileComputing/MyApplication/app/src/main/res/layout/recipe_list_item.xml" value="0.4" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.myapplication"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
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 {
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation 'androidx.preference:preference:1.1.1'
}
\ No newline at end of file
# 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
package com.example.myapplication
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.example.myapplication", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.myapplication">
<activity android:name=".SettingsActivity" />
<activity
android:name="com.example.myapplication.MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RecipeDetailActivity" />
<activity android:name="com.example.myapplication.CookingModeActivity" />
</application>
</manifest>
\ No newline at end of file
package com.example.myapplication
import android.app.Dialog
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
class AddRecipeDialogFragment(
private val onRecipeAdded: (Recipe) -> Unit // Callback für das Hinzufügen eines Rezepts
) : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Dialog-Builder initialisieren
val builder = AlertDialog.Builder(requireContext())
// Dialog-Layout inflaten
val dialogView = requireActivity().layoutInflater.inflate(R.layout.dialog_add_recipe, null)
// UI-Elemente aus dem Layout referenzieren
val nameEditText = dialogView.findViewById<EditText>(R.id.recipeNameInput)
val ingredientsEditText = dialogView.findViewById<EditText>(R.id.recipeIngredientsInput)
val instructionsEditText = dialogView.findViewById<EditText>(R.id.recipeInstructionsInput)
val addButton = dialogView.findViewById<Button>(R.id.saveRecipeButton)
// Dialog-Layout setzen
builder.setView(dialogView)
val dialog = builder.create()
// Hinzufügen-Button Logik
addButton.setOnClickListener {
val name = nameEditText.text.toString().trim()
val ingredients = ingredientsEditText.text.toString().trim()
val instructions = instructionsEditText.text.toString().trim()
// Schritte aus den Anweisungen extrahieren
val steps = instructions.split(",").map { it.trim() }.filter { it.isNotEmpty() }
if (name.isNotEmpty() && ingredients.isNotEmpty() && instructions.isNotEmpty()) {
// Neues Rezept erstellen und übergeben
val newRecipe = Recipe(name, ingredients, instructions, steps)
onRecipeAdded(newRecipe)
dialog.dismiss()
// Erfolgsmeldung
Toast.makeText(requireContext(), "Rezept wurde erfolgreich gespeichert!", Toast.LENGTH_SHORT).show()
} else {
// Fehlerhinweis, wenn nicht alle Felder ausgefüllt wurden
Toast.makeText(requireContext(), "Bitte füllen Sie alle Felder aus", Toast.LENGTH_SHORT).show()
}
}
return dialog
}
}
package com.example.myapplication
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class CookingModeActivity : AppCompatActivity() {
private var currentStepIndex = 0
private var timer: CountDownTimer? = null
private var remainingTime: Long = 60_000 // Standardzeit pro Schritt: 1 Minute
private var isPaused = true
private lateinit var steps: List<String> // Liste der Schritte für das Rezept
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Theme basierend auf Einstellungen setzen
val preferences = getSharedPreferences("appSettings", MODE_PRIVATE)
val savedColorOption = preferences.getInt("colorOption", 1)
setTheme(if (savedColorOption == 1) R.style.Theme_myapplication else R.style.Theme_MyApp_BlueTheme)
setContentView(R.layout.activity_cooking_mode)
// Rezept und Schritte aus Intent abrufen
val recipe = intent.getParcelableExtra<Recipe>("RECIPE")
steps = recipe?.steps ?: emptyList()
// UI-Elemente initialisieren
val currentStepText = findViewById<TextView>(R.id.currentStepText)
val timerText = findViewById<TextView>(R.id.timerText)
val startPauseButton = findViewById<Button>(R.id.startPauseButton)
val nextStepButton = findViewById<Button>(R.id.nextStepButton)
// Button-Text basierend auf dem Timer-Zustand setzen
startPauseButton.text = if (isPaused) "Pause" else "Start"
// UI initialisieren
updateUI(currentStepText, timerText, startPauseButton, nextStepButton)
// Start/Pause-Button Logik
startPauseButton.setOnClickListener {
if (isPaused) {
startTimer(timerText)
startPauseButton.text = "Pause"
Toast.makeText(this, "Zeit läuft weiter", Toast.LENGTH_SHORT).show()
} else {
pauseTimer()
startPauseButton.text = "Start"
Toast.makeText(this, "Zeit angehalten", Toast.LENGTH_SHORT).show()
}
}
// Nächster Schritt-Button Logik
nextStepButton.setOnClickListener {
pauseTimer()
currentStepIndex++
updateUI(currentStepText, timerText, startPauseButton, nextStepButton)
startPauseButton.text = "Pause"
}
}
private fun startTimer(timerText: TextView) {
timer?.cancel() // Vorherigen Timer abbrechen
timer = object : CountDownTimer(remainingTime, 1_000) {
override fun onTick(millisUntilFinished: Long) {
remainingTime = millisUntilFinished
val minutes = millisUntilFinished / 60_000
val seconds = (millisUntilFinished / 1_000) % 60
timerText.text = String.format("%02d:%02d", minutes, seconds)
}
override fun onFinish() {
currentStepIndex++
updateUI(
findViewById(R.id.currentStepText),
findViewById(R.id.timerText),
findViewById(R.id.startPauseButton),
findViewById(R.id.nextStepButton)
)
}
}.start()
isPaused = false
}
private fun pauseTimer() {
timer?.cancel()
isPaused = true
}
private fun updateUI(
currentStepText: TextView,
timerText: TextView,
startPauseButton: Button,
nextStepButton: Button
) {
if (currentStepIndex < steps.size) {
currentStepText.text = steps[currentStepIndex]
remainingTime = 60_000
startTimer(timerText)
} else {
// Alle Schritte abgeschlossen
currentStepText.text = "Alle Schritte abgeschlossen!"
timer?.cancel()
startPauseButton.isEnabled = false
nextStepButton.isEnabled = false
timerText.text = "00:00"
}
}
}
\ No newline at end of file
package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import android.widget.ImageButton
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
class MainActivity : AppCompatActivity() {
private val recipes = mutableListOf<Recipe>() // Rezeptliste
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Theme basierend auf Einstellungen setzen
val preferences = getSharedPreferences("appSettings", MODE_PRIVATE)
val savedColorOption = preferences.getInt("colorOption", 1)
setTheme(if (savedColorOption == 1) R.style.Theme_myapplication else R.style.Theme_MyApp_BlueTheme)
setContentView(R.layout.activity_main)
// RecyclerView und FloatingActionButton initialisieren
val recyclerView = findViewById<RecyclerView>(R.id.recipeRecyclerView)
val addButton = findViewById<FloatingActionButton>(R.id.addRecipeButton)
// Dummy-Rezepte hinzufügen
recipes.addAll(
listOf(
Recipe("Tomatensuppe", "Tomaten, Salz, Pfeffer", "Tomaten kochen und würzen", listOf("Tomaten klein schneiden", "Mit Wasser in einem Topf kochen", "Mit Salz und Pfeffer würzen"), "android.resource://com.example.myapplication/drawable/tomatensuppe"),
Recipe("Spaghetti Bolognese", "Spaghetti, Hackfleisch, Tomatensoße", "Spaghetti kochen und Hackfleisch anbraten", listOf("Spaghetti in Salzwasser kochen", "Hackfleisch in einer Pfanne anbraten", "Tomatensauce hinzufügen und würzen"), "android.resource://com.example.myapplication/drawable/spaghetti"),
Recipe("Pancakes", "Mehl, Milch, Eier", "Alles verrühren und braten", listOf("Alle Zutaten in einer Schüssel verrühren", "In einer Pfanne Butter schmelzen", "Teig in die Pfanne gießen und 5 min ausbacken"), "android.resource://com.example.myapplication/drawable/pancakes")
)
)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = RecipeAdapter(recipes) { recipe ->
val intent = Intent(this, RecipeDetailActivity::class.java)
intent.putExtra("RECIPE", recipe)
startActivity(intent)
}
addButton.setOnClickListener {
AddRecipeDialogFragment { newRecipe ->
recipes.add(newRecipe)
recyclerView.adapter?.notifyItemInserted(recipes.size - 1)
}.show(supportFragmentManager, "AddRecipeDialogFragment")
}
findViewById<ImageButton>(R.id.settingsButton).setOnClickListener {
startActivity(Intent(this, SettingsActivity::class.java))
}
}
}
\ No newline at end of file
package com.example.myapplication
import android.os.Parcel
import android.os.Parcelable
data class Recipe(
val name: String,
val ingredients: String,
val instructions: String,
val steps: List<String>,
val imageUri: String? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.readString() ?: "",
parcel.createStringArrayList() ?: listOf(),
parcel.readString(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeString(ingredients)
parcel.writeString(instructions)
parcel.writeStringList(steps)
parcel.writeString(imageUri)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Recipe> {
override fun createFromParcel(parcel: Parcel): Recipe {
return Recipe(parcel)
}
override fun newArray(size: Int): Array<Recipe?> {
return arrayOfNulls(size)
}
}
}
package com.example.myapplication
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class RecipeAdapter(
private val recipes: List<Recipe>, // Liste der Rezepte
private val onRecipeClick: (Recipe) -> Unit // Callback für Klick auf ein Rezept
) : RecyclerView.Adapter<RecipeAdapter.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(recipe: Recipe) {
val title = itemView.findViewById<TextView>(R.id.recipeTitle)
val image = itemView.findViewById<ImageView>(R.id.recipeImage)
// Rezeptdaten anzeigen
title.text = recipe.name
if (recipe.imageUri != null) {
image.setImageURI(Uri.parse(recipe.imageUri))
} else {
image.setImageResource(R.drawable.platzhalter) // Platzhalterbild
}
// Klick-Listener für das Rezept
itemView.setOnClickListener {
onRecipeClick(recipe)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recipe, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(recipes[position])
}
override fun getItemCount() = recipes.size // Gesamtanzahl der Rezepte
}
\ No newline at end of file
package com.example.myapplication
import android.net.Uri
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import android.widget.TextView
import android.widget.ImageView
import android.content.Intent
class RecipeDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Theme basierend auf gespeicherten Einstellungen setzen
val preferences = getSharedPreferences("appSettings", MODE_PRIVATE)
val savedColorOption = preferences.getInt("colorOption", 1)
setTheme(if (savedColorOption == 1) R.style.Theme_myapplication else R.style.Theme_MyApp_BlueTheme)
setContentView(R.layout.activity_recipe_detail)
// Rezeptdaten aus Intent abrufen
val recipe = intent.getParcelableExtra<Recipe>("RECIPE")
// UI-Elemente initialisieren
val recipeNameTextView = findViewById<TextView>(R.id.recipeTitle)
val recipeImageView = findViewById<ImageView>(R.id.recipeImage)
val recipeIngredientsTextView = findViewById<TextView>(R.id.recipeIngredients)
val recipeInstructionsTextView = findViewById<TextView>(R.id.recipeInstructions)
val startCookingButton = findViewById<Button>(R.id.startCookingButton)
// Rezeptinformationen anzeigen
recipe?.let {
recipeNameTextView.text = it.name
recipeIngredientsTextView.text = "Zutaten: ${it.ingredients}"
recipeInstructionsTextView.text = "Anleitung: ${it.instructions}"
if (!it.imageUri.isNullOrEmpty()) {
recipeImageView.setImageURI(Uri.parse(it.imageUri))
} else {
recipeImageView.setImageResource(R.drawable.platzhalter) // Platzhalterbild
}
}
// Klick-Listener für den "Kochen starten"-Button
startCookingButton.setOnClickListener {
val intent = Intent(this, CookingModeActivity::class.java)
intent.putExtra("RECIPE", recipe)
startActivity(intent)
}
}
}
package com.example.myapplication
import android.content.SharedPreferences
import android.os.Bundle
import android.widget.Button
import android.widget.RadioButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Theme basierend auf Einstellungen setzen
val preferences = getSharedPreferences("appSettings", MODE_PRIVATE)
val savedColorOption = preferences.getInt("colorOption", 1)
setTheme(if (savedColorOption == 1) R.style.Theme_myapplication else R.style.Theme_MyApp_BlueTheme)
setContentView(R.layout.activity_settings)
val colorOption1 = findViewById<RadioButton>(R.id.colorOption1)
val colorOption2 = findViewById<RadioButton>(R.id.colorOption2)
val saveButton = findViewById<Button>(R.id.saveButton)
// Aktuelle Einstellungen laden
colorOption1.isChecked = savedColorOption == 1
colorOption2.isChecked = savedColorOption == 2
saveButton.setOnClickListener {
// Auswahl speichern
val newColorOption = if (colorOption1.isChecked) 1 else 2
preferences.edit().putInt("colorOption", newColorOption).apply()
// Rückmeldung an den Nutzer
Toast.makeText(this, "Einstellungen erfolgreich gespeichert!", Toast.LENGTH_SHORT).show()
}
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
app/src/main/res/drawable/add.png

571 B