Compare commits

..

3 Commits

Author SHA1 Message Date
Fawkes100
f99bbf3899 Merge remote-tracking branch 'origin/notevault-2' into notevault-2
# Conflicts:
#	.idea/deploymentTargetDropDown.xml
2025-01-18 08:04:45 +01:00
Fawkes100
193f8ee18c Clear Project for Rewriting 2025-01-18 08:04:31 +01:00
Fawkes100
6daedc49da Clear Project for Rewriting 2025-01-18 08:04:23 +01:00
109 changed files with 244 additions and 4403 deletions

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
NoteVault

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="music_database" uuid="7fd8322d-6535-44e0-a663-86c7b54f4fbd">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/music_database</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="music_database [2]" uuid="6f801a54-4854-4db5-910c-ae63a8207d3e">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/music_database</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="music_database [3]" uuid="3acbe0c9-6415-4726-8c75-c07350984fcc">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$USER_HOME$/.cache/JetBrains/IntelliJIdea2024.1/device-explorer/Pixel Tablet API 30/_/data/data/core.notevault/databases/music_database</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

View File

@ -2,9 +2,6 @@
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="NoteVault">
<State />
</entry>
<entry key="app">
<State />
</entry>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -1,12 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

View File

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,16 +1,15 @@
plugins {
alias(libs.plugins.android.application)
id("com.android.application")
}
android {
namespace = "core.notevault"
compileSdk = 35
namespace = "com.stormtales.notevault"
compileSdk = 34
defaultConfig {
applicationId = "core.notevault"
minSdk = 28
targetSdk = 35
applicationId = "com.stormtales.notevault"
minSdk = 29
targetSdk = 34
versionCode = 1
versionName = "1.0"
@ -22,10 +21,6 @@ android {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
debug {
// Setzt den Manifest-Platzhalter nur für den Debug-Build auf true
manifestPlaceholders["usesCleartextTraffic"] = "true"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
@ -38,32 +33,14 @@ android {
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.lifecycle.livedata.ktx)
implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
implementation(libs.room.common)
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation(libs.car.ui.lib)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
val room_version = "2.6.1" // Aktuelle Room-Version
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version") // Für Annotation Processing
// Optional: Unterstützung für Kotlin Coroutines oder RxJava
implementation("androidx.room:room-ktx:$room_version") // Für Kotlin-Extensions
implementation("androidx.room:room-rxjava3:$room_version") // Für RxJava-Unterstützung
implementation("androidx.work:work-runtime:2.10.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.7")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
implementation("androidx.navigation:navigation-fragment:2.8.3")
implementation("androidx.navigation:navigation-ui:2.8.3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
}

View File

@ -1,4 +1,4 @@
package core.notevault;
package com.stormtales.notevault;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
@ -20,6 +20,6 @@ public class ExampleInstrumentedTest {
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("core.notevault", appContext.getPackageName());
assertEquals("com.stormtales.notevault", appContext.getPackageName());
}
}

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -12,7 +11,6 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NoteVault"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
@ -25,12 +23,6 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ui.home.FullScreenImageActivity"
android:theme="@style/Theme.AppCompat.NoActionBar">
<!-- Intent Filter hinzufügen, wenn Activity vom Launcher oder externen Apps aufgerufen werden soll -->
</activity>
</application>
</manifest>

View File

@ -0,0 +1,62 @@
package com.stormtales.notevault;
import android.os.Bundle;
import android.view.View;
import android.view.Menu;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.navigation.NavigationView;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import com.stormtales.notevault.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}

View File

@ -0,0 +1,35 @@
package com.stormtales.notevault.ui.gallery;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.stormtales.notevault.databinding.FragmentGalleryBinding;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
GalleryViewModel galleryViewModel =
new ViewModelProvider(this).get(GalleryViewModel.class);
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textGallery;
galleryViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,19 @@
package com.stormtales.notevault.ui.gallery;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class GalleryViewModel extends ViewModel {
private final MutableLiveData<String> mText;
public GalleryViewModel() {
mText = new MutableLiveData<>();
mText.setValue("This is gallery fragment");
}
public LiveData<String> getText() {
return mText;
}
}

View File

@ -0,0 +1,35 @@
package com.stormtales.notevault.ui.home;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.stormtales.notevault.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,19 @@
package com.stormtales.notevault.ui.home;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class HomeViewModel extends ViewModel {
private final MutableLiveData<String> mText;
public HomeViewModel() {
mText = new MutableLiveData<>();
mText.setValue("This is home fragment");
}
public LiveData<String> getText() {
return mText;
}
}

View File

@ -1,4 +1,4 @@
package core.notevault.ui.slideshow;
package com.stormtales.notevault.ui.slideshow;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -8,7 +8,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import core.notevault.databinding.FragmentSlideshowBinding;
import com.stormtales.notevault.databinding.FragmentSlideshowBinding;
public class SlideshowFragment extends Fragment {

View File

@ -1,4 +1,4 @@
package core.notevault.ui.slideshow;
package com.stormtales.notevault.ui.slideshow;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

View File

@ -1,308 +0,0 @@
package core.notevault;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.Menu;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.fragment.NavHostFragment;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.navigation.NavigationView;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AppCompatActivity;
import core.notevault.data.*;
import core.notevault.databinding.ActivityMainBinding;
import core.notevault.sync.SyncWorker;
import core.notevault.sync.auth.AuthRepository;
import core.notevault.sync.auth.LoginCallback;
import core.notevault.ui.gallery.GalleryFragment;
import core.notevault.ui.gallery.detail.ConcertSongSelector;
import core.notevault.ui.gallery.editor.ConcertEditorDialog;
import core.notevault.ui.home.HomeFragment;
import core.notevault.ui.login.LoginCallBackImpl;
import core.notevault.ui.login.LoginDialogFragment;
import core.notevault.ui.login.RegisterCallback;
import core.notevault.ui.metadatadialog.MetaDataDialog;
import core.notevault.ui.metadatadialog.SongEditDialog;
import core.notevault.util.NoteSheetsUtil;
import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener,
ConcertEditorDialog.OnConcertEditorListener, ConcertSongSelector.OnSongSelectedListener, LoginCallback, SongEditDialog.SongEditorListener {
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
private MusicDatabase musicDB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setOpenableLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
setupLoginButton();
musicDB = MusicDatabase.getDatabase(this);
scheduleSync();
}
public void scheduleSync() {
// Setze eine Uhrzeit für die Synchronisation
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY,11);
calendar.set(Calendar.MINUTE, 32);
calendar.set(Calendar.SECOND, 0);
long triggerTime = calendar.getTimeInMillis(); // Zeitstempel für den Trigger
// Erstelle die OneTimeWorkRequest für den SyncWorker
OneTimeWorkRequest syncRequest = new OneTimeWorkRequest.Builder(SyncWorker.class)
.setInitialDelay(triggerTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS) // Verzögerung bis zur Ausführung
.build();
// Planen des Workers mit WorkManager
WorkManager.getInstance(getBaseContext()).enqueue(syncRequest);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem loginItem = menu.findItem(R.id.action_login);
if (isLoggedIn()) {
loginItem.setIcon(R.drawable.logout); // Setze das Logout-Symbol
} else {
loginItem.setIcon(R.drawable.login); // Setze das Login-Symbol
}
return super.onPrepareOptionsMenu(menu);
}
public void updateLoginButton() {
invalidateOptionsMenu(); // Menü neu zeichnen
}
private void setupLoginButton() {
binding.appBarMain.toolbar.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.action_login) {
if (isLoggedIn()) {
performLogout(); // Logout-Logik aufrufen
} else {
showLoginDialog(); // Zeige das Login-Fenster an
}
return true;
}
return false;
});
}
private boolean isLoggedIn() {
AuthRepository authRepository = new AuthRepository(this);
return !TextUtils.isEmpty(authRepository.getToken());
}
private void showLoginDialog() {
LoginDialogFragment loginDialogFragment = LoginDialogFragment.newInstance(new LoginCallBackImpl(this), new RegisterCallback(this));
loginDialogFragment.show(getSupportFragmentManager(), "login");
}
private void performLogout() {
// Füge hier deine Logout-Logik hinzu
AuthRepository authRepository = new AuthRepository(this);
authRepository.logout(); // Implementiere die Token-Löschung
updateLoginButton();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == R.id.action_login) {
openLoginDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void openLoginDialog() {
LoginDialogFragment loginDialogFragment = new LoginDialogFragment();
loginDialogFragment.show(getSupportFragmentManager(), "LOGIN_TAG");
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
@Override
public void onMetadataEntered(Uri[] uris, String title, String composer, int year, String genre) {
new Thread(() -> {
MusicNote musicNote = new MusicNote(title, composer, year, genre);
long musicNoteID = musicDB.musicNoteDao().insert(musicNote);
musicNote.setMusicNoteId(musicNoteID);
Log.d("MainActivity", "MusicNoteID of inserted song: " + musicNoteID);
Log.d("MainActivity", "MusicNoteID of referenced song: " + musicNote.getMusicNoteId());
for(Uri uri: uris) {
String filePath = saveImageInternally(uri);
NoteSheet noteSheet = new NoteSheet(musicNoteID, filePath);
musicDB.musicNoteDao().insertNoteSheet(noteSheet);
}
runOnUiThread(() -> {
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main);
Fragment currentFragment = navHostFragment != null ? navHostFragment.getChildFragmentManager().getFragments().get(0) : null;
if(currentFragment instanceof HomeFragment) {
HomeFragment homeFragment = (HomeFragment) currentFragment;
homeFragment.addSong(musicNote);
}
});
}).start();
}
// Speichere eine Kopie des Bilds im internen Speicher
private String saveImageInternally(Uri uri) {
try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
File imageFile = new File(getFilesDir(), "saved_image_" + System.currentTimeMillis() + ".jpg");
try (OutputStream outputStream = Files.newOutputStream(imageFile.toPath())) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
return imageFile.getAbsolutePath(); // Pfad speichern, um später darauf zuzugreifen
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
public void onConcertEditorClosed(String concertTitle, String concertDate, int concertID) {
new Thread(() -> {
if(concertID < 0) {
Concert concert = new Concert(concertTitle, concertDate);
Log.d("ConcertEditor", "Saved Concert: " + concertTitle + " on " + concertDate);
musicDB.musicNoteDao().insertConcert(concert);
runOnUiThread(() -> {
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main);
Fragment currentFragment = navHostFragment != null ? navHostFragment.getChildFragmentManager().getFragments().get(0) : null;
Log.d("MainActivity", "Test for GalleryFragment");
Log.d("MainActivity", "Current Fragment: " + currentFragment.getClass().getSimpleName());
if(currentFragment instanceof GalleryFragment) {
Log.d("MainActivity", "GalleryFragment Found");
GalleryFragment galleryFragment = (GalleryFragment) currentFragment;
galleryFragment.addConcert(concert);
} else {
Log.d("MainActivity", "GalleryFragment Not Found");
}
});
} else {
Concert concert = new Concert(concertID, concertTitle, concertDate);
musicDB.musicNoteDao().updateConcert(concert);
runOnUiThread(() -> {
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main);
Fragment currentFragment = navHostFragment != null ? navHostFragment.getChildFragmentManager().getFragments().get(0) : null;
Log.d("MainActivity", "Test for GalleryFragment");
Log.d("MainActivity", "Current Fragment: " + currentFragment.getClass().getSimpleName());
if(currentFragment instanceof GalleryFragment) {
Log.d("MainActivity", "GalleryFragment Found");
GalleryFragment galleryFragment = (GalleryFragment) currentFragment;
galleryFragment.updateConcert(concert);
} else {
Log.d("MainActivity", "GalleryFragment Not Found");
}
});
}
}).start();
}
@Override
public void onSongsSelected(List<MusicNote> songs, int concertID) {
Log.d("MainActivity", "Inserted Songs: " + songs.size());
new Thread(() -> {
for(MusicNote musicNote : songs) {
ConcertSong concertSong = new ConcertSong(musicNote.getMusicNoteId(), concertID);
musicDB.musicNoteDao().insertConcertSong(concertSong);
Log.d("MainActivity", "Insert Song: " + musicNote.getTitle());
}
}).start();
}
@Override
public void onSuccess() {
updateLoginButton();
}
@Override
public void onError(String error) {
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
Log.d("LoginError", error);
}
@Override
public void onSongEdited(MusicNote updatedSong) {
new Thread(() -> {
musicDB.musicNoteDao().updateSong(updatedSong);
Log.d("MainActivity", "Updated Song: " + updatedSong.getTitle());
}).start();
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_main);
Fragment currentFragment = navHostFragment != null ? navHostFragment.getChildFragmentManager().getFragments().get(0) : null;
if(currentFragment instanceof HomeFragment) {
HomeFragment homeFragment = (HomeFragment) currentFragment;
homeFragment.updateSong(updatedSong);
} else if(currentFragment instanceof GalleryFragment) {
GalleryFragment galleryFragment = (GalleryFragment) currentFragment;
}
}
}

