mirror of
https://github.com/nextcloud/android.git
synced 2024-12-19 23:42:03 +03:00
Merge master
Signed-off-by: alperozturk <alper_ozturk@proton.me>
This commit is contained in:
commit
e6f606614e
7 changed files with 746 additions and 714 deletions
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
@ -1,691 +0,0 @@
|
||||||
/*
|
|
||||||
* Nextcloud Android client application
|
|
||||||
*
|
|
||||||
* @author Mario Danic
|
|
||||||
* @author TSI-mc
|
|
||||||
* Copyright (C) 2017 Mario Danic
|
|
||||||
* Copyright (C) 2017 Nextcloud GmbH.
|
|
||||||
* Copyright (C) 2023 TSI-mc
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.owncloud.android.ui.fragment.contactsbackup;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.DatePickerDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.DatePicker;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.nextcloud.client.account.User;
|
|
||||||
import com.nextcloud.client.di.Injectable;
|
|
||||||
import com.nextcloud.client.jobs.BackgroundJobManager;
|
|
||||||
import com.nextcloud.java.util.Optional;
|
|
||||||
import com.owncloud.android.R;
|
|
||||||
import com.owncloud.android.databinding.BackupFragmentBinding;
|
|
||||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
|
||||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
|
||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
|
||||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
|
||||||
import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
|
|
||||||
import com.owncloud.android.ui.activity.SettingsActivity;
|
|
||||||
import com.owncloud.android.ui.fragment.FileFragment;
|
|
||||||
import com.owncloud.android.utils.DisplayUtils;
|
|
||||||
import com.owncloud.android.utils.MimeTypeUtil;
|
|
||||||
import com.owncloud.android.utils.PermissionUtil;
|
|
||||||
import com.owncloud.android.utils.theme.ThemeUtils;
|
|
||||||
import com.owncloud.android.utils.theme.ViewThemeUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import third_parties.daveKoeller.AlphanumComparator;
|
|
||||||
|
|
||||||
import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP;
|
|
||||||
import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP;
|
|
||||||
|
|
||||||
public class BackupFragment extends FileFragment implements DatePickerDialog.OnDateSetListener, Injectable {
|
|
||||||
public static final String TAG = BackupFragment.class.getSimpleName();
|
|
||||||
private static final String ARG_SHOW_SIDEBAR = "SHOW_SIDEBAR";
|
|
||||||
private static final String KEY_CALENDAR_PICKER_OPEN = "IS_CALENDAR_PICKER_OPEN";
|
|
||||||
private static final String KEY_CALENDAR_DAY = "CALENDAR_DAY";
|
|
||||||
private static final String KEY_CALENDAR_MONTH = "CALENDAR_MONTH";
|
|
||||||
private static final String KEY_CALENDAR_YEAR = "CALENDAR_YEAR";
|
|
||||||
|
|
||||||
public static final String PREFERENCE_CONTACTS_BACKUP_ENABLED = "PREFERENCE_CONTACTS_BACKUP_ENABLED";
|
|
||||||
public static final String PREFERENCE_CALENDAR_BACKUP_ENABLED = "PREFERENCE_CALENDAR_BACKUP_ENABLED";
|
|
||||||
|
|
||||||
|
|
||||||
private BackupFragmentBinding binding;
|
|
||||||
|
|
||||||
@Inject BackgroundJobManager backgroundJobManager;
|
|
||||||
@Inject ThemeUtils themeUtils;
|
|
||||||
|
|
||||||
@Inject ArbitraryDataProvider arbitraryDataProvider;
|
|
||||||
@Inject ViewThemeUtils viewThemeUtils;
|
|
||||||
|
|
||||||
private Date selectedDate;
|
|
||||||
private boolean calendarPickerOpen;
|
|
||||||
|
|
||||||
private DatePickerDialog datePickerDialog;
|
|
||||||
|
|
||||||
private CompoundButton.OnCheckedChangeListener dailyBackupCheckedChangeListener;
|
|
||||||
private CompoundButton.OnCheckedChangeListener contactsCheckedListener;
|
|
||||||
private CompoundButton.OnCheckedChangeListener calendarCheckedListener;
|
|
||||||
private User user;
|
|
||||||
private boolean showSidebar = true;
|
|
||||||
//flag to check if calendar backup should be shown and backup should be done or not
|
|
||||||
private boolean showCalendarBackup = true;
|
|
||||||
public static BackupFragment create(boolean showSidebar) {
|
|
||||||
BackupFragment fragment = new BackupFragment();
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putBoolean(ARG_SHOW_SIDEBAR, showSidebar);
|
|
||||||
fragment.setArguments(bundle);
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCalendarBackupEnabled() {
|
|
||||||
return arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CALENDAR_BACKUP_ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCalendarBackupEnabled(final boolean enabled) {
|
|
||||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREFERENCE_CALENDAR_BACKUP_ENABLED, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isContactsBackupEnabled() {
|
|
||||||
return arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_BACKUP_ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setContactsBackupEnabled(final boolean enabled) {
|
|
||||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREFERENCE_CONTACTS_BACKUP_ENABLED, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
|
|
||||||
// use grey as fallback for elements where custom theming is not available
|
|
||||||
if (themeUtils.themingEnabled(getContext())) {
|
|
||||||
getContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
binding = BackupFragmentBinding.inflate(inflater, container, false);
|
|
||||||
View view = binding.getRoot();
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
if (getArguments() != null) {
|
|
||||||
showSidebar = getArguments().getBoolean(ARG_SHOW_SIDEBAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
showCalendarBackup = requireContext().getResources().getBoolean(R.bool.show_calendar_backup);
|
|
||||||
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
user = contactsPreferenceActivity.getUser().orElseThrow(RuntimeException::new);
|
|
||||||
|
|
||||||
ActionBar actionBar = contactsPreferenceActivity != null ? contactsPreferenceActivity.getSupportActionBar() : null;
|
|
||||||
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
|
||||||
viewThemeUtils.files.themeActionBar(requireContext(), actionBar,
|
|
||||||
showCalendarBackup ? R.string.backup_title : R.string.contact_backup_title);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
viewThemeUtils.androidx.colorSwitchCompat(binding.contacts);
|
|
||||||
viewThemeUtils.androidx.colorSwitchCompat(binding.calendar);
|
|
||||||
viewThemeUtils.androidx.colorSwitchCompat(binding.dailyBackup);
|
|
||||||
binding.dailyBackup.setChecked(arbitraryDataProvider.getBooleanValue(user,
|
|
||||||
PREFERENCE_CONTACTS_AUTOMATIC_BACKUP));
|
|
||||||
|
|
||||||
binding.contacts.setChecked(isContactsBackupEnabled() && checkContactBackupPermission());
|
|
||||||
binding.calendar.setChecked(isCalendarBackupEnabled() && checkCalendarBackupPermission(getContext()));
|
|
||||||
|
|
||||||
binding.calendar.setVisibility(showCalendarBackup ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
setupCheckListeners();
|
|
||||||
|
|
||||||
setBackupNowButtonVisibility();
|
|
||||||
|
|
||||||
binding.backupNow.setOnClickListener(v -> backupNow());
|
|
||||||
|
|
||||||
binding.contactsDatepicker.setOnClickListener(v -> openCleanDate());
|
|
||||||
|
|
||||||
// display last backup
|
|
||||||
Long lastBackupTimestamp = arbitraryDataProvider.getLongValue(user, PREFERENCE_CONTACTS_LAST_BACKUP);
|
|
||||||
|
|
||||||
if (lastBackupTimestamp == -1) {
|
|
||||||
binding.lastBackupWithDate.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
binding.lastBackupWithDate.setText(
|
|
||||||
String.format(getString(R.string.last_backup),
|
|
||||||
DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState != null && savedInstanceState.getBoolean(KEY_CALENDAR_PICKER_OPEN, false)) {
|
|
||||||
if (savedInstanceState.getInt(KEY_CALENDAR_YEAR, -1) != -1 &&
|
|
||||||
savedInstanceState.getInt(KEY_CALENDAR_MONTH, -1) != -1 &&
|
|
||||||
savedInstanceState.getInt(KEY_CALENDAR_DAY, -1) != -1) {
|
|
||||||
selectedDate = new Date(savedInstanceState.getInt(KEY_CALENDAR_YEAR),
|
|
||||||
savedInstanceState.getInt(KEY_CALENDAR_MONTH), savedInstanceState.getInt(KEY_CALENDAR_DAY));
|
|
||||||
}
|
|
||||||
calendarPickerOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.backupNow);
|
|
||||||
viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.contactsDatepicker);
|
|
||||||
|
|
||||||
viewThemeUtils.platform.colorTextView(binding.dataToBackUpTitle);
|
|
||||||
viewThemeUtils.platform.colorTextView(binding.backupSettingsTitle);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupCheckListeners() {
|
|
||||||
dailyBackupCheckedChangeListener = (buttonView, isChecked) -> {
|
|
||||||
if (checkAndAskForContactsReadPermission()) {
|
|
||||||
setAutomaticBackup(isChecked);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
binding.dailyBackup.setOnCheckedChangeListener(dailyBackupCheckedChangeListener);
|
|
||||||
|
|
||||||
|
|
||||||
contactsCheckedListener = (buttonView, isChecked) -> {
|
|
||||||
if (isChecked) {
|
|
||||||
if (checkAndAskForContactsReadPermission()) {
|
|
||||||
setContactsBackupEnabled(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setContactsBackupEnabled(false);
|
|
||||||
}
|
|
||||||
setBackupNowButtonVisibility();
|
|
||||||
setAutomaticBackup(binding.dailyBackup.isChecked());
|
|
||||||
};
|
|
||||||
binding.contacts.setOnCheckedChangeListener(contactsCheckedListener);
|
|
||||||
|
|
||||||
calendarCheckedListener = (buttonView, isChecked) -> {
|
|
||||||
if (isChecked) {
|
|
||||||
if (checkAndAskForCalendarReadPermission()) {
|
|
||||||
setCalendarBackupEnabled(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setCalendarBackupEnabled(false);
|
|
||||||
}
|
|
||||||
setBackupNowButtonVisibility();
|
|
||||||
setAutomaticBackup(binding.dailyBackup.isChecked());
|
|
||||||
};
|
|
||||||
binding.calendar.setOnCheckedChangeListener(calendarCheckedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setBackupNowButtonVisibility() {
|
|
||||||
if (binding.contacts.isChecked() || binding.calendar.isChecked()) {
|
|
||||||
binding.backupNow.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
binding.backupNow.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
if (calendarPickerOpen) {
|
|
||||||
if (selectedDate != null) {
|
|
||||||
openDate(selectedDate);
|
|
||||||
} else {
|
|
||||||
openDate(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
if (contactsPreferenceActivity != null) {
|
|
||||||
String backupFolderPath = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
|
|
||||||
refreshBackupFolder(backupFolderPath,
|
|
||||||
contactsPreferenceActivity.getApplicationContext(),
|
|
||||||
contactsPreferenceActivity.getStorageManager());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshBackupFolder(final String backupFolderPath,
|
|
||||||
final Context context,
|
|
||||||
final FileDataStorageManager storageManager) {
|
|
||||||
AsyncTask<String, Integer, Boolean> task = new AsyncTask<String, Integer, Boolean>() {
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(String... path) {
|
|
||||||
OCFile folder = storageManager.getFileByPath(path[0]);
|
|
||||||
|
|
||||||
if (folder != null) {
|
|
||||||
RefreshFolderOperation operation = new RefreshFolderOperation(folder, System.currentTimeMillis(),
|
|
||||||
false, false, storageManager, user, context);
|
|
||||||
|
|
||||||
RemoteOperationResult result = operation.execute(user, context);
|
|
||||||
return result.isSuccess();
|
|
||||||
} else {
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
if (result && binding != null) {
|
|
||||||
OCFile backupFolder = storageManager.getFileByPath(backupFolderPath);
|
|
||||||
|
|
||||||
List<OCFile> backupFiles = storageManager
|
|
||||||
.getFolderContent(backupFolder, false);
|
|
||||||
|
|
||||||
Collections.sort(backupFiles, new AlphanumComparator<>());
|
|
||||||
|
|
||||||
if (backupFiles == null || backupFiles.isEmpty()) {
|
|
||||||
binding.contactsDatepicker.setVisibility(View.INVISIBLE);
|
|
||||||
} else {
|
|
||||||
binding.contactsDatepicker.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
task.execute(backupFolderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
|
|
||||||
boolean retval;
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case android.R.id.home:
|
|
||||||
if (showSidebar) {
|
|
||||||
if (contactsPreferenceActivity.isDrawerOpen()) {
|
|
||||||
contactsPreferenceActivity.closeDrawer();
|
|
||||||
} else {
|
|
||||||
contactsPreferenceActivity.openDrawer();
|
|
||||||
}
|
|
||||||
} else if (getActivity() != null) {
|
|
||||||
getActivity().finish();
|
|
||||||
} else {
|
|
||||||
Intent settingsIntent = new Intent(getContext(), SettingsActivity.class);
|
|
||||||
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
startActivity(settingsIntent);
|
|
||||||
}
|
|
||||||
retval = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
retval = super.onOptionsItemSelected(item);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
|
|
||||||
if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC) {
|
|
||||||
for (int index = 0; index < permissions.length; index++) {
|
|
||||||
if (Manifest.permission.READ_CONTACTS.equalsIgnoreCase(permissions[index])) {
|
|
||||||
if (grantResults[index] >= 0) {
|
|
||||||
// if approved, exit for loop
|
|
||||||
setContactsBackupEnabled(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not accepted, disable again
|
|
||||||
binding.contacts.setOnCheckedChangeListener(null);
|
|
||||||
binding.contacts.setChecked(false);
|
|
||||||
binding.contacts.setOnCheckedChangeListener(contactsCheckedListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestCode == PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC) {
|
|
||||||
boolean readGranted = false;
|
|
||||||
boolean writeGranted = false;
|
|
||||||
for (int index = 0; index < permissions.length; index++) {
|
|
||||||
if (Manifest.permission.WRITE_CALENDAR.equalsIgnoreCase(permissions[index]) && grantResults[index] >= 0) {
|
|
||||||
writeGranted = true;
|
|
||||||
} else if (Manifest.permission.READ_CALENDAR.equalsIgnoreCase(permissions[index]) && grantResults[index] >= 0) {
|
|
||||||
readGranted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!readGranted || !writeGranted) {
|
|
||||||
// if not accepted, disable again
|
|
||||||
binding.calendar.setOnCheckedChangeListener(null);
|
|
||||||
binding.calendar.setChecked(false);
|
|
||||||
binding.calendar.setOnCheckedChangeListener(calendarCheckedListener);
|
|
||||||
} else {
|
|
||||||
setCalendarBackupEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setBackupNowButtonVisibility();
|
|
||||||
setAutomaticBackup(binding.dailyBackup.isChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void backupNow() {
|
|
||||||
if (isContactsBackupEnabled() && checkContactBackupPermission()) {
|
|
||||||
startContactsBackupJob();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showCalendarBackup && isCalendarBackupEnabled() && checkCalendarBackupPermission(requireContext())) {
|
|
||||||
startCalendarBackupJob();
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayUtils.showSnackMessage(requireView().findViewById(R.id.contacts_linear_layout),
|
|
||||||
R.string.contacts_preferences_backup_scheduled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startContactsBackupJob() {
|
|
||||||
ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
Optional<User> optionalUser = activity.getUser();
|
|
||||||
if (optionalUser.isPresent()) {
|
|
||||||
backgroundJobManager.startImmediateContactsBackup(optionalUser.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startCalendarBackupJob() {
|
|
||||||
ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
Optional<User> optionalUser = activity.getUser();
|
|
||||||
if (optionalUser.isPresent()) {
|
|
||||||
backgroundJobManager.startImmediateCalendarBackup(optionalUser.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAutomaticBackup(final boolean enabled) {
|
|
||||||
|
|
||||||
final ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
if (activity == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Optional<User> optionalUser = activity.getUser();
|
|
||||||
if (!optionalUser.isPresent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
User user = optionalUser.get();
|
|
||||||
if (enabled) {
|
|
||||||
if (isContactsBackupEnabled()) {
|
|
||||||
Log_OC.d(TAG, "Scheduling contacts backup job");
|
|
||||||
backgroundJobManager.schedulePeriodicContactsBackup(user);
|
|
||||||
} else {
|
|
||||||
Log_OC.d(TAG, "Cancelling contacts backup job");
|
|
||||||
backgroundJobManager.cancelPeriodicContactsBackup(user);
|
|
||||||
}
|
|
||||||
if (isCalendarBackupEnabled()) {
|
|
||||||
Log_OC.d(TAG, "Scheduling calendar backup job");
|
|
||||||
backgroundJobManager.schedulePeriodicCalendarBackup(user);
|
|
||||||
} else {
|
|
||||||
Log_OC.d(TAG, "Cancelling calendar backup job");
|
|
||||||
backgroundJobManager.cancelPeriodicCalendarBackup(user);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log_OC.d(TAG, "Cancelling all backup jobs");
|
|
||||||
backgroundJobManager.cancelPeriodicContactsBackup(user);
|
|
||||||
backgroundJobManager.cancelPeriodicCalendarBackup(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
|
|
||||||
PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
|
|
||||||
String.valueOf(enabled));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkAndAskForContactsReadPermission() {
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
|
|
||||||
// check permissions
|
|
||||||
if (PermissionUtil.checkSelfPermission(contactsPreferenceActivity, Manifest.permission.READ_CONTACTS)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// No explanation needed, request the permission.
|
|
||||||
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
|
|
||||||
PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkAndAskForCalendarReadPermission() {
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
|
|
||||||
// check permissions
|
|
||||||
if (checkCalendarBackupPermission(contactsPreferenceActivity)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// No explanation needed, request the permission.
|
|
||||||
requestPermissions(new String[]{Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR},
|
|
||||||
PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkCalendarBackupPermission(final Context context) {
|
|
||||||
return PermissionUtil.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) && PermissionUtil.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkContactBackupPermission() {
|
|
||||||
return PermissionUtil.checkSelfPermission(getContext(), Manifest.permission.READ_CONTACTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openCleanDate() {
|
|
||||||
if (checkAndAskForCalendarReadPermission() && checkAndAskForContactsReadPermission()) {
|
|
||||||
openDate(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openDate(@Nullable Date savedDate) {
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
|
|
||||||
if (contactsPreferenceActivity == null) {
|
|
||||||
Toast.makeText(getContext(), getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String contactsBackupFolderString =
|
|
||||||
getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
|
|
||||||
String calendarBackupFolderString =
|
|
||||||
getResources().getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR;
|
|
||||||
|
|
||||||
FileDataStorageManager storageManager = contactsPreferenceActivity.getStorageManager();
|
|
||||||
|
|
||||||
OCFile contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString);
|
|
||||||
OCFile calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString);
|
|
||||||
|
|
||||||
List<OCFile> backupFiles = storageManager.getFolderContent(contactsBackupFolder, false);
|
|
||||||
backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false));
|
|
||||||
|
|
||||||
Collections.sort(backupFiles, (o1, o2) -> {
|
|
||||||
return Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp());
|
|
||||||
});
|
|
||||||
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
int year;
|
|
||||||
int month;
|
|
||||||
int day;
|
|
||||||
|
|
||||||
if (savedDate == null) {
|
|
||||||
year = cal.get(Calendar.YEAR);
|
|
||||||
month = cal.get(Calendar.MONTH) + 1;
|
|
||||||
day = cal.get(Calendar.DAY_OF_MONTH);
|
|
||||||
} else {
|
|
||||||
year = savedDate.getYear();
|
|
||||||
month = savedDate.getMonth();
|
|
||||||
day = savedDate.getDay();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backupFiles.size() > 0 && backupFiles.get(backupFiles.size() - 1) != null) {
|
|
||||||
datePickerDialog = new DatePickerDialog(contactsPreferenceActivity, this, year, month, day);
|
|
||||||
datePickerDialog.getDatePicker().setMaxDate(backupFiles.get(backupFiles.size() - 1)
|
|
||||||
.getModificationTimestamp());
|
|
||||||
datePickerDialog.getDatePicker().setMinDate(backupFiles.get(0).getModificationTimestamp());
|
|
||||||
|
|
||||||
datePickerDialog.setOnDismissListener(dialog -> selectedDate = null);
|
|
||||||
|
|
||||||
datePickerDialog.setTitle("");
|
|
||||||
datePickerDialog.show();
|
|
||||||
|
|
||||||
viewThemeUtils.platform.colorTextButtons(datePickerDialog.getButton(DatePickerDialog.BUTTON_NEGATIVE),
|
|
||||||
datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE));
|
|
||||||
|
|
||||||
// set background to transparent
|
|
||||||
datePickerDialog.getButton(DatePickerDialog.BUTTON_NEGATIVE).setBackgroundColor(0x00000000);
|
|
||||||
datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).setBackgroundColor(0x00000000);
|
|
||||||
} else {
|
|
||||||
DisplayUtils.showSnackMessage(getView().findViewById(R.id.contacts_linear_layout),
|
|
||||||
R.string.contacts_preferences_something_strange_happened);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
binding = null;
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
if (datePickerDialog != null) {
|
|
||||||
datePickerDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
if (datePickerDialog != null) {
|
|
||||||
outState.putBoolean(KEY_CALENDAR_PICKER_OPEN, datePickerDialog.isShowing());
|
|
||||||
|
|
||||||
if (datePickerDialog.isShowing()) {
|
|
||||||
outState.putInt(KEY_CALENDAR_DAY, datePickerDialog.getDatePicker().getDayOfMonth());
|
|
||||||
outState.putInt(KEY_CALENDAR_MONTH, datePickerDialog.getDatePicker().getMonth());
|
|
||||||
outState.putInt(KEY_CALENDAR_YEAR, datePickerDialog.getDatePicker().getYear());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
|
|
||||||
final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
|
|
||||||
|
|
||||||
if (contactsPreferenceActivity == null) {
|
|
||||||
Toast.makeText(getContext(), getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedDate = new Date(year, month, dayOfMonth);
|
|
||||||
|
|
||||||
String contactsBackupFolderString =
|
|
||||||
getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
|
|
||||||
String calendarBackupFolderString =
|
|
||||||
getResources().getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR;
|
|
||||||
|
|
||||||
FileDataStorageManager storageManager = contactsPreferenceActivity.getStorageManager();
|
|
||||||
|
|
||||||
OCFile contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString);
|
|
||||||
OCFile calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString);
|
|
||||||
|
|
||||||
List<OCFile> backupFiles = storageManager.getFolderContent(contactsBackupFolder, false);
|
|
||||||
backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false));
|
|
||||||
|
|
||||||
// find file with modification with date and time between 00:00 and 23:59
|
|
||||||
// if more than one file exists, take oldest
|
|
||||||
Calendar date = Calendar.getInstance();
|
|
||||||
date.set(year, month, dayOfMonth);
|
|
||||||
|
|
||||||
// start
|
|
||||||
date.set(Calendar.HOUR, 0);
|
|
||||||
date.set(Calendar.MINUTE, 0);
|
|
||||||
date.set(Calendar.SECOND, 1);
|
|
||||||
date.set(Calendar.MILLISECOND, 0);
|
|
||||||
date.set(Calendar.AM_PM, Calendar.AM);
|
|
||||||
long start = date.getTimeInMillis();
|
|
||||||
|
|
||||||
// end
|
|
||||||
date.set(Calendar.HOUR, 23);
|
|
||||||
date.set(Calendar.MINUTE, 59);
|
|
||||||
date.set(Calendar.SECOND, 59);
|
|
||||||
long end = date.getTimeInMillis();
|
|
||||||
|
|
||||||
OCFile contactsBackupToRestore = null;
|
|
||||||
List<OCFile> calendarBackupsToRestore = new ArrayList<>();
|
|
||||||
|
|
||||||
for (OCFile file : backupFiles) {
|
|
||||||
if (start < file.getModificationTimestamp() && end > file.getModificationTimestamp()) {
|
|
||||||
// contact
|
|
||||||
if (MimeTypeUtil.isVCard(file)) {
|
|
||||||
if (contactsBackupToRestore == null) {
|
|
||||||
contactsBackupToRestore = file;
|
|
||||||
} else if (contactsBackupToRestore.getModificationTimestamp() < file.getModificationTimestamp()) {
|
|
||||||
contactsBackupToRestore = file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calendars
|
|
||||||
if (showCalendarBackup && MimeTypeUtil.isCalendar(file)) {
|
|
||||||
calendarBackupsToRestore.add(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<OCFile> backupToRestore = new ArrayList<>();
|
|
||||||
|
|
||||||
if (contactsBackupToRestore != null) {
|
|
||||||
backupToRestore.add(contactsBackupToRestore);
|
|
||||||
}
|
|
||||||
|
|
||||||
backupToRestore.addAll(calendarBackupsToRestore);
|
|
||||||
|
|
||||||
|
|
||||||
if (backupToRestore.isEmpty()) {
|
|
||||||
DisplayUtils.showSnackMessage(getView().findViewById(R.id.contacts_linear_layout),
|
|
||||||
R.string.contacts_preferences_no_file_found);
|
|
||||||
} else {
|
|
||||||
final User user = contactsPreferenceActivity.getUser().orElseThrow(RuntimeException::new);
|
|
||||||
OCFile[] files = new OCFile[backupToRestore.size()];
|
|
||||||
Fragment contactListFragment = BackupListFragment.newInstance(backupToRestore.toArray(files), user);
|
|
||||||
|
|
||||||
contactsPreferenceActivity.getSupportFragmentManager().
|
|
||||||
beginTransaction()
|
|
||||||
.replace(R.id.frame_container, contactListFragment, BackupListFragment.TAG)
|
|
||||||
.addToBackStack(ContactsPreferenceActivity.BACKUP_TO_LIST)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,726 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Mario Danic
|
||||||
|
* @author TSI-mc
|
||||||
|
* Copyright (C) 2017 Mario Danic
|
||||||
|
* Copyright (C) 2017 Nextcloud GmbH.
|
||||||
|
* Copyright (C) 2023 TSI-mc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.fragment.contactsbackup
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.DatePickerDialog
|
||||||
|
import android.app.DatePickerDialog.OnDateSetListener
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import android.widget.DatePicker
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.nextcloud.client.account.User
|
||||||
|
import com.nextcloud.client.di.Injectable
|
||||||
|
import com.nextcloud.client.jobs.BackgroundJobManager
|
||||||
|
import com.owncloud.android.R
|
||||||
|
import com.owncloud.android.databinding.BackupFragmentBinding
|
||||||
|
import com.owncloud.android.datamodel.ArbitraryDataProvider
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.lib.common.utils.Log_OC
|
||||||
|
import com.owncloud.android.operations.RefreshFolderOperation
|
||||||
|
import com.owncloud.android.ui.activity.ContactsPreferenceActivity
|
||||||
|
import com.owncloud.android.ui.activity.SettingsActivity
|
||||||
|
import com.owncloud.android.ui.fragment.FileFragment
|
||||||
|
import com.owncloud.android.utils.DisplayUtils
|
||||||
|
import com.owncloud.android.utils.MimeTypeUtil
|
||||||
|
import com.owncloud.android.utils.PermissionUtil
|
||||||
|
import com.owncloud.android.utils.PermissionUtil.checkSelfPermission
|
||||||
|
import com.owncloud.android.utils.theme.ThemeUtils
|
||||||
|
import com.owncloud.android.utils.theme.ViewThemeUtils
|
||||||
|
import third_parties.daveKoeller.AlphanumComparator
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.Date
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("TooManyFunctions")
|
||||||
|
class BackupFragment : FileFragment(), OnDateSetListener, Injectable {
|
||||||
|
private lateinit var binding: BackupFragmentBinding
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var backgroundJobManager: BackgroundJobManager? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var themeUtils: ThemeUtils? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var arbitraryDataProvider: ArbitraryDataProvider? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var viewThemeUtils: ViewThemeUtils? = null
|
||||||
|
|
||||||
|
private var selectedDate: Date? = null
|
||||||
|
private var calendarPickerOpen = false
|
||||||
|
private var datePickerDialog: DatePickerDialog? = null
|
||||||
|
private var contactsCheckedListener: CompoundButton.OnCheckedChangeListener? = null
|
||||||
|
private var calendarCheckedListener: CompoundButton.OnCheckedChangeListener? = null
|
||||||
|
private var user: User? = null
|
||||||
|
private var showSidebar = true
|
||||||
|
|
||||||
|
// flag to check if calendar backup should be shown and backup should be done or not
|
||||||
|
private var showCalendarBackup = true
|
||||||
|
private var isCalendarBackupEnabled: Boolean
|
||||||
|
get() = user?.let { arbitraryDataProvider?.getBooleanValue(it, PREFERENCE_CALENDAR_BACKUP_ENABLED) } ?: false
|
||||||
|
private set(enabled) {
|
||||||
|
arbitraryDataProvider!!.storeOrUpdateKeyValue(
|
||||||
|
user!!.accountName,
|
||||||
|
PREFERENCE_CALENDAR_BACKUP_ENABLED,
|
||||||
|
enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isContactsBackupEnabled: Boolean
|
||||||
|
get() = user?.let { arbitraryDataProvider?.getBooleanValue(it, PREFERENCE_CONTACTS_BACKUP_ENABLED) } ?: false
|
||||||
|
private set(enabled) {
|
||||||
|
arbitraryDataProvider!!.storeOrUpdateKeyValue(
|
||||||
|
user!!.accountName,
|
||||||
|
PREFERENCE_CONTACTS_BACKUP_ENABLED,
|
||||||
|
enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
// use grey as fallback for elements where custom theming is not available
|
||||||
|
if (themeUtils?.themingEnabled(context) == true) {
|
||||||
|
requireContext().theme.applyStyle(R.style.FallbackThemingTheme, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding = BackupFragmentBinding.inflate(inflater, container, false)
|
||||||
|
val view: View = binding.root
|
||||||
|
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
|
if (arguments != null) {
|
||||||
|
showSidebar = requireArguments().getBoolean(ARG_SHOW_SIDEBAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
showCalendarBackup = requireContext().resources.getBoolean(R.bool.show_calendar_backup)
|
||||||
|
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
user = contactsPreferenceActivity?.user?.orElseThrow { RuntimeException() }
|
||||||
|
|
||||||
|
setupSwitches(user)
|
||||||
|
|
||||||
|
setupCheckListeners()
|
||||||
|
setBackupNowButtonVisibility()
|
||||||
|
|
||||||
|
setOnClickListeners()
|
||||||
|
|
||||||
|
contactsPreferenceActivity?.let {
|
||||||
|
displayLastBackup(it)
|
||||||
|
applyUserColorToActionBar(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDates(savedInstanceState)
|
||||||
|
applyUserColor()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSwitches(user: User?) {
|
||||||
|
user?.let {
|
||||||
|
binding.dailyBackup.isChecked = arbitraryDataProvider?.getBooleanValue(
|
||||||
|
it,
|
||||||
|
ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP
|
||||||
|
) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.contacts.isChecked = isContactsBackupEnabled && checkContactBackupPermission()
|
||||||
|
binding.calendar.isChecked = isCalendarBackupEnabled && checkCalendarBackupPermission(requireContext())
|
||||||
|
binding.calendar.visibility = if (showCalendarBackup) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupCheckListeners() {
|
||||||
|
binding.dailyBackup.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||||
|
if (checkAndAskForContactsReadPermission()) {
|
||||||
|
setAutomaticBackup(isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initContactsCheckedListener()
|
||||||
|
binding.contacts.setOnCheckedChangeListener(contactsCheckedListener)
|
||||||
|
|
||||||
|
initCalendarCheckedListener()
|
||||||
|
binding.calendar.setOnCheckedChangeListener(calendarCheckedListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initContactsCheckedListener() {
|
||||||
|
contactsCheckedListener =
|
||||||
|
CompoundButton.OnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||||
|
if (isChecked) {
|
||||||
|
if (checkAndAskForContactsReadPermission()) {
|
||||||
|
isContactsBackupEnabled = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isContactsBackupEnabled = false
|
||||||
|
}
|
||||||
|
setBackupNowButtonVisibility()
|
||||||
|
setAutomaticBackup(binding.dailyBackup.isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initCalendarCheckedListener() {
|
||||||
|
calendarCheckedListener =
|
||||||
|
CompoundButton.OnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||||
|
if (isChecked) {
|
||||||
|
if (checkAndAskForCalendarReadPermission()) {
|
||||||
|
isCalendarBackupEnabled = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isCalendarBackupEnabled = false
|
||||||
|
}
|
||||||
|
setBackupNowButtonVisibility()
|
||||||
|
setAutomaticBackup(binding.dailyBackup.isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBackupNowButtonVisibility() {
|
||||||
|
binding.backupNow.visibility =
|
||||||
|
if (binding.contacts.isChecked || binding.calendar.isChecked) View.VISIBLE else View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setOnClickListeners() {
|
||||||
|
binding.backupNow.setOnClickListener { backupNow() }
|
||||||
|
binding.contactsDatepicker.setOnClickListener { openCleanDate() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayLastBackup(contactsPreferenceActivity: ContactsPreferenceActivity) {
|
||||||
|
val lastBackupTimestamp = user?.let {
|
||||||
|
arbitraryDataProvider?.getLongValue(
|
||||||
|
it,
|
||||||
|
ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP
|
||||||
|
)
|
||||||
|
} ?: -1L
|
||||||
|
|
||||||
|
if (lastBackupTimestamp == -1L) {
|
||||||
|
binding.lastBackupWithDate.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.lastBackupWithDate.text = String.format(
|
||||||
|
getString(R.string.last_backup),
|
||||||
|
DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyUserColorToActionBar(contactsPreferenceActivity: ContactsPreferenceActivity) {
|
||||||
|
val actionBar = contactsPreferenceActivity.supportActionBar
|
||||||
|
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true)
|
||||||
|
viewThemeUtils?.files?.themeActionBar(
|
||||||
|
requireContext(),
|
||||||
|
actionBar,
|
||||||
|
if (showCalendarBackup) R.string.backup_title else R.string.contact_backup_title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupDates(savedInstanceState: Bundle?) {
|
||||||
|
if (savedInstanceState != null && savedInstanceState.getBoolean(KEY_CALENDAR_PICKER_OPEN, false)) {
|
||||||
|
if (savedInstanceState.getInt(KEY_CALENDAR_YEAR, -1) != -1 && savedInstanceState.getInt(
|
||||||
|
KEY_CALENDAR_MONTH,
|
||||||
|
-1
|
||||||
|
) != -1 && savedInstanceState.getInt(
|
||||||
|
KEY_CALENDAR_DAY, -1
|
||||||
|
) != -1
|
||||||
|
) {
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
cal[Calendar.YEAR] = savedInstanceState.getInt(KEY_CALENDAR_YEAR)
|
||||||
|
cal[Calendar.MONTH] = savedInstanceState.getInt(KEY_CALENDAR_MONTH)
|
||||||
|
cal[Calendar.DAY_OF_MONTH] = savedInstanceState.getInt(KEY_CALENDAR_DAY)
|
||||||
|
selectedDate = cal.time
|
||||||
|
}
|
||||||
|
calendarPickerOpen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyUserColor() {
|
||||||
|
viewThemeUtils?.androidx?.colorSwitchCompat(binding.contacts)
|
||||||
|
viewThemeUtils?.androidx?.colorSwitchCompat(binding.calendar)
|
||||||
|
viewThemeUtils?.androidx?.colorSwitchCompat(binding.dailyBackup)
|
||||||
|
|
||||||
|
viewThemeUtils?.material?.colorMaterialButtonPrimaryFilled(binding.backupNow)
|
||||||
|
viewThemeUtils?.material?.colorMaterialButtonPrimaryOutlined(binding.contactsDatepicker)
|
||||||
|
|
||||||
|
viewThemeUtils?.platform?.colorTextView(binding.dataToBackUpTitle)
|
||||||
|
viewThemeUtils?.platform?.colorTextView(binding.backupSettingsTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
if (calendarPickerOpen) {
|
||||||
|
if (selectedDate != null) {
|
||||||
|
openDate(selectedDate)
|
||||||
|
} else {
|
||||||
|
openDate(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
if (contactsPreferenceActivity != null) {
|
||||||
|
val backupFolderPath = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
|
||||||
|
refreshBackupFolder(
|
||||||
|
backupFolderPath,
|
||||||
|
contactsPreferenceActivity.applicationContext,
|
||||||
|
contactsPreferenceActivity.storageManager
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshBackupFolder(
|
||||||
|
backupFolderPath: String,
|
||||||
|
context: Context,
|
||||||
|
storageManager: FileDataStorageManager
|
||||||
|
) {
|
||||||
|
val task: AsyncTask<String, Int, Boolean> =
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
object : AsyncTask<String, Int, Boolean>() {
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun doInBackground(vararg path: String): Boolean {
|
||||||
|
val folder = storageManager.getFileByPath(path[0])
|
||||||
|
return if (folder != null) {
|
||||||
|
val operation = RefreshFolderOperation(
|
||||||
|
folder,
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
storageManager,
|
||||||
|
user,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
val result = operation.execute(user, context)
|
||||||
|
result.isSuccess
|
||||||
|
} else {
|
||||||
|
java.lang.Boolean.FALSE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun onPostExecute(result: Boolean) {
|
||||||
|
if (result) {
|
||||||
|
val backupFolder = storageManager.getFileByPath(backupFolderPath)
|
||||||
|
val backupFiles = storageManager
|
||||||
|
.getFolderContent(backupFolder, false)
|
||||||
|
Collections.sort(backupFiles, AlphanumComparator())
|
||||||
|
if (backupFiles == null || backupFiles.isEmpty()) {
|
||||||
|
binding.contactsDatepicker.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
binding.contactsDatepicker.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task.execute(backupFolderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
|
||||||
|
val retval: Boolean
|
||||||
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
if (showSidebar) {
|
||||||
|
if (contactsPreferenceActivity!!.isDrawerOpen) {
|
||||||
|
contactsPreferenceActivity.closeDrawer()
|
||||||
|
} else {
|
||||||
|
contactsPreferenceActivity.openDrawer()
|
||||||
|
}
|
||||||
|
} else if (activity != null) {
|
||||||
|
requireActivity().finish()
|
||||||
|
} else {
|
||||||
|
val settingsIntent = Intent(context, SettingsActivity::class.java)
|
||||||
|
settingsIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
|
startActivity(settingsIntent)
|
||||||
|
}
|
||||||
|
retval = true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> retval = super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
@Suppress("NestedBlockDepth")
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
|
if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC) {
|
||||||
|
for (index in permissions.indices) {
|
||||||
|
if (Manifest.permission.READ_CONTACTS.equals(permissions[index], ignoreCase = true)) {
|
||||||
|
if (grantResults[index] >= 0) {
|
||||||
|
// if approved, exit for loop
|
||||||
|
isContactsBackupEnabled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not accepted, disable again
|
||||||
|
binding.contacts.setOnCheckedChangeListener(null)
|
||||||
|
binding.contacts.isChecked = false
|
||||||
|
binding.contacts.setOnCheckedChangeListener(contactsCheckedListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestCode == PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC) {
|
||||||
|
var readGranted = false
|
||||||
|
var writeGranted = false
|
||||||
|
for (index in permissions.indices) {
|
||||||
|
if (Manifest.permission.WRITE_CALENDAR.equals(
|
||||||
|
permissions[index],
|
||||||
|
ignoreCase = true
|
||||||
|
) && grantResults[index] >= 0
|
||||||
|
) {
|
||||||
|
writeGranted = true
|
||||||
|
} else if (Manifest.permission.READ_CALENDAR.equals(
|
||||||
|
permissions[index],
|
||||||
|
ignoreCase = true
|
||||||
|
) && grantResults[index] >= 0
|
||||||
|
) {
|
||||||
|
readGranted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!readGranted || !writeGranted) {
|
||||||
|
// if not accepted, disable again
|
||||||
|
binding.calendar.setOnCheckedChangeListener(null)
|
||||||
|
binding.calendar.isChecked = false
|
||||||
|
binding.calendar.setOnCheckedChangeListener(calendarCheckedListener)
|
||||||
|
} else {
|
||||||
|
isCalendarBackupEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setBackupNowButtonVisibility()
|
||||||
|
setAutomaticBackup(binding.dailyBackup.isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupNow() {
|
||||||
|
if (isContactsBackupEnabled && checkContactBackupPermission()) {
|
||||||
|
startContactsBackupJob()
|
||||||
|
}
|
||||||
|
if (showCalendarBackup && isCalendarBackupEnabled && checkCalendarBackupPermission(requireContext())) {
|
||||||
|
startCalendarBackupJob()
|
||||||
|
}
|
||||||
|
DisplayUtils.showSnackMessage(
|
||||||
|
requireView().findViewById<View>(R.id.contacts_linear_layout),
|
||||||
|
R.string.contacts_preferences_backup_scheduled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startContactsBackupJob() {
|
||||||
|
val activity = activity as ContactsPreferenceActivity?
|
||||||
|
if (activity != null) {
|
||||||
|
val optionalUser = activity.user
|
||||||
|
if (optionalUser.isPresent) {
|
||||||
|
backgroundJobManager!!.startImmediateContactsBackup(optionalUser.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startCalendarBackupJob() {
|
||||||
|
val activity = activity as ContactsPreferenceActivity?
|
||||||
|
if (activity != null) {
|
||||||
|
val optionalUser = activity.user
|
||||||
|
if (optionalUser.isPresent) {
|
||||||
|
backgroundJobManager!!.startImmediateCalendarBackup(optionalUser.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setAutomaticBackup(enabled: Boolean) {
|
||||||
|
val activity = activity as ContactsPreferenceActivity? ?: return
|
||||||
|
val optionalUser = activity.user
|
||||||
|
if (!optionalUser.isPresent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val user = optionalUser.get()
|
||||||
|
if (enabled) {
|
||||||
|
if (isContactsBackupEnabled) {
|
||||||
|
Log_OC.d(TAG, "Scheduling contacts backup job")
|
||||||
|
backgroundJobManager?.schedulePeriodicContactsBackup(user)
|
||||||
|
} else {
|
||||||
|
Log_OC.d(TAG, "Cancelling contacts backup job")
|
||||||
|
backgroundJobManager?.cancelPeriodicContactsBackup(user)
|
||||||
|
}
|
||||||
|
if (isCalendarBackupEnabled) {
|
||||||
|
Log_OC.d(TAG, "Scheduling calendar backup job")
|
||||||
|
backgroundJobManager?.schedulePeriodicCalendarBackup(user)
|
||||||
|
} else {
|
||||||
|
Log_OC.d(TAG, "Cancelling calendar backup job")
|
||||||
|
backgroundJobManager?.cancelPeriodicCalendarBackup(user)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log_OC.d(TAG, "Cancelling all backup jobs")
|
||||||
|
backgroundJobManager?.cancelPeriodicContactsBackup(user)
|
||||||
|
backgroundJobManager?.cancelPeriodicCalendarBackup(user)
|
||||||
|
}
|
||||||
|
arbitraryDataProvider?.storeOrUpdateKeyValue(
|
||||||
|
user.accountName,
|
||||||
|
ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
|
||||||
|
enabled.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAndAskForContactsReadPermission(): Boolean {
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
|
||||||
|
// check permissions
|
||||||
|
return if (checkSelfPermission(contactsPreferenceActivity!!, Manifest.permission.READ_CONTACTS)) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// No explanation needed, request the permission.
|
||||||
|
requestPermissions(
|
||||||
|
arrayOf(Manifest.permission.READ_CONTACTS),
|
||||||
|
PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC
|
||||||
|
)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAndAskForCalendarReadPermission(): Boolean {
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
|
||||||
|
// check permissions
|
||||||
|
return if (contactsPreferenceActivity?.let { checkCalendarBackupPermission(it) } == true) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// No explanation needed, request the permission.
|
||||||
|
requestPermissions(
|
||||||
|
arrayOf(
|
||||||
|
Manifest.permission.READ_CALENDAR,
|
||||||
|
Manifest.permission.WRITE_CALENDAR
|
||||||
|
),
|
||||||
|
PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC
|
||||||
|
)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCalendarBackupPermission(context: Context): Boolean {
|
||||||
|
return checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) && checkSelfPermission(
|
||||||
|
context, Manifest.permission.WRITE_CALENDAR
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkContactBackupPermission(): Boolean {
|
||||||
|
return checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openCleanDate() {
|
||||||
|
if (checkAndAskForCalendarReadPermission() && checkAndAskForContactsReadPermission()) {
|
||||||
|
openDate(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openDate(savedDate: Date?) {
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
if (contactsPreferenceActivity == null) {
|
||||||
|
Toast.makeText(context, getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val contactsBackupFolderString = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
|
||||||
|
val calendarBackupFolderString = resources.getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR
|
||||||
|
val storageManager = contactsPreferenceActivity.storageManager
|
||||||
|
val contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString)
|
||||||
|
val calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString)
|
||||||
|
|
||||||
|
val backupFiles = storageManager.getFolderContent(contactsBackupFolder, false)
|
||||||
|
backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false))
|
||||||
|
backupFiles.sortWith { o1: OCFile?, o2: OCFile? ->
|
||||||
|
if (o1 != null && o2 != null) {
|
||||||
|
o1.modificationTimestamp.compareTo(o2.modificationTimestamp)
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
val year: Int
|
||||||
|
val month: Int
|
||||||
|
val day: Int
|
||||||
|
if (savedDate == null) {
|
||||||
|
year = cal[Calendar.YEAR]
|
||||||
|
month = cal[Calendar.MONTH] + 1
|
||||||
|
day = cal[Calendar.DAY_OF_MONTH]
|
||||||
|
} else {
|
||||||
|
year = savedDate.year
|
||||||
|
month = savedDate.month
|
||||||
|
day = savedDate.day
|
||||||
|
}
|
||||||
|
if (backupFiles.size > 0 && backupFiles[backupFiles.size - 1] != null) {
|
||||||
|
datePickerDialog = DatePickerDialog(contactsPreferenceActivity, this, year, month, day)
|
||||||
|
datePickerDialog?.datePicker?.maxDate = backupFiles[backupFiles.size - 1]!!
|
||||||
|
.modificationTimestamp
|
||||||
|
datePickerDialog?.datePicker?.minDate = backupFiles[0]!!.modificationTimestamp
|
||||||
|
datePickerDialog?.setOnDismissListener { selectedDate = null }
|
||||||
|
datePickerDialog?.setTitle("")
|
||||||
|
datePickerDialog?.show()
|
||||||
|
|
||||||
|
viewThemeUtils?.platform?.colorTextButtons(
|
||||||
|
datePickerDialog!!.getButton(DatePickerDialog.BUTTON_NEGATIVE),
|
||||||
|
datePickerDialog!!.getButton(DatePickerDialog.BUTTON_POSITIVE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// set background to transparent
|
||||||
|
datePickerDialog?.getButton(DatePickerDialog.BUTTON_NEGATIVE)?.setBackgroundColor(0x00000000)
|
||||||
|
datePickerDialog?.getButton(DatePickerDialog.BUTTON_POSITIVE)?.setBackgroundColor(0x00000000)
|
||||||
|
} else {
|
||||||
|
DisplayUtils.showSnackMessage(
|
||||||
|
requireView().findViewById<View>(R.id.contacts_linear_layout),
|
||||||
|
R.string.contacts_preferences_something_strange_happened
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
|
||||||
|
datePickerDialog?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
|
||||||
|
datePickerDialog?.let {
|
||||||
|
outState.putBoolean(KEY_CALENDAR_PICKER_OPEN, it.isShowing)
|
||||||
|
|
||||||
|
if (it.isShowing) {
|
||||||
|
outState.putInt(KEY_CALENDAR_DAY, it.datePicker.dayOfMonth)
|
||||||
|
outState.putInt(KEY_CALENDAR_MONTH, it.datePicker.month)
|
||||||
|
outState.putInt(KEY_CALENDAR_YEAR, it.datePicker.year)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "ComplexMethod", "LongMethod", "MagicNumber")
|
||||||
|
override fun onDateSet(view: DatePicker, year: Int, month: Int, dayOfMonth: Int) {
|
||||||
|
val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
|
||||||
|
if (contactsPreferenceActivity == null) {
|
||||||
|
Toast.makeText(context, getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedDate = Date(year, month, dayOfMonth)
|
||||||
|
val contactsBackupFolderString = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
|
||||||
|
val calendarBackupFolderString = resources.getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR
|
||||||
|
val storageManager = contactsPreferenceActivity.storageManager
|
||||||
|
val contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString)
|
||||||
|
val calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString)
|
||||||
|
val backupFiles = storageManager.getFolderContent(contactsBackupFolder, false)
|
||||||
|
backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false))
|
||||||
|
|
||||||
|
// find file with modification with date and time between 00:00 and 23:59
|
||||||
|
// if more than one file exists, take oldest
|
||||||
|
val date = Calendar.getInstance()
|
||||||
|
date[year, month] = dayOfMonth
|
||||||
|
|
||||||
|
// start
|
||||||
|
date[Calendar.HOUR] = 0
|
||||||
|
date[Calendar.MINUTE] = 0
|
||||||
|
date[Calendar.SECOND] = 1
|
||||||
|
date[Calendar.MILLISECOND] = 0
|
||||||
|
date[Calendar.AM_PM] = Calendar.AM
|
||||||
|
val start = date.timeInMillis
|
||||||
|
|
||||||
|
// end
|
||||||
|
date[Calendar.HOUR] = 23
|
||||||
|
date[Calendar.MINUTE] = 59
|
||||||
|
date[Calendar.SECOND] = 59
|
||||||
|
val end = date.timeInMillis
|
||||||
|
var contactsBackupToRestore: OCFile? = null
|
||||||
|
val calendarBackupsToRestore: MutableList<OCFile> = ArrayList()
|
||||||
|
for (file in backupFiles) {
|
||||||
|
if (start < file.modificationTimestamp && end > file.modificationTimestamp) {
|
||||||
|
// contact
|
||||||
|
if (MimeTypeUtil.isVCard(file)) {
|
||||||
|
if (contactsBackupToRestore == null) {
|
||||||
|
contactsBackupToRestore = file
|
||||||
|
} else if (contactsBackupToRestore.modificationTimestamp < file.modificationTimestamp) {
|
||||||
|
contactsBackupToRestore = file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calendars
|
||||||
|
if (showCalendarBackup && MimeTypeUtil.isCalendar(file)) {
|
||||||
|
calendarBackupsToRestore.add(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val backupToRestore: MutableList<OCFile> = ArrayList()
|
||||||
|
if (contactsBackupToRestore != null) {
|
||||||
|
backupToRestore.add(contactsBackupToRestore)
|
||||||
|
}
|
||||||
|
backupToRestore.addAll(calendarBackupsToRestore)
|
||||||
|
if (backupToRestore.isEmpty()) {
|
||||||
|
DisplayUtils.showSnackMessage(
|
||||||
|
requireView().findViewById<View>(R.id.contacts_linear_layout),
|
||||||
|
R.string.contacts_preferences_no_file_found
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val user = contactsPreferenceActivity.user.orElseThrow { RuntimeException() }
|
||||||
|
val files: Array<OCFile?> = arrayOfNulls(backupToRestore.size)
|
||||||
|
|
||||||
|
val contactListFragment = BackupListFragment.newInstance(files, user)
|
||||||
|
|
||||||
|
contactsPreferenceActivity.supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.frame_container, contactListFragment, BackupListFragment.TAG)
|
||||||
|
.addToBackStack(ContactsPreferenceActivity.BACKUP_TO_LIST)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val TAG = BackupFragment::class.java.simpleName
|
||||||
|
private const val ARG_SHOW_SIDEBAR = "SHOW_SIDEBAR"
|
||||||
|
private const val KEY_CALENDAR_PICKER_OPEN = "IS_CALENDAR_PICKER_OPEN"
|
||||||
|
private const val KEY_CALENDAR_DAY = "CALENDAR_DAY"
|
||||||
|
private const val KEY_CALENDAR_MONTH = "CALENDAR_MONTH"
|
||||||
|
private const val KEY_CALENDAR_YEAR = "CALENDAR_YEAR"
|
||||||
|
const val PREFERENCE_CONTACTS_BACKUP_ENABLED = "PREFERENCE_CONTACTS_BACKUP_ENABLED"
|
||||||
|
const val PREFERENCE_CALENDAR_BACKUP_ENABLED = "PREFERENCE_CALENDAR_BACKUP_ENABLED"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun create(showSidebar: Boolean): BackupFragment {
|
||||||
|
val fragment = BackupFragment()
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putBoolean(ARG_SHOW_SIDEBAR, showSidebar)
|
||||||
|
fragment.arguments = bundle
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,8 +17,8 @@
|
||||||
You should have received a copy of the GNU Affero General Public
|
You should have received a copy of the GNU Affero General Public
|
||||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/contacts_linear_layout"
|
android:id="@+id/contacts_linear_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -30,14 +30,14 @@
|
||||||
android:layout_margin="@dimen/standard_margin"
|
android:layout_margin="@dimen/standard_margin"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/data_to_back_up_title"
|
android:id="@+id/data_to_back_up_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/data_to_back_up"
|
android:text="@string/data_to_back_up"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/contacts"
|
android:id="@+id/contacts"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -46,22 +46,21 @@
|
||||||
android:textColor="@color/text_color"
|
android:textColor="@color/text_color"
|
||||||
android:textSize="@dimen/two_line_primary_text_size" />
|
android:textSize="@dimen/two_line_primary_text_size" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/calendar"
|
android:id="@+id/calendar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp"
|
android:minHeight="48dp"
|
||||||
android:text="@string/calendar"
|
android:text="@string/calendar"
|
||||||
android:textColor="@color/text_color"
|
android:textColor="@color/text_color"
|
||||||
android:textSize="@dimen/two_line_primary_text_size"
|
android:textSize="@dimen/two_line_primary_text_size" />
|
||||||
/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/backup_settings_title"
|
android:id="@+id/backup_settings_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -73,7 +72,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp">
|
android:layout_height="48dp">
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/daily_backup"
|
android:id="@+id/daily_backup"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -89,14 +88,14 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/daily_backup"
|
android:text="@string/daily_backup"
|
||||||
android:textColor="@color/text_color"
|
android:textColor="@color/text_color"
|
||||||
android:textSize="@dimen/two_line_primary_text_size" />
|
android:textSize="@dimen/two_line_primary_text_size" />
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:id="@+id/last_backup_with_date"
|
android:id="@+id/last_backup_with_date"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -113,32 +112,27 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="@dimen/standard_margin"
|
android:layout_marginTop="@dimen/standard_margin"
|
||||||
android:gravity="center"
|
android:gravity="end"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal">
|
||||||
android:weightSum="1.0">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/contacts_datepicker"
|
android:id="@+id/contacts_datepicker"
|
||||||
style="@style/OutlinedButton"
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="@dimen/backup_button_width"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="0.5"
|
|
||||||
android:minHeight="@dimen/minimum_size_for_touchable_area"
|
android:minHeight="@dimen/minimum_size_for_touchable_area"
|
||||||
android:text="@string/restore_backup"
|
android:text="@string/restore_backup"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
app:cornerRadius="@dimen/button_corner_radius"
|
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/backup_now"
|
android:id="@+id/backup_now"
|
||||||
android:layout_width="0dp"
|
android:layout_width="@dimen/backup_button_width"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginStart="@dimen/standard_half_margin"
|
android:layout_marginStart="@dimen/standard_half_margin"
|
||||||
android:layout_weight="0.5"
|
|
||||||
android:minHeight="@dimen/minimum_size_for_touchable_area"
|
android:minHeight="@dimen/minimum_size_for_touchable_area"
|
||||||
android:text="@string/contacts_backup_button"
|
android:text="@string/contacts_backup_button"
|
||||||
android:theme="@style/Button.Primary"
|
android:theme="@style/Widget.Material3.Button.IconButton.Filled" />
|
||||||
app:cornerRadius="@dimen/button_corner_radius" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -660,6 +660,7 @@
|
||||||
<string name="share_no_password_title">Đặt mật khẩu</string>
|
<string name="share_no_password_title">Đặt mật khẩu</string>
|
||||||
<string name="share_password_title">Mật khẩu được bảo vệ</string>
|
<string name="share_password_title">Mật khẩu được bảo vệ</string>
|
||||||
<string name="share_permission_can_edit">Có thể chỉnh sửa</string>
|
<string name="share_permission_can_edit">Có thể chỉnh sửa</string>
|
||||||
|
<string name="share_permission_file_drop">Thả file</string>
|
||||||
<string name="share_permission_view_only">Chỉ xem</string>
|
<string name="share_permission_view_only">Chỉ xem</string>
|
||||||
<string name="share_permissions">Quyền kho</string>
|
<string name="share_permissions">Quyền kho</string>
|
||||||
<string name="share_remote_clarification">%1$s (từ xa)</string>
|
<string name="share_remote_clarification">%1$s (từ xa)</string>
|
||||||
|
|
|
@ -133,6 +133,8 @@
|
||||||
<dimen name="bottom_sheet_text_size">16sp</dimen>
|
<dimen name="bottom_sheet_text_size">16sp</dimen>
|
||||||
<dimen name="permission_dialog_text_size">18sp</dimen>
|
<dimen name="permission_dialog_text_size">18sp</dimen>
|
||||||
<dimen name="button_corner_radius">24dp</dimen>
|
<dimen name="button_corner_radius">24dp</dimen>
|
||||||
|
<dimen name="backup_button_width">160dp</dimen>
|
||||||
|
|
||||||
<integer name="media_grid_width">4</integer>
|
<integer name="media_grid_width">4</integer>
|
||||||
<dimen name="account_action_button_margin">12dp</dimen>
|
<dimen name="account_action_button_margin">12dp</dimen>
|
||||||
<dimen name="account_action_button_height">50dp</dimen>
|
<dimen name="account_action_button_height">50dp</dimen>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
DO NOT TOUCH; GENERATED BY DRONE
|
DO NOT TOUCH; GENERATED BY DRONE
|
||||||
<span class="mdl-layout-title">Lint Report: 80 warnings</span>
|
<span class="mdl-layout-title">Lint Report: 79 warnings</span>
|
||||||
|
|
Loading…
Reference in a new issue