diff --git a/README.md b/README.md index 5b6c4de..3f99ef6 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ The app uses: - AutoSuggestTextViewAPICall (https://github.com/Truiton/AutoSuggestTextViewAPICall) which is licensed under Apache License Version 2.0 - Map data from OpenStreetMap, licensed under the Open Data Commons Open Database License (ODbL) by the OpenStreetMap Foundation (OSMF) (https://www.openstreetmap.org/copyright) - Solar positioning library (https://github.com/klausbrunner/solarpositioning) which is licensed under MIT License +- Zip4j (https://github.com/srikanth-lingala/zip4j) which is licensed under Apache License Version 2.0 ## Contributing If you find a bug, please open an issue in the Github repository, assuming one does not already exist. diff --git a/app/build.gradle b/app/build.gradle index 5a0a807..7f4c7f6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,6 +38,7 @@ dependencies { implementation 'com.android.volley:volley:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.cardview:cardview:1.0.0' + implementation 'net.lingala.zip4j:zip4j:2.9.1' implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" //needed due to duplicate class error implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" //needed due to duplicate class error } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a1dac11..5425ae1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,9 +4,12 @@ + + + + + + { + dialog.cancel(); + 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 zipExtract(Context context, File targetDir, Uri zipFile) { + ZipEntry zipEntry; + int readLen; + byte[] readBuffer = new byte[4096]; + try { + 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); + } + } + } + } + } catch (IOException ioException) { + ioException.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java b/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java new file mode 100644 index 0000000..ebcfeaa --- /dev/null +++ b/app/src/main/java/org/woheller69/weather/activities/BackupRestoreActivity.java @@ -0,0 +1,115 @@ +package org.woheller69.weather.activities; + +import static android.os.Environment.DIRECTORY_DOCUMENTS; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.view.Gravity; +import android.view.View; +import android.widget.Toast; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; + +import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.exception.ZipException; + +import org.woheller69.weather.Backup; +import org.woheller69.weather.R; + +import java.io.File; +import java.util.Objects; + + +public class BackupRestoreActivity extends NavigationActivity{ + ActivityResultLauncher mRestore; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_backuprestore); + + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + mRestore = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + File intData = new File(Environment.getDataDirectory() + "//databases//" + this.getPackageName()); + if (result.getData()!=null && result.getData().getData()!=null) Backup.zipExtract(this, intData, result.getData().getData()); + }); + } + + @Override + protected int getNavigationDrawerID() { + return R.id.nav_backuprestore; + } + + public void performBackup(View view) { + File extStorage; + File intData; + intData = new File(Environment.getDataDirectory()+"//data//" + this.getPackageName() + "//databases//"); + extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS); + String filesBackup = getResources().getString(R.string.app_name)+".zip"; + final File dbBackup = new File(extStorage, filesBackup); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getResources().getString(R.string.backup_database) +" -> " + dbBackup.toString()); + builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> { + if (!Backup.checkPermissionStorage(this)) { + Backup.requestPermission(this); + } else { + if (dbBackup.exists()){ + if (!dbBackup.delete()){ + Toast.makeText(this,getResources().getString(R.string.toast_delete), Toast.LENGTH_LONG).show(); + } + } + try { + new ZipFile(dbBackup).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); + + } + + public void performRestore(View view) { + File extStorage; + File intData; + 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.restore_database)); + 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.setType("application/zip"); + mRestore.launch(intent); + } else { + Backup.zipExtract(this, intData, Uri.fromFile(zipFileBackup)); + } + } + }); + builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel()); + AlertDialog dialog = builder.create(); + dialog.show(); + Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM); + } +} diff --git a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java index 7efa3e1..d9a159e 100644 --- a/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java +++ b/app/src/main/java/org/woheller69/weather/activities/NavigationActivity.java @@ -169,6 +169,9 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio }else if(itemId==R.id.nav_help) { intent = new Intent(this, HelpActivity.class); startActivity(intent); + }else if(itemId==R.id.nav_backuprestore) { + intent = new Intent(this, BackupRestoreActivity.class); + startActivity(intent); }else if (itemId==R.id.star_on_github){ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.GITHUB_URL))); diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml b/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml new file mode 100644 index 0000000..edc6466 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_backup_restore_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml b/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml new file mode 100644 index 0000000..dfc11fb --- /dev/null +++ b/app/src/main/res/drawable/ic_warning_amber_black_24dp.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/layout/activity_backuprestore.xml b/app/src/main/res/layout/activity_backuprestore.xml new file mode 100644 index 0000000..c0122dc --- /dev/null +++ b/app/src/main/res/layout/activity_backuprestore.xml @@ -0,0 +1,52 @@ + + + + + + + + +