View File

@ -1,90 +0,0 @@
package core.notevault.data;
import androidx.room.*;
import core.notevault.data.sync.SyncStatus;
import core.notevault.data.sync.SyncStatusConverter;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Locale;
@Entity(tableName = "concerts")
@TypeConverters(SyncStatusConverter.class)
public class Concert {
@PrimaryKey(autoGenerate = true)
private int id;
private String serverSpecificConcertID;
private String title;
private String concertDate;
private SyncStatus syncStatus;
private LocalDateTime lastModified;
@Ignore
public Concert(String title, String concertDate) {
this.title = title;
this.concertDate = concertDate;
this.lastModified = LocalDateTime.now();
}
public Concert() {
}
@Ignore
public Concert(int id, String title, String concertDate) {
this.id = id;
this.title = title;
this.concertDate = concertDate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getConcertDate() {
return concertDate;
}
public void setConcertDate(String concertDate) {
this.concertDate = concertDate;
}
public String getServerSpecificConcertID() {
return serverSpecificConcertID;
}
public void setServerSpecificConcertID(String serverSpecificConcertID) {
this.serverSpecificConcertID = serverSpecificConcertID;
}
public SyncStatus getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(SyncStatus syncStatus) {
this.syncStatus = syncStatus;
}
public LocalDateTime getLastModified() {
return lastModified;
}
public void setLastModified(LocalDateTime lastModified) {
this.lastModified = lastModified;
}
}

View File

@ -1,55 +0,0 @@
package core.notevault.data;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
@Entity(tableName = "concert_songs")
public class ConcertSong {
@PrimaryKey(autoGenerate = true)
private int id;
private long musicNoteID;
private int concertID;
@Ignore
public ConcertSong(long musicNoteID, int concertID) {
this.musicNoteID = musicNoteID;
this.concertID = concertID;
}
@Ignore
public ConcertSong(int id, long musicNoteID, int concertID) {
this.id = id;
this.musicNoteID = musicNoteID;
this.concertID = concertID;
}
public ConcertSong() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getMusicNoteID() {
return musicNoteID;
}
public void setMusicNoteID(long musicNoteID) {
this.musicNoteID = musicNoteID;
}
public int getConcertID() {
return concertID;
}
public void setConcertID(int concertID) {
this.concertID = concertID;
}
}

View File

@ -1,20 +0,0 @@
package core.notevault.data;
import androidx.room.TypeConverter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateConverter {
// Konvertiere LocalDateTime in String
@TypeConverter
public static String fromLocalDateTime(LocalDateTime localDateTime) {
return localDateTime == null ? null : localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
// Konvertiere String zurück in LocalDateTime
@TypeConverter
public static LocalDateTime toLocalDateTime(String dateTimeString) {
return dateTimeString == null ? null : LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}

View File

@ -1,27 +0,0 @@
package core.notevault.data;
import android.content.Context;
import androidx.room.*;
import core.notevault.data.sync.SyncDataObject;
import core.notevault.data.sync.SyncStatusConverter;
@Database(entities = {MusicNote.class, NoteSheet.class, Concert.class, ConcertSong.class, SyncDataObject.class}, version = 2, exportSchema = false)
@TypeConverters(DateConverter.class)
public abstract class MusicDatabase extends RoomDatabase {
public abstract MusicNoteDAO musicNoteDao();
private static MusicDatabase INSTANCE;
public static MusicDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (MusicDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
MusicDatabase.class, "music_database")
.build();
}
}
}
return INSTANCE;
}
}

View File

@ -1,107 +0,0 @@
package core.notevault.data;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import core.notevault.data.sync.SyncStatus;
import java.time.LocalDateTime;
@Entity(tableName = "music_notes")
public class MusicNote {
@PrimaryKey(autoGenerate = true)
private long musicNoteId;
private String serverID;
private SyncStatus syncStatus;
private LocalDateTime last_sync;
private String title;
private String composer;
private int year;
private String genre;
@Ignore
public MusicNote(String title, String composer, int year, String genre) {
this.title = title;
this.composer = composer;
this.year = year;
this.genre = genre;
this.syncStatus = SyncStatus.CREATED;
}
public MusicNote(long musicNoteId, String title) {
this.musicNoteId = musicNoteId;
this.title = title;
this.syncStatus = SyncStatus.CREATED;
}
@Ignore
public MusicNote() {
}
public long getMusicNoteId() {
return musicNoteId;
}
public void setMusicNoteId(long musicNoteId) {
this.musicNoteId = musicNoteId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComposer() {
return composer;
}
public void setComposer(String composer) {
this.composer = composer;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
public SyncStatus getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(SyncStatus syncStatus) {
this.syncStatus = syncStatus;
}
public LocalDateTime getLast_sync() {
return last_sync;
}
public void setLast_sync(LocalDateTime last_sync) {
this.last_sync = last_sync;
}
}

View File

@ -1,89 +0,0 @@
package core.notevault.data;
import androidx.room.*;
import core.notevault.data.sync.SyncDataObject;
import core.notevault.data.sync.SyncStatus;
import core.notevault.sync.synchronisation.songs.creation.SongCreationResponse;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Dao
public interface MusicNoteDAO {
@Insert
long insert(MusicNote musicNote);
@Insert
void insertNoteSheet(NoteSheet noteSheet);
@Query("SELECT * FROM music_notes WHERE syncStatus != 'DELETED'")
List<MusicNote> getAllNotes();
@Query("SELECT * FROM note_sheets WHERE musicNoteId = :musicNoteId")
List<NoteSheet> getNoteSheetsForMusicSong(long musicNoteId);
@Query("SELECT * FROM concerts WHERE syncStatus = :status")
List<Concert> getConcertsWithSyncStatus(SyncStatus status);
@Query("SELECT * FROM music_notes WHERE syncStatus = :status")
List<MusicNote> getSongsWithSyncStatus(SyncStatus status);
@Insert
void insertConcert(Concert concert);
@Query("SELECT * FROM concerts")
List<Concert> getAllConcerts();
@Delete
void deleteConcert(Concert concert);
@Update
void updateConcert(Concert concert);
@Delete
void deleteSong(MusicNote musicNote);
@Insert
void insertConcertSong(ConcertSong concertSong);
@Query("SELECT m.* FROM music_notes m JOIN concert_songs cs ON m.musicNoteId = cs.musicNoteID WHERE cs.id = :concertID")
List<MusicNote> getAllMusicNotesOfConcert(long concertID);
@Query("DELETE FROM concert_songs WHERE concertID = :concertID AND musicNoteID = :songID")
void deleteConcertSong(long songID, int concertID);
@Transaction
default void storeSongSyncResponses(List<SongCreationResponse> songCreationResponses) {
for (SongCreationResponse response : songCreationResponses) {
storeSongSyncResponse(response.getLocalID(), SyncStatus.SYNCED, response.getServerID());
}
}
@Query("UPDATE music_notes SET syncStatus = :status, serverID = :serverID WHERE musicNoteId = :localID")
void storeSongSyncResponse(long localID, SyncStatus status, String serverID);
@Query("UPDATE music_notes SET syncStatus = :status, last_sync = :currentTime WHERE serverID IN (:serverIDs)")
void storeSongSyncModifyResponses(List<String> serverIDs, SyncStatus status, LocalDateTime currentTime);
@Update
void updateSong(MusicNote musicNote);
@Query("DELETE FROM music_notes WHERE serverID IN (:serverIDs)")
void deleteSongs(List<String> serverIDs);
@Query("SELECT * FROM note_sheets ns WHERE ns.syncStatus = :status")
List<NoteSheet> getNoteSheetsWithStatus(SyncStatus status);
@Query("SELECT mn.serverID FROM music_notes mn WHERE mn.musicNoteId =:musicNoteId")
Optional<String> getMusicNoteByLocalID(long musicNoteId);
@Update
void updateNoteSheet(NoteSheet noteSheet);
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE) // oder andere Strategie je nach Bedarf
void insertSyncDataObjects(List<SyncDataObject> syncDataObjects);
}

View File

@ -1,56 +0,0 @@
package core.notevault.data;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import core.notevault.data.sync.SyncStatus;
@Entity(tableName = "note_sheets")
public class NoteSheet {
@PrimaryKey(autoGenerate = true)
private int id;
private SyncStatus syncStatus;
private long musicNoteId;
private String filePath;
public NoteSheet(long musicNoteID, String filePath) {
this.musicNoteId = musicNoteID;
this.filePath = filePath;
this.syncStatus = SyncStatus.CREATED;
}
public NoteSheet() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getMusicNoteId() {
return musicNoteId;
}
public void setMusicNoteId(long musicNoteId) {
this.musicNoteId = musicNoteId;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public SyncStatus getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(SyncStatus syncStatus) {
this.syncStatus = syncStatus;
}
}

View File

@ -1,49 +0,0 @@
package core.notevault.data.sync;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;
@Entity(tableName = "sync_data_objects")
@TypeConverters(SyncDataObjectTypeConverter.class)
public class SyncDataObject {
@PrimaryKey(autoGenerate = true)
private int id;
private String serverID;
private SyncDataObjectType type;
@Ignore
public SyncDataObject(String serverID, SyncDataObjectType type) {
this.serverID = serverID;
this.type = type;
}
public SyncDataObject() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
public SyncDataObjectType getType() {
return type;
}
public void setType(SyncDataObjectType type) {
this.type = type;
}
}

View File

@ -1,5 +0,0 @@
package core.notevault.data.sync;
public enum SyncDataObjectType {
MUSIC_NOTE
}

View File

@ -1,15 +0,0 @@
package core.notevault.data.sync;
import androidx.room.TypeConverter;
public class SyncDataObjectTypeConverter {
@TypeConverter
public static SyncDataObjectType fromInt(int value) {
return SyncDataObjectType.values()[value];
}
@TypeConverter
public static int toInt(SyncDataObjectType objectType) {
return objectType.ordinal();
}
}

View File

@ -1,9 +0,0 @@
package core.notevault.data.sync;
public enum SyncStatus {
CREATED,
DELETED,
MODIFIED,
REMOTE_MODIFIED,
SYNCED;
}

View File

@ -1,15 +0,0 @@
package core.notevault.data.sync;
import androidx.room.TypeConverter;
public class SyncStatusConverter {
@TypeConverter
public static SyncStatus fromInt(int value) {
return SyncStatus.values()[value];
}
@TypeConverter
public static int toInt(SyncStatus syncStatus) {
return syncStatus.ordinal();
}
}

View File

@ -1,7 +0,0 @@
package core.notevault.sync;
public interface APICallback {
void onSuccess();
void onError(String error);
}

View File

@ -1,34 +0,0 @@
package core.notevault.sync;
import android.content.Context;
import android.content.SharedPreferences;
import core.notevault.sync.auth.AuthInterceptor;
import core.notevault.sync.auth.TokenManager;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
private static Retrofit retrofit;
public static Retrofit getRetrofitInstance(Context context) {
if (retrofit == null) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
TokenManager tokenManager = new TokenManager(context);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new AuthInterceptor(tokenManager))
.addInterceptor(loggingInterceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.178.30:8000/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

View File

@ -1,14 +0,0 @@
package core.notevault.sync;
public class StatusResponse {
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@ -1,38 +0,0 @@
package core.notevault.sync;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import core.notevault.data.MusicDatabase;
import core.notevault.sync.synchronisation.*;
import core.notevault.sync.synchronisation.songs.SongSyncWorker;
public class SyncWorker extends Worker{
private SongSyncWorker songSyncWorker;
public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
SyncService syncService = ApiClient.getRetrofitInstance(context).create(SyncService.class);
MusicDatabase musicDatabase = MusicDatabase.getDatabase(context);
this.songSyncWorker = new SongSyncWorker(syncService, musicDatabase.musicNoteDao(), context);
}
@NonNull
@Override
public Result doWork() {
try {
songSyncWorker.syncSongCreations();
songSyncWorker.syncSongModifications();
songSyncWorker.syncSongDeletions();
songSyncWorker.uploadNoteSheets();
songSyncWorker.fetchModifiedSongs();
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.failure();
}
}
}

View File

@ -1,32 +0,0 @@
package core.notevault.sync.auth;
import android.content.SharedPreferences;
import android.util.Log;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class AuthInterceptor implements Interceptor {
private TokenManager tokenManager;
public AuthInterceptor(TokenManager tokenManager) {
this.tokenManager = tokenManager;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String token = tokenManager.getToken();
if (token == null) {
return chain.proceed(originalRequest);
}
Request authenticatedRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + token).build();
return chain.proceed(authenticatedRequest);
}
}

View File

@ -1,80 +0,0 @@
package core.notevault.sync.auth;
import android.content.Context;
import core.notevault.sync.APICallback;
import core.notevault.sync.ApiClient;
import core.notevault.sync.StatusResponse;
import core.notevault.sync.auth.apimodel.LoginRequest;
import core.notevault.sync.auth.apimodel.LoginResponse;
import core.notevault.sync.auth.apimodel.RegisterRequest;
import core.notevault.sync.auth.apimodel.RegisterResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class AuthRepository {
private final AuthService authService;
private final TokenManager tokenManager;
public AuthRepository(Context context) {
this.authService = ApiClient.getRetrofitInstance(context).create(AuthService.class);
this.tokenManager = new TokenManager(context);
}
public void performLogin(String email, String password, APICallback callback) {
LoginRequest loginRequest = new LoginRequest(email, password);
authService.login(loginRequest).enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.isSuccessful() && response.body() != null) {
String token = response.body().getToken();
saveToken(token);
// Erfolgsrückmeldung an den Callback senden
callback.onSuccess();
} else {
// Fehlermeldung an den Callback senden
callback.onError("Login fehlgeschlagen. Überprüfe Benutzername und Passwort.");
}
}
@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
// Netzwerkfehler an den Callback senden
callback.onError("Netzwerkfehler: " + t.getMessage());
}
});
}
public void performRegistration(String email, String username, String password, APICallback callback) {
RegisterRequest registerRequest = new RegisterRequest(username, password, email);
authService.registration(registerRequest).enqueue(new Callback<StatusResponse>() {
@Override
public void onResponse(Call<StatusResponse> call, Response<StatusResponse> response) {
if(response.isSuccessful() && response.body() != null) {
callback.onSuccess();
} else {
callback.onError("Registration fehlgeschlagen. Überprüfe Benutzername und Passwort.");
}
}
@Override
public void onFailure(Call<StatusResponse> call, Throwable throwable) {
callback.onError("Netzwerkfehler: " + throwable.getMessage());
}
});
}
private void saveToken(String token) {
tokenManager.saveToken(token);
}
public String getToken() {
return tokenManager.getToken();
}
public void logout() {
tokenManager.clearToken();
}
}

