forked from penguin86/luna-tracker
		
	First working sync (with hardcoded credentials and no concurrent modification checks)
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -103,3 +103,6 @@ app/release/output-metadata.json
 | 
				
			|||||||
.idea/misc.xml
 | 
					.idea/misc.xml
 | 
				
			||||||
.idea/deploymentTargetSelector.xml
 | 
					.idea/deploymentTargetSelector.xml
 | 
				
			||||||
.idea/other.xml
 | 
					.idea/other.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Other
 | 
				
			||||||
 | 
					app/src/main/java/it/danieleverducci/lunatracker/TemporaryHardcodedCredentials.kt
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,3 +0,0 @@
 | 
				
			|||||||
# Default ignored files
 | 
					 | 
				
			||||||
/shelf/
 | 
					 | 
				
			||||||
/workspace.xml
 | 
					 | 
				
			||||||
							
								
								
									
										6
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
								
							@@ -1,6 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					 | 
				
			||||||
<project version="4">
 | 
					 | 
				
			||||||
  <component name="CompilerConfiguration">
 | 
					 | 
				
			||||||
    <bytecodeTargetLevel target="21" />
 | 
					 | 
				
			||||||
  </component>
 | 
					 | 
				
			||||||
</project>
 | 
					 | 
				
			||||||
							
								
								
									
										57
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										57
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
								
							@@ -1,57 +0,0 @@
 | 
				
			|||||||
<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" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" 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" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
    <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
 | 
					 | 
				
			||||||
      <option name="composableFile" value="true" />
 | 
					 | 
				
			||||||
      <option name="previewFile" value="true" />
 | 
					 | 
				
			||||||
    </inspection_tool>
 | 
					 | 
				
			||||||
  </profile>
 | 
					 | 
				
			||||||
</component>
 | 
					 | 
				
			||||||
