1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-20 07:21:40 +02:00

Auto backup (#247)

* implementation of auto-backup function
add an option to overwrite previous backup

* open import and export file chooser intent.
This commit is contained in:
OliE
2018-04-21 09:33:33 +02:00
committed by GitHub
parent 062db32266
commit d4dc9c9f6a
7 changed files with 426 additions and 122 deletions

View File

@@ -22,6 +22,7 @@ import android.arch.persistence.room.RoomDatabase;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -54,6 +55,7 @@ import com.health.openscale.gui.views.MeasurementViewSettings;
import com.health.openscale.gui.views.WaterMeasurementView;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -107,7 +109,7 @@ public class OpenScale {
return instance;
}
public void reopenDatabase() {
public void reopenDatabase() throws SQLiteDatabaseCorruptException {
if (appDB != null) {
appDB.close();
}
@@ -393,6 +395,56 @@ public class OpenScale {
}
}
public void importDatabase(Uri importFile) throws IOException {
File exportFile = context.getApplicationContext().getDatabasePath("openScale.db");
File tmpExportFile = context.getApplicationContext().getDatabasePath("openScale_tmp.db");
try {
copyFile(Uri.fromFile(exportFile), Uri.fromFile(tmpExportFile));
copyFile(importFile, Uri.fromFile(exportFile));
reopenDatabase();
if (!getScaleUserList().isEmpty()) {
selectScaleUser(getScaleUserList().get(0).getId());
updateScaleData();
}
} catch (SQLiteDatabaseCorruptException e) {
copyFile(Uri.fromFile(tmpExportFile), Uri.fromFile(exportFile));
throw new IOException(e.getMessage());
} finally {
tmpExportFile.delete();
}
}
public void exportDatabase(Uri exportFile) throws IOException {
File dbFile = context.getApplicationContext().getDatabasePath("openScale.db");
copyFile(Uri.fromFile(dbFile), exportFile);
}
private void copyFile(Uri src, Uri dst) throws IOException {
InputStream input = context.getContentResolver().openInputStream(src);
OutputStream output = context.getContentResolver().openOutputStream(dst);
try {
byte[] bytes = new byte[4096];
int count;
while ((count = input.read(bytes)) != -1){
output.write(bytes, 0, count);
}
} finally {
if (input != null) {
input.close();
}
if (output != null) {
output.flush();
output.close();
}
}
}
public void importData(Uri uri) {
try {
final String filename = getFilenameFromUri(uri);

View File

@@ -0,0 +1,113 @@
/* Copyright (C) 2018 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.alarm;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import com.health.openscale.core.OpenScale;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
public class AlarmBackupHandler
{
public static final String INTENT_EXTRA_BACKUP_ALARM = "alarmBackupIntent";
private static final int ALARM_NOTIFICATION_ID = 0x02;
public void scheduleAlarms(Context context)
{
disableAlarm(context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("autoBackup", true)) {
String backupSchedule = prefs.getString("autoBackup_Schedule", "Monthly");
long intervalDayMultiplicator = 0;
switch (backupSchedule) {
case "Daily":
intervalDayMultiplicator = 1;
break;
case "Weekly":
intervalDayMultiplicator = 7;
break;
case "Monthly":
intervalDayMultiplicator = 30;
break;
}
PendingIntent alarmPendingIntent = getPendingAlarmIntent(context);
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
AlarmManager.INTERVAL_DAY * intervalDayMultiplicator, alarmPendingIntent);
}
}
private PendingIntent getPendingAlarmIntent(Context context)
{
Intent alarmIntent = new Intent(context, ReminderBootReceiver.class);
alarmIntent.putExtra(INTENT_EXTRA_BACKUP_ALARM, true);
return PendingIntent.getBroadcast(context, ALARM_NOTIFICATION_ID, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public void disableAlarm(Context context) {
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.cancel(getPendingAlarmIntent(context));
}
public void executeBackup(Context context) {
OpenScale openScale = OpenScale.getInstance(context);
String databaseName = "openScale.db";
File exportDir = new File(Environment.getExternalStorageDirectory(), PreferenceManager.getDefaultSharedPreferences(context).getString("exportDir", "openScale Backup"));
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
return;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean("overwriteBackup", false)) {
databaseName = DateFormat.getDateInstance(DateFormat.SHORT).format(new Date()) + "_" + databaseName;
}
File exportFile = new File(exportDir, databaseName);
if (!exportDir.exists()) {
exportDir.mkdirs();
}
try {
openScale.exportDatabase(Uri.fromFile(exportFile));
Log.d("AlarmBackupHandler", "openScale Auto Backup to " + exportFile);
} catch (IOException e) {
Log.e("AlarmBackupHandler", "Error while exporting database: " + e.getMessage());
}
}
}

View File

@@ -27,6 +27,8 @@ public class ReminderBootReceiver extends BroadcastReceiver
{
if (intent.hasExtra(AlarmHandler.INTENT_EXTRA_ALARM)) handleAlarm(context);
if (intent.hasExtra(AlarmBackupHandler.INTENT_EXTRA_BACKUP_ALARM)) handleBackupAlarm(context);
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) scheduleAlarms(context);
}
@@ -36,9 +38,18 @@ public class ReminderBootReceiver extends BroadcastReceiver
alarmHandler.showAlarmNotification(context);
}
private void handleBackupAlarm(Context context)
{
AlarmBackupHandler alarmBackupHandler = new AlarmBackupHandler();
alarmBackupHandler.executeBackup(context);
}
private void scheduleAlarms(Context context)
{
AlarmHandler alarmHandler = new AlarmHandler();
AlarmBackupHandler alarmBackupHandler = new AlarmBackupHandler();
alarmHandler.scheduleAlarms(context);
alarmBackupHandler.scheduleAlarms(context);
}
}

View File

@@ -15,32 +15,48 @@
*/
package com.health.openscale.gui.preferences;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.MultiSelectListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.widget.Toast;
import com.health.openscale.R;
import com.health.openscale.core.OpenScale;
import com.health.openscale.core.datatypes.ScaleUser;
import com.health.openscale.core.alarm.AlarmBackupHandler;
import com.health.openscale.core.alarm.ReminderBootReceiver;
import com.health.openscale.gui.utils.PermissionHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class BackupPreferences extends PreferenceFragment {
private static final String PREFERENCE_KEY_EXPORT_DIR = "exportDir";
import static android.app.Activity.RESULT_OK;
public class BackupPreferences extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String PREFERENCE_KEY_IMPORT_BACKUP = "importBackup";
private static final String PREFERENCE_KEY_EXPORT_BACKUP = "exportBackup";
private static final String PREFERENCE_KEY_AUTO_BACKUP = "autoBackup";
private EditTextPreference exportDir;
private static final int IMPORT_DATA_REQUEST = 100;
private static final int EXPORT_DATA_REQUEST = 101;
private Preference importBackup;
private Preference exportBackup;
private CheckBoxPreference autoBackup;
private boolean isAutoBackupAskForPermission;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -48,150 +64,230 @@ public class BackupPreferences extends PreferenceFragment {
addPreferencesFromResource(R.xml.backup_preferences);
exportDir = (EditTextPreference) findPreference(PREFERENCE_KEY_EXPORT_DIR);
exportDir.setSummary(exportDir.getText());
exportDir.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
preference.setSummary((String) newValue);
return true;
}
});
importBackup = (Preference) findPreference(PREFERENCE_KEY_IMPORT_BACKUP);
importBackup.setOnPreferenceClickListener(new onClickListenerImportBackup());
Preference importBackup = findPreference(PREFERENCE_KEY_IMPORT_BACKUP);
importBackup.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (PermissionHelper.requestReadPermission(getActivity())) {
importBackup();
}
return true;
}
});
exportBackup = (Preference) findPreference(PREFERENCE_KEY_EXPORT_BACKUP);
exportBackup.setOnPreferenceClickListener(new onClickListenerExportBackup());
Preference exportBackup = findPreference(PREFERENCE_KEY_EXPORT_BACKUP);
exportBackup.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (PermissionHelper.requestWritePermission(getActivity())) {
exportBackup();
}
return true;
}
});
autoBackup = (CheckBoxPreference) findPreference(PREFERENCE_KEY_AUTO_BACKUP);
autoBackup.setOnPreferenceClickListener(new onClickListenerAutoBackup());
initSummary(getPreferenceScreen());
updateBackupPreferences();
}
private boolean isExternalStoragePresent() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
void updateBackupPreferences() {
ComponentName receiver = new ComponentName(getActivity().getApplicationContext(), ReminderBootReceiver.class);
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
AlarmBackupHandler alarmBackupHandler = new AlarmBackupHandler();
isAutoBackupAskForPermission = false;
if (autoBackup.isChecked()) {
alarmBackupHandler.scheduleAlarms(getActivity());
pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
} else {
alarmBackupHandler.disableAlarm(getActivity());
pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}
private File getExportDir() {
if (!isExternalStoragePresent()) {
return null;
@Override
public void onResume()
{
super.onResume();
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause()
{
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
updatePrefSummary(findPreference(key));
updateBackupPreferences();
}
private void initSummary(Preference p) {
if (p instanceof PreferenceGroup) {
PreferenceGroup pGrp = (PreferenceGroup) p;
for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
initSummary(pGrp.getPreference(i));
}
} else {
updatePrefSummary(p);
}
}
private void updatePrefSummary(Preference p)
{
if (p instanceof ListPreference)
{
ListPreference listPref = (ListPreference) p;
p.setSummary(listPref.getEntry());
}
return new File(Environment.getExternalStorageDirectory(), exportDir.getText());
if (p instanceof EditTextPreference)
{
EditTextPreference editTextPref = (EditTextPreference) p;
if (p.getTitle().toString().contains("assword"))
{
p.setSummary("******");
}
else
{
p.setSummary(editTextPref.getText());
}
}
if (p instanceof MultiSelectListPreference)
{
MultiSelectListPreference editMultiListPref = (MultiSelectListPreference) p;
CharSequence[] entries = editMultiListPref.getEntries();
CharSequence[] entryValues = editMultiListPref.getEntryValues();
List<String> currentEntries = new ArrayList<>();
Set<String> currentEntryValues = editMultiListPref.getValues();
for (int i = 0; i < entries.length; i++)
{
if (currentEntryValues.contains(entryValues[i].toString())) currentEntries.add(entries[i].toString());
}
p.setSummary(currentEntries.toString());
}
}
private class onClickListenerAutoBackup implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
if (autoBackup.isChecked()) {
isAutoBackupAskForPermission = true;
PermissionHelper.requestWritePermission(getActivity());
}
return true;
}
}
private class onClickListenerImportBackup implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
if (PermissionHelper.requestReadPermission(getActivity())) {
importBackup();
}
return true;
}
}
private class onClickListenerExportBackup implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
if (PermissionHelper.requestWritePermission(getActivity())) {
exportBackup();
}
return true;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK || data == null) {
return;
}
OpenScale openScale = OpenScale.getInstance(getActivity().getApplicationContext());
switch (requestCode) {
case IMPORT_DATA_REQUEST:
Uri importURI = data.getData();
try {
openScale.importDatabase(importURI);
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.info_data_imported) + " " + importURI.getPath(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.error_importing) + " " + e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
break;
case EXPORT_DATA_REQUEST:
Uri exportURI = data.getData();
try {
openScale.exportDatabase(exportURI);
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.info_data_exported) + " " + exportURI.getPath(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.error_exporting) + " " + e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
break;
}
}
private boolean importBackup() {
File exportDir = getExportDir();
if (exportDir == null) {
return false;
}
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("*/*");
File dbFile = getActivity().getDatabasePath(OpenScale.DATABASE_NAME);
File importFile = new File(exportDir, OpenScale.DATABASE_NAME);
if (!importFile.exists()) {
Toast.makeText(getActivity(), getResources().getString(R.string.error_importing)
+ " " + importFile.getPath() + " "
+ getResources().getString(R.string.label_not_found), Toast.LENGTH_SHORT).show();
return false;
}
try {
dbFile.createNewFile();
copyFile(importFile, dbFile);
Toast.makeText(getActivity(), getResources().getString(R.string.info_data_imported)
+ " " + importFile.getPath(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getActivity(), getResources().getString(R.string.error_importing)
+ " " + e.getMessage(), Toast.LENGTH_SHORT).show();
return false;
}
OpenScale openScale = OpenScale.getInstance(getActivity());
openScale.reopenDatabase();
List<ScaleUser> scaleUserList = openScale.getScaleUserList();
if (!scaleUserList.isEmpty()) {
openScale.selectScaleUser(scaleUserList.get(0).getId());
openScale.updateScaleData();
}
startActivityForResult(
Intent.createChooser(intent, getResources().getString(R.string.label_import)),
IMPORT_DATA_REQUEST);
return true;
}
private boolean exportBackup() {
File exportDir = getExportDir();
if (exportDir == null) {
return false;
}
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setType("*/*");
// Make sure all changes are written to the file before exporting
OpenScale.getInstance(getActivity()).reopenDatabase();
File dbFile = getActivity().getDatabasePath(OpenScale.DATABASE_NAME);
File file = new File(exportDir, OpenScale.DATABASE_NAME);
if (!exportDir.exists()) {
exportDir.mkdirs();
}
try {
file.createNewFile();
copyFile(dbFile, file);
Toast.makeText(getActivity(), getResources().getString(R.string.info_data_exported)
+ " " + file.getPath(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getActivity(), getResources().getString(R.string.error_exporting)
+ " " + e.getMessage(), Toast.LENGTH_SHORT).show();
return false;
}
startActivityForResult(intent, EXPORT_DATA_REQUEST);
return true;
}
private void copyFile(File src, File dst) throws IOException {
FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel();
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
}
}
public void onMyOwnRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_READ_STORAGE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
importBackup();
} else {
Toast.makeText(getActivity(), R.string.permission_not_granted, Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show();
}
break;
case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
exportBackup();
if (isAutoBackupAskForPermission) {
autoBackup.setChecked(true);
} else {
exportBackup();
}
} else {
Toast.makeText(getActivity(), R.string.permission_not_granted, Toast.LENGTH_SHORT).show();
if (isAutoBackupAskForPermission) {
autoBackup.setChecked(false);
}
Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show();
}
break;
}