View File

@ -1,20 +0,0 @@
package core.notevault.sync.auth;
import core.notevault.sync.StatusResponse;
import core.notevault.sync.auth.apimodel.LoginRequest;
import core.notevault.sync.auth.apimodel.LoginResponse;
import core.notevault.sync.auth.apimodel.RegisterRequest;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface AuthService {
@POST("/login/")
Call<LoginResponse> login(@Body LoginRequest loginRequest);
@POST("/register/")
@Headers("Content-Type: application/json")
Call<StatusResponse> registration(@Body RegisterRequest registerRequest);
}

View File

@ -1,7 +0,0 @@
package core.notevault.sync.auth;
public interface LoginCallback {
void onSuccess();
void onError(String error);
}

View File

@ -1,31 +0,0 @@
package core.notevault.sync.auth;
import android.content.Context;
import android.content.SharedPreferences;
public class TokenManager {
private static final String PREF_NAME = "app_preferences";
private static final String KEY_TOKEN = "jwt_token";
private SharedPreferences sharedPreferences;
public TokenManager(Context context) {
sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
public void saveToken(String token) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY_TOKEN, token);
editor.apply();
}
public String getToken() {
return sharedPreferences.getString(KEY_TOKEN, null);
}
public void clearToken() {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(KEY_TOKEN);
editor.apply();
}
}

View File

@ -1,13 +0,0 @@
package core.notevault.sync.auth.apimodel;
public class LoginRequest {
private String email;
private String password;
public LoginRequest(String email, String password) {
this.email = email;
this.password = password;
}
}

View File

