Merge pull request 'nextNoteVault' (#23) from nextNoteVault into master
Reviewed-on: #23
This commit is contained in:
commit
58d2658dc4
6
.gitignore
vendored
6
.gitignore
vendored
@ -13,3 +13,9 @@
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/music_database
|
||||
/music_database-shm
|
||||
/music_database-wal
|
||||
/note_database
|
||||
/note_database-shm
|
||||
/note_database-wal
|
||||
|
1
.idea/.gitignore
vendored
1
.idea/.gitignore
vendored
@ -6,3 +6,4 @@
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
/AndroidProjectSystem.xml
|
||||
|
1
.idea/.name
Normal file
1
.idea/.name
Normal file
@ -0,0 +1 @@
|
||||
NoteVault
|
6
.idea/AndroidProjectSystem.xml
Normal file
6
.idea/AndroidProjectSystem.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
10
.idea/codeStyles/Project.xml
Normal file
10
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
@ -1,5 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
@ -1,31 +1,11 @@
|
||||
<?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">
|
||||
<data-source source="LOCAL" name="music_database" uuid="95a3c2ec-2c29-4336-900a-3993de90ae66">
|
||||
<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-url>jdbc:sqlite:$USER_HOME$/.cache/JetBrains/IntelliJIdea2025.1/device-explorer/samsung SM-P610/_/data/data/com.stormtales.notevault/databases/music_database</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
</jdbc-additional-properties>
|
||||
@ -39,5 +19,53 @@
|
||||
</library>
|
||||
</libraries>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="note_database" uuid="b3770d7c-0a73-40c6-aab8-010effaa19b6">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/.cache/JetBrains/IntelliJIdea2025.1/device-explorer/samsung SM-P610/_/data/data/come.stormborntales.notevault/databases/note_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>
|
||||
<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>
|
||||
<data-source source="LOCAL" name="note_database [2]" uuid="ad94cfd9-e485-4151-8bf4-a080c51fa27c">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/note_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>
|
||||
<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>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="NoteVault">
|
||||
<State />
|
||||
</entry>
|
||||
<entry key="app">
|
||||
<State />
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
@ -4,6 +4,14 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-05-03T08:34:29.354537334Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
@ -5,7 +5,6 @@
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="21" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
50
.idea/inspectionProfiles/Project_Default.xml
Normal file
50
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
6
.idea/kotlinc.xml
Normal file
6
.idea/kotlinc.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.0.0" />
|
||||
</component>
|
||||
</project>
|
@ -3,10 +3,5 @@
|
||||
<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" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
|
||||
</project>
|
17
.idea/runConfigurations.xml
Normal file
17
.idea/runConfigurations.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -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>
|
@ -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>
|
@ -1,16 +1,18 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
alias(libs.plugins.ksp) // NEU
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "core.notevault"
|
||||
compileSdk = 35
|
||||
namespace = "come.stormborntales.notevault"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "core.notevault"
|
||||
minSdk = 28
|
||||
targetSdk = 35
|
||||
applicationId = "come.stormborntales.notevault"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@ -22,48 +24,43 @@ 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
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
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")
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
implementation(libs.androidx.foundation)
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
implementation(libs.lifecycle.livedata.ktx)
|
||||
implementation(libs.compose.runtime.livedata)
|
||||
implementation(libs.coil.compose)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package come.stormborntales.notevault
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("come.stormborntales.notevault", appContext.packageName)
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package core.notevault;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("core.notevault", appContext.getPackageName());
|
||||
}
|
||||
}
|
@ -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,24 +11,19 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.NoteVault"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.NoteVault.NoActionBar">
|
||||
android:theme="@style/Theme.NoteVault">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<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>
|
||||
<activity android:name=".FullscreenImageViewerActivity" />
|
||||
|
||||
</application>
|
||||
|
||||
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 197 KiB |
@ -0,0 +1,26 @@
|
||||
package come.stormborntales.notevault
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import come.stormborntales.notevault.ui.screens.FullscreenImageViewer
|
||||
import come.stormborntales.notevault.ui.theme.NoteVaultTheme
|
||||
|
||||
// FullscreenImageViewerActivity.kt
|
||||
class FullscreenImageViewerActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val imageUris = intent.getParcelableArrayListExtra<Uri>("imageUris") ?: emptyList()
|
||||
|
||||
setContent {
|
||||
NoteVaultTheme {
|
||||
FullscreenImageViewer(
|
||||
images = imageUris,
|
||||
onClose = { finish() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
253
app/src/main/java/come/stormborntales/notevault/MainActivity.kt
Normal file
253
app/src/main/java/come/stormborntales/notevault/MainActivity.kt
Normal file
@ -0,0 +1,253 @@
|
||||
package come.stormborntales.notevault
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalDrawerSheet
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationDrawerItem
|
||||
import androidx.compose.material3.NavigationDrawerItemDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import come.stormborntales.notevault.data.local.AppDatabase
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
import come.stormborntales.notevault.data.repository.NoteRepository
|
||||
import come.stormborntales.notevault.ui.screens.AddNoteDialog
|
||||
import come.stormborntales.notevault.ui.screens.MainScreen
|
||||
import come.stormborntales.notevault.ui.screens.NotesScreen
|
||||
import come.stormborntales.notevault.ui.screens.SettingsScreen
|
||||
import come.stormborntales.notevault.ui.theme.NoteVaultTheme
|
||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
|
||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModelFactory
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val applicationContext = applicationContext
|
||||
val database = AppDatabase.getDatabase(applicationContext)
|
||||
val repository = NoteRepository(database.noteDao())
|
||||
val viewModelFactory = NoteViewModelFactory(repository)
|
||||
|
||||
setContent {
|
||||
NoteVaultTheme {
|
||||
val navController = rememberNavController()
|
||||
val viewModel: NoteViewModel = viewModel(factory = viewModelFactory)
|
||||
|
||||
var selectedUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
var noteToEdit by remember { mutableStateOf<NoteEntity?>(null) }
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val imagePickerLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenMultipleDocuments(),
|
||||
onResult = { uris ->
|
||||
if (uris.isNotEmpty()) {
|
||||
selectedUris = uris
|
||||
showDialog = true
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val openDialog: (NoteEntity?) -> Unit = { note ->
|
||||
Log.d("EditNote", "NoteEntity: ${note?.title}")
|
||||
noteToEdit = note
|
||||
showDialog = true
|
||||
}
|
||||
|
||||
val navItems = listOf(
|
||||
"Home" to "main",
|
||||
"Notes" to "notes",
|
||||
"Einstellungen" to "settings"
|
||||
)
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Text("Navigation", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
|
||||
navItems.forEach { (label, route) ->
|
||||
NavigationDrawerItem(
|
||||
label = { Text(label) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
navController.navigate(route) {
|
||||
popUpTo(navController.graph.startDestinationId) { saveState = true }
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
scope.launch { drawerState.close() }
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
TopAppBar(
|
||||
title = {
|
||||
if(currentRoute == "main") {
|
||||
TextField(
|
||||
value = searchQuery,
|
||||
onValueChange = {
|
||||
searchQuery = it
|
||||
viewModel.searchQuery.value = it
|
||||
},
|
||||
placeholder = { Text("Suche...") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 48.dp), // Platz für Avatar
|
||||
textStyle = MaterialTheme.typography.bodyLarge,
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
containerColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Text("NoteVault")
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { scope.launch { drawerState.open() } }) {
|
||||
Icon(Icons.Default.Menu, contentDescription = "Menü öffnen")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
Box {
|
||||
IconButton(onClick = { isMenuExpanded = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AccountCircle, // Avatar-Icon
|
||||
contentDescription = "Benutzerprofil"
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = isMenuExpanded,
|
||||
onDismissRequest = { isMenuExpanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Profil") },
|
||||
onClick = {
|
||||
isMenuExpanded = false
|
||||
// TODO: Navigiere ggf. zu Profilscreen
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Abmelden") },
|
||||
onClick = {
|
||||
isMenuExpanded = false
|
||||
// TODO: Logout-Logik
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = "main",
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
) {
|
||||
composable("main") {
|
||||
MainScreen(
|
||||
viewModel = viewModel,
|
||||
onAddNoteClicked = {
|
||||
imagePickerLauncher.launch(arrayOf("image/*"))
|
||||
},
|
||||
onEditNote = openDialog
|
||||
)
|
||||
}
|
||||
composable("settings") {
|
||||
SettingsScreen()
|
||||
}
|
||||
composable("notes") {
|
||||
NotesScreen()
|
||||
}
|
||||
}
|
||||
|
||||
if (showDialog) {
|
||||
val context = LocalContext.current
|
||||
AddNoteDialog(
|
||||
onDismiss = { showDialog = false },
|
||||
onSave = { title, composer, year, genre, description ->
|
||||
if (noteToEdit == null) {
|
||||
viewModel.addNote(
|
||||
context = context,
|
||||
title = title,
|
||||
composer = composer,
|
||||
year = year,
|
||||
genre = genre,
|
||||
description = description,
|
||||
selectedUris = selectedUris,
|
||||
onDone = { showDialog = false }
|
||||
)
|
||||
} else {
|
||||
viewModel.updateNote(
|
||||
editedNote = noteToEdit!!,
|
||||
updatedTitle = title,
|
||||
updatedComposer = composer,
|
||||
updatedYear = year,
|
||||
updatedGenre = genre,
|
||||
updatedDescription = description,
|
||||
onDone = { showDialog = false }
|
||||
)
|
||||
}
|
||||
},
|
||||
initialTitle = noteToEdit?.title ?: "",
|
||||
initialComposer = noteToEdit?.composer,
|
||||
initialYear = noteToEdit?.year,
|
||||
initialGenre = noteToEdit?.genre,
|
||||
initialDescription = noteToEdit?.description
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package come.stormborntales.notevault.data.local
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import come.stormborntales.notevault.data.local.dao.NoteDao
|
||||
import come.stormborntales.notevault.data.local.entity.NoteCollection
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
|
||||
@Database(entities = [NoteEntity::class, NoteCollection::class], version = 1)
|
||||
@TypeConverters(UriListConverter::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun noteDao(): NoteDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
fun getDatabase(context: Context): AppDatabase {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"note_database"
|
||||
).build()
|
||||
INSTANCE = instance
|
||||
Log.d("AppDatabase", "Datenbank erstellt: ${instance.openHelper.writableDatabase}")
|
||||
instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package come.stormborntales.notevault.data.local
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
|
||||
class UriListConverter {
|
||||
@TypeConverter
|
||||
fun fromList(list: List<String>): String = list.joinToString(",")
|
||||
|
||||
@TypeConverter
|
||||
fun toList(data: String): List<String> = if (data.isBlank()) emptyList() else data.split(",")
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package come.stormborntales.notevault.data.local.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface NoteDao {
|
||||
@Query("SELECT * FROM notes")
|
||||
fun getAll(): Flow<List<NoteEntity>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(note: NoteEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(note: NoteEntity)
|
||||
|
||||
@Update
|
||||
suspend fun update(note: NoteEntity)
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package come.stormborntales.notevault.data.local.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity("note_collections")
|
||||
data class NoteCollection(
|
||||
@PrimaryKey(autoGenerate = true) var id: Int = 0,
|
||||
var name: String,
|
||||
var parentId: Int? = null
|
||||
)
|
@ -0,0 +1,24 @@
|
||||
package come.stormborntales.notevault.data.local.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "notes",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = NoteCollection::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["collectionId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)])
|
||||
data class NoteEntity(
|
||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||
var title: String,
|
||||
val images: List<String>, // oder String + TypeConverter
|
||||
var composer: String?,
|
||||
var year: Int?,
|
||||
var genre: String?,
|
||||
var description: String?,
|
||||
val imagePreview: String,
|
||||
val collectionId: Int?
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package come.stormborntales.notevault.data.model
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
class NoteEntry(
|
||||
val title: String, val images: List<Uri>, val composer: String?, val year: Int?,
|
||||
val genre: String?, val description: String?
|
||||
) {
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package come.stormborntales.notevault.data.repository
|
||||
|
||||
import come.stormborntales.notevault.data.local.dao.NoteDao
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class NoteRepository(private val dao: NoteDao) {
|
||||
val allNotes: Flow<List<NoteEntity>> = dao.getAll()
|
||||
|
||||
suspend fun insert(note: NoteEntity) = dao.insert(note)
|
||||
suspend fun delete(note: NoteEntity) = dao.delete(note)
|
||||
|
||||
suspend fun update(note: NoteEntity) = dao.update(note);
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package come.stormborntales.notevault.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun AddNoteDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onSave: (title: String, composer: String?, year: Int?, genre: String?, description: String?) -> Unit,
|
||||
initialTitle: String = "",
|
||||
initialComposer: String? = null,
|
||||
initialYear: Int? = null,
|
||||
initialGenre: String? = null,
|
||||
initialDescription: String? = null
|
||||
) {
|
||||
var title by remember { mutableStateOf(initialTitle) }
|
||||
var composer by remember { mutableStateOf(initialComposer ?: "") }
|
||||
var yearText by remember { mutableStateOf(initialYear?.toString() ?: "") }
|
||||
var genre by remember { mutableStateOf(initialGenre ?: "") }
|
||||
var description by remember { mutableStateOf(initialDescription ?: "") }
|
||||
var showTitleError by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
if (title.isBlank()) {
|
||||
showTitleError = true
|
||||
} else {
|
||||
val year = yearText.toIntOrNull()
|
||||
onSave(
|
||||
title.trim(),
|
||||
composer.takeIf { it.isNotBlank() },
|
||||
year,
|
||||
genre.takeIf { it.isNotBlank() },
|
||||
description.takeIf { it.isNotBlank() }
|
||||
)
|
||||
}
|
||||
}) {
|
||||
Text("Speichern")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Abbrechen")
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text("Notenblatt hinzufügen", style = MaterialTheme.typography.titleLarge)
|
||||
},
|
||||
text = {
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
OutlinedTextField(
|
||||
value = title,
|
||||
onValueChange = {
|
||||
title = it
|
||||
if (it.isNotBlank()) showTitleError = false
|
||||
},
|
||||
label = { Text("Titel*") },
|
||||
isError = showTitleError,
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
)
|
||||
if (showTitleError) {
|
||||
Text(
|
||||
"Titel darf nicht leer sein",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = composer,
|
||||
onValueChange = { composer = it },
|
||||
label = { Text("Komponist (optional)") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = yearText,
|
||||
onValueChange = { yearText = it.filter { c -> c.isDigit() } },
|
||||
label = { Text("Erscheinungsjahr (optional)") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = genre,
|
||||
onValueChange = { genre = it },
|
||||
label = { Text("Genre (optional)") },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = description,
|
||||
onValueChange = { description = it },
|
||||
label = { Text("Beschreibung (optional)") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
maxLines = 4
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package come.stormborntales.notevault.ui.screens
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FullscreenImageViewer(
|
||||
images: List<Uri>,
|
||||
onClose: () -> Unit
|
||||
) {
|
||||
val pagerState = rememberPagerState(pageCount = { images.size })
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
HorizontalPager(state = pagerState) { page ->
|
||||
val context = LocalContext.current
|
||||
val imageBitmap = remember(images[page]) {
|
||||
loadImageBitmap(context, images[page].toString())
|
||||
}
|
||||
|
||||
imageBitmap?.let {
|
||||
Image(
|
||||
bitmap = it,
|
||||
contentDescription = "Notenbild",
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black),
|
||||
contentScale = ContentScale.Fit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = onClose,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Schließen", tint = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
package come.stormborntales.notevault.ui.screens
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.ImageDecoder
|
||||
import android.media.Image
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import come.stormborntales.notevault.FullscreenImageViewerActivity
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
import come.stormborntales.notevault.data.model.NoteEntry
|
||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
|
||||
import java.io.InputStream
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
|
||||
fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
|
||||
return try {
|
||||
val uri = uriString.toUri()
|
||||
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
bitmap?.asImageBitmap()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}@Composable
|
||||
fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (NoteEntity) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp // Bildschirmbreite in dp
|
||||
|
||||
// Dynamische Bildgröße basierend auf der Bildschirmbreite
|
||||
val imageSize = if (screenWidth < 400) 80.dp else 120.dp
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
|
||||
) {
|
||||
// Responsive Layout: Überprüfen, ob der Bildschirm schmaler als 360 dp ist
|
||||
if (screenWidth < 400) {
|
||||
// Wenn der Bildschirm schmal ist, arrangiere die Elemente vertikal
|
||||
Row(modifier = Modifier.padding(16.dp)) {
|
||||
AsyncImage(
|
||||
model = note.imagePreview,
|
||||
contentDescription = "Vorschaubild",
|
||||
modifier = Modifier
|
||||
.size(imageSize)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
)
|
||||
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = note.title,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
note.composer?.let {
|
||||
Text("von $it", style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
note.year?.let {
|
||||
Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
|
||||
note.genre?.let {
|
||||
Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
|
||||
note.description?.let {
|
||||
Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp)) // Abstand zwischen Text und Buttons
|
||||
}
|
||||
}
|
||||
// Buttons unter dem Text
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
val uris = ArrayList<Uri>()
|
||||
note.images.forEach { uris.add(it.toUri()) }
|
||||
|
||||
val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
|
||||
putParcelableArrayListExtra("imageUris", ArrayList(uris))
|
||||
}
|
||||
context.startActivity(intent)
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
onEditNote(note)
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
onDeleteNote(note)
|
||||
},
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.error
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Löschen", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Wenn der Bildschirm breiter als 360 dp ist, arrangiere die Elemente nebeneinander
|
||||
Row(modifier = Modifier.padding(16.dp)) {
|
||||
// Linkes Vorschaubild
|
||||
AsyncImage(
|
||||
model = note.imagePreview,
|
||||
contentDescription = "Vorschaubild",
|
||||
modifier = Modifier
|
||||
.size(imageSize)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
)
|
||||
|
||||
// Rechte Info-Spalte
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f) // Damit die Spalte den verfügbaren Platz nutzt
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(start = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = note.title,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
note.composer?.let {
|
||||
Text("von $it", style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
note.year?.let {
|
||||
Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
|
||||
note.genre?.let {
|
||||
Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
|
||||
note.description?.let {
|
||||
Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Buttons in einer Row anordnen
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.fillMaxWidth() // Stellt sicher, dass die Row den gesamten verfügbaren Platz einnimmt
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
val uris = ArrayList<Uri>()
|
||||
note.images.forEach { uris.add(it.toUri()) }
|
||||
|
||||
val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
|
||||
putParcelableArrayListExtra("imageUris", ArrayList(uris))
|
||||
}
|
||||
context.startActivity(intent)
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
onEditNote(note)
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
onDeleteNote(note)
|
||||
},
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.error
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
|
||||
) {
|
||||
Text("Löschen", style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
onAddNoteClicked: () -> Unit, // Übergib hier deine Logik für den Import
|
||||
viewModel: NoteViewModel,
|
||||
onEditNote: (NoteEntity) -> Unit
|
||||
) {
|
||||
val notes by viewModel.filteredNotes.collectAsState(initial = emptyList())
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = onAddNoteClicked
|
||||
) {
|
||||
Icon(Icons.Default.Add, contentDescription = "Neue Noten hinzufügen")
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.End
|
||||
) { innerPadding ->
|
||||
LazyColumn(
|
||||
contentPadding = innerPadding,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
items(notes) { note ->
|
||||
NoteCard(note = note, onDeleteNote = { noteEntity ->
|
||||
viewModel.deleteNote(noteEntity)
|
||||
}, onEditNote = onEditNote)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package come.stormborntales.notevault.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
|
||||
@Composable
|
||||
fun NotesScreen() {
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package come.stormborntales.notevault.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Einstellungen",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
|
||||
Divider()
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Beispielhafte Einstellung
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "Dark Mode",
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Switch(
|
||||
checked = false,
|
||||
onCheckedChange = { /* TODO: Dark Mode aktivieren */ }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "Benachrichtigungen",
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Switch(
|
||||
checked = true,
|
||||
onCheckedChange = { /* TODO: Benachrichtigungseinstellungen */ }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { /* TODO: Impressum anzeigen */ },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Impressum")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package come.stormborntales.notevault.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
@ -0,0 +1,58 @@
|
||||
package come.stormborntales.notevault.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun NoteVaultTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package come.stormborntales.notevault.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
@ -0,0 +1,134 @@
|
||||
package come.stormborntales.notevault.ui.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
|
||||
import come.stormborntales.notevault.data.repository.NoteRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import androidx.core.graphics.scale
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class NoteViewModel(
|
||||
private val repository: NoteRepository,
|
||||
) : ViewModel() {
|
||||
// Sucheingabe als StateFlow
|
||||
val searchQuery = MutableStateFlow("")
|
||||
|
||||
// Alle Notizen als Flow aus der Datenbank (NICHT blockierend!)
|
||||
private val allNotes: Flow<List<NoteEntity>> = repository.allNotes
|
||||
|
||||
// Gefilterte Notizen basierend auf Sucheingabe
|
||||
val filteredNotes: StateFlow<List<NoteEntity>> = combine(searchQuery, allNotes) { query, notes ->
|
||||
if (query.isBlank()) {
|
||||
notes
|
||||
} else {
|
||||
notes.filter {
|
||||
it.title.contains(query, ignoreCase = true) ||
|
||||
it.description?.contains(query, ignoreCase = true) == true
|
||||
}
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
|
||||
fun createPreviewImage(context: Context, uri: Uri): File? {
|
||||
return try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val originalBitmap = BitmapFactory.decodeStream(inputStream) ?: return null
|
||||
|
||||
val previewBitmap = originalBitmap.scale(128, 128, false)
|
||||
val previewFile = File(context.filesDir, "preview_${System.currentTimeMillis()}.jpg")
|
||||
previewFile.outputStream().use { out ->
|
||||
previewBitmap.compress(Bitmap.CompressFormat.JPEG, 75, out)
|
||||
}
|
||||
previewFile
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun addNote(context: Context, title: String, composer: String?, year: Int?, genre: String?, description: String?, selectedUris: List<Uri>, onDone:() -> Unit) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val copiedUris = selectedUris.mapNotNull { uri ->
|
||||
try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val extension = MimeTypeMap.getSingleton()
|
||||
.getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "jpg"
|
||||
|
||||
val outputFile = File(context.filesDir, "note_${System.currentTimeMillis()}.$extension")
|
||||
inputStream?.use { input ->
|
||||
outputFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
Log.d("NoteViewModel", "NoteEntityFile" + outputFile.absolutePath)
|
||||
Uri.fromFile(outputFile).toString() // speichern als String
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (copiedUris.isNotEmpty()) {
|
||||
val preview_image_path = createPreviewImage(context, uri = selectedUris[0])
|
||||
|
||||
val note = NoteEntity(
|
||||
title = title,
|
||||
images = copiedUris, // muss als List<String> gespeichert sein
|
||||
composer = composer,
|
||||
year = year,
|
||||
genre = genre,
|
||||
description = description,
|
||||
imagePreview = preview_image_path.toString(),
|
||||
collectionId = null
|
||||
)
|
||||
|
||||
repository.insert(note)
|
||||
}
|
||||
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteNote(note: NoteEntity) {
|
||||
viewModelScope.launch {
|
||||
repository.delete(note)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNote(
|
||||
editedNote: NoteEntity,
|
||||
updatedTitle: String,
|
||||
updatedComposer: String?,
|
||||
updatedYear: Int?,
|
||||
updatedGenre: String?,
|
||||
updatedDescription: String?,
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
editedNote.title = updatedTitle
|
||||
editedNote.year = updatedYear;
|
||||
editedNote.composer = updatedComposer
|
||||
editedNote.genre = updatedGenre
|
||||
editedNote.description = updatedDescription
|
||||
|
||||
repository.update(editedNote)
|
||||
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package come.stormborntales.notevault.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import come.stormborntales.notevault.data.repository.NoteRepository
|
||||
|
||||
class NoteViewModelFactory(
|
||||
private val repository: NoteRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(NoteViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return NoteViewModel(repository) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
public enum SyncDataObjectType {
|
||||
MUSIC_NOTE
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
public enum SyncStatus {
|
||||
CREATED,
|
||||
DELETED,
|
||||
MODIFIED,
|
||||
REMOTE_MODIFIED,
|
||||
SYNCED;
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package core.notevault.sync;
|
||||
|
||||
public interface APICallback {
|
||||
|
||||
void onSuccess();
|
||||
void onError(String error);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package core.notevault.sync.auth;
|
||||
|
||||
public interface LoginCallback {
|
||||
|
||||
void onSuccess();
|
||||
void onError(String error);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package core.notevault.sync.synchronisation.songs.notesheets;
|
||||
|
||||
public class UploadResponse {
|
||||
private String serverID;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
@ -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";
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package core.notevault.ui.slideshow;
|
||||
|
||||
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 core.notevault.databinding.FragmentSlideshowBinding;
|
||||
|
||||
public class SlideshowFragment extends Fragment {
|
||||
|
||||
private FragmentSlideshowBinding binding;
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
SlideshowViewModel slideshowViewModel =
|
||||
new ViewModelProvider(this).get(SlideshowViewModel.class);
|
||||
|
||||
binding = FragmentSlideshowBinding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
final TextView textView = binding.textSlideshow;
|
||||
slideshowViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package core.notevault.ui.slideshow;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class SlideshowViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<String> mText;
|
||||
|
||||
public SlideshowViewModel() {
|
||||
mText = new MutableLiveData<>();
|
||||
mText.setValue("This is slideshow fragment");
|
||||
}
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user