Skip to content

Commit c853e93

Browse files
authored
Merge pull request #38 from standardsats/backups-fix
Backups fix 4.4.1
2 parents e97e3cc + 131a8f7 commit c853e93

5 files changed

Lines changed: 121 additions & 45 deletions

File tree

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ android {
1717
targetSdkVersion 33
1818
minSdkVersion 21
1919

20-
versionName '4.4.0'
21-
versionCode 103
20+
versionName '4.4.1'
21+
versionCode 104
2222
}
2323

2424
dexOptions {

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package="com.btcontract.walletfiat">
66

77
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
8+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
89
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
910
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
1011
<uses-permission android:name="android.permission.INTERNET"/>

app/src/main/java/com/btcontract/walletfiat/WalletApp.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import android.widget.{EditText, Toast}
1414
import androidx.appcompat.app.AppCompatDelegate
1515
import androidx.multidex.MultiDex
1616
import BaseActivity.StringOps
17-
import com.btcontract.walletfiat.utils.{AwaitService, DelayedNotification, LocalBackup}
17+
import com.btcontract.walletfiat.utils.{AwaitService, LocalBackup}
1818
import com.btcontract.walletfiat.sqlite.DBInterfaceSQLiteAndroidMisc
1919
import com.btcontract.walletfiat.utils.DelayedNotification
2020
import com.btcontract.walletfiat.R.string._
@@ -120,7 +120,7 @@ object WalletApp {
120120
try LNParams.fiatRates.becomeShutDown catch none
121121
try LNParams.feeRates.becomeShutDown catch none
122122
try LNParams.cm.becomeShutDown catch none
123-
// Make non-alive and non-operational
123+
// Make non-alive and non-operational`
124124
LNParams.secret = null
125125
txDataBag = null
126126
}

app/src/main/java/com/btcontract/walletfiat/utils/LocalBackup.scala

Lines changed: 115 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@ import android.os.{Build, Environment}
1414
import android.provider.{DocumentsContract, MediaStore}
1515
import androidx.documentfile.provider.DocumentFile
1616
import com.btcontract.walletfiat.WalletApp
17-
import scodec.bits.{BitVector, ByteVector}
17+
import com.btcontract.walletfiat.WalletApp.customBackupLocation
18+
import scodec.bits.ByteVector
1819
import immortan.crypto.Tools
1920
import immortan.wire.ExtCodecs
2021
import scodec.Attempt.{Failure, Successful}
2122

2223
import scala.util.Try
2324
import java.io.{BufferedInputStream, File, FileInputStream}
24-
import scala.collection.immutable.HashMap
25-
import scala.collection.JavaConverters._
26-
2725
object LocalBackup { me =>
2826
final val BACKUP_NAME = "encrypted.channels"
2927
final val GRAPH_NAME = "graph.snapshot"
@@ -42,60 +40,137 @@ object LocalBackup { me =>
4240
}
4341

4442
final val LOCAL_BACKUP_REQUEST_NUMBER = 105
45-
def askPermission(activity: AppCompatActivity): Unit = ActivityCompat.requestPermissions(activity, Array(android.Manifest.permission.WRITE_EXTERNAL_STORAGE), LOCAL_BACKUP_REQUEST_NUMBER)
46-
def isAllowed(context: Context): Boolean = ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
43+
def askPermission(activity: AppCompatActivity): Unit = ActivityCompat.requestPermissions(activity, Array(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE), LOCAL_BACKUP_REQUEST_NUMBER)
44+
def isAllowed(context: Context): Boolean = {
45+
(ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
46+
ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) || customBackupLocation.nonEmpty
47+
}
48+
4749
// Note that the function returns directory in the internal storage, then we copy backups to external dir
4850
def downloadsDir(context: Context): File = context.getExternalFilesDir(DIRECTORY_DOWNLOADS)
4951

52+
// This is directory in external storage
5053
private val DOWNLOAD_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
54+
private def downloadDirFileUri(context: Context, fileName: String): Uri = {
55+
val file = new File(DOWNLOAD_DIR.getAbsolutePath + "/" + fileName)
56+
FileProvider.getUriForFile(context, s"${context.getPackageName}", file)
57+
}
5158

52-
// val finalUri : Uri? = copyFileToDownloads(context, downloadedFile)
59+
// A way to get direct access to file inside download dir
60+
def findFileDirectlyInDownloads(context: Context, fileName: String): Option[Uri] = {
61+
val downloadDir = new File(DOWNLOAD_DIR.getAbsolutePath)
62+
val files = downloadDir.listFiles()
63+
val authority = s"${context.getPackageName}"
64+
println(files.mkString("\n"))
65+
files.find(_.getName == fileName).map(FileProvider.getUriForFile(context, authority, _))
66+
}
5367

54-
def copyFileToDirectory(context: Context, directory: Option[Uri], downloadedFile: File): Uri = {
55-
val resolver = context.getContentResolver
56-
val downloadedUri: Uri = directory match {
68+
// Helper to print exception to logs if any
69+
def printExceptions[T](body: => T): T = {
70+
val result = Try {body}
71+
result match {
72+
case util.Failure(exception) => {
73+
println(exception.getMessage)
74+
exception.printStackTrace()
75+
}
76+
case _ => ()
77+
}
78+
result.get
79+
}
80+
81+
// Depending on the Android version choose the best place to locate backup in the external storage.
82+
// Takes into account:
83+
// * directory - is custom directory selected by user to store backup at
84+
// * version - older versions will use direct File API, newer will prefer SAF if possible.
85+
// * existing of old backup - older one will be overwritten
86+
def selectDestinationUri(context: Context, resolver: ContentResolver, directory: Option[Uri], downloadedFile: File): Uri = {
87+
val fileName = downloadedFile.getName
88+
directory match {
5789
case None => {
5890
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
59-
60-
val contentValues = new ContentValues()
61-
contentValues.put(MediaStore.MediaColumns.IS_PENDING, true)
62-
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, downloadedFile.getName)
63-
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, resolver.getType(android.net.Uri.fromFile(downloadedFile)))
64-
contentValues.put(MediaStore.MediaColumns.SIZE, String.valueOf(downloadedFile.length()))
65-
66-
resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
91+
val existedFile = findFileDirectlyInDownloads(context, fileName)
92+
existedFile match {
93+
case Some(uri) => {
94+
println("LocalBackup: found the backup file at " + uri.toString)
95+
val contentValues = new ContentValues()
96+
contentValues.put(MediaStore.MediaColumns.IS_PENDING, true)
97+
contentValues.put(MediaStore.MediaColumns.SIZE, String.valueOf(downloadedFile.length()))
98+
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, resolver.getType(android.net.Uri.fromFile(downloadedFile)))
99+
resolver.update(uri, contentValues, null, null)
100+
uri
101+
}
102+
case _ => {
103+
println("LocalBackup: no backup file at downloads")
104+
downloadDirFileUri(context, fileName)
105+
}
106+
}
67107
} else {
68-
val authority = s"${context.getPackageName}.provider"
69-
val destinyFile = new File(directory.map(uri => new File(uri.getPath)).getOrElse(DOWNLOAD_DIR), downloadedFile.getName)
108+
val authority = s"${context.getPackageName}"
109+
val destinyFile = new File(directory.map(uri => new File(uri.getPath)).getOrElse(DOWNLOAD_DIR), fileName)
70110
FileProvider.getUriForFile(context, authority, destinyFile)
71111
}
72112
}
73113
case Some(uri) => {
74-
val triedUri = Try {
75-
println("Got custom directory " + uri)
76-
val dirFile: DocumentFile = DocumentFile.fromTreeUri(context, uri)
77-
println("Can create files in directory: " ++ dirFile.canWrite.toString)
78-
dirFile.listFiles.find(_.getName.equals(downloadedFile.getName)) match {
79-
case Some(existingFile) =>
80-
println("We found file to rewrite: " ++ existingFile.getUri.toString)
81-
existingFile.getUri
82-
83-
case None =>
84-
println("Creating new file")
85-
DocumentsContract.createDocument(resolver, dirFile.getUri, "application/valet", downloadedFile.getName)
86-
}
114+
println("LocalBackup: Got custom directory " + uri)
115+
val dirFile: DocumentFile = DocumentFile.fromTreeUri(context, uri)
116+
println("LocalBackup: Can create files in directory: " ++ dirFile.canWrite.toString)
117+
dirFile.listFiles.find(_.getName.equals(fileName)) match {
118+
case Some(existingFile) =>
119+
println("LocalBackup: We found file to rewrite: " ++ existingFile.getUri.toString)
120+
existingFile.getUri
121+
122+
case None =>
123+
println("LocalBackup: Creating new file")
124+
DocumentsContract.createDocument(resolver, dirFile.getUri, "application/valet", fileName)
87125
}
88-
triedUri match {
89-
case util.Failure(exception) => {
90-
println(exception.getMessage)
91-
exception.printStackTrace()
126+
}
127+
}
128+
}
129+
130+
def copyFileToDirectory(context: Context, directory: Option[Uri], downloadedFile: File): Uri = printExceptions {
131+
val resolver = context.getContentResolver
132+
val fileName = downloadedFile.getName
133+
val downloadedUri: Uri = directory match {
134+
case None => {
135+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
136+
val existedFile = findFileDirectlyInDownloads(context, fileName)
137+
existedFile match {
138+
case Some(uri) => {
139+
println("LocalBackup: found the backup file at " + uri.toString)
140+
val contentValues = new ContentValues()
141+
contentValues.put(MediaStore.MediaColumns.IS_PENDING, true)
142+
contentValues.put(MediaStore.MediaColumns.SIZE, String.valueOf(downloadedFile.length()))
143+
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, resolver.getType(android.net.Uri.fromFile(downloadedFile)))
144+
resolver.update(uri, contentValues, null, null)
145+
uri
146+
}
147+
case _ => {
148+
println("LocalBackup: no backup file at downloads")
149+
downloadDirFileUri(context, fileName)
150+
}
92151
}
93-
case _ => ()
152+
} else {
153+
val authority = s"${context.getPackageName}"
154+
val destinyFile = new File(directory.map(uri => new File(uri.getPath)).getOrElse(DOWNLOAD_DIR), fileName)
155+
FileProvider.getUriForFile(context, authority, destinyFile)
156+
}
157+
}
158+
case Some(uri) => {
159+
println("LocalBackup: Got custom directory " + uri)
160+
val dirFile: DocumentFile = DocumentFile.fromTreeUri(context, uri)
161+
println("LocalBackup: Can create files in directory: " ++ dirFile.canWrite.toString)
162+
dirFile.listFiles.find(_.getName.equals(fileName)) match {
163+
case Some(existingFile) =>
164+
println("LocalBackup: We found file to rewrite: " ++ existingFile.getUri.toString)
165+
existingFile.getUri
166+
167+
case None =>
168+
println("LocalBackup: Creating new file")
169+
DocumentsContract.createDocument(resolver, dirFile.getUri, "application/valet", fileName)
94170
}
95-
triedUri.get
96171
}
97172
}
98-
println("Will write backup to: " ++ downloadedUri.toString)
173+
println("LocalBackup: Will write backup to: " ++ downloadedUri.toString)
99174

100175
val outputStream = resolver.openOutputStream(downloadedUri, "wt")
101176
val brr = Array.ofDim[Byte](1024)

sign-apk.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -xe
44
echo $PATH
55
zipalign=~/Android/Sdk/build-tools/30.0.3/zipalign
6-
VERSION=4.3.0
6+
VERSION=4.4.1
77
rm app/build/outputs/apk/release/Valet-$VERSION-aligned.apk || true
88
$zipalign -v 4 app/build/outputs/apk/release/Valet-$VERSION.apk app/build/outputs/apk/release/Valet-$VERSION-aligned.apk
99
apksigner sign --ks release.keystore --ks-key-alias release --v1-signing-enabled true --v2-signing-enabled true app/build/outputs/apk/release/Valet-$VERSION-aligned.apk

0 commit comments

Comments
 (0)