@ -1,22 +0,0 @@
package core.notevault.sync.auth.apimodel;
public class LoginResponse {
private String token;
private String username;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

View File

@ -1,14 +0,0 @@
package core.notevault.sync.auth.apimodel;
public class RegisterRequest {
private String username;
private String email;
private String password;
public RegisterRequest(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
}

View File

@ -1,31 +0,0 @@
package core.notevault.sync.auth.apimodel;
public class RegisterResponse {
private String username;
private String email;
public RegisterResponse(String username, String email) {
this.username = username;
this.email = email;
}
public RegisterResponse() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation;
import java.time.LocalDateTime;
public class FetchRequest {
private LocalDateTime client_last_sync;
public FetchRequest(LocalDateTime client_last_sync) {
this.client_last_sync = client_last_sync;
}
public LocalDateTime getClient_last_sync() {
return client_last_sync;
}
public void setClient_last_sync(LocalDateTime client_last_sync) {
this.client_last_sync = client_last_sync;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation;
import java.util.List;
public class FetchResponse {
private List<String> serverIDs;
public FetchResponse(List<String> serverIDs) {
this.serverIDs = serverIDs;
}
public List<String> getServerIDs() {
return serverIDs;
}
public void setServerIDs(List<String> serverIDs) {
this.serverIDs = serverIDs;
}
}

View File

@ -1,36 +0,0 @@
package core.notevault.sync.synchronisation;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchRequest;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchResponse;
import core.notevault.sync.synchronisation.songs.deletion.SongDeletionBatchRequest;
import core.notevault.sync.synchronisation.songs.deletion.SongDeletionBatchResponse;
import core.notevault.sync.synchronisation.songs.modification.SongModificationBatchRequest;
import core.notevault.sync.synchronisation.songs.modification.SongModificationBatchResponse;
import core.notevault.sync.synchronisation.songs.notesheets.UploadResponse;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.*;
import java.time.LocalDateTime;
public interface SyncService {
@POST("/sync/songs/create")
Call<SongCreationBatchResponse> performSongCreation(@Body SongCreationBatchRequest syncRequest);
@POST("/sync/songs/modify")
Call<SongModificationBatchResponse> performSongModification(@Body SongModificationBatchRequest syncRequest);
@POST("/sync/songs/delete")
Call<SongDeletionBatchResponse> performSongDeletion(@Body SongDeletionBatchRequest syncRequest);
@Multipart
@POST("/sync/songs/note_sheet/upload")
Call<UploadResponse> uploadNoteSheet(@Part("serverID") RequestBody serverID, @Part("fileName") RequestBody fileName,
@Part MultipartBody.Part image);
@GET("/sync/songs/fetch")
Call<FetchResponse> fetchModifiedSongs(@Query("last_client_sync") LocalDateTime last_client_sync);
}

View File

@ -1,199 +0,0 @@
package core.notevault.sync.synchronisation.songs;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.data.NoteSheet;
import core.notevault.data.sync.SyncDataObject;
import core.notevault.data.sync.SyncDataObjectType;
import core.notevault.data.sync.SyncStatus;
import core.notevault.sync.synchronisation.FetchRequest;
import core.notevault.sync.synchronisation.FetchResponse;
import core.notevault.sync.synchronisation.SyncService;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchRequest;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchResponse;
import core.notevault.sync.synchronisation.songs.creation.SongCreationRequest;
import core.notevault.sync.synchronisation.songs.deletion.SongDeletionBatchRequest;
import core.notevault.sync.synchronisation.songs.deletion.SongDeletionBatchResponse;
import core.notevault.sync.synchronisation.songs.modification.SongModificationBatchRequest;
import core.notevault.sync.synchronisation.songs.modification.SongModificationBatchResponse;
import core.notevault.sync.synchronisation.songs.modification.SongModificationRequest;
import core.notevault.sync.synchronisation.songs.notesheets.UploadResponse;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class SongSyncWorker {
private SyncService syncService;
private MusicNoteDAO database;
private Context context;
private ExecutorService executorService = Executors.newSingleThreadExecutor();
public SongSyncWorker(SyncService syncService, MusicNoteDAO database, Context context) {
this.syncService = syncService;
this.database = database;
this.context = context;
}
private SongCreationBatchRequest buildCreationRequest() {
List<MusicNote> created_songs = database.getSongsWithSyncStatus(SyncStatus.CREATED);
List<SongCreationRequest> songCreationRequests = new ArrayList<>();
for(MusicNote musicNote : created_songs) {
songCreationRequests.add(new SongCreationRequest(musicNote.getMusicNoteId(), musicNote.getTitle(), musicNote.getComposer(), musicNote.getGenre(), musicNote.getYear()));
}
return new SongCreationBatchRequest(songCreationRequests);
}
public void syncSongCreations() {
SongCreationBatchRequest request = buildCreationRequest();
syncService.performSongCreation(request).enqueue(new Callback<SongCreationBatchResponse>() {
@Override
public void onResponse(Call<SongCreationBatchResponse> call, Response<SongCreationBatchResponse> response) {
if(response.isSuccessful() && response.body() != null) {
executorService.execute(() -> {
database.storeSongSyncResponses(response.body().getSongs());
});
} else {
Toast.makeText(context, "Song creation failed", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<SongCreationBatchResponse> call, Throwable throwable) {
Toast.makeText(context, "Song creation failed", Toast.LENGTH_LONG).show();
}
});
}
private SongModificationBatchRequest buildModificationRequest() {
List<MusicNote> modified_songs = database.getSongsWithSyncStatus(SyncStatus.MODIFIED);
List<SongModificationRequest> songModificationRequests = new ArrayList<>();
for(MusicNote musicNote : modified_songs) {
songModificationRequests.add(new SongModificationRequest(musicNote.getServerID(), musicNote.getTitle(), musicNote.getComposer(), musicNote.getGenre(), musicNote.getYear()));
}
return new SongModificationBatchRequest(songModificationRequests);
}
public void syncSongModifications() {
SongModificationBatchRequest request = buildModificationRequest();
syncService.performSongModification(request).enqueue(new Callback<SongModificationBatchResponse>() {
@Override
public void onResponse(Call<SongModificationBatchResponse> call, Response<SongModificationBatchResponse> response) {
executorService.execute(() -> {
database.storeSongSyncModifyResponses(response.body().getUpdated_songs(), SyncStatus.SYNCED, LocalDateTime.now());
});
}
@Override
public void onFailure(Call<SongModificationBatchResponse> call, Throwable throwable) {
Toast.makeText(context, "Sync of Modified Songs failed", Toast.LENGTH_LONG).show();
}
});
}
private SongDeletionBatchRequest buildDeletionBatchRequest() {
List<MusicNote> deleted_songs = database.getSongsWithSyncStatus(SyncStatus.DELETED);
List<String> deleted_song_ids = deleted_songs.stream().map(MusicNote::getServerID).collect(Collectors.toList());
return new SongDeletionBatchRequest(deleted_song_ids);
}
public void syncSongDeletions() {
SongDeletionBatchRequest request = buildDeletionBatchRequest();
syncService.performSongDeletion(request).enqueue(new Callback<SongDeletionBatchResponse>() {
@Override
public void onResponse(Call<SongDeletionBatchResponse> call, Response<SongDeletionBatchResponse> response) {
if(response.isSuccessful() && response.body() != null) {
executorService.execute(() -> {
database.deleteSongs(response.body().getDeletedSongs());
});
}
}
@Override
public void onFailure(Call<SongDeletionBatchResponse> call, Throwable throwable) {
Toast.makeText(context, "Sync of Deleted Songs failed", Toast.LENGTH_LONG).show();
}
});
}
public void uploadNoteSheets() {
List<NoteSheet> noteSheets = database.getNoteSheetsWithStatus(SyncStatus.CREATED);
for(NoteSheet noteSheet : noteSheets) {
Optional<String> serverID = database.getMusicNoteByLocalID(noteSheet.getMusicNoteId());
if(serverID.isPresent()) {
File imageFile = new File(noteSheet.getFilePath());
// Erstelle Request-Bodies für serverID und fileName
RequestBody serverIDBody = RequestBody.create(MediaType.parse("text/plain"), serverID.get());
RequestBody fileNameBody = RequestBody.create(MediaType.parse("text/plain"), imageFile.getName());
// Erstelle den Multipart-Body für die Bilddatei
RequestBody imageRequestBody = RequestBody.create(MediaType.parse("image/*"), imageFile);
MultipartBody.Part imagePart = MultipartBody.Part.createFormData("image", imageFile.getName(), imageRequestBody);
syncService.uploadNoteSheet(serverIDBody, fileNameBody, imagePart).enqueue(new Callback<UploadResponse>() {
@Override
public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) {
if (response.isSuccessful() && response.body() != null) {
// Erfolg: Antwort verarbeiten
Toast.makeText(context, "Upload erfolgreich", Toast.LENGTH_LONG).show();
executorService.execute(() -> {
noteSheet.setSyncStatus(SyncStatus.SYNCED);
database.updateNoteSheet(noteSheet);
});
} else {
// Fehler: Antwort prüfen
Toast.makeText(context, "Fehler beim Upload: " + response.code(), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<UploadResponse> call, Throwable throwable) {
Toast.makeText(context, "Fehler beim Upload: " + throwable.toString(), Toast.LENGTH_LONG).show();
}
});
}
}
}
public void fetchModifiedSongs() {
FetchRequest request = new FetchRequest(LocalDateTime.MIN);
syncService.fetchModifiedSongs(LocalDateTime.MIN).enqueue(new Callback<FetchResponse>() {
@Override
public void onResponse(Call<FetchResponse> call, Response<FetchResponse> response) {
if(response.isSuccessful() && response.body() != null) {
executorService.execute(() -> {
List<SyncDataObject> syncDataObjects = new ArrayList<>();
for(String serverID: response.body().getServerIDs()) {
syncDataObjects.add(new SyncDataObject(serverID, SyncDataObjectType.MUSIC_NOTE));
}
database.insertSyncDataObjects(syncDataObjects);
});
} else {
Toast.makeText(context, "Fehler beim Fetch: " + response.code(), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<FetchResponse> call, Throwable throwable) {
Toast.makeText(context, "Fehler beim Fetch: " + throwable.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
}

View File

@ -1,11 +0,0 @@
package core.notevault.sync.synchronisation.songs.creation;
import java.util.List;
public class SongCreationBatchRequest {
private List<SongCreationRequest> songs;
public SongCreationBatchRequest(List<SongCreationRequest> songs) {
this.songs = songs;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation.songs.creation;
import java.util.List;
public class SongCreationBatchResponse {
private List<SongCreationResponse> songs;
public SongCreationBatchResponse(List<SongCreationResponse> songs) {
this.songs = songs;
}
public List<SongCreationResponse> getSongs() {
return songs;
}
public void setSongs(List<SongCreationResponse> songs) {
this.songs = songs;
}
}

View File

@ -1,17 +0,0 @@
package core.notevault.sync.synchronisation.songs.creation;
public class SongCreationRequest {
private long localID;
private String title;
private String composer;
private String genre;
private int year;
public SongCreationRequest(long localID, String title, String composer, String genre, int year) {
this.localID = localID;
this.title = title;
this.composer = composer;
this.genre = genre;
this.year = year;
}
}

View File

@ -1,27 +0,0 @@
package core.notevault.sync.synchronisation.songs.creation;
public class SongCreationResponse {
private int localID;
private String serverID;
public SongCreationResponse(int localID, String serverID) {
this.localID = localID;
this.serverID = serverID;
}
public int getLocalID() {
return localID;
}
public void setLocalID(int localID) {
this.localID = localID;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation.songs.deletion;
import java.util.List;
public class SongDeletionBatchRequest {
private List<String> songs;
public SongDeletionBatchRequest(List<String> songs) {
this.songs = songs;
}
public List<String> getSongs() {
return songs;
}
public void setSongs(List<String> songs) {
this.songs = songs;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation.songs.deletion;
import java.util.List;
public class SongDeletionBatchResponse {
private List<String> deletedSongs;
public SongDeletionBatchResponse(List<String> deletedSongs) {
this.deletedSongs = deletedSongs;
}
public List<String> getDeletedSongs() {
return deletedSongs;
}
public void setDeletedSongs(List<String> deletedSongs) {
this.deletedSongs = deletedSongs;
}
}

View File

@ -1,11 +0,0 @@
package core.notevault.sync.synchronisation.songs.modification;
import java.util.List;
public class SongModificationBatchRequest {
private List<SongModificationRequest> songs;
public SongModificationBatchRequest(List<SongModificationRequest> songs) {
this.songs = songs;
}
}

View File

@ -1,19 +0,0 @@
package core.notevault.sync.synchronisation.songs.modification;
import java.util.List;
public class SongModificationBatchResponse {
private List<String> songs;
public SongModificationBatchResponse(List<String> songs) {
this.songs = songs;
}
public List<String> getUpdated_songs() {
return songs;
}
public void setUpdated_songs(List<String> updated_songs) {
this.songs = updated_songs;
}
}

View File

@ -1,57 +0,0 @@
package core.notevault.sync.synchronisation.songs.modification;
public class SongModificationRequest {
private String serverID;
private String title;
private String composer;
private String genre;
private int year;
public SongModificationRequest(String serverID, String title, String composer, String genre, int year) {
this.serverID = serverID;
this.title = title;
this.composer = composer;
this.genre = genre;
this.year = year;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getComposer() {
return composer;
}
public void setComposer(String composer) {
this.composer = composer;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
}

View File

@ -1,5 +0,0 @@
package core.notevault.sync.synchronisation.songs.notesheets;
public class UploadResponse {
private String serverID;
}

View File

@ -1,86 +0,0 @@
package core.notevault.ui.gallery;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import core.notevault.R;
import core.notevault.data.Concert;
import core.notevault.ui.home.NoteSongAdapter;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class ConcertAdapter extends RecyclerView.Adapter<ConcertAdapter.ConcertViewHolder> {
private final List<Concert> concertList;
private final OnConcertClickListener concertClickListener;
public interface OnConcertClickListener {
void onDeleteConcert(Concert concert);
void onOpenConcertEditor(Concert concert);
void onConcertClick(Concert concert);
}
public ConcertAdapter(List<Concert> concertList, OnConcertClickListener concertClickListener) {
this.concertList = concertList;
this.concertClickListener = concertClickListener;
}
public ConcertAdapter(OnConcertClickListener onConcertClickListener) {
this.concertClickListener = onConcertClickListener;
this.concertList = new ArrayList<>();
}
@Override
public void onBindViewHolder(@NonNull ConcertViewHolder holder, int position) {
holder.concertTitleView.setText(concertList.get(position).getTitle());
holder.dateHolder.setText(concertList.get(position).getConcertDate());
holder.deleteButton.setOnClickListener(v -> concertClickListener.onDeleteConcert(concertList.get(position)));
holder.editButton.setOnClickListener(v -> concertClickListener.onOpenConcertEditor(concertList.get(position)));
holder.itemView.setOnClickListener(v -> concertClickListener.onConcertClick(concertList.get(position)));
}
@Override
public int getItemCount() {
return concertList.size();
}
@NonNull
@Override
public ConcertViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.concert_item, parent, false);
return new ConcertViewHolder(view);
}
public void updateConcerts(List<Concert> concerts) {
Log.d("ConcertAdapter", "Update Concerts: " + this.concertList.size() + "vs new: " + concerts.size());
this.concertList.clear();
this.concertList.addAll(concerts);
notifyDataSetChanged();
}
public class ConcertViewHolder extends RecyclerView.ViewHolder {
TextView concertTitleView;
TextView dateHolder;
ImageButton deleteButton;
ImageButton editButton;
public ConcertViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
concertTitleView = itemView.findViewById(R.id.concert_title);
dateHolder = itemView.findViewById(R.id.concert_date);
deleteButton = itemView.findViewById(R.id.delete_concert_button);
editButton = itemView.findViewById(R.id.edit_concert_button);
}
}
}

View File

@ -1,153 +0,0 @@
package core.notevault.ui.gallery;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import core.notevault.R;
import core.notevault.data.Concert;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.databinding.FragmentGalleryBinding;
import core.notevault.ui.gallery.editor.ConcertEditorDialog;
import java.util.List;
public class GalleryFragment extends Fragment {
private FragmentGalleryBinding binding;
private ConcertAdapter concertAdapter;
private GalleryViewModel galleryViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
galleryViewModel =
new ViewModelProvider(this).get(GalleryViewModel.class);
binding = FragmentGalleryBinding.inflate(inflater, container, false);
View root = binding.getRoot();
RecyclerView recyclerView = root.findViewById(R.id.concert_recycler_view);
concertAdapter = new ConcertAdapter(new ConcertAdapter.OnConcertClickListener() {
@Override
public void onDeleteConcert(Concert concert) {
deleteConcert(concert);
}
@Override
public void onOpenConcertEditor(Concert concert) {
editConcert(concert);
}
@Override
public void onConcertClick(Concert concert) {
Log.d("GalleryFragment", "Clicked on Concert: " + concert.getTitle());
Bundle bundle = new Bundle();
bundle.putInt("concertID", concert.getId());
bundle.putString("concertTitle", concert.getTitle());
bundle.putString("concertDate", concert.getConcertDate());
NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment_content_main);
navController.navigate(R.id.concertDetailFragment, bundle);
}
});
recyclerView.setAdapter(concertAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), LinearLayoutManager.VERTICAL);
recyclerView.addItemDecoration(dividerItemDecoration);
FloatingActionButton addConcertBtn = root.findViewById(R.id.add_concert_btn);
addConcertBtn.setOnClickListener(v -> onCreateNewConcert());
galleryViewModel.getConcerts().observe(getViewLifecycleOwner(), newConcerts -> {
concertAdapter.updateConcerts(newConcerts);
});
new LoadConcerts().execute();
return root;
}
public void deleteConcert(Concert concert) {
Log.d("GalleryFragment", "Delete Concert");
new Thread(() -> {
MusicDatabase musicDatabase = MusicDatabase.getDatabase(this.getContext());
MusicNoteDAO musicNoteDAO = musicDatabase.musicNoteDao();
musicNoteDAO.deleteConcert(concert);
new Handler(Looper.getMainLooper()).post(() -> {
galleryViewModel.deleteConcert(concert);
Log.d("GalleryFragment", "Concert deleted successfully and ViewModel updated");
});
}).start();
}
public void editConcert(Concert concert) {
ConcertEditorDialog concertEditorDialog = new ConcertEditorDialog();
Bundle args = new Bundle();
args.putString("concert_title", concert.getTitle());
args.putString("concert_date", concert.getConcertDate());
args.putInt("concertID", concert.getId());
concertEditorDialog.setArguments(args);
concertEditorDialog.show(getParentFragmentManager(), ConcertEditorDialog.TAG);
}
public void updateConcert(Concert concert) {
galleryViewModel.updateConcert(concert);
}
private class LoadConcerts extends AsyncTask<Void, Void, List<Concert>> {
@Override
protected List<Concert> doInBackground(Void... voids) {
MusicDatabase db = MusicDatabase.getDatabase(getContext());
MusicNoteDAO musicNoteDAO = db.musicNoteDao();
List<Concert> concerts = musicNoteDAO.getAllConcerts();
Log.d("GalleryFragment", "Fetching concerts: " + concerts.size());
return concerts;
}
@Override
protected void onPostExecute(List<Concert> concerts) {
Log.d("GalleryFragment", "Concerts size in onPostExecute: " + concerts.size());
// concertAdapter.updateConcerts(concerts); // Aktualisiere den Adapter mit den Titeln
galleryViewModel.setConcerts(concerts);
}
}
public void onCreateNewConcert() {
ConcertEditorDialog concertEditorDialog = new ConcertEditorDialog();
concertEditorDialog.show(getParentFragmentManager(), ConcertEditorDialog.TAG);
}
public void addConcert(Concert concert) {
this.galleryViewModel.addConcert(concert);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -1,58 +0,0 @@
package core.notevault.ui.gallery;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import core.notevault.data.Concert;
import java.util.List;
public class GalleryViewModel extends ViewModel {
private final MutableLiveData<List<Concert>> concerts;
public GalleryViewModel() {
this.concerts = new MutableLiveData<>();
}
public LiveData<List<Concert>> getConcerts() {
return concerts;
}
public void setConcerts(List<Concert> concerts) {
this.concerts.setValue(concerts);
}
public void addConcert(Concert concert) {
List<Concert> currentConcerts = this.concerts.getValue();
Log.d("GalleryViewModel", "Add Concert");
if(currentConcerts != null) {
currentConcerts.add(concert);
concerts.setValue(currentConcerts);
}
}
public void deleteConcert(Concert concert) {
List<Concert> currentConcerts = concerts.getValue();
if(currentConcerts != null) {
currentConcerts.remove(concert);
concerts.setValue(currentConcerts);
}
}
public void updateConcert(Concert concert) {
List<Concert> currentConcerts = concerts.getValue();
if(currentConcerts != null) {
for(int i = 0; i < currentConcerts.size(); i++) {
if(currentConcerts.get(i).getId() == concert.getId()) {
currentConcerts.get(i).setTitle(concert.getTitle());
currentConcerts.get(i).setConcertDate(concert.getConcertDate());
break;
}
}
concerts.setValue(currentConcerts);
}
}
}

View File

@ -1,119 +0,0 @@
package core.notevault.ui.gallery.detail;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.room.util.ViewInfo;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import core.notevault.data.Concert;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.databinding.FragmentConcertDetailBinding;
import core.notevault.ui.home.NoteSongAdapter;
import core.notevault.ui.metadatadialog.SongEditDialog;
import java.util.Collections;
import java.util.List;
public class ConcertDetailFragment extends Fragment {
public interface OnConcertSongsLoadedListener {
void onSongsLoaded(List<MusicNote> songs);
}
private FragmentConcertDetailBinding binding;
private Concert concert;
private ConcertDetailViewModel concertDetailViewModel;
private NoteSongAdapter noteSongAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
concertDetailViewModel = new ViewModelProvider(this).get(ConcertDetailViewModel.class);
binding = FragmentConcertDetailBinding.inflate(inflater, container, false);
int concertId = getArguments().getInt("concertID");
String concertTitle = getArguments().getString("concertTitle");
String concertDate = getArguments().getString("concertDate");
concert = new Concert(concertId, concertTitle, concertDate);
binding.textConcertDetailsTitle.setText(concertTitle);
binding.textConcertDetailsDate.setText(concertDate);
RecyclerView recyclerView = binding.noteRecyclerView;
noteSongAdapter = new NoteSongAdapter(this::deleteSongFromConcert, this::editSongFromConcert);
recyclerView.setAdapter(noteSongAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), LinearLayoutManager.VERTICAL);
recyclerView.addItemDecoration(dividerItemDecoration);
FloatingActionButton fab = binding.addSongConcert;
fab.setOnClickListener(view -> {
ConcertSongSelector concertSongSelector = new ConcertSongSelector();
Bundle bundle = new Bundle();
bundle.putString("concertTitle", concertTitle);
bundle.putString("concertDate", concertDate);
bundle.putInt("concertID", concertId);
concertSongSelector.setArguments(bundle);
concertSongSelector.show(getParentFragmentManager(), ConcertSongSelector.TAG);
});
concertDetailViewModel.getConcertSongs().observe(getViewLifecycleOwner(), songs -> {
noteSongAdapter.updateSongTitles(songs);
});
new LoadConcertSongsTask(concertId, songs -> {
this.concertDetailViewModel.setConcertSongs(songs);
}).execute();
return binding.getRoot();
}
private void deleteSongFromConcert(MusicNote musicNote) {
this.concertDetailViewModel.deleteConcertSong(musicNote);
new Thread(() -> {
MusicNoteDAO musicNoteDAO = MusicDatabase.getDatabase(getContext()).musicNoteDao();
musicNoteDAO.deleteConcertSong(musicNote.getMusicNoteId(), concert.getId());
}).start();
}
private void editSongFromConcert(MusicNote musicNote) {
SongEditDialog songEditDialog = new SongEditDialog();
songEditDialog.setSong(musicNote);
songEditDialog.show(getParentFragmentManager(), SongEditDialog.TAG);
}
private class LoadConcertSongsTask extends AsyncTask<Void, Void, List<MusicNote>> {
private final int concertID;
private final OnConcertSongsLoadedListener listener;
public LoadConcertSongsTask(int concertID, OnConcertSongsLoadedListener listener) {
this.concertID = concertID;
this.listener = listener;
}
@Override
protected List<MusicNote> doInBackground(Void... voids) {
MusicDatabase db = MusicDatabase.getDatabase(getContext());
return db.musicNoteDao().getAllMusicNotesOfConcert(concertID);
}
@Override
protected void onPostExecute(List<MusicNote> songs) {
Log.d("ConcertDetailFragment", "Loaded Concertsongs: " + songs.size());
listener.onSongsLoaded(songs);
}
}
}

View File

@ -1,41 +0,0 @@
package core.notevault.ui.gallery.detail;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import core.notevault.data.MusicNote;
import java.util.ArrayList;
import java.util.List;
public class ConcertDetailViewModel extends ViewModel {
private final MutableLiveData<List<MusicNote>> concertSongs;
public ConcertDetailViewModel() {
this.concertSongs = new MutableLiveData<>(new ArrayList<>());
}
public LiveData<List<MusicNote>> getConcertSongs() {
return concertSongs;
}
public void setConcertSongs(List<MusicNote> concertSongs) {
this.concertSongs.setValue(concertSongs);
}
public void deleteConcertSong(MusicNote note) {
List<MusicNote> currentConcertSongs = concertSongs.getValue();
if(currentConcertSongs != null) {
currentConcertSongs.remove(note);
this.concertSongs.setValue(currentConcertSongs);
}
}
public void addConcertSongs(List<MusicNote> concertSongs) {
List<MusicNote> currentConcertSongs = this.concertSongs.getValue();
if(currentConcertSongs != null) {
currentConcertSongs.addAll(concertSongs);
this.concertSongs.setValue(currentConcertSongs);
}
}
}

View File

@ -1,116 +0,0 @@
package core.notevault.ui.gallery.detail;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SearchView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import core.notevault.R;
import core.notevault.data.ConcertSong;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.ui.metadatadialog.MetaDataDialog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ConcertSongSelector extends DialogFragment {
private ConcertSongSelectorAdapter adapter;
private OnSongSelectedListener listener;
private int concertID;
public interface OnSongSelectedListener {
void onSongsSelected(List<MusicNote> songs, int concertID);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_concert_song_selector, null);
String concertTitle = getArguments().getString("concertTitle");
String concertDate = getArguments().getString("concertDate");
concertID = getArguments().getInt("concertID");
TextView dialogTitle = dialogView.findViewById(R.id.dialog_title_concert_song_selector);
dialogTitle.setText("Wähle Stücke für das Konzert '" + concertTitle + "' am " + concertDate + " aus");
RecyclerView recyclerView = dialogView.findViewById(R.id.concert_song_selector_recycler);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new ConcertSongSelectorAdapter(new ArrayList<>());
recyclerView.setAdapter(adapter);
SearchView searchView = dialogView.findViewById(R.id.search_view);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
return false;
}
@Override
public boolean onQueryTextChange(String s) {
adapter.filter(s);
return true;
}
});
fetchAllSongs();
return new AlertDialog.Builder(requireContext())
.setView(dialogView)
.setPositiveButton("Speichern", (dialog, which) -> {
List<MusicNote> selectedSongs = adapter.getSelectedSongs();
listener.onSongsSelected(selectedSongs, concertID);
})
.setNegativeButton("Abbrechen", (dialog, which) -> {} )
.create();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof ConcertSongSelector.OnSongSelectedListener) {
listener = (ConcertSongSelector.OnSongSelectedListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnMetadataListener");
}
}
private void fetchAllSongs() {
// Ersetze dies durch das Laden der Songs aus der Datenbank oder einer anderen Quelle
new LoadSongTitlesTask().execute();
}
private class LoadSongTitlesTask extends AsyncTask<Void, Void, List<MusicNote>> {
@Override
protected List<MusicNote> doInBackground(Void... voids) {
MusicDatabase db = MusicDatabase.getDatabase(getContext());
MusicNoteDAO musicNoteDAO = db.musicNoteDao();
return musicNoteDAO.getAllNotes();
}
@Override
protected void onPostExecute(List<MusicNote> songs) {
adapter.setSongs(songs);
}
}
public static String TAG = "ConcertSongSelector";
}

View File

@ -1,90 +0,0 @@
package core.notevault.ui.gallery.detail;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import core.notevault.R;
import core.notevault.data.MusicNote;
import java.util.ArrayList;
import java.util.List;
public class ConcertSongSelectorAdapter extends RecyclerView.Adapter<ConcertSongSelectorAdapter.ConcertSongSelectorViewHolder> {
private List<MusicNote> songs;
private List<MusicNote> selectedSongs = new ArrayList<>();
private List<MusicNote> filteredSongs;
public ConcertSongSelectorAdapter(List<MusicNote> songs) {
this.songs = songs;
this.filteredSongs = new ArrayList<>(songs);
}
@NonNull
@Override
public ConcertSongSelectorViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_concert_song, parent, false);
return new ConcertSongSelectorViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ConcertSongSelectorViewHolder holder, int position) {
MusicNote song = filteredSongs.get(position);
holder.bind(song);
}
@Override
public int getItemCount() {
return filteredSongs.size();
}
public List<MusicNote> getSelectedSongs() {
return selectedSongs;
}
public void setSongs(List<MusicNote> musicNotes) {
songs = musicNotes;
filteredSongs = new ArrayList<>(musicNotes);
notifyDataSetChanged();
}
public void filter(String query) {
filteredSongs.clear();
if (query.isEmpty()) {
filteredSongs.addAll(songs);
} else {
for (MusicNote song : songs) {
if (song.getTitle().toLowerCase().contains(query.toLowerCase())) {
filteredSongs.add(song);
}
}
}
notifyDataSetChanged();
}
class ConcertSongSelectorViewHolder extends RecyclerView.ViewHolder {
private final TextView songTitle;
private final CheckBox songCheckbox;
ConcertSongSelectorViewHolder(View itemView) {
super(itemView);
songTitle = itemView.findViewById(R.id.song_title);
songCheckbox = itemView.findViewById(R.id.song_checkbox);
songCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
MusicNote musicNote = filteredSongs.get(getAbsoluteAdapterPosition());
selectedSongs.add(musicNote);
});
}
void bind(MusicNote song) {
songTitle.setText(song.getTitle());
songCheckbox.setChecked(selectedSongs.contains(song));
}
}
}

View File

@ -1,111 +0,0 @@
package core.notevault.ui.gallery.editor;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import core.notevault.R;
import core.notevault.data.Concert;
import core.notevault.ui.metadatadialog.MetaDataDialog;
import java.util.Calendar;
import java.util.Date;
public class ConcertEditorDialog extends DialogFragment {
private Concert concert;
private EditText concert_date_input;
private EditText concertTitleInput;
private OnConcertEditorListener listener;
private int concertID = -1;
public void setConcert(Concert concert) {
concert_date_input.setText(concert.getConcertDate());
concertTitleInput.setText(concert.getTitle());
this.concert = concert;
}
public interface OnConcertEditorListener {
void onConcertEditorClosed(String concertTitle, String concertDate, int concertID);
}
public interface OnConcertEditorEditListener {
void onConcertEditorEditClosed(String concertTitle, String concertDate);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = requireActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.concert_editor, null);
EditText title_input = dialogView.findViewById(R.id.concert_title_input);
concert_date_input = dialogView.findViewById(R.id.concert_date_input);
concert_date_input.setOnClickListener(v -> showDatePickerDialog());
if(getArguments() != null) {
String concertTitle = getArguments().getString("concert_title");
String concertDate = getArguments().getString("concert_date");
concertID = getArguments().getInt("concertID");
title_input.setText(concertTitle);
concert_date_input.setText(concertDate);
}
return new AlertDialog.Builder(requireContext())
.setView(dialogView)
.setPositiveButton("Speichern", (dialog, which) -> {
String title = title_input.getText().toString();
String concertDate = concert_date_input.getText().toString();
Log.d("ConcertEditor", "ConcertDate: " + concertDate);
listener.onConcertEditorClosed(title, concertDate, concertID);
})
.setNegativeButton("Abbrechen", (dialog, which) -> {} )
.create();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof OnConcertEditorListener) {
listener = (OnConcertEditorListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnMetadataListener");
}
}
private void showDatePickerDialog() {
// Erhalte das aktuelle Datum
final Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
// Erstelle den DatePickerDialog
DatePickerDialog datePickerDialog = new DatePickerDialog(this.getContext(),
(view, selectedYear, selectedMonth, selectedDay) -> {
// Setze das ausgewählte Datum in das EditText
concert_date_input.setText(selectedDay + "/" + (selectedMonth + 1) + "/" + selectedYear);
}, year, month, day);
// Zeige den Dialog an
datePickerDialog.show();
}
public static String TAG = "ConcertEditorDialog";
}

View File

@ -1,35 +0,0 @@
package core.notevault.ui.home;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import core.notevault.R;
import core.notevault.ui.noteviewer.ImagePagerAdapter;
import java.util.Arrays;
import java.util.List;
public class FullScreenImageActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen_image); // Erstelle eine Layout-Datei
ViewPager2 imageView = findViewById(R.id.viewPager); // Angenommen, du hast ein ImageView
// Die URI aus dem Intent erhalten
Intent intent = getIntent();
String[] imageUris = intent.getStringArrayExtra("imageUris");
if (imageUris != null) {
List<String> uriList = Arrays.asList(imageUris);
ImagePagerAdapter adapter = new ImagePagerAdapter(this, uriList);
imageView.setAdapter(adapter); // Setze die URI in das ImageView
} else {
throw new NullPointerException();
}
}
}

View File

@ -1,160 +0,0 @@
package core.notevault.ui.home;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import core.notevault.R;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.data.sync.SyncStatus;
import core.notevault.databinding.FragmentHomeBinding;
import core.notevault.ui.metadatadialog.MetaDataDialog;
import core.notevault.ui.metadatadialog.SongEditDialog;
import core.notevault.util.NoteSheetsUtil;
import java.util.List;
public class HomeFragment extends Fragment {
private NoteSongAdapter noteSongAdapter;
private static final int PICK_FILE_REQUEST_CODE = 1;
private FragmentHomeBinding binding;
private HomeViewModel homeViewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
FloatingActionButton importBtn = root.findViewById(R.id.add_song_concert);
importBtn.setOnClickListener(v -> openFileChooser());
RecyclerView recyclerView = root.findViewById(R.id.note_recycler_view);
noteSongAdapter = new NoteSongAdapter(this::deleteSong, this::editSong);
recyclerView.setAdapter(noteSongAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), LinearLayoutManager.VERTICAL);
recyclerView.addItemDecoration(dividerItemDecoration);
homeViewModel.getNoteTitles().observe(getViewLifecycleOwner(), songs -> {
noteSongAdapter.updateSongTitles(songs);
});
new LoadSongTitlesTask().execute();
return root;
}
private void editSong(MusicNote musicNote) {
//Open Dialog
SongEditDialog songEditDialog = new SongEditDialog();
songEditDialog.setSong(musicNote);
songEditDialog.show(getParentFragmentManager(), SongEditDialog.TAG);
}
public void deleteSong(MusicNote musicNote) {
new Thread(() -> {
MusicDatabase musicDatabase = MusicDatabase.getDatabase(getContext());
MusicNoteDAO musicNoteDAO = musicDatabase.musicNoteDao();
musicNote.setSyncStatus(SyncStatus.DELETED);
musicNoteDAO.updateSong(musicNote);
Log.d("HomeFragment", "Delete Song: " + musicNote.getTitle());
new Handler(Looper.getMainLooper()).post(()-> {
homeViewModel.deleteSong(musicNote);
});
}).start();
}
private class LoadSongTitlesTask extends AsyncTask<Void, Void, List<MusicNote>> {
@Override
protected List<MusicNote> doInBackground(Void... voids) {
MusicDatabase db = MusicDatabase.getDatabase(getContext());
MusicNoteDAO musicNoteDAO = db.musicNoteDao();
return musicNoteDAO.getAllNotes();
}
@Override
protected void onPostExecute(List<MusicNote> songs) {
homeViewModel.setNoteTitles(songs);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == getActivity().RESULT_OK && data != null) {
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount();
Uri[] uris = new Uri[count];
for(int i = 0; i < count; i++) {
uris[i] = data.getClipData().getItemAt(i).getUri();
}
handleFile(uris);
} else if(data.getData() != null) {
Uri uri = data.getData();
handleFile(uri);
}
}
}
private void handleFile(Uri... uris) {
// Hier kannst du die Logik zum Speichern oder Anzeigen der Datei implementieren
NoteSheetsUtil.sortNoteSheetsByTimestamp(getContext(), uris);
MetaDataDialog metaDataDialog = new MetaDataDialog();
metaDataDialog.setFileUri(uris);
metaDataDialog.show(getParentFragmentManager(), MetaDataDialog.TAG);
Toast.makeText(getActivity(), "Datei ausgewählt: " + uris[0].getPath(), Toast.LENGTH_SHORT).show();
}
private void openFileChooser() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*"); // Alle Dateitypen
String[] mimeTypes = {"application/pdf", "image/png", "image/jpeg"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, PICK_FILE_REQUEST_CODE);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
public void addSong(MusicNote musicNote) {
this.homeViewModel.addSong(musicNote);
}
public void updateSong(MusicNote song) {
this.homeViewModel.updateSong(song);
}
}

View File

@ -1,63 +0,0 @@
package core.notevault.ui.home;
import android.net.Uri;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import core.notevault.MainActivity;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.ui.metadatadialog.MetaDataDialog;
import java.util.ArrayList;
import java.util.List;
public class HomeViewModel extends ViewModel {
private final MutableLiveData<List<MusicNote>> noteTitles;
public HomeViewModel() {
noteTitles = new MutableLiveData<>(new ArrayList<>());
}
public LiveData<List<MusicNote>> getNoteTitles() {
return noteTitles;
}
public void setNoteTitles(List<MusicNote> noteTitles) {
this.noteTitles.setValue(noteTitles);
}
public void addSong(MusicNote song) {
List<MusicNote> songs = noteTitles.getValue();
if(songs != null) {
songs.add(song);
this.noteTitles.setValue(songs);
}
}
public void deleteSong(MusicNote musicNote) {
List<MusicNote> currentSongs = noteTitles.getValue();
if(currentSongs != null) {
currentSongs.remove(musicNote);
noteTitles.setValue(currentSongs);
}
}
public void updateSong(MusicNote updatedSong) {
List<MusicNote> currentSongs = noteTitles.getValue();
if(currentSongs != null) {
for(int i=0; i<currentSongs.size(); i++) {
if(currentSongs.get(i).getMusicNoteId() == updatedSong.getMusicNoteId()) {
currentSongs.set(i, updatedSong);
break;
}
}
noteTitles.setValue(currentSongs);
}
}
}

View File

@ -1,157 +0,0 @@
package core.notevault.ui.home;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import core.notevault.R;
import core.notevault.data.MusicDatabase;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.data.NoteSheet;
import java.util.ArrayList;
import java.util.List;
public class NoteSongAdapter extends RecyclerView.Adapter<NoteSongAdapter.NoteViewHolder> {
private final List<MusicNote> noteTitles;
private final OnSongDeleteListener onSongDeleteListener;
private final OnSongEditListener onSongEditListener;
public interface OnSongDeleteListener {
void onSongDeleted(MusicNote song);
}
public interface OnSongEditListener {
void onSongEdit(MusicNote song);
}
public NoteSongAdapter(List<MusicNote> noteTitles, OnSongDeleteListener onSongDeleteListener, OnSongEditListener onSongEditListener) {
this.noteTitles = noteTitles;
this.onSongDeleteListener = onSongDeleteListener;
this.onSongEditListener = onSongEditListener;
}
public NoteSongAdapter(OnSongDeleteListener onSongDeleteListener, OnSongEditListener onSongEditListener) {
this.onSongDeleteListener = onSongDeleteListener;
this.noteTitles = new ArrayList<>();
this.onSongEditListener = onSongEditListener;
}
@NonNull
@Override
public NoteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_song, parent, false);
return new NoteViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull NoteViewHolder holder, int position) {
holder.titleTextView.setText(noteTitles.get(position).getTitle());
holder.yearTextView.setText(String.valueOf(noteTitles.get(position).getYear()));
holder.compositionTextView.setText(noteTitles.get(position).getComposer());
holder.deleteButton.setOnClickListener(v -> {
onSongDeleteListener.onSongDeleted(noteTitles.get(position));
});
holder.editButton.setOnClickListener(v -> {
onSongEditListener.onSongEdit(noteTitles.get(position));
});
}
@Override
public int getItemCount() {
return noteTitles.size();
}
public void updateSongTitles(List<MusicNote> songs) {
this.noteTitles.clear();
this.noteTitles.addAll(songs);
notifyDataSetChanged();
}
public class NoteViewHolder extends RecyclerView.ViewHolder{
TextView titleTextView;
TextView yearTextView;
TextView compositionTextView;
ImageButton deleteButton;
ImageButton editButton;
NoteViewHolder(View itemView) {
super(itemView);
titleTextView = itemView.findViewById(R.id.text_title);
yearTextView = itemView.findViewById(R.id.text_year);
compositionTextView = itemView.findViewById(R.id.text_composition);
deleteButton = itemView.findViewById(R.id.delete_song_button);
editButton = itemView.findViewById(R.id.edit_song_button);
titleTextView.setOnClickListener(v -> {
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION) {
MusicNote musicNote = noteTitles.get(position);
Intent intent = new Intent(itemView.getContext(), FullScreenImageActivity.class);
new LoadNoteSheetsTask(musicNote.getMusicNoteId(),
MusicDatabase.getDatabase(itemView.getContext()).musicNoteDao(), new OnNoteSheedsLoadedListener() {
@Override
public void onNoteSheetsLoaded(List<NoteSheet> noteSheets) {
String[] noteSheetFiles = new String[noteSheets.size()];
for (int i = 0; i < noteSheets.size(); i++) {
noteSheetFiles[i] = noteSheets.get(i).getFilePath();
}
intent.putExtra("imageUris", noteSheetFiles);
itemView.getContext().startActivity(intent);
}
}).execute();
}
});
}
public void bind(MusicNote note) {
titleTextView.setText(note.getTitle());
}
}
public interface OnNoteSheedsLoadedListener {
void onNoteSheetsLoaded(List<NoteSheet> noteSheets);
}
private class LoadNoteSheetsTask extends AsyncTask<Void, Void, List<NoteSheet>> {
private final long musicNoteId;
private final MusicNoteDAO musicNoteDAO;
private final OnNoteSheedsLoadedListener listener;
public LoadNoteSheetsTask(long musicNoteId, MusicNoteDAO musicNoteDAO, OnNoteSheedsLoadedListener listener) {
this.musicNoteId = musicNoteId;
this.musicNoteDAO = musicNoteDAO;
this.listener = listener;
}
@Override
protected List<NoteSheet> doInBackground(Void... voids) {
List<NoteSheet> sheets = musicNoteDAO.getNoteSheetsForMusicSong(this.musicNoteId);
Log.d("LoadedNoteSheetsTask", "ID: " + this.musicNoteId);
Log.d("LoadNoteSheetsTask", "Loaded NoteSheets: " + sheets.size());
return sheets;
}
@Override
protected void onPostExecute(List<NoteSheet> sheets) {
listener.onNoteSheetsLoaded(sheets);
}
}
}

