/* * Copyright (C) 2012-2016 The Android Money Manager Ex Project Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.money.manager.ex.sync; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import com.money.manager.ex.BuildConfig; import com.money.manager.ex.MoneyManagerApplication; import com.money.manager.ex.R; import com.money.manager.ex.common.MmxBaseFragmentActivity; import com.money.manager.ex.core.Core; import com.money.manager.ex.core.UIHelper; import com.money.manager.ex.home.DatabaseMetadataFactory; import com.money.manager.ex.home.DatabaseMetadata; import com.money.manager.ex.home.RecentDatabasesProvider; import com.money.manager.ex.settings.AppSettings; import com.money.manager.ex.sync.events.DbFileDownloadedEvent; import com.money.manager.ex.settings.PreferenceConstants; import com.money.manager.ex.utils.MmxDatabaseUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import java.io.File; import javax.inject.Inject; import dagger.Lazy; import rx.SingleSubscriber; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; import rx.functions.Func1; import rx.schedulers.Schedulers; import timber.log.Timber; import static android.content.Context.MODE_PRIVATE; /** * Synchronization preferences fragment. */ public class SyncPreferenceFragment extends PreferenceFragmentCompat { public static final int REQUEST_REMOTE_FILE = 1; public static final String EXTRA_REMOTE_FILE = "remote_file"; public SyncPreferenceFragment() { // Required empty public constructor } @Inject Lazy<RecentDatabasesProvider> mDatabases; private SyncPreferencesViewHolder viewHolder; private SyncManager mSyncManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MoneyManagerApplication.getApp().iocComponent.inject(this); } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { PreferenceManager prefMgr = getPreferenceManager(); prefMgr.setSharedPreferencesName(PreferenceConstants.SYNC_PREFERENCES); prefMgr.setSharedPreferencesMode(MODE_PRIVATE); // MODE_WORLD_READABLE addPreferencesFromResource(R.xml.preferences_sync); initializePreferences(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_REMOTE_FILE: handleFileSelection(resultCode, data); break; } } @Override public void onStart() { super.onStart(); // register as event bus listener EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } /** * Called when file is downloaded from the cloud storage. */ @Subscribe public void onEvent(DbFileDownloadedEvent event) { // open the new database. getSyncManager().useDownloadedDatabase(); } /* Private */ private void handleFileSelection(int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK || data == null) return; // get value String remoteFile = data.getStringExtra(SyncPreferenceFragment.EXTRA_REMOTE_FILE); // show selected value viewHolder.remoteFile.setSummary(remoteFile); // update recent db. saveDatabaseMetadata(remoteFile); // start sync service getSyncManager().startSyncServiceHeartbeat(); ((MmxBaseFragmentActivity) getActivity()).compositeSubscription.add( new UIHelper(getActivity()).binaryDialog(R.string.download, R.string.confirm_download, android.R.string.yes, android.R.string.no) .filter(new Func1<Boolean, Boolean>() { @Override public Boolean call(Boolean aBoolean) { // proceed only if user accepts return aBoolean; } }) .subscribe(new Action1<Boolean>() { @Override public void call(Boolean aBoolean) { // download db from the cloud storage checkIfLocalFileExistsAndDownload(); } }) ); } private SyncManager getSyncManager() { if (mSyncManager == null) { mSyncManager = new SyncManager(getActivity()); } return mSyncManager; } private RecentDatabasesProvider getDatabases() { return mDatabases.get(); } private void initializePreferences() { viewHolder = new SyncPreferencesViewHolder(this); // enable/disable sync. viewHolder.syncEnabled.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object o) { // switch the sync heartbeat Boolean enabled = (Boolean) o; getSyncManager().setEnabled(enabled); if (enabled) { getSyncManager().startSyncServiceHeartbeat(); } else { getSyncManager().stopSyncServiceAlarm(); } return true; } }); // provider viewHolder.providerList.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object o) { final SyncManager sync = getSyncManager(); // set the new provider sync.setProvider(CloudStorageProviderEnum.valueOf(o.toString())); // log in to the provider immediately and update to persistence. ((MmxBaseFragmentActivity) getActivity()).compositeSubscription.add( sync.login() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SingleSubscriber<Void>() { @Override public void onSuccess(Void value) { // nothing. } @Override public void onError(Throwable error) { String errorMessage = error.getMessage(); if (!TextUtils.isEmpty(errorMessage) && errorMessage.equals("Authentication was cancelled")) { Timber.w("authentication cancelled"); } else { Timber.e(error, "logging in to cloud provider"); } } }) ); return true; } }); // remote file viewHolder.remoteFile.setSummary(getSyncManager().getRemotePath()); viewHolder.remoteFile.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { // show the file browser/picker. Intent intent = new Intent(getActivity(), CloudFilePickerActivity.class); startActivityForResult(intent, REQUEST_REMOTE_FILE); return false; } }); // interval if (BuildConfig.DEBUG) { // Insert a 1-minute in debug mode. CharSequence[] entries = viewHolder.syncInterval.getEntries(); String[] newEntries = new String[entries.length + 1]; newEntries[0] = "1-minute"; System.arraycopy(entries, 0, newEntries, 1, entries.length); viewHolder.syncInterval.setEntries(newEntries); // values CharSequence[] values = viewHolder.syncInterval.getEntryValues(); String[] newValues = new String[values.length + 1]; newValues[0] = "1"; System.arraycopy(values, 0, newValues, 1, values.length); viewHolder.syncInterval.setEntryValues(newValues); } viewHolder.syncInterval.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object o) { // reset timer. SyncManager sync = getSyncManager(); int interval = Integer.parseInt(o.toString()); sync.setSyncInterval(interval); Timber.d("sync interval set to %d", interval); sync.stopSyncServiceAlarm(); if (interval > 0) { // don't start sync service if the interval is set to 0. sync.startSyncServiceHeartbeat(); } return true; } }); // Download. viewHolder.download.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { forceDownload(); return true; } }); // Upload. viewHolder.upload.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { forceUpload(); return false; } }); // reset preferences viewHolder.resetPreferences.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { final SyncManager sync = getSyncManager(); sync.logout() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SingleSubscriber<Void>() { @Override public void onSuccess(Void value) { sync.resetPreferences(); sync.stopSyncServiceAlarm(); new Core(getActivity()).alert(R.string.preferences_reset); getActivity().recreate(); } @Override public void onError(Throwable error) { Timber.e(error, "logging out the cloud provider"); } }); return false; } }); } private void checkIfLocalFileExistsAndDownload() { String local = getDatabases().getCurrent().localPath; // check if the file exists and prompt the user. if (new File(local).exists()) { // prompt ((MmxBaseFragmentActivity) getActivity()).compositeSubscription.add( new UIHelper(getActivity()).binaryDialog(R.string.file_exists, R.string.file_exists_long) .filter(new Func1<Boolean, Boolean>() { @Override public Boolean call(Boolean aBoolean) { // proceed only if user confirms return aBoolean; } }) .subscribe(new Action1<Boolean>() { @Override public void call(Boolean aBoolean) { // finally download the file. forceDownload(); } }) ); } else { forceDownload(); } } private void forceDownload() { getSyncManager().triggerDownload(); } private void forceUpload() { String remotePath = getSyncManager().getRemotePath(); if (TextUtils.isEmpty(remotePath)) { new Core(getActivity()).alert(R.string.no_remote_file); return; } try { getSyncManager().triggerUpload(); new UIHelper(getActivity()).showToast(R.string.sync_uploading); } catch (RuntimeException e) { Timber.e(e, "uploading database"); } } private void saveDatabaseMetadata(String remoteFile) { String fileName = new File(remoteFile).getName(); String localPath = new MmxDatabaseUtils(getActivity()) .getDefaultDatabaseDirectory() .concat(File.separator).concat(fileName); // update current database path new AppSettings(getActivity()).set(R.string.pref_database_path, localPath); DatabaseMetadata db = DatabaseMetadataFactory.getInstance(localPath, remoteFile); //RecentDatabasesProvider databases = new RecentDatabasesProvider(getActivity()); getDatabases().add(db); } }