Skip to content
This repository was archived by the owner on Jun 27, 2020. It is now read-only.

Commit 5130ee3

Browse files
author
Evgenii Kanivets
committed
#96[3h]. Implement backup feature.
1 parent 92e4a69 commit 5130ee3

File tree

13 files changed

+231
-15
lines changed

13 files changed

+231
-15
lines changed

app/libs/json_simple-1.1.jar

15.7 KB
Binary file not shown.

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.blogspot.e_kanivets.moneytracker">
44

5+
<!-- Used only for Dropbox backup -->
56
<uses-permission android:name="android.permission.INTERNET" />
67

78
<application

app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/base/BaseActivity.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.support.annotation.LayoutRes;
66
import android.support.annotation.Nullable;
77
import android.support.annotation.StringRes;
8+
import android.support.v7.app.AlertDialog;
89
import android.support.v7.app.AppCompatActivity;
910
import android.support.v7.widget.Toolbar;
1011
import android.widget.Toast;
@@ -77,6 +78,18 @@ public void stopProgress() {
7778
getProgressBar().dismiss();
7879
}
7980

81+
public void showAlert(@Nullable String title, @Nullable String message) {
82+
AlertDialog.Builder builder = new AlertDialog.Builder(BaseActivity.this);
83+
builder.setTitle(title);
84+
builder.setMessage(message);
85+
builder.setPositiveButton(android.R.string.ok, null);
86+
builder.setCancelable(false);
87+
88+
AlertDialog dialog = builder.create();
89+
dialog.setCanceledOnTouchOutside(false);
90+
dialog.show();
91+
}
92+
8093
private ProgressDialog getProgressBar() {
8194
if (progressDialog == null) progressDialog = new ProgressDialog(this);
8295
return progressDialog;
Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package com.blogspot.e_kanivets.moneytracker.activity.external;
22

3+
import android.view.View;
4+
import android.widget.ListView;
5+
36
import com.blogspot.e_kanivets.moneytracker.R;
47
import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity;
8+
import com.blogspot.e_kanivets.moneytracker.controller.BackupController;
59
import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController;
610
import com.dropbox.client2.DropboxAPI;
711
import com.dropbox.client2.android.AndroidAuthSession;
12+
import com.dropbox.client2.exception.DropboxException;
813
import com.dropbox.client2.session.AppKeyPair;
914

1015
import javax.inject.Inject;
1116

17+
import butterknife.Bind;
18+
import butterknife.OnClick;
1219
import timber.log.Timber;
1320

1421
public class BackupActivity extends BaseBackActivity {
@@ -17,9 +24,15 @@ public class BackupActivity extends BaseBackActivity {
1724

1825
@Inject
1926
PreferenceController preferenceController;
27+
@Inject
28+
BackupController backupController;
2029

21-
private DropboxAPI<AndroidAuthSession> mDBApi;
22-
private String accessToken;
30+
private DropboxAPI<AndroidAuthSession> dbApi;
31+
32+
@Bind(R.id.btn_backup_now)
33+
View btnBackupNow;
34+
@Bind(R.id.list_view)
35+
ListView listView;
2336

2437
@Override
2538
protected int getContentViewId() {
@@ -31,28 +44,60 @@ protected boolean initData() {
3144
getAppComponent().inject(BackupActivity.this);
3245

3346
AppKeyPair appKeys = new AppKeyPair(APP_KEY, APP_SECRET);
34-
AndroidAuthSession session = new AndroidAuthSession(appKeys);
35-
mDBApi = new DropboxAPI<>(session);
47+
String accessToken = preferenceController.readDropboxAccessToken();
3648

37-
accessToken = preferenceController.readDropboxAccessToken();
38-
if (accessToken == null) mDBApi.getSession().startOAuth2Authentication(BackupActivity.this);
49+
AndroidAuthSession session = new AndroidAuthSession(appKeys);
50+
dbApi = new DropboxAPI<>(session);
51+
if (accessToken == null) dbApi.getSession().startOAuth2Authentication(BackupActivity.this);
52+
else dbApi.getSession().setOAuth2AccessToken(accessToken);
3953

4054
return super.initData();
4155
}
4256

57+
@Override
58+
protected void initViews() {
59+
super.initViews();
60+
btnBackupNow.setEnabled(preferenceController.readDropboxAccessToken() != null);
61+
}
62+
4363
@Override
4464
protected void onResume() {
4565
super.onResume();
4666

47-
if (accessToken == null && mDBApi.getSession().authenticationSuccessful()) {
67+
if (dbApi.getSession().authenticationSuccessful()) {
4868
try {
4969
// Required to complete auth, sets the access token on the session
50-
mDBApi.getSession().finishAuthentication();
51-
accessToken = mDBApi.getSession().getOAuth2AccessToken();
52-
preferenceController.writeDropboxAccessToken(accessToken);
70+
dbApi.getSession().finishAuthentication();
71+
preferenceController.writeDropboxAccessToken(dbApi.getSession().getOAuth2AccessToken());
72+
btnBackupNow.setEnabled(true);
5373
} catch (IllegalStateException e) {
5474
Timber.e("Error authenticating: %s", e.getMessage());
5575
}
5676
}
5777
}
78+
79+
@OnClick(R.id.btn_backup_now)
80+
public void backupNow() {
81+
startProgress();
82+
backupController.makeBackup(dbApi, new BackupController.OnBackupListener() {
83+
@Override
84+
public void onBackupSuccess() {
85+
Timber.d("Backup success.");
86+
stopProgress();
87+
}
88+
89+
@Override
90+
public void onBackupFailure(String reason) {
91+
Timber.d("Backup failure.");
92+
stopProgress();
93+
showToast(R.string.failed_create_backup);
94+
95+
if (BackupController.OnBackupListener.ERROR_AUTHENTICATION.equals(reason)) {
96+
preferenceController.writeDropboxAccessToken(null);
97+
dbApi.getSession().startOAuth2Authentication(BackupActivity.this);
98+
btnBackupNow.setEnabled(false);
99+
}
100+
}
101+
});
102+
}
58103
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package com.blogspot.e_kanivets.moneytracker.controller;
2+
3+
import android.os.AsyncTask;
4+
import android.support.annotation.NonNull;
5+
import android.support.annotation.Nullable;
6+
7+
import com.blogspot.e_kanivets.moneytracker.BuildConfig;
8+
import com.dropbox.client2.DropboxAPI;
9+
import com.dropbox.client2.android.AndroidAuthSession;
10+
import com.dropbox.client2.exception.DropboxException;
11+
import com.dropbox.client2.exception.DropboxUnlinkedException;
12+
13+
import java.io.File;
14+
import java.io.FileInputStream;
15+
import java.io.FileNotFoundException;
16+
17+
/**
18+
* Controller class to encapsulate backup logic.
19+
* Created on 8/10/16.
20+
*
21+
* @author Evgenii Kanivets
22+
*/
23+
public class BackupController {
24+
private static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
25+
26+
private FormatController formatController;
27+
private String filesDir;
28+
29+
public BackupController(FormatController formatController, String filesDir) {
30+
this.formatController = formatController;
31+
this.filesDir = filesDir;
32+
}
33+
34+
public void makeBackup(@NonNull DropboxAPI<AndroidAuthSession> dbApi,
35+
@Nullable OnBackupListener listener) {
36+
FileInputStream fileInputStream = readAppDb();
37+
long fileLength = readAppDbFileLength();
38+
if (fileInputStream == null) return;
39+
40+
DropboxBackupAsyncTask asyncTask = new DropboxBackupAsyncTask(dbApi,
41+
formatController.formatDateAndTime(System.currentTimeMillis()),
42+
fileInputStream, fileLength, listener);
43+
asyncTask.execute();
44+
}
45+
46+
@Nullable
47+
private FileInputStream readAppDb() {
48+
File dbFile = new File(getAppDbFileName());
49+
FileInputStream fis = null;
50+
51+
try {
52+
fis = new FileInputStream(dbFile);
53+
} catch (FileNotFoundException e) {
54+
e.printStackTrace();
55+
}
56+
57+
return fis;
58+
}
59+
60+
private long readAppDbFileLength() {
61+
File dbFile = new File(getAppDbFileName());
62+
63+
if (dbFile.exists()) return dbFile.length();
64+
else return 0;
65+
}
66+
67+
@NonNull
68+
private String getAppDbFileName() {
69+
return filesDir + PACKAGE_NAME + "/databases/database";
70+
}
71+
72+
private class DropboxBackupAsyncTask extends AsyncTask<Void, String, String> {
73+
private DropboxAPI<AndroidAuthSession> dbApi;
74+
private String fileName;
75+
private FileInputStream fileInputStream;
76+
private long fileLength;
77+
78+
@Nullable
79+
private OnBackupListener listener;
80+
81+
public DropboxBackupAsyncTask(DropboxAPI<AndroidAuthSession> dbApi, String fileName,
82+
FileInputStream fileInputStream, long fileLength,
83+
@Nullable OnBackupListener listener) {
84+
this.dbApi = dbApi;
85+
this.fileName = fileName;
86+
this.fileInputStream = fileInputStream;
87+
this.fileLength = fileLength;
88+
this.listener = listener;
89+
}
90+
91+
@Override
92+
protected String doInBackground(Void... params) {
93+
DropboxAPI.Entry response = null;
94+
try {
95+
response = dbApi.putFile(fileName, fileInputStream, fileLength, null, null);
96+
} catch (DropboxUnlinkedException e) {
97+
e.printStackTrace();
98+
return OnBackupListener.ERROR_AUTHENTICATION;
99+
} catch (DropboxException e) {
100+
e.printStackTrace();
101+
}
102+
103+
if (response == null) return null;
104+
else return OnBackupListener.SUCCESS;
105+
}
106+
107+
@Override
108+
protected void onPostExecute(String result) {
109+
super.onPostExecute(result);
110+
if (listener == null) return;
111+
112+
if (OnBackupListener.SUCCESS.equals(result)) listener.onBackupSuccess();
113+
else listener.onBackupFailure(result);
114+
}
115+
}
116+
117+
public interface OnBackupListener {
118+
String SUCCESS = "success";
119+
String ERROR_AUTHENTICATION = "error_authentication";
120+
121+
void onBackupSuccess();
122+
123+
void onBackupFailure(String reason);
124+
}
125+
}

app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/module/ControllerModule.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.content.Context;
44
import android.support.annotation.NonNull;
55

6+
import com.blogspot.e_kanivets.moneytracker.controller.BackupController;
67
import com.blogspot.e_kanivets.moneytracker.controller.external.ExportController;
78
import com.blogspot.e_kanivets.moneytracker.controller.FormatController;
89
import com.blogspot.e_kanivets.moneytracker.controller.PeriodController;
@@ -122,4 +123,11 @@ public ExportController providesExportController(RecordController recordControll
122123
public ImportController providesImportController(RecordController recordController) {
123124
return new ImportController(recordController);
124125
}
126+
127+
@Provides
128+
@NonNull
129+
@Singleton
130+
public BackupController providesBackupController(FormatController formatController) {
131+
return new BackupController(formatController, "/data/data/");
132+
}
125133
}

app/src/main/res/layout/activity_backup.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,25 @@
2121

2222
</android.support.design.widget.AppBarLayout>
2323

24+
<LinearLayout
25+
android:layout_width="match_parent"
26+
android:layout_height="match_parent"
27+
android:orientation="vertical"
28+
app:layout_behavior="@string/appbar_scrolling_view_behavior">
29+
30+
<ListView
31+
android:id="@+id/list_view"
32+
android:layout_width="match_parent"
33+
android:layout_height="0dp"
34+
android:layout_weight="1" />
35+
36+
<Button
37+
android:id="@+id/btn_backup_now"
38+
android:layout_width="match_parent"
39+
android:layout_height="wrap_content"
40+
android:background="@drawable/selector_add_income"
41+
android:padding="@dimen/normal_margin"
42+
android:text="Backup now" />
43+
</LinearLayout>
44+
2445
</android.support.design.widget.CoordinatorLayout>

app/src/main/res/layout/activity_import.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
android:layout_height="wrap_content"
4545
android:layout_marginTop="@dimen/activity_vertical_margin"
4646
android:background="@drawable/selector_add_income"
47-
android:padding="15dp"
47+
android:enabled="false"
48+
android:padding="@dimen/normal_margin"
4849
android:text="@string/title_import" />
4950

5051
</LinearLayout>

app/src/main/res/layout/content_accounts.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
android:layout_height="wrap_content"
2929
android:layout_weight="1"
3030
android:background="@drawable/selector_add_income"
31-
android:padding="15dp"
31+
android:padding="@dimen/normal_margin"
3232
android:text="@string/add_account" />
3333
</LinearLayout>
3434

app/src/main/res/layout/content_exchange_rates.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
android:layout_height="wrap_content"
2929
android:layout_weight="1"
3030
android:background="@drawable/selector_add_income"
31-
android:padding="15dp"
31+
android:padding="@dimen/normal_margin"
3232
android:text="@string/add_exchange_rate" />
3333
</LinearLayout>
3434

0 commit comments

Comments
 (0)