View File

@ -1,27 +0,0 @@
package core.notevault.ui.login;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import core.notevault.sync.APICallback;
import core.notevault.sync.auth.LoginCallback;
public class LoginCallBackImpl implements APICallback {
private Context context;
public LoginCallBackImpl(Context context) {
this.context = context;
}
@Override
public void onSuccess() {
Toast.makeText(context, "Login successfull", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(String error) {
Log.d("Login", error);
Toast.makeText(context, "Login not successfull: " + error, Toast.LENGTH_LONG).show();
}
}

View File

@ -1,100 +0,0 @@
package core.notevault.ui.login;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import core.notevault.R;
import core.notevault.sync.APICallback;
import core.notevault.sync.auth.AuthRepository;
import core.notevault.sync.auth.LoginCallback;
import retrofit2.Call;
import retrofit2.Callback;
public class LoginDialogFragment extends DialogFragment {
private APICallback loginCallback;
private APICallback registerCallback;
private boolean isLoginMode = true; // Default mode
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (loginCallback == null || registerCallback == null) {
throw new IllegalStateException("LoginDialogFragment must be created using newInstance()");
}
}
public static LoginDialogFragment newInstance(APICallback loginCallback, APICallback registerCallback) {
LoginDialogFragment fragment = new LoginDialogFragment();
fragment.loginCallback = loginCallback;
fragment.registerCallback = registerCallback;
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
/*return new AlertDialog.Builder(requireContext())
.setView(loginView)
.setPositiveButton("Speichern", (dialog, which) -> {
String username = usernameInput.getText().toString();
String password = passwordInput.getText().toString();
authRepository.performLogin(username, password, loginCallback);
} )
.setNegativeButton("Abbrechen", (dialog, which) -> {} )
.create();*/
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
LayoutInflater inflater = requireActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.fragment_login_dialog, null);
TextView textViewTitle = view.findViewById(R.id.textViewTitle);
EditText editTextUsername = view.findViewById(R.id.editTextUsername);
EditText editTextEmail = view.findViewById(R.id.editTextEmail);
EditText editTextPassword = view.findViewById(R.id.editTextPassword);
Button buttonAction = view.findViewById(R.id.buttonAction);
TextView textViewSwitch = view.findViewById(R.id.textViewSwitch);
// Handle action button click
buttonAction.setOnClickListener(v -> {
if (isLoginMode) {
// Handle login logic
String email = editTextEmail.getText().toString();
String password = editTextPassword.getText().toString();
AuthRepository authRepository = new AuthRepository(this.getContext());
authRepository.performLogin(email, password, loginCallback);
} else {
// Handle registration logic
String email = editTextEmail.getText().toString();
String password = editTextPassword.getText().toString();
String username = editTextUsername.getText().toString();
AuthRepository authRepository = new AuthRepository(this.getContext());
authRepository.performRegistration(email, username, password, registerCallback);
}
});
// Toggle between Login and Registration
textViewSwitch.setOnClickListener(v -> {
isLoginMode = !isLoginMode;
textViewTitle.setText(isLoginMode ? "Login" : "Register");
editTextUsername.setVisibility(isLoginMode ? View.GONE : View.VISIBLE);
buttonAction.setText(isLoginMode ? "Login" : "Register");
textViewSwitch.setText(isLoginMode ? "Don't have an account? Register" : "Already have an account? Login");
});
builder.setView(view);
return builder.create();
}
}

