diff --git a/SettingsPage.tsx b/SettingsPage.tsx index 8f70006..b9152ed 100644 --- a/SettingsPage.tsx +++ b/SettingsPage.tsx @@ -84,6 +84,7 @@ export default function SettingsPage() { {name: 'Show unit', value: settings.showUnit, key: 'showUnit'}, {name: 'Show steps', value: settings.steps, key: 'steps'}, {name: 'Show date', value: settings.showDate, key: 'showDate'}, + {name: 'Automatic backup', value: settings.backup, key: 'backup'}, ], [settings], ) @@ -131,6 +132,15 @@ export default function SettingsPage() { if (value) toast('Disable sound on rest timer alarms.') else toast('Enabled sound for rest timer alarms.') return + case 'backup': + if (value) { + toast('Backup database daily.') + NativeModules.BackupModule.start() + } else { + toast('Stopped backing up daily') + NativeModules.BackupModule.stop() + } + return } }, [ignoring, setValue, update], @@ -207,7 +217,7 @@ export default function SettingsPage() { key: 'date', }, ] - }, [settings.date, darkColor, formatOptions, theme, lightColor]) + }, [settings, darkColor, formatOptions, theme, lightColor]) const renderSelect = useCallback( (item: Input) => ( diff --git a/android/app/build.gradle b/android/app/build.gradle index 72dab7d..5cd6a98 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,8 +41,8 @@ android { missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 36155 - versionName "1.129" + versionCode 36156 + versionName "1.130" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/android/app/src/main/java/com/massive/BackupModule.kt b/android/app/src/main/java/com/massive/BackupModule.kt new file mode 100644 index 0000000..e4549d8 --- /dev/null +++ b/android/app/src/main/java/com/massive/BackupModule.kt @@ -0,0 +1,87 @@ +package com.massive + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build +import android.os.Environment +import androidx.annotation.RequiresApi +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.* + +class BackupModule constructor(context: ReactApplicationContext?) : + ReactContextBaseJavaModule(context) { + val context: ReactApplicationContext = reactApplicationContext + + private val copyReceiver = object : BroadcastReceiver() { + @RequiresApi(Build.VERSION_CODES.O) + override fun onReceive(context: Context?, intent: Intent?) { + val sourceFile = File(context?.getDatabasePath("massive.db")!!.path) + + val targetDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val targetFile = File(targetDir, "massive.db") + + val input = FileInputStream(sourceFile) + val output = FileOutputStream(targetFile) + + input.copyTo(output) + input.close() + output.close() + } + } + + @RequiresApi(Build.VERSION_CODES.M) + @ReactMethod + fun start() { + val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val intent = Intent(COPY_BROADCAST) + val pendingIntent = + PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + pendingIntent.send() + + val calendar = Calendar.getInstance().apply { + timeInMillis = System.currentTimeMillis() + set(Calendar.HOUR_OF_DAY, 6) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + } + + alarmMgr.setRepeating( + AlarmManager.RTC_WAKEUP, + calendar.timeInMillis, + AlarmManager.INTERVAL_DAY, + pendingIntent + ) + } + + @RequiresApi(Build.VERSION_CODES.M) + @ReactMethod + fun stop() { + val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val intent = Intent(COPY_BROADCAST) + val pendingIntent = + PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + alarmMgr.cancel(pendingIntent) + } + + init { + reactApplicationContext.registerReceiver(copyReceiver, IntentFilter(COPY_BROADCAST)) + } + + companion object { + const val COPY_BROADCAST = "copy-event" + } + + override fun getName(): String { + return "BackupModule" + } +} diff --git a/android/app/src/main/java/com/massive/MassivePackage.kt b/android/app/src/main/java/com/massive/MassivePackage.kt index 36c7d0b..ffbee06 100644 --- a/android/app/src/main/java/com/massive/MassivePackage.kt +++ b/android/app/src/main/java/com/massive/MassivePackage.kt @@ -16,6 +16,7 @@ class MassivePackage : ReactPackage { val modules: MutableList = ArrayList() modules.add(AlarmModule(reactContext)) modules.add(SettingsModule(reactContext)) + modules.add(BackupModule(reactContext)) return modules } } diff --git a/data-source.ts b/data-source.ts index 31a35c2..10a08ae 100644 --- a/data-source.ts +++ b/data-source.ts @@ -24,6 +24,7 @@ import {addSetsCreated1667186451005} from './migrations/1667186451005-add-sets-c import {addNoSound1667186456118} from './migrations/1667186456118-add-no-sound' import {dropMigrations1667190214743} from './migrations/1667190214743-drop-migrations' import {splitColor1669420187764} from './migrations/1669420187764-split-color' +import {addBackup1678334268359} from './migrations/1678334268359-add-backup' import {Plan} from './plan' import Settings from './settings' @@ -59,5 +60,6 @@ export const AppDataSource = new DataSource({ addNoSound1667186456118, dropMigrations1667190214743, splitColor1669420187764, + addBackup1678334268359, ], }) diff --git a/migrations/1678334268359-add-backup.ts b/migrations/1678334268359-add-backup.ts new file mode 100644 index 0000000..d1044b3 --- /dev/null +++ b/migrations/1678334268359-add-backup.ts @@ -0,0 +1,13 @@ +import {MigrationInterface, QueryRunner} from 'typeorm' + +export class addBackup1678334268359 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner + .query('ALTER TABLE settings ADD COLUMN backup BOOLEAN DEFAULT false') + .catch(() => null) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn('settings', 'backup') + } +} diff --git a/package.json b/package.json index f499fc3..3d3d0cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "massive", - "version": "1.129", + "version": "1.130", "private": true, "license": "GPL-3.0-only", "scripts": { diff --git a/settings.ts b/settings.ts index b3fb576..5367cd3 100644 --- a/settings.ts +++ b/settings.ts @@ -43,4 +43,7 @@ export default class Settings { @Column('boolean') noSound: boolean + + @Column('boolean') + backup: boolean }