View File

@@ -209,6 +209,12 @@
<string name="label_percent">Percent</string>
<string name="label_estimated">Estimated</string>
<string name="label_estimate_measurement_summary">Based on weight, height, age, gender, etc.</string>
<string name="label_auto_backup">Auto backup</string>
<string name="label_auto_backup_schedule">Backup schedule</string>
<string name="label_overwrite_backup">Overwrite previous backup</string>
<string name="label_daily">daily</string>
<string name="label_weekly">weekly</string>
<string name="label_monthly">monthly</string>
<string name="label_scale_not_supported">Is your scale not supported?</string>
<string name="label_click_to_help_add_support">Click here to help add support for it</string>
</resources>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string-array name="auto_backup_entries">
<item>@string/label_daily</item>
<item>@string/label_weekly</item>
<item>@string/label_monthly</item>
</string-array>
<string-array name="auto_backup_values">
<item>Daily</item>
<item>Weekly</item>
<item>Monthly</item>
</string-array>
<string-array name="auto_backup_default"/>
</resources>

View File

@@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:title="@string/label_export_dir" android:key="exportDir" android:defaultValue="openScale backup" />
<Preference android:title="@string/label_exportBackup" android:key="exportBackup"/>
<Preference android:title="@string/label_importBackup" android:key="importBackup"/>
<CheckBoxPreference android:title="@string/label_auto_backup" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="autoBackup" android:defaultValue="true"/>
<CheckBoxPreference android:title="@string/label_overwrite_backup" android:dependency="autoBackup" android:summaryOn="@string/info_is_enable" android:summaryOff="@string/info_is_not_enable" android:key="overwriteBackup" android:defaultValue="false"/>
<ListPreference
android:dependency="autoBackup"
android:entries="@array/auto_backup_entries"
android:entryValues="@array/auto_backup_values"
android:key="autoBackup_Schedule"
android:title="@string/label_auto_backup_schedule"
android:defaultValue="Monthly"/>
<EditTextPreference android:title="@string/label_export_dir" android:key="exportDir" android:dependency="autoBackup" android:defaultValue="openScale backup" />
</PreferenceScreen>