View File

@ -1,25 +0,0 @@
package core.notevault.ui.login;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import core.notevault.sync.APICallback;
public class RegisterCallback implements APICallback {
private Context context;
public RegisterCallback(Context context) {
this.context = context;
}
@Override
public void onSuccess() {
Toast.makeText(context, "Registration successfull", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(String error) {
Log.d("Login", error);
Toast.makeText(context, "Login not successfull: " + error, Toast.LENGTH_LONG).show();
}
}

View File

@ -1,108 +0,0 @@
package core.notevault.ui.metadatadialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import core.notevault.R;
public class MetaDataDialog extends DialogFragment {
private Uri[] fileUri;
public interface OnMetadataListener {
void onMetadataEntered(Uri[] uri, String title, String composer, int year, String genre);
}
private OnMetadataListener listener;
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
LayoutInflater inflater = requireActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.fragment_metadata_dialog, null); // Ersetze 'your_dialog_layout' durch deinen Dateinamen
EditText title_input = dialogView.findViewById(R.id.title_input);
title_input.setText(this.extraxtTitleFromFilePath());
EditText composer_input = dialogView.findViewById(R.id.composer_input);
EditText year_input = dialogView.findViewById(R.id.year_input);
EditText genre_input = dialogView.findViewById(R.id.genre_input);
return new AlertDialog.Builder(requireContext())
.setView(dialogView)
.setPositiveButton("Speichern", (dialog, which) -> {
String title = title_input.getText().toString();
String composer = composer_input.getText().toString();
String year_string = year_input.getText().toString();
int year = 0;
if(!year_string.isEmpty()) {
year = Integer.parseInt(year_input.getText().toString());
}
String genre = genre_input.getText().toString();
listener.onMetadataEntered(fileUri, title, composer, year, genre);
} )
.setNegativeButton("Abbrechen", (dialog, which) -> {} )
.create();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof OnMetadataListener) {
listener = (OnMetadataListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnMetadataListener");
}
}
public Uri[] getFileUri() {
return fileUri;
}
public void setFileUri(Uri[] fileUri) {
this.fileUri = fileUri;
}
private String extraxtTitleFromFilePath() {
String fileName = "";
// Überprüfen, ob die Uri ein Content-Uri ist
if (this.fileUri[0].getScheme().equals("content")) {
// ContentResolver verwenden, um die Datei zu finden
Cursor cursor = requireContext().getContentResolver().query(this.fileUri[0], null, null, null, null);
if (cursor != null) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (cursor.moveToFirst()) {
// Den Dateinamen aus dem Cursor abfragen
fileName = cursor.getString(nameIndex);
}
cursor.close();
}
} else if (this.fileUri[0].getScheme().equals("file")) {
// Bei einer Datei-Uri einfach den letzten Pfadsegment verwenden
fileName = this.fileUri[0].getLastPathSegment();
}
if(fileName.contains(".")) {
fileName = fileName.substring(0, fileName.lastIndexOf("."));
fileName = fileName.replace("_", " ");
fileName = fileName.replace("-", "");
}
return fileName;
}
public static String TAG = "MetaDataDialog";
}

