Add sets exporting
This commit is contained in:
parent
76e6ffbc25
commit
3e09f38ef0
3
App.tsx
3
App.tsx
|
@ -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 (
|
||||
|
|
12
Settings.tsx
12
Settings.tsx
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
|
123
android/app/src/main/java/com/massive/ExportActivity.kt
Normal file
123
android/app/src/main/java/com/massive/ExportActivity.kt
Normal 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
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user