Java tutorial
/* * Nextcloud Android client application * * @author Mario Danic * Copyright (C) 2017 Mario Danic * 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 <http://www.gnu.org/licenses/>. */ package com.owncloud.android.ui.fragment.contactsbackup; import android.Manifest; import android.accounts.Account; import android.app.DatePickerDialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.PorterDuff; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v7.widget.AppCompatButton; import android.support.v7.widget.SwitchCompat; 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.TextView; import android.widget.Toast; 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.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.jobs.ContactsBackupJob; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.RefreshFolderOperation; import com.owncloud.android.ui.activity.ContactsPreferenceActivity; import com.owncloud.android.ui.activity.Preferences; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.PermissionUtil; import com.owncloud.android.utils.ThemeUtils; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Vector; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; 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 ContactsBackupFragment extends FileFragment implements DatePickerDialog.OnDateSetListener { public static final String TAG = ContactsBackupFragment.class.getSimpleName(); @BindView(R.id.contacts_automatic_backup) public SwitchCompat backupSwitch; @BindView(R.id.contacts_datepicker) public AppCompatButton contactsDatePickerBtn; @BindView(R.id.contacts_last_backup_timestamp) public TextView lastBackup; private Date selectedDate = null; private boolean calendarPickerOpen; private DatePickerDialog datePickerDialog; private CompoundButton.OnCheckedChangeListener onCheckedChangeListener; 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"; private ArbitraryDataProvider arbitraryDataProvider; private Account account; private boolean showSidebar = true; @Override public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // use grey as fallback for elements where custom theming is not available if (ThemeUtils.themingEnabled()) { getContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true); } View view = inflater.inflate(R.layout.contacts_backup_fragment, null); ButterKnife.bind(this, view); setHasOptionsMenu(true); if (getArguments() != null) { showSidebar = getArguments().getBoolean(ContactsPreferenceActivity.EXTRA_SHOW_SIDEBAR); } final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); account = (Account) getArguments().get(ContactListFragment.ACCOUNT); contactsPreferenceActivity.getSupportActionBar().setTitle(R.string.actionbar_contacts); contactsPreferenceActivity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver()); ThemeUtils.tintSwitch(backupSwitch, ThemeUtils.primaryAccentColor()); backupSwitch .setChecked(arbitraryDataProvider.getBooleanValue(account, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)); onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (checkAndAskForContactsReadPermission()) { if (isChecked) { setAutomaticBackup(true); } else { setAutomaticBackup(false); } } } }; backupSwitch.setOnCheckedChangeListener(onCheckedChangeListener); // display last backup Long lastBackupTimestamp = arbitraryDataProvider.getLongValue(account, PREFERENCE_CONTACTS_LAST_BACKUP); if (lastBackupTimestamp == -1) { lastBackup.setText(R.string.contacts_preference_backup_never); } else { lastBackup.setText(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; } int accentColor = ThemeUtils.primaryAccentColor(); contactsDatePickerBtn.getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP); view.findViewById(R.id.contacts_backup_now).getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP); AppCompatButton chooseDate = (AppCompatButton) view.findViewById(R.id.contacts_datepicker); chooseDate.getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP); chooseDate.setTextColor(ThemeUtils.fontColor()); return view; } @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); } } ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); String backupFolderPath = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR; refreshBackupFolder(backupFolderPath, contactsPreferenceActivity); } private void refreshBackupFolder(final String backupFolderPath, final ContactsPreferenceActivity contactsPreferenceActivity) { AsyncTask<String, Integer, Boolean> task = new AsyncTask<String, Integer, Boolean>() { @Override protected Boolean doInBackground(String... path) { FileDataStorageManager storageManager = new FileDataStorageManager(account, getActivity().getContentResolver()); OCFile folder = storageManager.getFileByPath(path[0]); if (folder != null) { RefreshFolderOperation operation = new RefreshFolderOperation(folder, System.currentTimeMillis(), false, false, false, storageManager, account, getContext()); RemoteOperationResult result = operation.execute(account, getContext()); return result.isSuccess(); } else { return false; } } @Override protected void onPostExecute(Boolean result) { if (result) { OCFile backupFolder = contactsPreferenceActivity.getStorageManager() .getFileByPath(backupFolderPath); Vector<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager() .getFolderContent(backupFolder, false); if (backupFiles == null || backupFiles.size() == 0) { contactsDatePickerBtn.setVisibility(View.GONE); } else { contactsDatePickerBtn.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 { Intent settingsIntent = new Intent(getContext(), Preferences.class); 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) { setAutomaticBackup(true); } else { backupSwitch.setOnCheckedChangeListener(null); backupSwitch.setChecked(false); backupSwitch.setOnCheckedChangeListener(onCheckedChangeListener); } 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; } } } } @OnClick(R.id.contacts_backup_now) public void backupContacts() { if (checkAndAskForContactsReadPermission()) { startContactsBackupJob(); } } private void startContactsBackupJob() { final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); PersistableBundleCompat bundle = new PersistableBundleCompat(); bundle.putString(ContactsBackupJob.ACCOUNT, contactsPreferenceActivity.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(getView().findViewById(R.id.contacts_linear_layout), R.string.contacts_preferences_backup_scheduled, Snackbar.LENGTH_LONG).show(); } private void setAutomaticBackup(final boolean bool) { final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); if (bool) { ContactsPreferenceActivity.startContactBackupJob(contactsPreferenceActivity.getAccount()); } else { ContactsPreferenceActivity.cancelContactBackupJobForAccount(contactsPreferenceActivity, contactsPreferenceActivity.getAccount()); } arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, String.valueOf(bool)); } private boolean checkAndAskForContactsReadPermission() { final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); // check permissions if ((PermissionUtil.checkSelfPermission(contactsPreferenceActivity, Manifest.permission.READ_CONTACTS))) { return true; } else { // Check if we should show an explanation if (PermissionUtil.shouldShowRequestPermissionRationale(contactsPreferenceActivity, android.Manifest.permission.READ_CONTACTS)) { // Show explanation to the user and then request permission Snackbar snackbar = Snackbar .make(getView().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) { requestPermissions(new String[] { Manifest.permission.READ_CONTACTS }, PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC); } }); ThemeUtils.colorSnackbar(contactsPreferenceActivity, snackbar); snackbar.show(); return false; } else { // No explanation needed, request the permission. requestPermissions(new String[] { Manifest.permission.READ_CONTACTS }, PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC); return false; } } } @OnClick(R.id.contacts_datepicker) public void openCleanDate() { openDate(null); } public void openDate(@Nullable Date savedDate) { final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity(); String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR; OCFile backupFolder = contactsPreferenceActivity.getStorageManager().getFileByPath(backupFolderString); Vector<OCFile> backupFiles = contactsPreferenceActivity.getStorageManager().getFolderContent(backupFolder, false); Collections.sort(backupFiles, new Comparator<OCFile>() { @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; 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.lastElement() != null) { datePickerDialog = new DatePickerDialog(contactsPreferenceActivity, this, year, month, day); datePickerDialog.getDatePicker().setMaxDate(backupFiles.lastElement().getModificationTimestamp()); datePickerDialog.getDatePicker().setMinDate(backupFiles.firstElement().getModificationTimestamp()); datePickerDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { selectedDate = null; } }); datePickerDialog.show(); } else { Toast.makeText(contactsPreferenceActivity, R.string.contacts_preferences_something_strange_happened, Toast.LENGTH_SHORT).show(); } } @Override public void onStop() { super.onStop(); if (datePickerDialog != null) { datePickerDialog.dismiss(); } } @Override public void onSaveInstanceState(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(); selectedDate = new Date(year, month, dayOfMonth); String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR; OCFile backupFolder = contactsPreferenceActivity.getStorageManager().getFileByPath(backupFolderString); Vector<OCFile> backupFiles = contactsPreferenceActivity.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, contactsPreferenceActivity.getAccount()); contactsPreferenceActivity.getSupportFragmentManager().beginTransaction() .replace(R.id.frame_container, contactListFragment, ContactListFragment.TAG) .addToBackStack(ContactsPreferenceActivity.BACKUP_TO_LIST).commit(); } else { Toast.makeText(contactsPreferenceActivity, R.string.contacts_preferences_no_file_found, Toast.LENGTH_SHORT).show(); } } }