Skip to content

Commit

Permalink
Backup to singe file using zip4j
Browse files Browse the repository at this point in the history
Remove MANAGE_EXTERNAL_STORAGE permission
  • Loading branch information
woheller69 committed Feb 7, 2022
1 parent fb1f946 commit ef4ca59
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 102 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ Backup/restore is also available.

This app is published under GNU GPL V3 License.

The app uses:
Zip4j (https://github.com/srikanth-lingala/zip4j) which is licensed under Apache License Version 2.0
AndroidX libraries (https://github.com/androidx/androidx) which is licensed under Apache License Version 2.0
MPAndroidChart (https://github.com/PhilJay/MPAndroidChart) which is licensed under Apache License Version 2.0

The original version of this code is published under MIT license, Copyright (c) 2014 Reece Stevens.

https://github.com/ReeceStevens/ut_ewh_audiometer_2014
Expand Down
5 changes: 3 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ android {
defaultConfig {
minSdkVersion 23
targetSdkVersion 30
versionCode 110
versionName "1.1"
versionCode 120
versionName "1.2"
}
buildTypes {
release {
Expand All @@ -29,5 +29,6 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'net.lingala.zip4j:zip4j:2.9.1'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
1 change: 0 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />

<application
android:allowBackup="true"
Expand Down
80 changes: 21 additions & 59 deletions app/src/main/java/org/woheller69/audiometry/Backup.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,30 @@
/*
Part of this code is taken from https://github.com/scoute-dich/browser
which is published under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
<http://www.gnu.org/licenses/>.
*/

package org.woheller69.audiometry;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;

import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class Backup {
public static final int PERMISSION_REQUEST_CODE = 123;

public static boolean checkPermissionStorage (Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
} else {
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE);
int result1 = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
}

public static void requestPermission(Activity activity) {
Expand All @@ -49,57 +34,34 @@ public static void requestPermission(Activity activity) {
builder.setMessage(activity.getResources().getString(R.string.permission_message,activity.getResources().getString(R.string.app_name)));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, which) -> {
dialog.cancel();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",activity.getPackageName())));
activity.startActivity(intent);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
activity.startActivity(intent);
}
} else {
//below android 11
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
}

public static void copyDirectory(File sourceLocation, File targetLocation) {

public static void zipExtract(Context context, File targetDir, Uri zipFile) {
ZipEntry zipEntry;
int readLen;
byte[] readBuffer = new byte[4096];
try {
if (sourceLocation.isDirectory()) {
if (!targetLocation.exists() && !targetLocation.mkdirs()) {
throw new IOException("Cannot create dir " + targetLocation.getAbsolutePath());
}
String[] children = sourceLocation.list();
for (String aChildren : Objects.requireNonNull(children)) {
copyDirectory(new File(sourceLocation, aChildren), new File(targetLocation, aChildren));
}
} else {
// make sure the directory we plan to store the recording in exists
File directory = targetLocation.getParentFile();
if (directory != null && !directory.exists() && !directory.mkdirs()) {
throw new IOException("Cannot create dir " + directory.getAbsolutePath());
}

InputStream in = new FileInputStream(sourceLocation);
OutputStream out = new FileOutputStream(targetLocation);
// Copy the bits from InputStream to OutputStream
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
InputStream src = context.getContentResolver().openInputStream(zipFile);
try {
try (ZipInputStream zipInputStream = new ZipInputStream(src)) {
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
File extractedFile = new File(targetDir ,zipEntry.getName());
try (OutputStream outputStream = new FileOutputStream(extractedFile)) {
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
outputStream.write(readBuffer, 0, readLen);
}
}
}
}
in.close();
out.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
} catch (IOException e) {
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Expand Down
108 changes: 68 additions & 40 deletions app/src/main/java/org/woheller69/audiometry/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@

import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.File;
import java.util.Objects;

Expand All @@ -22,6 +30,8 @@

public class MainActivity extends AppCompatActivity {

ActivityResultLauncher<Intent> mRestore;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -32,9 +42,15 @@ protected void onCreate(Bundle savedInstanceState) {
requestPermissions( new String[]{Manifest.permission.RECORD_AUDIO},1);
checkShowInvisibleButtons();

mRestore = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
File intData = new File(Environment.getDataDirectory() + "//data//" + this.getPackageName());
Backup.zipExtract(this, intData, result.getData().getData());
checkShowInvisibleButtons();
});
}


private void checkShowInvisibleButtons(){
Button startTest = findViewById(R.id.main_startTest);
Button startSingleTest = findViewById(R.id.main_startSingleTest);
Expand Down Expand Up @@ -101,51 +117,63 @@ public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
File ext_storage;
File int_data;
File extStorage;
File intData;
int id = item.getItemId();
if (id==R.id.backup) {
ext_storage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
int_data = Environment.getDataDirectory();
String files = "//data//" + this.getPackageName();
String files_backup = getResources().getString(R.string.app_name);
final File previewsFolder_app = new File(int_data, files);
final File previewsFolder_backup = new File(ext_storage, files_backup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.main_backup));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
} else {
Backup.copyDirectory(previewsFolder_app, previewsFolder_backup);
intData = new File(Environment.getDataDirectory()+"//data//" + this.getPackageName() + "//files//");
extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
String filesBackup = getResources().getString(R.string.app_name)+".zip";
final File zipFileBackup = new File(extStorage, filesBackup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.main_backup));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
} else {
if (zipFileBackup.exists()){
if (!zipFileBackup.delete()){
Toast.makeText(this,getResources().getString(R.string.toast_delete), Toast.LENGTH_LONG).show();
}
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}else if (id==R.id.restore){
ext_storage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
int_data = Environment.getDataDirectory();
String files = "//data//" + this.getPackageName();
String files_backup = getResources().getString(R.string.app_name);
final File previewsFolder_app = new File(int_data, files);
final File previewsFolder_backup = new File(ext_storage, files_backup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.main_restore));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
try {
new ZipFile(zipFileBackup).addFolder(intData);
} catch (ZipException e) {
e.printStackTrace();
}
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}else if (id==R.id.restore){
intData = new File(Environment.getDataDirectory() + "//data//" + this.getPackageName());
extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
String filesBackup = getResources().getString(R.string.app_name)+".zip";
final File zipFileBackup = new File(extStorage, filesBackup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.main_restore_message));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, this.getExternalFilesDir(DIRECTORY_DOCUMENTS));
intent.setType("application/zip");
mRestore.launch(intent);
} else {
Backup.copyDirectory(previewsFolder_backup, previewsFolder_app);
Backup.zipExtract(this, intData, Uri.fromFile(zipFileBackup));
checkShowInvisibleButtons();
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}

return super.onOptionsItemSelected(item);
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
<string name="dialog_StarOnGitHub">Do you like this app? Please star it on GitHub!</string>
<string name="visit_on_github">Visit on GitHub</string>
<string name="main_restore">Restore Data from Documents folder</string>
<string name="main_restore_message">Restore Data from Documents folder. Select hEARtest.zip if needed!</string>
<string name="main_backup">Backup Data to Documents folder</string>
<string name="permission_required">Permission required</string>
<string name="permission_message">%s needs access to external storage. Please accept permission and try again.</string>
<string name="toast_delete">Please delete file and try again</string>
</resources>
2 changes: 2 additions & 0 deletions fastlane/metadata/android/de-DE/changelogs/120.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Backup in einzelnes ZIP file
MANAGE_EXTERNAL_STORAGE permission entfernt
2 changes: 2 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/120.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Backup to single ZIP file
Remove MANAGE_EXTERNAL_STORAGE permission

0 comments on commit ef4ca59

Please sign in to comment.