Skip to content

Commit 6ed0080

Browse files
Update to v3.3.0
2 parents 963f1d6 + ed0f031 commit 6ed0080

39 files changed

Lines changed: 1650 additions & 136 deletions

apk/GPSLogger-3.3.0.apk

5.2 MB
Binary file not shown.

apk/GPSLogger-latest.apk

23.7 KB
Binary file not shown.

app/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ apply plugin: 'com.android.application'
2323

2424
android {
2525
namespace 'eu.basicairdata.graziano.gpslogger'
26-
compileSdk 34
26+
compileSdk 35
2727

2828
defaultConfig {
2929
applicationId "eu.basicairdata.graziano.gpslogger"
3030
minSdkVersion 14
31-
targetSdkVersion 34
31+
targetSdkVersion 35
3232

3333
// -----------------------------------------------------------------------------------------
3434
// We use the Semantic Versioning (https://semver.org/):
35-
versionName '3.2.3'
36-
versionCode 52
35+
versionName '3.3.0'
36+
versionCode 53
3737
// -----------------------------------------------------------------------------------------
3838

3939
vectorDrawables.useSupportLibrary = true
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/*
2+
* AppDataManager - Java Class for Android
3+
* Created by G.Capelli on 14/3/2026
4+
* This file is part of BasicAirData GPS Logger
5+
*
6+
* Copyright (C) 2011 BasicAirData
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
package eu.basicairdata.graziano.gpslogger;
23+
24+
import android.net.Uri;
25+
import android.util.Log;
26+
27+
import androidx.documentfile.provider.DocumentFile;
28+
29+
import org.jetbrains.annotations.Nullable;
30+
31+
import java.io.BufferedInputStream;
32+
import java.io.File;
33+
import java.io.FileInputStream;
34+
import java.io.FileNotFoundException;
35+
import java.io.FileOutputStream;
36+
import java.io.IOException;
37+
import java.io.InputStream;
38+
import java.io.OutputStream;
39+
import java.text.SimpleDateFormat;
40+
import java.util.Date;
41+
import java.util.Locale;
42+
import java.util.zip.ZipEntry;
43+
import java.util.zip.ZipInputStream;
44+
import java.util.zip.ZipOutputStream;
45+
46+
/**
47+
* A class to manage some operations with the app data.
48+
* We use it for example to export and import the database of the tracks.
49+
*/
50+
public class AppDataManager {
51+
52+
public boolean isLastOperationSuccessful = false; // The outcome of the last operation carried out
53+
54+
private String appDataRootFolder = "/data/data/eu.basicairdata.graziano.gpslogger";
55+
private String zipFileFolder = GPSApplication.getInstance().getPrefExportFolder();
56+
57+
58+
59+
/**
60+
* It adds a specific folder recursively to a zip file.
61+
*
62+
* @param srcFolder The folder to add
63+
* @param destZipFile The zip file
64+
*/
65+
private void zipFolder(String srcFolder, OutputStream destZipFile)
66+
throws Exception {
67+
ZipOutputStream zip = null;
68+
OutputStream fileWriter = null;
69+
fileWriter = destZipFile;
70+
zip = new ZipOutputStream(fileWriter);
71+
addFolderToZip("", srcFolder, zip);
72+
zip.flush();
73+
zip.close();
74+
}
75+
76+
/**
77+
* It adds a specific file to a zip file.
78+
*
79+
* @param path The path of the file to add
80+
* @param srcFile The name of the file
81+
* @param zip The zip file
82+
*/
83+
private void addFileToZip(String path, String srcFile,
84+
ZipOutputStream zip) throws Exception {
85+
File folder = new File(srcFile);
86+
if (folder.isDirectory()) {
87+
addFolderToZip(path, srcFile, zip);
88+
} else {
89+
byte[] buf = new byte[1024];
90+
int len;
91+
try {
92+
if ((srcFile.contains("/databases/") || srcFile.contains("/Thumbnails/"))
93+
&& !srcFile.endsWith("-journal") && !srcFile.endsWith("-shm") && !srcFile.endsWith("-wal")) {
94+
FileInputStream in = new FileInputStream(srcFile);
95+
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
96+
Log.w("myApp", "[#] AppDataManager.java - Adding file " + path + "/" + folder.getName());
97+
while ((len = in.read(buf)) > 0) {
98+
zip.write(buf, 0, len);
99+
}
100+
if (srcFile.endsWith("GPSLogger")) {
101+
isLastOperationSuccessful = true;
102+
Log.w("myApp", "[#] AppDataManager.java - The database \"GPSLogger\" has been successfully backupped");
103+
}
104+
}
105+
} catch (FileNotFoundException e) {
106+
107+
}
108+
}
109+
}
110+
111+
/**
112+
* It adds a specific folder to a zip file.
113+
*
114+
* @param path The path of the file to add
115+
* @param srcFolder The name of the file
116+
* @param zip The zip file
117+
*/
118+
private void addFolderToZip(String path, String srcFolder,
119+
ZipOutputStream zip) throws Exception {
120+
// java.lang.RuntimeException: java.io.FileNotFoundException:
121+
// /data/data/eu.basicairdata.graziano.gpslogger/code_cache/.studio/.canary: open failed: EACCES (Permission denied)
122+
if (!srcFolder.endsWith("/code_cache") && !srcFolder.endsWith("/cache")) {
123+
Log.w("myApp", "[#] AppDataManager.java - Adding folder " + srcFolder);
124+
File folder = new File(srcFolder);
125+
for (String fileName : folder.list()) {
126+
if (path.equals("")) {
127+
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip);
128+
} else {
129+
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip);
130+
}
131+
}
132+
}
133+
}
134+
135+
/**
136+
* It exports the app data folder to a zip file into the exporting folder.
137+
* It creates a single zip file of the whole /data/data/eu.basicairdata.graziano.gpslogger folder.
138+
* This is the method used for Android API 14 to 18. API 19+ uses the other method:
139+
* exportAppDataToZipFile(Uri zipDocumentUri)
140+
*/
141+
public void exportAppDataToZipFile_API14() {
142+
isLastOperationSuccessful = false;
143+
String backupFileName = "BACKUP GPSLogger Tracklist.zip";
144+
DocumentFile pickedDir;
145+
pickedDir = DocumentFile.fromFile(new File(zipFileFolder));
146+
DocumentFile zipDocumentFile = pickedDir.findFile(backupFileName);
147+
if ((zipDocumentFile != null) && (zipDocumentFile.exists())) zipDocumentFile.delete();
148+
zipDocumentFile = pickedDir.createFile("", backupFileName);
149+
150+
try {
151+
OutputStream outputStream = GPSApplication.getInstance().getContentResolver().openOutputStream(zipDocumentFile.getUri(), "w");
152+
zipFolder(appDataRootFolder, outputStream);
153+
} catch (FileNotFoundException e) {
154+
throw new RuntimeException(e);
155+
} catch (NullPointerException e) {
156+
Log.w("myApp", "[#] AppDataManager.java - UNABLE TO CREATE THE ZIP FILE into " + zipFileFolder);
157+
throw new RuntimeException(e);
158+
} catch (IOException e) {
159+
throw new RuntimeException(e);
160+
} catch (Exception e) {
161+
Log.w("myApp", "[#] AppDataManager.java - UNABLE TO CREATE THE ZIP FILE into" + zipFileFolder);
162+
throw new RuntimeException(e);
163+
}
164+
}
165+
166+
/**
167+
* It exports the app data folder to the zip file specified as parameter.
168+
* It creates a single zip file of the whole /data/data/eu.basicairdata.graziano.gpslogger folder.
169+
* * @param zipDocumentUri The Uri of the new ZIP file
170+
*/
171+
public void exportAppDataToZipFile(Uri zipDocumentUri) {
172+
isLastOperationSuccessful = false;
173+
174+
try {
175+
DocumentFile zipDocumentFile;
176+
zipDocumentFile = DocumentFile.fromSingleUri(GPSApplication.getInstance(), zipDocumentUri);
177+
OutputStream outputStream = GPSApplication.getInstance().getContentResolver().openOutputStream(zipDocumentFile.getUri(), "w");
178+
zipFolder(appDataRootFolder, outputStream);
179+
} catch (FileNotFoundException e) {
180+
throw new RuntimeException(e);
181+
} catch (NullPointerException e) {
182+
Log.w("myApp", "[#] AppDataManager.java - UNABLE TO CREATE THE ZIP FILE " + zipDocumentUri.toString());
183+
throw new RuntimeException(e);
184+
} catch (IOException e) {
185+
throw new RuntimeException(e);
186+
} catch (Exception e) {
187+
Log.w("myApp", "[#] AppDataManager.java - UNABLE TO CREATE THE ZIP FILE " + zipDocumentUri.toString());
188+
throw new RuntimeException(e);
189+
}
190+
}
191+
192+
/**
193+
* It imports the Database and the Thumbnails from the zip file specified as parameter into the private data folder of the app.
194+
* * @param zipDocumentUri The Uri of the ZIP file
195+
*/
196+
public void importTracklistFromZipFile(Uri zipDocumentUri) {
197+
isLastOperationSuccessful = false;
198+
try {
199+
DocumentFile zipDocumentFile = getDocumentFile(zipDocumentUri);
200+
201+
// Open InputStream from the DocumentFile
202+
InputStream inputStream = GPSApplication.getInstance().getBaseContext().getContentResolver().openInputStream(zipDocumentFile.getUri());
203+
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
204+
205+
ZipEntry ze = null;
206+
207+
// Check if the archive contains the database and the thumbnails folder
208+
boolean isThumbnailsPresent = false;
209+
boolean isDatabasePresent = false;
210+
while ((ze = zipInputStream.getNextEntry()) != null) {
211+
if (ze.getName().contains("/Thumbnails/GPSLogger/") && !isThumbnailsPresent) {
212+
Log.w("myApp", "[#] AppDataManager.java - Thumbnails folder found");
213+
isThumbnailsPresent = true;
214+
}
215+
if (ze.getName().endsWith("/GPSLogger") && !isDatabasePresent) {
216+
Log.w("myApp", "[#] AppDataManager.java - Database found");
217+
isDatabasePresent = true;
218+
}
219+
}
220+
zipInputStream.close();
221+
inputStream.close();
222+
223+
// If the ZIP file is valid, restore the Tracklist files
224+
if (isDatabasePresent) {
225+
inputStream = GPSApplication.getInstance().getBaseContext().getContentResolver().openInputStream(zipDocumentFile.getUri());
226+
zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
227+
228+
// Delete all the existing Thumbnails
229+
File folderThumbnails = new File( GPSApplication.getInstance().getTHUMBNAILS_FOLDER());
230+
Log.w("myApp", "[#] AppDataManager.java - Thumbnail Folder: " + folderThumbnails.getPath());
231+
if (folderThumbnails.isDirectory())
232+
{
233+
String[] children = folderThumbnails.list();
234+
for (int i = 0; i < children.length; i++)
235+
{
236+
new File(folderThumbnails, children[i]).delete();
237+
}
238+
}
239+
240+
// Delete the existing Database
241+
File folderDatabase = new File(GPSApplication.getInstance().getDatabasePath("GPSLogger").getParent());
242+
Log.w("myApp", "[#] AppDataManager.java - Databases Folder: " + folderDatabase.getPath());
243+
if (folderDatabase.isDirectory())
244+
{
245+
String[] children = folderDatabase.list();
246+
for (int i = 0; i < children.length; i++)
247+
{
248+
new File(folderDatabase, children[i]).delete();
249+
}
250+
}
251+
252+
// the ZIP file is valid
253+
Log.w("myApp", "[#] AppDataManager.java - The ZIP file is valid, Restoring...");
254+
while ((ze = zipInputStream.getNextEntry()) != null) {
255+
// Import a Thumbnail
256+
if (ze.getName().contains("/Thumbnails/GPSLogger")) {
257+
Log.w("myApp", "[#] AppDataManager.java - UNZIP Thumbnail: " + ze.getName().substring(ze.getName().lastIndexOf("/") + 1));
258+
byte[] buffer = new byte[1024];
259+
FileOutputStream fout = new FileOutputStream(folderThumbnails + "/" + ze.getName().substring(ze.getName().lastIndexOf("/") + 1));
260+
int i = 0;
261+
int len;
262+
while ((len = zipInputStream.read(buffer)) > 0) {
263+
fout.write(buffer, 0, len);
264+
}
265+
zipInputStream.closeEntry();
266+
fout.close();
267+
}
268+
// Import the Database
269+
if (ze.getName().endsWith("/GPSLogger")) {
270+
Log.w("myApp", "[#] AppDataManager.java - UNZIP Database: " + ze.getName().substring(ze.getName().lastIndexOf("/") + 1));
271+
byte[] buffer = new byte[1024];
272+
FileOutputStream fout = new FileOutputStream(folderDatabase + "/" + ze.getName().substring(ze.getName().lastIndexOf("/") + 1));
273+
int i = 0;
274+
int len;
275+
while ((len = zipInputStream.read(buffer)) > 0) {
276+
fout.write(buffer, 0, len);
277+
}
278+
zipInputStream.closeEntry();
279+
fout.close();
280+
isLastOperationSuccessful = true;
281+
}
282+
}
283+
zipInputStream.close();
284+
inputStream.close();
285+
286+
// Update the signature to invalidate the Glide cache
287+
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US);
288+
GPSApplication.getInstance().setTracklistSignature(sdf.format(new Date()));
289+
Log.w("myApp", "[#] AppDataManager.java - New signature for Thumbnails: " + GPSApplication.getInstance().getTracklistSignature());
290+
}
291+
} catch (Exception e) {
292+
System.out.println(e);
293+
}
294+
}
295+
296+
@Nullable
297+
private static DocumentFile getDocumentFile(Uri zipDocumentUri) {
298+
DocumentFile zipDocumentFile;
299+
zipDocumentFile = DocumentFile.fromSingleUri(GPSApplication.getInstance(), zipDocumentUri);
300+
return zipDocumentFile;
301+
}
302+
}