View File

@ -1,90 +0,0 @@
package core.notevault.ui.metadatadialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import core.notevault.R;
import core.notevault.data.MusicNote;
import core.notevault.data.sync.SyncStatus;
public class SongEditDialog extends DialogFragment {
private MusicNote song;
private SongEditorListener songEditorListener;
public interface SongEditorListener {
void onSongEdited(MusicNote updatedSong);
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
LayoutInflater inflater = requireActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.song_metadata_edit_dialog, null); // Ersetze 'your_dialog_layout' durch deinen Dateinamen
EditText title_input = dialogView.findViewById(R.id.title_input);
title_input.setText(song.getTitle());
EditText composer_input = dialogView.findViewById(R.id.composer_input);
composer_input.setText(song.getComposer());
EditText year_input = dialogView.findViewById(R.id.year_input);
year_input.setText(String.valueOf(song.getYear()));
EditText genre_input = dialogView.findViewById(R.id.genre_input);
genre_input.setText(song.getGenre());
return new AlertDialog.Builder(requireContext())
.setView(dialogView)
.setPositiveButton("Speichern", (dialog, which) -> {
String title = title_input.getText().toString();
String composer = composer_input.getText().toString();
String year_string = year_input.getText().toString();
int year = 0;
if(!year_string.isEmpty()) {
year = Integer.parseInt(year_input.getText().toString());
}
String genre = genre_input.getText().toString();
song.setTitle(title);
song.setComposer(composer);
song.setGenre(genre);
song.setYear(year);
if(song.getSyncStatus() == SyncStatus.SYNCED) {
song.setSyncStatus(SyncStatus.MODIFIED);
}
songEditorListener.onSongEdited(song);
} )
.setNegativeButton("Abbrechen", (dialog, which) -> {} )
.create();
}
public MusicNote getSong() {
return song;
}
public void setSong(MusicNote song) {
this.song = song;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof MetaDataDialog.OnMetadataListener) {
songEditorListener = (SongEditorListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnMetadataListener");
}
}
public static String TAG = "SONGEDITDIALOG";
}

