/**
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2017 Tobias Kaminsky
* Copyright (C) 2017 Nextcloud GmbH.
*
* 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 .
*/
package com.owncloud.android.ui.activity;
import android.Manifest;
import android.accounts.Account;
import android.app.DatePickerDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.SwitchCompat;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.Toast;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.evernote.android.job.util.support.PersistableBundleCompat;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.services.ContactsBackupJob;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.PermissionUtil;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.Vector;
/**
* This activity shows all settings for contact backup/restore
*/
public class ContactsPreferenceActivity extends FileActivity implements FileFragment.ContainerActivity {
public static final String TAG = ContactsPreferenceActivity.class.getSimpleName();
public static final String PREFERENCE_CONTACTS_AUTOMATIC_BACKUP = "PREFERENCE_CONTACTS_AUTOMATIC_BACKUP";
public static final String PREFERENCE_CONTACTS_LAST_BACKUP = "PREFERENCE_CONTACTS_LAST_BACKUP";
private SwitchCompat backupSwitch;
private ArbitraryDataProvider arbitraryDataProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts_preference);
// setup toolbar
setupToolbar();
// setup drawer
setupDrawer(R.id.nav_contacts);
getSupportActionBar().setTitle(R.string.actionbar_contacts);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
backupSwitch = (SwitchCompat) findViewById(R.id.contacts_automatic_backup);
backupSwitch.setChecked(arbitraryDataProvider.getValue(getAccount(), PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)
.equals("true"));
backupSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked &&
checkAndAskForContactsReadPermission(PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC)) {
// store value
setAutomaticBackup(backupSwitch, true);
// enable daily job
startContactBackupJob(getAccount());
} else {
setAutomaticBackup(backupSwitch, false);
// cancel pending jobs
cancelContactBackupJob(getBaseContext());
}
}
});
// display last backup
TextView lastBackup = (TextView) findViewById(R.id.contacts_last_backup_timestamp);
Long lastBackupTimestamp = arbitraryDataProvider.getLongValue(getAccount(), PREFERENCE_CONTACTS_LAST_BACKUP);
if (lastBackupTimestamp == -1) {
lastBackup.setText(R.string.contacts_preference_backup_never);
} else {
lastBackup.setText(DisplayUtils.getRelativeTimestamp(getBaseContext(), lastBackupTimestamp));
}
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_view);
if (getResources().getBoolean(R.bool.bottom_toolbar_enabled)) {
bottomNavigationView.setVisibility(View.VISIBLE);
DisplayUtils.setupBottomBar(bottomNavigationView, getResources(), this, -1);
}
}
@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) {
setAutomaticBackup(backupSwitch, true);
} else {
setAutomaticBackup(backupSwitch, false);
}
break;
}
}
}
if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_MANUALLY) {
for (int index = 0; index < permissions.length; index++) {
if (Manifest.permission.READ_CONTACTS.equalsIgnoreCase(permissions[index])) {
if (grantResults[index] >= 0) {
startContactsBackupJob();
}
break;
}
}
}
}
public void backupContacts(View v) {
if (checkAndAskForContactsReadPermission(PermissionUtil.PERMISSIONS_READ_CONTACTS_MANUALLY)) {
startContactsBackupJob();
}
}
private void startContactsBackupJob() {
PersistableBundleCompat bundle = new PersistableBundleCompat();
bundle.putString(ContactsBackupJob.ACCOUNT, getAccount().name);
bundle.putBoolean(ContactsBackupJob.FORCE, true);
new JobRequest.Builder(ContactsBackupJob.TAG)
.setExtras(bundle)
.setExecutionWindow(3_000L, 10_000L)
.setRequiresCharging(false)
.setPersisted(false)
.setUpdateCurrent(false)
.build()
.schedule();
Snackbar.make(findViewById(R.id.contacts_linear_layout), R.string.contacts_preferences_backup_scheduled,
Snackbar.LENGTH_LONG).show();
}
private void setAutomaticBackup(SwitchCompat backupSwitch, boolean bool) {
backupSwitch.setChecked(bool);
arbitraryDataProvider.storeOrUpdateKeyValue(getAccount(),
PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, String.valueOf(bool));
}
private boolean checkAndAskForContactsReadPermission(final int permission) {
// check permissions
if ((PermissionUtil.checkSelfPermission(this, Manifest.permission.READ_CONTACTS))) {
return true;
} else {
// Check if we should show an explanation
if (PermissionUtil.shouldShowRequestPermissionRationale(ContactsPreferenceActivity.this,
android.Manifest.permission.READ_CONTACTS)) {
// Show explanation to the user and then request permission
Snackbar snackbar = Snackbar.make(findViewById(R.id.contacts_linear_layout),
R.string.contacts_read_permission, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.common_ok, new View.OnClickListener() {
@Override
public void onClick(View v) {
PermissionUtil.requestReadContactPermission(ContactsPreferenceActivity.this,
permission);
}
});
DisplayUtils.colorSnackbar(this, snackbar);
snackbar.show();
return false;
} else {
// No explanation needed, request the permission.
PermissionUtil.requestReadContactPermission(ContactsPreferenceActivity.this, permission);
return false;
}
}
}
public void openDate(View v) {
String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
OCFile backupFolder = getStorageManager().getFileByPath(backupFolderString);
Vector backupFiles = getStorageManager().getFolderContent(backupFolder, false);
Collections.sort(backupFiles, new Comparator() {
@Override
public int compare(OCFile o1, OCFile o2) {
if (o1.getModificationTimestamp() == o2.getModificationTimestamp()) {
return 0;
}
if (o1.getModificationTimestamp() > o2.getModificationTimestamp()) {
return 1;
} else {
return -1;
}
}
});
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
DatePickerDialog.OnDateSetListener dateSetListener = new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
OCFile backupFolder = getStorageManager().getFileByPath(backupFolderString);
Vector backupFiles = getStorageManager().getFolderContent(backupFolder, 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 backupToRestore = null;
for (OCFile file : backupFiles) {
if (start < file.getModificationTimestamp() && end > file.getModificationTimestamp()) {
if (backupToRestore == null) {
backupToRestore = file;
} else if (backupToRestore.getModificationTimestamp() < file.getModificationTimestamp()) {
backupToRestore = file;
}
}
}
if (backupToRestore != null) {
Fragment contactListFragment = ContactListFragment.newInstance(backupToRestore, getAccount());
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.contacts_linear_layout, contactListFragment);
transaction.commit();
} else {
Toast.makeText(ContactsPreferenceActivity.this, R.string.contacts_preferences_no_file_found,
Toast.LENGTH_SHORT).show();
}
}
};
DatePickerDialog datePickerDialog = new DatePickerDialog(this, dateSetListener, year, month, day);
datePickerDialog.getDatePicker().setMaxDate(backupFiles.lastElement().getModificationTimestamp());
datePickerDialog.getDatePicker().setMinDate(backupFiles.firstElement().getModificationTimestamp());
datePickerDialog.show();
}
public static void startContactBackupJob(Account account) {
Log_OC.d(TAG, "start daily contacts backup job");
PersistableBundleCompat bundle = new PersistableBundleCompat();
bundle.putString(ContactsBackupJob.ACCOUNT, account.name);
new JobRequest.Builder(ContactsBackupJob.TAG)
.setExtras(bundle)
.setRequiresCharging(false)
.setPersisted(true)
.setUpdateCurrent(true)
.setPeriodic(24 * 60 * 60 * 1000)
.build()
.schedule();
}
public static void cancelContactBackupJob(Context context) {
Log_OC.d(TAG, "disabling contacts backup job");
JobManager jobManager = JobManager.create(context);
Set jobs = jobManager.getAllJobRequestsForTag(ContactsBackupJob.TAG);
for (JobRequest jobRequest : jobs) {
jobManager.cancel(jobRequest.getJobId());
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean retval;
switch (item.getItemId()) {
case android.R.id.home:
if (isDrawerOpen()) {
closeDrawer();
} else {
openDrawer();
}
retval = true;
break;
default:
retval = super.onOptionsItemSelected(item);
break;
}
return retval;
}
@Override
public void showFiles(boolean onDeviceOnly) {
super.showFiles(onDeviceOnly);
Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class);
fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(fileDisplayActivity);
}
@Override
public void showDetails(OCFile file) {
// not needed
}
@Override
public void onBrowsedDownTo(OCFile folder) {
// not needed
}
@Override
public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
// not needed
}
}