app/src/main/java/eu/basicairdata/graziano/gpslogger/DatabaseHandler.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ class DatabaseHandler extends SQLiteOpenHelper {
6262
private static final int LOCATION_TYPE_LOCATION = 1;
6363
private static final int LOCATION_TYPE_PLACEMARK = 2;
6464

65-
// Database Name
66-
private static final String DATABASE_NAME = "GPSLogger";
67-
6865
// -------------------------------------------------------------------------------- Table names
6966
private static final String TABLE_LOCATIONS = "locations";
7067
private static final String TABLE_TRACKS = "tracks";
@@ -147,8 +144,8 @@ class DatabaseHandler extends SQLiteOpenHelper {
147144
private static final String KEY_TRACK_DESCRIPTION = "description";
148145

149146

150-
public DatabaseHandler(Context context) {
151-
super(context, DATABASE_NAME, null, DATABASE_VERSION);
147+
public DatabaseHandler(Context context, String databaseName) {
148+
super(context, databaseName, null, DATABASE_VERSION);
152149
}
153150

154151
/**
@@ -920,10 +917,15 @@ public long addTrack(Track track) {
920917
trkvalues.put(KEY_TRACK_DESCRIPTION, track.getDescription());
921918

922919
long TrackID;
923-
// Inserting Row
924-
TrackID = (db.insert(TABLE_TRACKS, null, trkvalues));
925-
926-
//Log.w("myApp", "[#] DatabaseHandler.java - addTrack " + TrackID);
920+
try {
921+
db.beginTransaction();
922+
// Inserting Row
923+
TrackID = (db.insert(TABLE_TRACKS, null, trkvalues));
924+
db.setTransactionSuccessful();
925+
//Log.w("myApp", "[#] DatabaseHandler.java - addTrack " + TrackID);
926+
} finally {
927+
db.endTransaction();
928+
}
927929

928930
return TrackID; // Insert this in the track ID !!!
929931
}

0 commit comments

Comments
 (0)