View File

@ -1,50 +0,0 @@
package core.notevault.ui.noteviewer;
import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.github.chrisbanes.photoview.PhotoView;
import core.notevault.R;
import java.util.List;
public class ImagePagerAdapter extends RecyclerView.Adapter<ImagePagerAdapter.ImageViewHolder> {
private final List<String> imageUris;
private final Context context;
public ImagePagerAdapter(Context context, List<String> imageUris) {
this.context = context;
this.imageUris = imageUris;
}
@NonNull
@Override
public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) {
Uri uri = Uri.parse(imageUris.get(position));
holder.photoView.setImageURI(uri);
}
@Override
public int getItemCount() {
return imageUris.size();
}
static class ImageViewHolder extends RecyclerView.ViewHolder {
PhotoView photoView;
ImageViewHolder(View itemView) {
super(itemView);
photoView = itemView.findViewById(R.id.photoView);
}
}
}

View File

@ -1,54 +0,0 @@
package core.notevault.util;
import android.content.Context;
import android.database.Cursor;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class NoteSheetsUtil {
private static long getImageTimestamp(Context context, Uri imageUri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(imageUri);
ExifInterface exif = new ExifInterface(inputStream);
String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
if(dateTime != null) {
SimpleDateFormat format = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.getDefault());
Date date = format.parse(dateTime);
return date != null ? date.getTime() : 0;
} else {
File file = new File(imageUri.getPath());
return file.lastModified();
}
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}
}
public static void sortNoteSheetsByTimestamp(Context context, Uri[] uris) {
ArrayList<UriTimestamp> uriTimestamps = new ArrayList<>();
for(Uri uri : uris) {
long timestamp = getImageTimestamp(context, uri);
uriTimestamps.add(new UriTimestamp(uri, timestamp));
}
Collections.sort(uriTimestamps, Comparator.comparingLong(UriTimestamp::getTimestamp));
for(int i=0; i<uriTimestamps.size(); i++) {
uris[i] = uriTimestamps.get(i).getUri();
}
}
}

View File

@ -1,21 +0,0 @@
package core.notevault.util;
import android.net.Uri;
public class UriTimestamp {
private final Uri uri;
private final long timestamp;
public UriTimestamp(Uri uri, long timestamp) {
this.uri = uri;
this.timestamp = timestamp;
}
public Uri getUri() {
return uri;
}
public long getTimestamp() {
return timestamp;
}
}

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M440,520L200,520v-80h240v-240h80v240h240v80L520,520v240h-80v-240Z"
android:fillColor="#e8eaed"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M280,840q-33,0 -56.5,-23.5T200,760v-520h-40v-80h200v-40h240v40h200v80h-40v520q0,33 -23.5,56.5T680,840L280,840ZM680,240L280,240v520h400v-520ZM360,680h80v-360h-80v360ZM520,680h80v-360h-80v360ZM280,240v520,-520Z"
android:fillColor="#e8eaed"/>
</vector>

View File

@ -1,6 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners android:radius="16dp" />
<padding android:left="16dp" android:top="16dp" android:right="16dp" android:bottom="16dp" />
<elevation android:height="4dp" />
</shape>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M200,760h57l391,-391 -57,-57 -391,391v57ZM120,840v-170l528,-527q12,-11 26.5,-17t30.5,-6q16,0 31,6t26,18l55,56q12,11 17.5,26t5.5,30q0,16 -5.5,30.5T817,313L290,840L120,840ZM760,256 L704,200 760,256ZM619,341 L591,312 648,369 619,341Z"
android:fillColor="#e8eaed"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M480,840v-80h280v-560L480,200v-80h280q33,0 56.5,23.5T840,200v560q0,33 -23.5,56.5T760,840L480,840ZM400,680 L345,622 447,520L120,520v-80h327L345,338l55,-58 200,200 -200,200Z"
android:fillColor="#e8eaed"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h280v80L200,200v560h280v80L200,840ZM640,680 L585,622 687,520L360,520v-80h327L585,338l55,-58 200,200 -200,200Z"
android:fillColor="#e8eaed"/>
</vector>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

View File

@ -23,4 +23,13 @@
<include layout="@layout/content_main"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:id="@+id/concert_editor_dialog">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Konzerteinstellungen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/concert_settings"
android:textStyle="bold"
android:textSize="18sp"/>
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Titel: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title_concert_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"
android:ems="10"
android:id="@+id/concert_title_input"
android:hint="Titel eingeben"
android:foregroundTint="#000000"
android:minHeight="48dp"
android:padding="16dp"
android:minWidth="48dp"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Datum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/concert_date_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="date"
android:ems="10"
android:id="@+id/concert_date_input"
android:hint="Datum eingeben"
android:foregroundTint="#000000"
android:minHeight="48dp"
android:padding="16dp"
android:minWidth="48dp"
android:focusable="false"
android:clickable="true"/>
</TableRow>
</TableLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:padding="16dp">
<!-- Linke Seite mit Datum und Titel -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/concert_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Concert Date"
android:textColor="@android:color/darker_gray"
android:textSize="14sp" />
<TextView
android:id="@+id/concert_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Concert Title"
android:textSize="18sp" />
</LinearLayout>
<!-- Abstandshalter, um die Buttons nach rechts zu schieben -->
<Space
android:layout_width="16dp"
android:layout_height="wrap_content" />
<!-- Rechte Seite mit den Buttons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/edit_concert_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/edit_24dp_e8eaed_fill0_wght400_grad0_opsz24"
app:tint="@color/teal_700"
android:contentDescription="Edit Concert"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
android:id="@+id/delete_concert_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/delete"
app:tint="@color/red"
android:contentDescription="Delete Concert"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
</LinearLayout>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:id="@+id/concert_song_selector_dialgog">
<TextView
android:text="Wähle Songs für das Konzert 'Jubiläum' am 19.10.2024 aus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dialog_title_concert_song_selector"
android:textStyle="bold"
android:textSize="18sp"/>
<SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:queryHint="Suche Stücke..." />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/concert_song_selector_recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" android:layout_above="@+id/add_song_concert"
android:padding="16dp" android:id="@+id/linearLayout">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:id="@+id/text_concert_details_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Konzerttitel"
android:textSize="18sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/text_concert_details_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="01.01.2024"
android:textSize="16sp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Hier können weitere Details hinzugefügt werden -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/note_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/add_song_concert"/>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:src="@drawable/add_24dp_e8eaed_fill0_wght400_grad0_opsz24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:id="@+id/add_song_concert"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp"
app:backgroundTint="@color/teal_200"
android:contentDescription="Add Concert Song"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,17 +7,17 @@
android:layout_height="match_parent"
tools:context=".ui.gallery.GalleryFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/concert_recycler_view"
<TextView
android:id="@+id/text_gallery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/add_concert_btn"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:src="@drawable/add_24dp_e8eaed_fill0_wght400_grad0_opsz24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true" android:id="@+id/add_concert_btn"
android:layout_marginEnd="16dp" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"/>
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,22 +7,17 @@
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/note_recycler_view"
<TextView
android:id="@+id/text_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/add_song_concert"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:src="@drawable/add_24dp_e8eaed_fill0_wght400_grad0_opsz24"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:id="@+id/add_song_concert"
android:layout_marginEnd="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp"
app:backgroundTint="@color/teal_200"
android:contentDescription="Add Music Notes"/>
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:background="@drawable/dialog_background"
android:layout_gravity="center_horizontal">
<!-- Title -->
<TextView
android:id="@+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:paddingBottom="16dp" />
<!-- Username Field (Visible only in Registration mode) -->
<EditText
android:id="@+id/editTextUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username"
android:visibility="gone" />
<!-- Email Field -->
<EditText
android:id="@+id/editTextEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:inputType="textEmailAddress" />
<!-- Password Field -->
<EditText
android:id="@+id/editTextPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
<!-- Action Button -->
<Button
android:id="@+id/buttonAction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
android:backgroundTint="?attr/colorPrimary"
android:textColor="@android:color/white"
android:layout_marginTop="16dp" />
<!-- Toggle Login/Registration -->
<TextView
android:id="@+id/textViewSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Don't have an account? Register"
android:gravity="center"
android:textColor="?attr/colorPrimary"
android:layout_marginTop="8dp"
android:padding="8dp"
android:clickable="true"
android:focusable="true" />
</LinearLayout>

View File

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:id="@+id/metadata_dialog">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Import-Einstellungen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dialog_title"
android:textStyle="bold"
android:textSize="18sp"/>
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Titel: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"
android:ems="10"
android:id="@+id/title_input"
android:hint="Titel eingeben"
android:foregroundTint="#000000"
android:minHeight="48dp"
android:padding="16dp"
android:minWidth="48dp"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Komponisten: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/concert_date_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="@+id/composer_input"
android:hint="Komponisten eingeben"
android:layout_weight="1"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="16dp"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Jahr: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/year_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:ems="10"
android:id="@+id/year_input"
android:hint="Erscheinungsjahr eingeben"
android:padding="16dp"
android:minWidth="48dp"
android:minHeight="48dp"
android:layout_weight="1"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Genre: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/genre_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="@+id/genre_input"
android:hint="Genre eingeben"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="16dp"
android:layout_weight="1"/>
</TableRow>
</TableLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:id="@+id/song_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Song Title" />
<CheckBox
android:id="@+id/song_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/photoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter" />
</FrameLayout>

View File

@ -1,77 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center_vertical">
<!-- Text Section -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- First Row: Year and Composition -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/text_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Year"
android:textSize="14sp"
android:textColor="@android:color/darker_gray" />
<TextView
android:id="@+id/text_composition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Composition"
android:textSize="14sp"
android:layout_marginStart="8dp"
android:textColor="@android:color/darker_gray" />
</LinearLayout>
<!-- Second Row: Title -->
<TextView
android:id="@+id/text_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="16sp"
android:textColor="@android:color/black"
android:paddingTop="4dp"/>
</LinearLayout>
<!-- Rechte Seite mit den Buttons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/edit_song_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/edit_24dp_e8eaed_fill0_wght400_grad0_opsz24"
app:tint="@color/teal_700"
android:contentDescription="Edit Song"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
android:id="@+id/delete_song_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/delete"
app:tint="@color/red"
android:contentDescription="Delete Song"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
</LinearLayout>

View File

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:id="@+id/metadata_dialog">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Import-Einstellungen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/dialog_title"
android:textStyle="bold"
android:textSize="18sp"/>
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Titel: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"
android:ems="10"
android:id="@+id/title_input"
android:hint="Titel eingeben"
android:foregroundTint="#000000"
android:minHeight="48dp"
android:padding="16dp"
android:minWidth="48dp"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Komponisten: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/concert_date_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="@+id/composer_input"
android:hint="Komponisten eingeben"
android:layout_weight="1"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="16dp"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Jahr: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/year_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:ems="10"
android:id="@+id/year_input"
android:hint="Erscheinungsjahr eingeben"
android:padding="16dp"
android:minWidth="48dp"
android:minHeight="48dp"
android:layout_weight="1"/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Genre: "
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/genre_label"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="@+id/genre_input"
android:hint="Genre eingeben"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="16dp"
android:layout_weight="1"/>
</TableRow>
</TableLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- res/menu/main_menu.xml -->
<menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_login"
android:title="Login"
android:icon="@drawable/logout"
app:showAsAction="always"/> <!-- Wichtig: immer in der Toolbar anzeigen -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"
app:showAsAction="never"/>
</menu>

Some files were not shown because too many files have changed in this diff Show More