@@ -51,6 +51,8 @@ dependencies {
 | 
				
			|||||||
    implementation(libs.androidx.material3)
 | 
					    implementation(libs.androidx.material3)
 | 
				
			||||||
    implementation(libs.androidx.appcompat)
 | 
					    implementation(libs.androidx.appcompat)
 | 
				
			||||||
    implementation(libs.androidx.recyclerview)
 | 
					    implementation(libs.androidx.recyclerview)
 | 
				
			||||||
 | 
					    implementation("com.github.thegrizzlylabs:sardine-android:v0.9")
 | 
				
			||||||
 | 
					    implementation(libs.material)
 | 
				
			||||||
    testImplementation(libs.junit)
 | 
					    testImplementation(libs.junit)
 | 
				
			||||||
    androidTestImplementation(libs.androidx.junit)
 | 
					    androidTestImplementation(libs.androidx.junit)
 | 
				
			||||||
    androidTestImplementation(libs.androidx.espresso.core)
 | 
					    androidTestImplementation(libs.androidx.espresso.core)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@
 | 
				
			|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:tools="http://schemas.android.com/tools">
 | 
					    xmlns:tools="http://schemas.android.com/tools">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.INTERNET"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
        android:allowBackup="true"
 | 
					        android:allowBackup="true"
 | 
				
			||||||
        android:dataExtractionRules="@xml/data_extraction_rules"
 | 
					        android:dataExtractionRules="@xml/data_extraction_rules"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,25 @@
 | 
				
			|||||||
package it.danieleverducci.lunatracker
 | 
					package it.danieleverducci.lunatracker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.Context
 | 
					 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.os.Handler
 | 
					import android.os.Handler
 | 
				
			||||||
import android.preference.PreferenceManager
 | 
					import android.util.Log
 | 
				
			||||||
import android.view.View
 | 
					import android.view.View
 | 
				
			||||||
import android.widget.NumberPicker
 | 
					import android.widget.NumberPicker
 | 
				
			||||||
import android.widget.TextView
 | 
					import android.widget.TextView
 | 
				
			||||||
import android.widget.Toast
 | 
					import android.widget.Toast
 | 
				
			||||||
import androidx.appcompat.app.AlertDialog
 | 
					import androidx.appcompat.app.AlertDialog
 | 
				
			||||||
import androidx.appcompat.app.AppCompatActivity
 | 
					import androidx.appcompat.app.AppCompatActivity
 | 
				
			||||||
import androidx.core.content.edit
 | 
					 | 
				
			||||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
import androidx.recyclerview.widget.RecyclerView
 | 
					import androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					import com.google.android.material.progressindicator.LinearProgressIndicator
 | 
				
			||||||
import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
 | 
					import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
 | 
				
			||||||
import it.danieleverducci.lunatracker.entities.Logbook
 | 
					import it.danieleverducci.lunatracker.entities.Logbook
 | 
				
			||||||
import it.danieleverducci.lunatracker.entities.LunaEvent
 | 
					import it.danieleverducci.lunatracker.entities.LunaEvent
 | 
				
			||||||
import it.danieleverducci.lunatracker.entities.LunaEventType
 | 
					import it.danieleverducci.lunatracker.entities.LunaEventType
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.repository.LogbookRepository
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.repository.LogbookSavedListener
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
 | 
				
			||||||
import kotlinx.coroutines.Runnable
 | 
					import kotlinx.coroutines.Runnable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MainActivity : AppCompatActivity() {
 | 
					class MainActivity : AppCompatActivity() {
 | 
				
			||||||
@@ -28,28 +31,30 @@ class MainActivity : AppCompatActivity() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    lateinit var logbook: Logbook
 | 
					    lateinit var logbook: Logbook
 | 
				
			||||||
    lateinit var adapter: LunaEventRecyclerAdapter
 | 
					    lateinit var adapter: LunaEventRecyclerAdapter
 | 
				
			||||||
 | 
					    lateinit var progressIndicator: LinearProgressIndicator
 | 
				
			||||||
    lateinit var recyclerView: RecyclerView
 | 
					    lateinit var recyclerView: RecyclerView
 | 
				
			||||||
    lateinit var handler: Handler
 | 
					    lateinit var handler: Handler
 | 
				
			||||||
    val updateListRunnable: Runnable = Runnable {
 | 
					    val updateListRunnable: Runnable = Runnable {
 | 
				
			||||||
        adapter.notifyDataSetChanged()
 | 
					        loadLogbook()
 | 
				
			||||||
        handler.postDelayed(updateListRunnable, 1000*30)
 | 
					        handler.postDelayed(updateListRunnable, 1000*60)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    val logbookRepo: LogbookRepository = WebDAVLogbookRepository(   // TODO: support also FileLogbookRepository
 | 
				
			||||||
 | 
					        TemporaryHardcodedCredentials.URL,
 | 
				
			||||||
 | 
					        TemporaryHardcodedCredentials.USERNAME,
 | 
				
			||||||
 | 
					        TemporaryHardcodedCredentials.PASSWORD
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
					    override fun onCreate(savedInstanceState: Bundle?) {
 | 
				
			||||||
        super.onCreate(savedInstanceState)
 | 
					        super.onCreate(savedInstanceState)
 | 
				
			||||||
        handler = Handler(mainLooper)
 | 
					        handler = Handler(mainLooper)
 | 
				
			||||||
 | 
					        adapter = LunaEventRecyclerAdapter(this)
 | 
				
			||||||
        // Load data
 | 
					 | 
				
			||||||
        logbook = Logbook.load(this)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Show view
 | 
					        // Show view
 | 
				
			||||||
        setContentView(R.layout.activity_main)
 | 
					        setContentView(R.layout.activity_main)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Show logbook
 | 
					        progressIndicator = findViewById<LinearProgressIndicator>(R.id.progress_indicator)
 | 
				
			||||||
        recyclerView = findViewById<RecyclerView>(R.id.list_events)
 | 
					        recyclerView = findViewById<RecyclerView>(R.id.list_events)
 | 
				
			||||||
        recyclerView.setLayoutManager(LinearLayoutManager(this))
 | 
					        recyclerView.setLayoutManager(LinearLayoutManager(applicationContext))
 | 
				
			||||||
        adapter = LunaEventRecyclerAdapter(this)
 | 
					 | 
				
			||||||
        adapter.items.addAll(logbook.logs)
 | 
					 | 
				
			||||||
        recyclerView.adapter = adapter
 | 
					        recyclerView.adapter = adapter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set listeners
 | 
					        // Set listeners
 | 
				
			||||||
@@ -82,11 +87,18 @@ class MainActivity : AppCompatActivity() {
 | 
				
			|||||||
        ) }
 | 
					        ) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun showLogbook() {
 | 
				
			||||||
 | 
					        // Show logbook
 | 
				
			||||||
 | 
					        adapter.items.clear()
 | 
				
			||||||
 | 
					        adapter.items.addAll(logbook.logs)
 | 
				
			||||||
 | 
					        adapter.notifyDataSetChanged()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onStart() {
 | 
					    override fun onStart() {
 | 
				
			||||||
        super.onStart()
 | 
					        super.onStart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Update list dates
 | 
					        // Update list dates
 | 
				
			||||||
        adapter.notifyDataSetChanged()
 | 
					        loadLogbook()
 | 
				
			||||||
        handler.postDelayed(updateListRunnable, 1000*30)
 | 
					        handler.postDelayed(updateListRunnable, 1000*30)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,13 +150,52 @@ class MainActivity : AppCompatActivity() {
 | 
				
			|||||||
        alertDialog.show()
 | 
					        alertDialog.show()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun loadLogbook() {
 | 
				
			||||||
 | 
					        // Load data
 | 
				
			||||||
 | 
					        progressIndicator.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					        logbookRepo.loadLogbook(this, object: LogbookLoadedListener{
 | 
				
			||||||
 | 
					            override fun onLogbookLoaded(lb: Logbook) {
 | 
				
			||||||
 | 
					                runOnUiThread({
 | 
				
			||||||
 | 
					                    progressIndicator.visibility = View.INVISIBLE
 | 
				
			||||||
 | 
					                    logbook = lb
 | 
				
			||||||
 | 
					                    showLogbook()
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            override fun onError(error: String) {
 | 
				
			||||||
 | 
					                runOnUiThread({
 | 
				
			||||||
 | 
					                    progressIndicator.visibility = View.INVISIBLE
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Unable to load logbook. Create a new one.")
 | 
				
			||||||
 | 
					                    logbook = Logbook()
 | 
				
			||||||
 | 
					                    showLogbook()
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun logEvent(event: LunaEvent) {
 | 
					    fun logEvent(event: LunaEvent) {
 | 
				
			||||||
        adapter.items.add(0, event)
 | 
					        adapter.items.add(0, event)
 | 
				
			||||||
        adapter.notifyItemInserted(0)
 | 
					        adapter.notifyItemInserted(0)
 | 
				
			||||||
        recyclerView.smoothScrollToPosition(0)
 | 
					        recyclerView.smoothScrollToPosition(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        progressIndicator.visibility = View.VISIBLE
 | 
				
			||||||
        logbook.logs.add(0, event)
 | 
					        logbook.logs.add(0, event)
 | 
				
			||||||
        logbook.save(this)
 | 
					        logbookRepo.saveLogbook(this, logbook, object: LogbookSavedListener{
 | 
				
			||||||
 | 
					            override fun onLogbookSaved() {
 | 
				
			||||||
 | 
					                Log.d(TAG, "Logbook saved")
 | 
				
			||||||
 | 
					                runOnUiThread({
 | 
				
			||||||
 | 
					                    progressIndicator.visibility = View.INVISIBLE
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            override fun onError(error: String) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "ERROR: Logbook was NOT saved!")
 | 
				
			||||||
 | 
					                runOnUiThread({
 | 
				
			||||||
 | 
					                    progressIndicator.visibility = View.INVISIBLE
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Toast.makeText(this, R.string.toast_event_added, Toast.LENGTH_SHORT).show()
 | 
					        Toast.makeText(this, R.string.toast_event_added, Toast.LENGTH_SHORT).show()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,42 +1,5 @@
 | 
				
			|||||||
package it.danieleverducci.lunatracker.entities
 | 
					package it.danieleverducci.lunatracker.entities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.Context
 | 
					 | 
				
			||||||
import android.util.Log
 | 
					 | 
				
			||||||
import org.json.JSONArray
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.io.FileInputStream
 | 
					 | 
				
			||||||
import java.io.FileNotFoundException
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Logbook {
 | 
					class Logbook {
 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        val TAG = "Logbook"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fun load(context: Context): Logbook {
 | 
					 | 
				
			||||||
            val logbook = Logbook()
 | 
					 | 
				
			||||||
            val file = File(context.getFilesDir(), "data.json")
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                val json = FileInputStream(file).bufferedReader().use { it.readText() }
 | 
					 | 
				
			||||||
                val ja = JSONArray(json)
 | 
					 | 
				
			||||||
                for (i in 0 until ja.length()) {
 | 
					 | 
				
			||||||
                    val jo = ja.getJSONObject(i)
 | 
					 | 
				
			||||||
                    val evt = LunaEvent.fromJson(jo)
 | 
					 | 
				
			||||||
                    logbook.logs.add(evt)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (e: FileNotFoundException) {
 | 
					 | 
				
			||||||
                Log.d(TAG, "No logbook file found")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return logbook
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val logs = ArrayList<LunaEvent>()
 | 
					    val logs = ArrayList<LunaEvent>()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun save(context: Context) {
 | 
					 | 
				
			||||||
        val file = File(context.getFilesDir(), "data.json")
 | 
					 | 
				
			||||||
        val ja = JSONArray()
 | 
					 | 
				
			||||||
        for (l in logs) {
 | 
					 | 
				
			||||||
            ja.put(l.toJson())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        file.writeText(ja.toString())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					package it.danieleverducci.lunatracker.repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.entities.Logbook
 | 
				
			||||||
 | 
					import android.util.Log
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.entities.LunaEvent
 | 
				
			||||||
 | 
					import org.json.JSONArray
 | 
				
			||||||
 | 
					import java.io.File
 | 
				
			||||||
 | 
					import java.io.FileInputStream
 | 
				
			||||||
 | 
					import java.io.FileNotFoundException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FileLogbookRepository: LogbookRepository {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        val TAG = "FileLogbookRepository"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun loadLogbook(context: Context, listener: LogbookLoadedListener) {
 | 
				
			||||||
 | 
					        val logbook = Logbook()
 | 
				
			||||||
 | 
					        val file = File(context.getFilesDir(), "data.json")
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            val json = FileInputStream(file).bufferedReader().use { it.readText() }
 | 
				
			||||||
 | 
					            val ja = JSONArray(json)
 | 
				
			||||||
 | 
					            for (i in 0 until ja.length()) {
 | 
				
			||||||
 | 
					                val jo = ja.getJSONObject(i)
 | 
				
			||||||
 | 
					                val evt = LunaEvent.fromJson(jo)
 | 
				
			||||||
 | 
					                logbook.logs.add(evt)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (e: FileNotFoundException) {
 | 
				
			||||||
 | 
					            Log.d(TAG, "No logbook file found")
 | 
				
			||||||
 | 
					            listener.onError(e.toString())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        listener.onLogbookLoaded(logbook)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun saveLogbook(
 | 
				
			||||||
 | 
					        context: Context,
 | 
				
			||||||
 | 
					        logbook: Logbook,
 | 
				
			||||||
 | 
					        listener: LogbookSavedListener
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        val file = File(context.getFilesDir(), "data.json")
 | 
				
			||||||
 | 
					        val ja = JSONArray()
 | 
				
			||||||
 | 
					        for (l in logbook.logs) {
 | 
				
			||||||
 | 
					            ja.put(l.toJson())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        file.writeText(ja.toString())
 | 
				
			||||||
 | 
					        listener.onLogbookSaved()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package it.danieleverducci.lunatracker.repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.entities.Logbook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface LogbookRepository {
 | 
				
			||||||
 | 
					    fun loadLogbook(context: Context, listener: LogbookLoadedListener)
 | 
				
			||||||
 | 
					    fun saveLogbook(context: Context, logbook: Logbook, listener: LogbookSavedListener)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface LogbookLoadedListener {
 | 
				
			||||||
 | 
					    fun onLogbookLoaded(logbook: Logbook)
 | 
				
			||||||
 | 
					    fun onError(error: String)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface LogbookSavedListener {
 | 
				
			||||||
 | 
					    fun onLogbookSaved()
 | 
				
			||||||
 | 
					    fun onError(error: String)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					package it.danieleverducci.lunatracker.repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context
 | 
				
			||||||
 | 
					import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine
 | 
				
			||||||
 | 
					import com.thegrizzlylabs.sardineandroid.impl.SardineException
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.TemporaryHardcodedCredentials
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.entities.Logbook
 | 
				
			||||||
 | 
					import it.danieleverducci.lunatracker.entities.LunaEvent
 | 
				
			||||||
 | 
					import kotlinx.coroutines.Runnable
 | 
				
			||||||
 | 
					import org.json.JSONArray
 | 
				
			||||||
 | 
					import java.io.BufferedReader
 | 
				
			||||||
 | 
					import kotlin.io.bufferedReader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        val TAG = "LogbookRepository"
 | 
				
			||||||
 | 
					        val FILE_NAME = "lunatracker_logbook.json"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    val sardine: OkHttpSardine = OkHttpSardine()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init {
 | 
				
			||||||
 | 
					        sardine.setCredentials(
 | 
				
			||||||
 | 
					            username,
 | 
				
			||||||
 | 
					            password
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun loadLogbook(context: Context, listener: LogbookLoadedListener) {
 | 
				
			||||||
 | 
					        Thread(Runnable {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                val inputStream = sardine.get("$webDavURL/$FILE_NAME")
 | 
				
			||||||
 | 
					                val json = inputStream.bufferedReader().use(BufferedReader::readText)
 | 
				
			||||||
 | 
					                val ja = JSONArray(json)
 | 
				
			||||||
 | 
					                val logbook = Logbook()
 | 
				
			||||||
 | 
					                for (i in 0 until ja.length()) {
 | 
				
			||||||
 | 
					                    val jo = ja.getJSONObject(i)
 | 
				
			||||||
 | 
					                    val evt = LunaEvent.fromJson(jo)
 | 
				
			||||||
 | 
					                    logbook.logs.add(evt)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                listener.onLogbookLoaded(logbook)
 | 
				
			||||||
 | 
					            } catch (e: SardineException) {
 | 
				
			||||||
 | 
					                listener.onError(e.toString())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }).start()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun saveLogbook(context: Context, logbook: Logbook, listener: LogbookSavedListener) {
 | 
				
			||||||
 | 
					        Thread(Runnable {
 | 
				
			||||||
 | 
					            // Lock logbook on WebDAV to avoid concurrent changes
 | 
				
			||||||
 | 
					            //sardine.lock(getUrl())
 | 
				
			||||||
 | 
					            // Reload logbook from WebDAV
 | 
				
			||||||
 | 
					            // Merge logbooks (based on time)
 | 
				
			||||||
 | 
					            // Write logbook
 | 
				
			||||||
 | 
					            // Unlock logbook on WebDAV
 | 
				
			||||||
 | 
					            //sardine.unlock(getUrl())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val ja = JSONArray()
 | 
				
			||||||
 | 
					            for (l in logbook.logs) {
 | 
				
			||||||
 | 
					                ja.put(l.toJson())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                sardine.put(getUrl(), ja.toString().toByteArray())
 | 
				
			||||||
 | 
					                listener.onLogbookSaved()
 | 
				
			||||||
 | 
					            } catch (e: SardineException) {
 | 
				
			||||||
 | 
					                listener.onError(e.toString())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }).start()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun getUrl(): String {
 | 
				
			||||||
 | 
					        return "${TemporaryHardcodedCredentials.URL}/$FILE_NAME"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<LinearLayout
 | 
					<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
    android:layout_height="match_parent"
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
    android:orientation="vertical"
 | 
					    android:orientation="vertical"
 | 
				
			||||||
    android:paddingTop="30dp"
 | 
					    android:paddingTop="30dp"
 | 
				
			||||||
    android:paddingLeft="15dp"
 | 
					    android:paddingLeft="15dp"
 | 
				
			||||||
@@ -116,11 +116,21 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <com.google.android.material.progressindicator.LinearProgressIndicator
 | 
				
			||||||
 | 
					        android:id="@+id/progress_indicator"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_margin="10dp"
 | 
				
			||||||
 | 
					        android:indeterminate="true"
 | 
				
			||||||
 | 
					        app:indicatorColor="@color/accent"
 | 
				
			||||||
 | 
					        android:visibility="invisible"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:text="Diario di bordo"
 | 
					        android:text="Diario di bordo"
 | 
				
			||||||
        android:layout_marginTop="30dp"/>
 | 
					        android:textColor="@color/accent"
 | 
				
			||||||
 | 
					        android:textStyle="bold"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <androidx.recyclerview.widget.RecyclerView
 | 
					    <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
        android:id="@+id/list_events"
 | 
					        android:id="@+id/list_events"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ activityCompose = "1.8.0"
 | 
				
			|||||||
composeBom = "2024.04.01"
 | 
					composeBom = "2024.04.01"
 | 
				
			||||||
appcompat = "1.7.0"
 | 
					appcompat = "1.7.0"
 | 
				
			||||||
recyclerview = "1.3.2"
 | 
					recyclerview = "1.3.2"
 | 
				
			||||||
 | 
					material = "1.12.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[libraries]
 | 
					[libraries]
 | 
				
			||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
 | 
					androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
 | 
				
			||||||
@@ -28,6 +29,7 @@ androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-man
 | 
				
			|||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
 | 
					androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
 | 
				
			||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
 | 
					androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
 | 
				
			||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
 | 
					androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
 | 
				
			||||||
 | 
					material = { group = "com.google.android.material", name = "material", version.ref = "material" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[plugins]
 | 
					[plugins]
 | 
				
			||||||
android-application = { id = "com.android.application", version.ref = "agp" }
 | 
					android-application = { id = "com.android.application", version.ref = "agp" }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ dependencyResolutionManagement {
 | 
				
			|||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        google()
 | 
					        google()
 | 
				
			||||||
        mavenCentral()
 | 
					        mavenCentral()
 | 
				
			||||||
 | 
					        maven(url = "https://jitpack.io")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user