mirror of
https://github.com/woheller69/solxpect.git
synced 2025-12-16 09:20:12 +01:00
add backup/restore
This commit is contained in:
parent
67027dc8e6
commit
7a2edcf814
12 changed files with 297 additions and 2 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
|
|
@ -56,6 +59,16 @@
|
|||
android:value="org.woheller69.weather.activities.ForecastCityActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.woheller69.weather.activities.BackupRestoreActivity"
|
||||
android:label="@string/activity_backuprestore"
|
||||
android:parentActivityName="org.woheller69.weather.activities.ForecastCityActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.woheller69.weather.activities.ForecastCityActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.woheller69.weather.activities.SettingsActivity"
|
||||
android:label="@string/activity_settings_title"
|
||||
|
|
|
|||
69
app/src/main/java/org/woheller69/weather/Backup.java
Normal file
69
app/src/main/java/org/woheller69/weather/Backup.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package org.woheller69.weather;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
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) {
|
||||
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) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setIcon(R.drawable.ic_warning_amber_black_24dp);
|
||||
builder.setTitle(activity.getResources().getString(R.string.permission_required));
|
||||
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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Intent> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,840q-139,0 -241,-91.5T122,520h82q14,104 92.5,172T480,760q117,0 198.5,-81.5T760,480q0,-117 -81.5,-198.5T480,200q-69,0 -129,32t-101,88h110v80L120,400L120,160h80v94q51,-64 124.5,-99T480,120q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,480q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,840Z"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_warning_amber_black_24dp.xml
Normal file
15
app/src/main/res/drawable/ic_warning_amber_black_24dp.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,5.99L19.53,19H4.47L12,5.99M12,2L1,21h22L12,2L12,2z"
|
||||
android:fillColor="@color/orange"/>
|
||||
<path
|
||||
android:pathData="M13,16l-2,0l0,2l2,0z"
|
||||
android:fillColor="@color/orange"/>
|
||||
<path
|
||||
android:pathData="M13,10l-2,0l0,5l2,0z"
|
||||
android:fillColor="@color/orange"/>
|
||||
</vector>
|
||||
52
app/src/main/res/layout/activity_backuprestore.xml
Normal file
52
app/src/main/res/layout/activity_backuprestore.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:openDrawer="start">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".activities.BackupRestoreActivity">
|
||||
|
||||
<include layout="@layout/toolbar" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/backup_database"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:onClick="performBackup"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/restore_database"
|
||||
android:onClick="performRestore"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:fitsSystemWindows="true"
|
||||
app:headerLayout="@layout/nav_header_main"
|
||||
app:menu="@menu/activity_main_drawer" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
||||
|
|
@ -25,6 +25,10 @@
|
|||
android:id="@+id/nav_help"
|
||||
android:icon="@drawable/ic_help_24px"
|
||||
android:title="@string/activity_help" />
|
||||
<item
|
||||
android:id="@+id/nav_backuprestore"
|
||||
android:icon="@drawable/ic_settings_backup_restore_24px"
|
||||
android:title="@string/activity_backuprestore" />
|
||||
<item
|
||||
android:id="@+id/nav_about"
|
||||
android:icon="@drawable/ic_info_24px"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<string name="about">Über</string>
|
||||
<string name="about_privacy_heading">Privatsphäre-Informationen</string>
|
||||
<string name="about_more_info">Mehr Informationen können gefunden werden auf:</string>
|
||||
<string name="about_license_text">Diese App ist abgeleitet von Privacy Friendly Weather, entwickelt von der Forschungsgruppe SECUSO. Quelltext lizenziert unter GPLv3. Die App benutzt Icons von Google Material Design Icons, lizenziert unter Apache License Version 2.0, die Leaflet Bibliothek, lizenziert unter 2-clause BSD License, AutoSuggestTextViewAPICall, lizenziert unter der Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning), lizenziert unter MIT Lizenz und die WilliamChart Bibliothek (com.db.chart), lizenziert unter der Apache License Version 2.0</string>
|
||||
<string name="about_license_text">Diese App ist abgeleitet von Privacy Friendly Weather, entwickelt von der Forschungsgruppe SECUSO. Quelltext lizenziert unter GPLv3. Die App benutzt Icons von Google Material Design Icons, lizenziert unter Apache License Version 2.0, die Leaflet Bibliothek, lizenziert unter 2-clause BSD License, AutoSuggestTextViewAPICall, lizenziert unter der Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning), lizenziert unter MIT Lizenz, Zip4j, lizenziert unter Apache License Version 2.0 und die WilliamChart Bibliothek (com.db.chart), lizenziert unter der Apache License Version 2.0</string>
|
||||
<string name="about_license">Lizenz</string>
|
||||
<string name="about_privacy_answer">solXpect benötigt nur Zugang zum Internet. Es werden weder persönliche Daten gesammelt, noch wird Werbung angezeigt.</string>
|
||||
<string name="about_where_from">Woher kommen die Wetterinformationen?</string>
|
||||
|
|
@ -95,5 +95,12 @@
|
|||
<string name="action_sun_position">Sonnenposition</string>
|
||||
<string name="action_sun_elevation">Elevation [°]</string>
|
||||
<string name="edit_location_hint_cells_temp_coeff">Temperaturkoeffizient [%/K]</string>
|
||||
<string name="activity_backuprestore">Sichern/Wiederherstellen</string>
|
||||
<string name="backup_database">Datenbank sichern</string>
|
||||
<string name="restore_database">Datenbank wiederherstellen</string>
|
||||
<string name="permission_required">Berechtigung erforderlich</string>
|
||||
<string name="permission_message">%s benötigt Zugriff auf den externen Speicher. Bitte die Berechtigung erteilen und erneut versuchen.</string>
|
||||
<string name="toast_delete">Bitte die Datei zuerst löschen und erneut versuchen.</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<string name="about">About</string>
|
||||
<string name="version_number" translatable="false">Version</string>
|
||||
<string name="about_license">License</string>
|
||||
<string name="about_license_text">This application is derived from Privacy Friendly Weather, developed by the research group SECUSO. Sourcecode licensed under GPLv3. The app uses icons from Google Material Design Icons licensed under Apache License Version 2.0, the Leaflet library which is licensed under 2-clause BSD License, AutoSuggestTextViewAPICall which is licensed under Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning) which is licensed under MIT License, and WilliamChart library (com.db.chart) which is licensed under Apache License Version 2.0</string>
|
||||
<string name="about_license_text">This application is derived from Privacy Friendly Weather, developed by the research group SECUSO. Sourcecode licensed under GPLv3. The app uses icons from Google Material Design Icons licensed under Apache License Version 2.0, the Leaflet library which is licensed under 2-clause BSD License, AutoSuggestTextViewAPICall which is licensed under Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning) which is licensed under MIT License, Zip4j (https://github.com/srikanth-lingala/zip4j) which is licensed under Apache License Version 2.0, and WilliamChart library (com.db.chart) which is licensed under Apache License Version 2.0</string>
|
||||
<string name="about_more_info">More information can be found on:</string>
|
||||
<string name="about_github" translatable="false"><a href="https://github.com/woheller69/solxpect">Github-Repo</a></string>
|
||||
<string name="about_openmeteo" translatable="false"><a href="https://open-meteo.com">Open-Meteo \n(Attribution 4.0 International CC BY 4.0)</a></string>
|
||||
|
|
@ -98,4 +98,10 @@
|
|||
<string name="action_sun_position">Sun position</string>
|
||||
<string name="action_sun_elevation">Elevation [°]</string>
|
||||
<string name="edit_location_hint_cells_temp_coeff">Temperature coefficient [%/K]</string>
|
||||
<string name="activity_backuprestore">Backup/Restore</string>
|
||||
<string name="backup_database">Backup database</string>
|
||||
<string name="restore_database">Restore database</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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue