Add sets exporting

This commit is contained in:
Brandon Presley 2022-07-06 00:06:16 +12:00
parent 76e6ffbc25
commit 3e09f38ef0
5 changed files with 163 additions and 19 deletions

View File

@ -6,7 +6,7 @@ import {
NavigationContainer,
} from '@react-navigation/native';
import React, {useEffect} from 'react';
import {NativeModules, StatusBar, useColorScheme} from 'react-native';
import {StatusBar, useColorScheme} from 'react-native';
import {setupSchema} from './db';
import Exercises from './Exercises';
import Home from './Home';
@ -28,7 +28,6 @@ const App = () => {
AsyncStorage.getItem('minutes').then(async minutes => {
if (!minutes) await AsyncStorage.setItem('minutes', '3');
});
console.log(NativeModules.ExportModule.sets());
}, []);
return (

View File

@ -1,7 +1,7 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import {NativeStackScreenProps} from '@react-navigation/native-stack';
import React, {useEffect, useState} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import {NativeModules, StyleSheet, Text, View} from 'react-native';
import {Button, Switch, TextInput} from 'react-native-paper';
import {RootStackParamList} from './App';
import {getDb} from './db';
@ -32,6 +32,10 @@ export default function Settings({
await db.executeSql(`DELETE FROM sets`);
};
const exportSets = () => {
NativeModules.ExportModule.sets();
};
return (
<View style={styles.container}>
<TextInput
@ -56,6 +60,12 @@ export default function Settings({
value={alarmEnabled}
onValueChange={setAlarmEnabled}
/>
<Button
style={{alignSelf: 'flex-start'}}
icon="arrow-down"
onPress={exportSets}>
Download
</Button>
<Button
style={{alignSelf: 'flex-start', marginTop: 'auto'}}
icon="trash"

View File

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".MainApplication"
@ -30,6 +31,7 @@
</intent-filter>
</activity>
<activity android:exported="true" android:process=":remote" android:name=".AlarmActivity" />
<activity android:exported="true" android:process=":remote" android:name=".ExportActivity" />
<service android:name=".StopTimer" android:exported="true" android:process=":remote" />
<service android:name=".AlarmService" android:exported="true" />
<service android:name=".TimerService" android:exported="true" />

View File

@ -0,0 +1,123 @@
package com.massive
import android.annotation.SuppressLint
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import java.io.*
class ExportActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("ExportActivity", "Started ExportActivity.")
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
type = "text/csv"
putExtra(Intent.EXTRA_TITLE, "sets.csv")
}
startActivityForResult(intent, CREATE_FILE, null)
}
@SuppressLint("Range")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Log.d("ExportActivity", "Got activity result: requestCode=$requestCode,resultCode=$resultCode")
data?.data?.also { uri ->
contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
FileWriter(fd.fileDescriptor).use { fw ->
Log.d("ExportActivity", "Got file writer: $fw")
fw.write("id,name,reps,weight,created,unit\n")
val db = MassiveHelper(applicationContext).readableDatabase
db.use {
with(it.query("sets", null, null, null, null, null, null)) {
while (moveToNext()) {
val id = getInt(getColumnIndex("id"))
val name = getString(getColumnIndex("name"))
val reps = getInt(getColumnIndex("reps"))
val weight = getInt(getColumnIndex("weight"))
val created = getString(getColumnIndex("created"))
val unit = getString(getColumnIndex("unit"))
fw.appendLine("$id,$name,$reps,$weight,$created,$unit\n")
}
}
}
fw.flush()
fw.close()
}
}
}
}
@Throws(IOException::class)
fun getFile(context: Context, uri: Uri): File? {
val destinationFilename =
File(context.filesDir.path + File.separatorChar + queryName(context, uri))
try {
context.contentResolver.openInputStream(uri).use { ins ->
if (ins != null) {
createFileFromStream(
ins,
destinationFilename
)
}
}
} catch (ex: Exception) {
Log.e("Save File", ex.message!!)
ex.printStackTrace()
}
return destinationFilename
}
private fun createFileFromStream(ins: InputStream, destination: File?) {
try {
FileOutputStream(destination).use { os ->
val buffer = ByteArray(4096)
var length: Int
while (ins.read(buffer).also { length = it } > 0) {
os.write(buffer, 0, length)
}
os.flush()
}
} catch (ex: Exception) {
Log.e("Save File", ex.message!!)
ex.printStackTrace()
}
}
private fun queryName(context: Context, uri: Uri): String {
val returnCursor: Cursor = context.contentResolver.query(uri, null, null, null, null)!!
val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
val name: String = returnCursor.getString(nameIndex)
returnCursor.close()
return name
}
companion object {
private const val CHANNEL_ID = "Exports"
private const val NOTIFICATION_ID = 2
// Request code for selecting a PDF document.
const val PICK_PDF_FILE = 2
// Request code for creating a PDF document.
const val CREATE_FILE = 1
}
}

View File

@ -1,10 +1,12 @@
package com.massive
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
@ -15,8 +17,8 @@ import androidx.core.app.NotificationManagerCompat
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import okhttp3.internal.notify
import java.io.File
import java.io.FileWriter
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@ -35,14 +37,13 @@ class ExportModule internal constructor(context: ReactApplicationContext?) :
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val formatted = current.format(formatter)
val sets = File(dir, "sets$formatted.csv")
sets.createNewFile()
sets.setWritable(true)
sets.setReadable(true)
sets.writeText("id,name,reps,weight,created,unit\n")
val file = File(dir, "sets-$formatted.csv")
file.createNewFile()
val writer = FileWriter(file)
writer.write("id,name,reps,weight,created,unit\n")
val db = MassiveHelper(reactApplicationContext).readableDatabase
db.use {
with (it.query("sets", null, null, null, null, null, null)) {
with(it.query("sets", null, null, null, null, null, null)) {
while (moveToNext()) {
val id = getInt(getColumnIndex("id"))
val name = getString(getColumnIndex("name"))
@ -50,33 +51,42 @@ class ExportModule internal constructor(context: ReactApplicationContext?) :
val weight = getInt(getColumnIndex("weight"))
val created = getString(getColumnIndex("created"))
val unit = getString(getColumnIndex("unit"))
sets.appendText("$id,$name,$reps,$weight,$created,$unit\n")
writer.appendLine("$id,$name,$reps,$weight,$created,$unit\n")
}
}
}
writer.flush()
writer.close()
sendNotification()
return file.path
}
@RequiresApi(Build.VERSION_CODES.M)
private fun sendNotification() {
val notificationManager = NotificationManagerCompat.from(reactApplicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_LOW
val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID, importance)
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_ID, importance)
channel.description = "Alarms for rest timings."
notificationManager.createNotificationChannel(channel)
}
val contentIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/csv"
}
val contentIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
val pendingContent =
PendingIntent.getActivity(reactApplicationContext, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
PendingIntent.getActivity(
reactApplicationContext,
0,
contentIntent,
PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(reactApplicationContext, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_arrow_downward_24)
.setContentTitle("Downloaded sets")
.setContentIntent(pendingContent)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
return sets.absolutePath
}
companion object {