From 76e5aeacfd07b2108703b214b28c7cf8d595ea5c Mon Sep 17 00:00:00 2001 From: Brandon Presley Date: Mon, 27 Mar 2023 14:29:12 +1300 Subject: [PATCH] Choose directory when backing up automatically - 1.135 Related to #146. --- SettingsPage.tsx | 9 ++- android/app/build.gradle | 4 +- .../src/main/java/com/massive/BackupModule.kt | 61 +++++-------------- jestSetup.ts | 1 + package.json | 3 +- yarn.lock | 27 ++++++++ 6 files changed, 55 insertions(+), 50 deletions(-) diff --git a/SettingsPage.tsx b/SettingsPage.tsx index a495ab6..b9a3c23 100644 --- a/SettingsPage.tsx +++ b/SettingsPage.tsx @@ -6,6 +6,7 @@ import {NativeModules, ScrollView, View} from 'react-native' import DocumentPicker from 'react-native-document-picker' import {Dirs, FileSystem} from 'react-native-file-access' import {Button, Subheading} from 'react-native-paper' +import {PERMISSIONS, request} from 'react-native-permissions' import ConfirmDialog from './ConfirmDialog' import {ITEM_PADDING, MARGIN} from './constants' import {AppDataSource} from './data-source' @@ -134,8 +135,14 @@ export default function SettingsPage() { return case 'backup': if (value) { + const granted = await request( + PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE, + ) + if (!granted) return + const result = await DocumentPicker.pickDirectory() + console.log(result.uri) toast('Backup database daily.') - NativeModules.BackupModule.start() + NativeModules.BackupModule.start(result.uri) } else { toast('Stopped backing up daily') NativeModules.BackupModule.stop() diff --git a/android/app/build.gradle b/android/app/build.gradle index 48c31e9..664852d 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 36160 - versionName "1.134" + versionCode 36161 + versionName "1.135" 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 index 01828ef..458d187 100644 --- a/android/app/src/main/java/com/massive/BackupModule.kt +++ b/android/app/src/main/java/com/massive/BackupModule.kt @@ -1,75 +1,44 @@ package com.massive -import android.Manifest 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.content.pm.PackageManager +import android.content.* +import android.net.Uri import android.os.Build -import android.os.Environment -import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.core.app.ActivityCompat +import androidx.documentfile.provider.DocumentFile 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.io.IOException +import java.io.* import java.util.* - class BackupModule constructor(context: ReactApplicationContext?) : ReactContextBaseJavaModule(context) { val context: ReactApplicationContext = reactApplicationContext + private var targetDir: String? = null private val copyReceiver = object : BroadcastReceiver() { @RequiresApi(Build.VERSION_CODES.O) override fun onReceive(context: Context?, intent: Intent?) { + val treeUri: Uri = Uri.parse(targetDir) + val documentFile = context?.let { DocumentFile.fromTreeUri(it, treeUri) } + val file = documentFile?.createFile("application/octet-stream", "massive.db") + val output = context?.contentResolver?.openOutputStream(file!!.uri) val sourceFile = File(context?.getDatabasePath("massive.db")!!.path) - val targetDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - val targetFile = File(targetDir, "massive.db") - - try { - val input = FileInputStream(sourceFile) - val output = FileOutputStream(targetFile) + val input = FileInputStream(sourceFile) + if (output != null) { input.copyTo(output) - input.close() - output.close() - } catch (e: IOException) { - Toast.makeText( - reactApplicationContext, - "Access to massive.db is denied. Try deleting it first.", - Toast.LENGTH_LONG - ).show() } + output?.flush() + output?.close() } } @RequiresApi(Build.VERSION_CODES.M) @ReactMethod - fun start() { - val permission: Int = - ActivityCompat.checkSelfPermission( - reactApplicationContext, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - if (permission != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions( - currentActivity!!, - arrayOf( - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ), - 1 - ) - } - + fun start(baseUri: String) { + targetDir = baseUri val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(COPY_BROADCAST) val pendingIntent = diff --git a/jestSetup.ts b/jestSetup.ts index 79027f0..fe3c97b 100644 --- a/jestSetup.ts +++ b/jestSetup.ts @@ -11,6 +11,7 @@ NativeModules.SettingsModule = NativeModules.SettingsModule || { jest.mock('react-native-file-access', () => jest.fn()) jest.mock('react-native-share', () => jest.fn()) +jest.mock('react-native-permissions', () => jest.fn()) jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper') jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter') diff --git a/package.json b/package.json index 5a29e0b..560f453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "massive", - "version": "1.134", + "version": "1.135", "private": true, "license": "GPL-3.0-only", "scripts": { @@ -39,6 +39,7 @@ "react-native-linear-gradient": "^2.6.2", "react-native-pager-view": "^6.0.1", "react-native-paper": "^4.12.5", + "react-native-permissions": "^3.8.0", "react-native-reanimated": "^2.12.0", "react-native-safe-area-context": "^4.4.1", "react-native-screens": "^3.18.2", diff --git a/yarn.lock b/yarn.lock index b2065b5..4984f9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7313,6 +7313,7 @@ __metadata: react-native-linear-gradient: ^2.6.2 react-native-pager-view: ^6.0.1 react-native-paper: ^4.12.5 + react-native-permissions: ^3.8.0 react-native-reanimated: ^2.12.0 react-native-safe-area-context: ^4.4.1 react-native-screens: ^3.18.2 @@ -8592,6 +8593,15 @@ __metadata: languageName: node linkType: hard +"pkg-dir@npm:^5.0.0": + version: 5.0.0 + resolution: "pkg-dir@npm:5.0.0" + dependencies: + find-up: ^5.0.0 + checksum: b167bb8dac7bbf22b1d5e30ec223e6b064b84b63010c9d49384619a36734caf95ed23ad23d4f9bd975e8e8082b60a83395f43a89bb192df53a7c25a38ecb57d9 + languageName: node + linkType: hard + "posix-character-classes@npm:^0.1.0": version: 0.1.1 resolution: "posix-character-classes@npm:0.1.1" @@ -8898,6 +8908,23 @@ __metadata: languageName: node linkType: hard +"react-native-permissions@npm:^3.8.0": + version: 3.8.0 + resolution: "react-native-permissions@npm:3.8.0" + dependencies: + picocolors: ^1.0.0 + pkg-dir: ^5.0.0 + peerDependencies: + react: ">=16.13.1" + react-native: ">=0.63.3" + react-native-windows: ">=0.62.0" + peerDependenciesMeta: + react-native-windows: + optional: true + checksum: acbd8605adabfc48e19fbc468a3a143fb85bd34d3ed0fe6a4b52f16304591b8a4b3c561a507c11643fcc8ce1b4dc09e31f149387d1ea461098a2e40c43989f7c + languageName: node + linkType: hard + "react-native-reanimated@npm:^2.12.0": version: 2.12.0 resolution: "react-native-reanimated@npm:2.12.0"