diff --git a/app/build.gradle b/app/build.gradle
index f7df93296..b42873acc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,8 +7,8 @@ android {
applicationId 'com.seafile.seadroid2'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 123
- versionName "2.2.48"
+ versionCode 125
+ versionName "2.2.50"
multiDexEnabled true
resValue "string", "authorities", applicationId + '.cameraupload.provider'
resValue "string", "account_type", "com.seafile.seadroid2.account.api2"
@@ -124,11 +124,17 @@ android {
implementation 'com.shuyu:gsyVideoPlayer-java:3.0.0'
implementation 'com.shuyu:gsyVideoPlayer-ex_so:3.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
+ implementation 'com.github.getActivity:XXPermissions:16.2'
+ implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
+ implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
+ implementation 'com.google.code.gson:gson:2.10'
+
+
implementation 'com.yydcdut:markdown-processor:0.1.3'
implementation 'ren.qinc.edit:lib:0.0.5'//editor undo redo
implementation 'com.github.tiagohm.MarkdownView:library:0.19.0'
- implementation 'org.greenrobot:eventbus:3.1.1'
+ implementation 'org.greenrobot:eventbus:3.3.1'
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 62f41589c..d6f0b8fa7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,7 +36,10 @@
+
+
+
-
-
+
@@ -159,6 +162,9 @@
+
+
@@ -192,6 +198,7 @@
+
diff --git a/app/src/main/java/com/seafile/seadroid2/SeadroidApplication.java b/app/src/main/java/com/seafile/seadroid2/SeadroidApplication.java
index 929cd4c8e..504e28adf 100644
--- a/app/src/main/java/com/seafile/seadroid2/SeadroidApplication.java
+++ b/app/src/main/java/com/seafile/seadroid2/SeadroidApplication.java
@@ -18,6 +18,7 @@
import com.seafile.seadroid2.data.StorageManager;
import com.seafile.seadroid2.gesturelock.AppLockManager;
import com.seafile.seadroid2.ui.CustomNotificationBuilder;
+import com.seafile.seadroid2.util.CrashHandler;
import com.seafile.seadroid2.util.Utils;
import java.io.File;
@@ -28,6 +29,8 @@ public class SeadroidApplication extends Application {
private int totalNumber;
private int scanUploadStatus;
private static SeadroidApplication instance;
+ private int totalBackup;
+ private int waitingBackup;
public void onCreate() {
super.onCreate();
@@ -40,7 +43,8 @@ public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
initNotificationChannel();
}
-
+ CrashHandler crashHandler = CrashHandler.getInstance();
+ crashHandler.init(this);
Utils.logPhoneModelInfo();
}
@@ -125,4 +129,16 @@ public int getScanUploadStatus() {
return scanUploadStatus;
}
+ public int getTotalBackup() {
+ return totalBackup;
+ }
+
+ public int getWaitingBackup() {
+ return waitingBackup;
+ }
+
+ public void setFolderBackupNumber(int totalBackup, int waitingBackup) {
+ this.totalBackup = totalBackup;
+ this.waitingBackup = waitingBackup;
+ }
}
diff --git a/app/src/main/java/com/seafile/seadroid2/SeafConnection.java b/app/src/main/java/com/seafile/seadroid2/SeafConnection.java
index 5262f95ad..9fdb8437e 100644
--- a/app/src/main/java/com/seafile/seadroid2/SeafConnection.java
+++ b/app/src/main/java/com/seafile/seadroid2/SeafConnection.java
@@ -880,8 +880,8 @@ private String commitUpload(String link, List blkIds, String dir, String
*/
public String uploadFile(String repoID, String dir, String filePath, ProgressMonitor monitor, boolean update)
throws SeafException, IOException {
- String url = getUploadLink(repoID, update, dir);
- return uploadFileCommon(url, repoID, dir, filePath, monitor, update);
+ String url = getUploadLink(repoID, update, "/");
+ return uploadFileCommon(url, repoID, dir, filePath, monitor, update);
}
@@ -903,7 +903,12 @@ private String uploadFileCommon(String link, String repoID, String dir,
String targetFilePath = Utils.pathJoin(dir, file.getName());
builder.addFormDataPart("target_file", targetFilePath);
} else {
- builder.addFormDataPart("parent_dir", dir);
+ builder.addFormDataPart("parent_dir", "/");
+ if (dir.charAt(0) == '/') {
+ dir = dir.substring(1);
+ dir = dir + "/";
+ }
+ builder.addFormDataPart("relative_path", dir);
}
builder.addFormDataPart("file", file.getName(), RequestManager.getInstance(account).createProgressRequestBody(monitor, file));
diff --git a/app/src/main/java/com/seafile/seadroid2/SettingsManager.java b/app/src/main/java/com/seafile/seadroid2/SettingsManager.java
index 480a890f5..0bf5d199a 100644
--- a/app/src/main/java/com/seafile/seadroid2/SettingsManager.java
+++ b/app/src/main/java/com/seafile/seadroid2/SettingsManager.java
@@ -69,6 +69,7 @@ private SettingsManager() {
public static final String CAMERA_UPLOAD_ADVANCED_SCREEN_KEY = "screen_camera_upload_advanced_feature";
public static final String CAMERA_UPLOAD_ADVANCED_CATEGORY_KEY = "category_camera_upload_advanced_key";
public static final String CAMERA_UPLOAD_ALLOW_DATA_PLAN_SWITCH_KEY = "allow_data_plan_switch_key";
+
public static final String CAMERA_UPLOAD_ALLOW_VIDEOS_SWITCH_KEY = "allow_videos_upload_switch_key";
public static final String CAMERA_UPLOAD_BUCKETS_KEY = "camera_upload_buckets_key";
public static final String CAMERA_UPLOAD_CATEGORY_KEY = "category_camera_upload_key";
@@ -102,6 +103,18 @@ private SettingsManager() {
public static final String PIC_CHECK_START = "pic_check_start";
public static final String UPLOAD_COMPLETED_TIME = "upload_completed_time";
+ //FolderBackupStatus
+ public static final String FOLDER_BACKUP_SWITCH_KEY = "folder_backup_switch_key";
+ public static final String FOLDER_BACKUP_ALLOW_DATA_PLAN_SWITCH_KEY = "folder_backup_allow_data_plan_switch_key";
+ public static final String FOLDER_AUTOMATIC_BACKUP_SWITCH_KEY = "folder_automatic_backup_switch_key";
+ public static final String FOLDER_BACKUP_ACCOUNT_EMAIL = "folder_backup_account_email";
+ public static final String FOLDER_BACKUP_CATEGORY_KEY = "folder_backup_category_key";
+ public static final String FOLDER_BACKUP_MODE = "folder_backup_mode";
+ public static final String FOLDER_BACKUP_LIBRARY_KEY = "folder_backup_library_key";
+ public static final String SELECTED_BACKUP_FOLDERS_KEY = "selected_backup_folders_key";
+ public static final String FOLDER_BACKUP_STATE = "folder_backup_state";
+ public static final String FOLDER_BACKUP_PATHS = "folder_backup_paths";
+
public static long lock_timestamp = 0;
public static final long LOCK_EXPIRATION_MSECS = 5 * 60 * 1000;
@@ -233,6 +246,9 @@ public boolean checkCameraUploadNetworkAvailable() {
public boolean isDataPlanAllowed() {
return settingsSharedPref.getBoolean(CAMERA_UPLOAD_ALLOW_DATA_PLAN_SWITCH_KEY, false);
}
+ public boolean isFolderBackupDataPlanAllowed() {
+ return settingsSharedPref.getBoolean(FOLDER_BACKUP_ALLOW_DATA_PLAN_SWITCH_KEY, false);
+ }
public boolean isVideosUploadAllowed() {
return settingsSharedPref.getBoolean(CAMERA_UPLOAD_ALLOW_VIDEOS_SWITCH_KEY, false);
@@ -241,6 +257,16 @@ public boolean isVideosUploadAllowed() {
public void saveDataPlanAllowed(boolean isAllowed) {
settingsSharedPref.edit().putBoolean(CAMERA_UPLOAD_ALLOW_DATA_PLAN_SWITCH_KEY, isAllowed).commit();
}
+ public void saveFolderBackupDataPlanAllowed(boolean isAllowed) {
+ settingsSharedPref.edit().putBoolean(FOLDER_BACKUP_ALLOW_DATA_PLAN_SWITCH_KEY, isAllowed).commit();
+ }
+
+ public void saveFolderAutomaticBackup(boolean isAllowed) {
+ settingsSharedPref.edit().putBoolean(FOLDER_AUTOMATIC_BACKUP_SWITCH_KEY, isAllowed).commit();
+ }
+ public boolean isFolderAutomaticBackup() {
+ return settingsSharedPref.getBoolean(FOLDER_AUTOMATIC_BACKUP_SWITCH_KEY, false);
+ }
public void saveVideosAllowed(boolean isVideosUploadAllowed) {
settingsSharedPref.edit().putBoolean(CAMERA_UPLOAD_ALLOW_VIDEOS_SWITCH_KEY, isVideosUploadAllowed).commit();
@@ -310,4 +336,22 @@ public void savePrivacyPolicyConfirmed(int type) {
public int getPrivacyPolicyConfirmed() {
return sharedPref.getInt(PRIVACY_POLICY_CONFIRMED, 0);
}
+
+ public void saveBackupPaths(String path) {
+ editor.putString(FOLDER_BACKUP_PATHS, path);
+ editor.commit();
+ }
+
+ public String getBackupPaths() {
+ return sharedPref.getString(SettingsManager.FOLDER_BACKUP_PATHS, null);
+ }
+
+ public void saveBackupEmail(String path) {
+ editor.putString(FOLDER_BACKUP_ACCOUNT_EMAIL, path);
+ editor.commit();
+ }
+
+ public String getBackupEmail() {
+ return sharedPref.getString(SettingsManager.FOLDER_BACKUP_ACCOUNT_EMAIL, null);
+ }
}
diff --git a/app/src/main/java/com/seafile/seadroid2/cameraupload/BucketsFragment.java b/app/src/main/java/com/seafile/seadroid2/cameraupload/BucketsFragment.java
index 66662f842..38f20e60d 100644
--- a/app/src/main/java/com/seafile/seadroid2/cameraupload/BucketsFragment.java
+++ b/app/src/main/java/com/seafile/seadroid2/cameraupload/BucketsFragment.java
@@ -1,8 +1,12 @@
package com.seafile.seadroid2.cameraupload;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,12 +14,23 @@
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
+import android.widget.BaseAdapter;
import android.widget.Button;
+import android.widget.GridView;
+import android.widget.ImageView;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
-import android.widget.RelativeLayout;
+import android.widget.TextView;
+
import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.SeadroidApplication;
import com.seafile.seadroid2.SettingsManager;
+import com.seafile.seadroid2.util.GlideApp;
+import com.seafile.seadroid2.util.Utils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
/**
* Buckets fragment
@@ -23,21 +38,21 @@
public class BucketsFragment extends Fragment {
private CameraUploadConfigActivity mActivity;
- private FragmentManager fm;
- private BucketsSelectionFragment mSelectionFragment;
private RadioGroup mRadioGroup;
private Button mDoneBtn;
private TranslateAnimation mSlideInAnimation;
private TranslateAnimation mSlideOutAnimation;
- private RelativeLayout mDirectoriesLayout;
+ private GridView mGridView;
+ private List buckets;
+ private boolean[] selectedBuckets;
+ private ImageAdapter imageAdapter;
+ private Bitmap[] thumbnails;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = (CameraUploadConfigActivity) getActivity();
View rootView = mActivity.getLayoutInflater().inflate(R.layout.cuc_local_directory_fragment, null);
- mDirectoriesLayout = (RelativeLayout) rootView.findViewById(R.id.cuc_local_directory_list_container);
-
mSlideInAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 2.0f,
@@ -54,35 +69,68 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
mSlideOutAnimation.setDuration(600);
mSlideOutAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mSlideOutAnimation.setAnimationListener(slideOutListener);
-
- fm = getChildFragmentManager();
- fm.beginTransaction()
- .add(R.id.cuc_local_directory_list_container, getSelectionFragment())
- .commit();
-
+ mGridView = (GridView) rootView.findViewById(R.id.cuc_bucket_selection_grid);
mRadioGroup = (RadioGroup) rootView.findViewById(R.id.cuc_local_directory_radio_group);
mDoneBtn = (Button) rootView.findViewById(R.id.cuc_local_directory_btn);
mDoneBtn.setOnClickListener(onClickListener);
if (mActivity.isChooseDirPage())
mDoneBtn.setVisibility(View.VISIBLE);
- // RadioButton mAutoScanRadioBtn = (RadioButton) mRadioGroup.findViewById(R.id.cuc_local_library_auto_scan_rb);
- // RadioButton mCustomPickRadioBtn = (RadioButton) mRadioGroup.findViewById(R.id.cuc_local_library_pick_folders_rb);
SettingsManager settingsManager = SettingsManager.instance();
-
if (settingsManager.getCameraUploadBucketList().isEmpty()) {
// auto scan
- mDirectoriesLayout.setVisibility(View.INVISIBLE);
- mDirectoriesLayout.setEnabled(false);
+ mGridView.setVisibility(View.INVISIBLE);
+ mGridView.setEnabled(false);
mRadioGroup.check(R.id.cuc_local_directory_auto_scan_rb);
} else {
// pick custom folders to scan
- mDirectoriesLayout.setVisibility(View.VISIBLE);
- mDirectoriesLayout.setEnabled(true);
+ mGridView.setVisibility(View.VISIBLE);
+ mGridView.setEnabled(true);
mRadioGroup.check(R.id.cuc_local_directory_pick_folders_rb);
}
mRadioGroup.setOnCheckedChangeListener(onCheckedChangeListener);
+ List currentBucketList = settingsManager.getCameraUploadBucketList();
+ buckets = GalleryBucketUtils.getMediaBuckets(getActivity().getApplicationContext());
+ selectedBuckets = new boolean[buckets.size()];
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ thumbnails = new Bitmap[buckets.size()];
+ for (int i = 0; i < buckets.size(); i++) {
+ GalleryBucketUtils.Bucket b = buckets.get(i);
+ if (b.image_id > 0) {
+ thumbnails[i] = MediaStore.Images.Thumbnails.getThumbnail(
+ getActivity().getApplicationContext().getContentResolver(), b.image_id,
+ MediaStore.Images.Thumbnails.MINI_KIND, null);
+ }
+ if (currentBucketList.size() > 0)
+ selectedBuckets[i] = currentBucketList.contains(b.id);
+ else
+ selectedBuckets[i] = b.isCameraBucket;
+ }
+ } else {
+ for (int i = 0; i < this.buckets.size(); i++) {
+ GalleryBucketUtils.Bucket b = this.buckets.get(i);
+ if (b.isImages != null && b.isImages.equals(GalleryBucketUtils.IMAGES)) {
+ Uri image_uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, b.imageId);
+ String image_path = Utils.getRealPathFromURI(SeadroidApplication.getAppContext(), image_uri, "images");
+ b.imagePath = image_path;
+ } else {
+ Uri video_uri = Uri.withAppendedPath(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, b.videoId);
+ String videoPath = Utils.getRealPathFromURI(SeadroidApplication.getAppContext(), video_uri, "video");
+ b.videoPath = videoPath;
+ }
+
+ // if the user has previously selected buckets, mark these.
+ // otherwise, select the ones that will be auto-guessed.
+ if (currentBucketList.size() > 0)
+ selectedBuckets[i] = currentBucketList.contains(b.id);
+ else
+ selectedBuckets[i] = b.isCameraBucket;
+ }
+ }
+
+ imageAdapter = new ImageAdapter();
+ mGridView.setAdapter(imageAdapter);
return rootView;
}
@@ -96,14 +144,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
public void onCheckedChanged(RadioGroup radioGroup, int radioButtonId) {
switch (radioButtonId) {
case R.id.cuc_local_directory_auto_scan_rb:
- mActivity.saveSettings();
- mDirectoriesLayout.startAnimation(mSlideOutAnimation);
- mDirectoriesLayout.setEnabled(false);
+ mGridView.startAnimation(mSlideOutAnimation);
+ mGridView.setEnabled(false);
mActivity.saveSettings();
break;
case R.id.cuc_local_directory_pick_folders_rb:
- mDirectoriesLayout.startAnimation(mSlideInAnimation);
- mDirectoriesLayout.setEnabled(true);
+ mGridView.startAnimation(mSlideInAnimation);
+ mGridView.setEnabled(true);
mActivity.saveSettings();
break;
}
@@ -131,7 +178,7 @@ public void onClick(View v) {
@Override
public void onAnimationEnd(Animation arg0) {
- mDirectoriesLayout.setVisibility(View.INVISIBLE);
+ mGridView.setVisibility(View.INVISIBLE);
}
@Override
@@ -139,7 +186,7 @@ public void onAnimationRepeat(Animation arg0) {}
@Override
public void onAnimationStart(Animation arg0) {
- mDirectoriesLayout.setVisibility(View.VISIBLE);
+ mGridView.setVisibility(View.VISIBLE);
}
};
@@ -151,7 +198,7 @@ public void onAnimationStart(Animation arg0) {
@Override
public void onAnimationEnd(Animation arg0) {
- mDirectoriesLayout.setVisibility(View.VISIBLE);
+ mGridView.setVisibility(View.VISIBLE);
}
@Override
@@ -159,21 +206,90 @@ public void onAnimationRepeat(Animation arg0) {}
@Override
public void onAnimationStart(Animation arg0) {
- mDirectoriesLayout.setVisibility(View.VISIBLE);
+ mGridView.setVisibility(View.VISIBLE);
}
};
- /**
- * Instantiates a new fragment if mSelectionFragment is null.
- * Returns the current fragment, otherwise.
- */
- public BucketsSelectionFragment getSelectionFragment() {
- if (mSelectionFragment == null) {
- mSelectionFragment = new BucketsSelectionFragment();
+
+ public class ImageAdapter extends BaseAdapter {
+ private LayoutInflater mInflater;
+
+ public ImageAdapter() {
+ mInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
- return mSelectionFragment;
+ public int getCount() {
+ return buckets.size();
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final ViewHolder holder;
+ if (convertView == null) {
+ holder = new ViewHolder();
+ convertView = mInflater.inflate(R.layout.bucket_item, null);
+ holder.imageview = (ImageView) convertView.findViewById(R.id.bucket_item_thumbImage);
+ holder.text = (TextView) convertView.findViewById(R.id.bucket_item_name);
+ holder.marking = (ImageView) convertView.findViewById(R.id.bucket_item_marking);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ holder.imageview.setId(position);
+ holder.text.setText(buckets.get(position).name);
+ holder.imageview.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View v) {
+ int id = v.getId();
+ selectedBuckets[id] = !selectedBuckets[id];
+ if (selectedBuckets[id])
+ holder.marking.setBackgroundResource(R.drawable.checkbox_checked);
+ else
+ holder.marking.setBackgroundResource(R.drawable.checkbox_unchecked);
+ }
+ });
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ holder.imageview.setImageBitmap(thumbnails[position]);
+ } else {
+ if (buckets.get(position).isImages != null && buckets.get(position).isImages.equals(GalleryBucketUtils.IMAGES)) {
+ GlideApp.with(getActivity()).load(buckets.get(position).imagePath).into(holder.imageview);
+ } else {
+ GlideApp.with(getActivity()).load(Uri.fromFile(new File(buckets.get(position).videoPath))).into(holder.imageview);
+ }
+ }
+ if (selectedBuckets[position])
+ holder.marking.setBackgroundResource(R.drawable.checkbox_checked);
+ else
+ holder.marking.setBackgroundResource(R.drawable.checkbox_unchecked);
+ holder.id = position;
+ return convertView;
+ }
+ }
+
+ static class ViewHolder {
+ ImageView imageview;
+ ImageView marking;
+ TextView text;
+ int id;
}
+ public List getSelectedBuckets() {
+ List ret = new ArrayList<>();
+ for (int i = 0; i < buckets.size(); i++) {
+ if (selectedBuckets[i]) {
+ ret.add(buckets.get(i).id);
+ }
+ }
+
+ return ret;
+ }
}
diff --git a/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraSyncAdapter.java b/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraSyncAdapter.java
index d635705b3..a517e746c 100644
--- a/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraSyncAdapter.java
+++ b/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraSyncAdapter.java
@@ -646,7 +646,7 @@ private void uploadFile(DataManager dataManager, File file, String bucketName) t
// Log.d(DEBUG_TAG, "uploading file " + file.getName() + " to " + serverPath);
Utils.utilsLogInfo(true,"====uploading file " + file.getName() + " to " + serverPath);
- int taskID = txService.addUploadTask(dataManager.getAccount(), targetRepoId, targetRepoName,
+ int taskID = txService.addCameraUploadTask(dataManager.getAccount(), targetRepoId, targetRepoName,
serverPath, file.getAbsolutePath(), false, false);
tasksInProgress.add(taskID);
}
diff --git a/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraUploadConfigActivity.java b/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraUploadConfigActivity.java
index 7a9747a18..e077b6e74 100644
--- a/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraUploadConfigActivity.java
+++ b/app/src/main/java/com/seafile/seadroid2/cameraupload/CameraUploadConfigActivity.java
@@ -102,7 +102,7 @@ public void saveSettings() {
if (isChooseBothPages || isChooseDirPage) {
SettingsManager settingsManager = SettingsManager.instance();
- List selectedBuckets = mBucketsFragment.getSelectionFragment().getSelectedBuckets();
+ List selectedBuckets = mBucketsFragment.getSelectedBuckets();
if (mBucketsFragment.isAutoScanSelected()) {
selectedBuckets.clear();
}
diff --git a/app/src/main/java/com/seafile/seadroid2/data/CameraSyncEvent.java b/app/src/main/java/com/seafile/seadroid2/data/CameraSyncEvent.java
index dbb450242..e0c985713 100644
--- a/app/src/main/java/com/seafile/seadroid2/data/CameraSyncEvent.java
+++ b/app/src/main/java/com/seafile/seadroid2/data/CameraSyncEvent.java
@@ -7,7 +7,6 @@ public CameraSyncEvent(String logInfo) {
this.logInfo = logInfo;
}
-
public String getLogInfo() {
return logInfo;
}
diff --git a/app/src/main/java/com/seafile/seadroid2/data/DataManager.java b/app/src/main/java/com/seafile/seadroid2/data/DataManager.java
index 74fa0036a..955f7e283 100644
--- a/app/src/main/java/com/seafile/seadroid2/data/DataManager.java
+++ b/app/src/main/java/com/seafile/seadroid2/data/DataManager.java
@@ -286,6 +286,9 @@ private synchronized String getRepoDir(String repoName, String repoID) throws Ru
* @param path
*/
public File getLocalRepoFile(String repoName, String repoID, String path) throws RuntimeException {
+ if (TextUtils.isEmpty(repoID)) {
+ return null;
+ }
String repoDir = getRepoDir(repoName, repoID);
if (TextUtils.isEmpty(repoDir)) {
return null;
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/CloudLibraryChooserFragment.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/CloudLibraryChooserFragment.java
new file mode 100644
index 000000000..3a5358d70
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/CloudLibraryChooserFragment.java
@@ -0,0 +1,740 @@
+package com.seafile.seadroid2.folderbackup;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.google.common.collect.Lists;
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.SeafConnection;
+import com.seafile.seadroid2.SeafException;
+import com.seafile.seadroid2.account.Account;
+import com.seafile.seadroid2.account.AccountManager;
+import com.seafile.seadroid2.avatar.Avatar;
+import com.seafile.seadroid2.avatar.AvatarManager;
+import com.seafile.seadroid2.cameraupload.CloudLibraryAccountAdapter;
+import com.seafile.seadroid2.cameraupload.CloudLibraryAdapter;
+import com.seafile.seadroid2.data.DataManager;
+import com.seafile.seadroid2.data.SeafDirent;
+import com.seafile.seadroid2.data.SeafRepo;
+import com.seafile.seadroid2.ui.NavContext;
+import com.seafile.seadroid2.ui.adapter.DirentsAdapter;
+import com.seafile.seadroid2.ui.dialog.PasswordDialog;
+import com.seafile.seadroid2.ui.dialog.TaskDialog;
+import com.seafile.seadroid2.util.ConcurrentAsyncTask;
+import com.seafile.seadroid2.util.Utils;
+
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CloudLibraryChooserFragment extends Fragment {
+
+ public static final String DEBUG_TAG = "CloudLibraryChooserFragment";
+ private FolderBackupConfigActivity mActivity;
+ private Button mDoneBtn;
+ public static final String PASSWORD_DIALOG_FRAGMENT_TAG = "passwordDialogFragmentTag";
+ public static final String ONLY_SHOW_WRITABLE_REPOS = "onlyShowWritableRepos";
+ public static final String ENCRYPTED_REPO_ID = "encryptedRepoId";
+ private static final int STEP_CHOOSE_ACCOUNT = 1;
+ private static final int STEP_CHOOSE_REPO = 2;
+ private static final int STEP_CHOOSE_DIR = 3;
+ private int mStep = 1;
+ private CloudLibraryAccountAdapter mAccountAdapter;
+ private CloudLibraryAdapter mReposAdapter;
+ private DirentsAdapter mDirentsAdapter;
+ private AccountManager mAccountManager;
+ private DataManager mDataManager;
+ private NavContext mNavContext;
+ private Account mAccount;
+ private LoadAccountsTask mLoadAccountsTask;
+ private LoadReposTask mLoadReposTask;
+ private LoadFolderTask mLoadFolderTask;
+ private AvatarManager avatarManager;
+ private RelativeLayout mUpLayout;
+ private TextView mCurrentFolderText;
+ private TextView mEmptyText, mErrorText;
+ private ImageView mRefreshBtn;
+ private View mProgressContainer, mListContainer;
+ private ListView mFoldersListView;
+ private Cursor mCursor;
+ private String mCurrentDir;
+ private boolean canChooseAccount;
+ private boolean onlyShowWritableRepos;
+ private String encryptedRepoId;
+ private boolean isOnlyChooseRepo;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ mActivity = (FolderBackupConfigActivity) getActivity();
+ View rootView = mActivity.getLayoutInflater().inflate(R.layout.folder_backup_remote_library_fragment, null);
+ Intent intent = mActivity.getIntent();
+ avatarManager = new AvatarManager();
+ Account account = intent.getParcelableExtra("account");
+ if (account == null) {
+ canChooseAccount = true;
+ } else {
+ mAccount = account;
+ }
+ onlyShowWritableRepos = intent.getBooleanExtra(ONLY_SHOW_WRITABLE_REPOS, true);
+ encryptedRepoId = intent.getStringExtra(ENCRYPTED_REPO_ID);
+ isOnlyChooseRepo = true;
+ mFoldersListView = (ListView) rootView.findViewById(R.id.cuc_multi_selection_lv);
+ mFoldersListView.setFastScrollEnabled(true);
+ mUpLayout = (RelativeLayout) rootView.findViewById(R.id.cuc_multi_selection_up_layout);
+ mCurrentFolderText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_current_folder_txt);
+ mEmptyText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_empty_msg);
+ mErrorText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_error_msg);
+ mRefreshBtn = (ImageView) rootView.findViewById(R.id.cuc_multi_selection_refresh_iv);
+ mProgressContainer = rootView.findViewById(R.id.cuc_multi_selection_progress_container);
+ mListContainer = rootView.findViewById(R.id.cuc_multi_selection_list_container);
+ mDoneBtn = (Button) rootView.findViewById(R.id.cuc_remote_library_btn);
+
+ mDoneBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mActivity.saveRepoConfig();
+ mActivity.finish();
+ }
+ });
+
+ mRefreshBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ refreshList(true);
+ }
+ });
+
+ mUpLayout.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ try {
+ stepBack();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ });
+
+ mFoldersListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ onListItemClick(parent, position, id);
+ }
+ });
+
+ if (canChooseAccount) {
+ chooseAccount();
+ } else {
+ chooseRepo();
+ }
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ loadAvatarUrls(48);
+ }
+
+ private void refreshList(final boolean forceRefresh) {
+ switch (mStep) {
+ case STEP_CHOOSE_ACCOUNT:
+ if (mLoadAccountsTask != null && mLoadAccountsTask.getStatus() != AsyncTask.Status.FINISHED) {
+ return;
+ } else {
+ chooseAccount(false);
+ break;
+ }
+ case STEP_CHOOSE_REPO:
+ if (mLoadReposTask != null && mLoadReposTask.getStatus() != AsyncTask.Status.FINISHED) {
+ return;
+ } else {
+ chooseRepo(forceRefresh);
+ break;
+ }
+ case STEP_CHOOSE_DIR:
+ if (mLoadFolderTask != null && mLoadFolderTask.getStatus() != AsyncTask.Status.FINISHED) {
+ return;
+ } else {
+ SeafRepo repo = getDataManager().getCachedRepoByID(getNavContext().getRepoID());
+ if (repo.encrypted && !getDataManager().getRepoPasswordSet(repo.id)) {
+ String password = getDataManager().getRepoPassword(repo.id);
+ showPasswordDialog(repo.name, repo.id,
+ new TaskDialog.TaskDialogListener() {
+ @Override
+ public void onTaskSuccess() {
+ chooseRepo(forceRefresh);
+ }
+ }, password);
+ }
+ chooseDir(forceRefresh);
+ break;
+ }
+ }
+ }
+
+ public void onListItemClick(final View v, final int position, final long id) {
+ NavContext nav = getNavContext();
+ SeafRepo repo = null;
+ if (mStep == STEP_CHOOSE_REPO) {
+ repo = getReposAdapter().getItem(position);
+ //mCurrentFolderText.setText(nav.getRepoName());
+ } else if (mStep == STEP_CHOOSE_DIR) {
+ repo = getDataManager().getCachedRepoByID(nav.getRepoID());
+ }
+
+ if (repo != null) {
+ if (repo.encrypted && !getDataManager().getRepoPasswordSet(repo.id)) {
+ String password = getDataManager().getRepoPassword(repo.id);
+ showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() {
+ @Override
+ public void onTaskSuccess() {
+ onListItemClick(v, position, id);
+ }
+ }, password);
+
+ return;
+ }
+ }
+
+ switch (mStep) {
+ case STEP_CHOOSE_ACCOUNT:
+ setAccount(getAccountAdapter().getItem(position));
+ mCurrentDir = mAccount.getDisplayName();
+ setCurrentDirText(mCurrentDir);
+ chooseRepo();
+ break;
+ case STEP_CHOOSE_REPO:
+ if (!isOnlyChooseRepo) {
+ nav.setRepoName(repo.name);
+ nav.setRepoID(repo.id);
+ nav.setDir("/", repo.root);
+ chooseDir();
+ }
+ mCurrentDir = getString(R.string.settings_cuc_remote_lib_repo, repo.name);
+ setCurrentDirText(mCurrentDir);
+ SeafRepo seafRepo = getReposAdapter().getItem(position);
+ onRepoSelected(mAccount, seafRepo);
+ break;
+ case STEP_CHOOSE_DIR:
+ SeafDirent dirent = getDirentsAdapter().getItem(position);
+ mCurrentDir += "/" + dirent.name;
+ setCurrentDirText(mCurrentDir);
+
+ if (dirent.type == SeafDirent.DirentType.FILE) {
+ return;
+ }
+
+ nav.setDir(Utils.pathJoin(nav.getDirPath(), dirent.name), dirent.id);
+ refreshFolder();
+ break;
+ }
+ }
+
+ private void onRepoSelected(Account account, SeafRepo seafRepo) {
+ mActivity.saveBackupLibrary(account, seafRepo);
+ getReposAdapter().setSelectedRepo(seafRepo);
+ getReposAdapter().notifyDataSetChanged();
+ }
+
+ private void stepBack() {
+ stepBack(false);
+ }
+
+ private void stepBack(boolean cancelIfFirstStep) {
+ switch (mStep) {
+ case STEP_CHOOSE_ACCOUNT:
+ if (cancelIfFirstStep) {
+ mActivity.finish();
+ }
+ mUpLayout.setVisibility(View.INVISIBLE);
+ break;
+ case STEP_CHOOSE_REPO:
+ if (canChooseAccount) {
+ mCurrentDir = getString(R.string.settings_cuc_remote_lib_account);
+ setCurrentDirText(mCurrentDir);
+ chooseAccount(false);
+ } else if (cancelIfFirstStep) {
+ mActivity.finish();
+ }
+ break;
+ case STEP_CHOOSE_DIR:
+ if (getNavContext().isRepoRoot()) {
+ mCurrentDir = getAccountManager().getCurrentAccount().getEmail();
+ setCurrentDirText(mCurrentDir);
+ chooseRepo();
+ } else {
+ String path = getNavContext().getDirPath();
+ mCurrentDir = getNavContext().getRepoName() + Utils.getParentPath(path);
+ setCurrentDirText(mCurrentDir);
+ getNavContext().setDir(Utils.getParentPath(path), null);
+ refreshFolder();
+ }
+ break;
+ }
+ }
+
+ private void showPasswordDialog() {
+ NavContext nav = getNavContext();
+ String repoName = nav.getRepoName();
+ String repoID = nav.getRepoID();
+
+ showPasswordDialog(repoName, repoID, new TaskDialog.TaskDialogListener() {
+ @Override
+ public void onTaskSuccess() {
+ refreshFolder();
+ }
+ }, null);
+ }
+
+ public void showPasswordDialog(String repoName, String repoID,
+ TaskDialog.TaskDialogListener listener, String password) {
+ PasswordDialog passwordDialog = new PasswordDialog();
+ passwordDialog.setRepo(repoName, repoID, mAccount);
+ if (password != null) {
+ passwordDialog.setPassword(password);
+ }
+ passwordDialog.setTaskDialogLisenter(listener);
+ passwordDialog.show(mActivity.getSupportFragmentManager(), PASSWORD_DIALOG_FRAGMENT_TAG);
+ }
+
+ private void chooseDir() {
+ chooseDir(false);
+ }
+
+ private void chooseDir(boolean forceRefresh) {
+ mStep = STEP_CHOOSE_DIR;
+ mUpLayout.setVisibility(View.VISIBLE);
+ mEmptyText.setText(R.string.dir_empty);
+ setListAdapter(getDirentsAdapter());
+ refreshFolder(forceRefresh);
+ }
+
+ private void chooseAccount() {
+ chooseAccount(true);
+ }
+
+ /**
+ * List all accounts
+ */
+ private void chooseAccount(boolean forwardIfOnlyOneAccount) {
+ mStep = STEP_CHOOSE_ACCOUNT;
+ mUpLayout.setVisibility(View.INVISIBLE);
+ mEmptyText.setText(R.string.no_account);
+ mCurrentDir = getString(R.string.settings_cuc_remote_lib_account);
+ setCurrentDirText(mCurrentDir);
+ mLoadAccountsTask = new LoadAccountsTask(getAccountManager(), forwardIfOnlyOneAccount);
+ ConcurrentAsyncTask.execute(mLoadAccountsTask);
+ setListAdapter(getAccountAdapter());
+ }
+
+ /**
+ * List all repos
+ */
+ private void chooseRepo() {
+ chooseRepo(false);
+ }
+
+ private void chooseRepo(boolean forceRefresh) {
+ mStep = STEP_CHOOSE_REPO;
+ mUpLayout.setVisibility(View.VISIBLE);
+ mCurrentDir = mAccount.getDisplayName();
+ setCurrentDirText(mCurrentDir);
+ setListAdapter(getReposAdapter());
+ getNavContext().setRepoID(null);
+
+ if (!Utils.isNetworkOn() || !forceRefresh) {
+ List repos = getDataManager().getReposFromCache();
+ if (repos != null) {
+ updateAdapterWithRepos(repos);
+ return;
+ }
+ }
+ mLoadReposTask = new LoadReposTask(getDataManager());
+ ConcurrentAsyncTask.execute(mLoadReposTask);
+ }
+
+ private void refreshFolder() {
+ refreshFolder(false);
+ }
+
+ private void refreshFolder(boolean forceRefresh) {
+ String repoID = getNavContext().getRepoID();
+ String dirPath = getNavContext().getDirPath();
+ if (!Utils.isNetworkOn() || !forceRefresh) {
+ List dirents = getDataManager().getCachedDirents(
+ getNavContext().getRepoID(), getNavContext().getDirPath());
+ if (dirents != null) {
+ updateAdapterWithDirents(dirents);
+ return;
+ }
+ }
+ mLoadFolderTask = new LoadFolderTask(repoID, dirPath, getDataManager());
+ ConcurrentAsyncTask.execute(mLoadFolderTask);
+ }
+
+ private void updateAdapterWithDirents(List dirents) {
+ getDirentsAdapter().setDirents(dirents);
+ showListOrEmptyText(dirents.size());
+ }
+
+ private void updateAdapterWithRepos(List repos) {
+ // remove encrypted repos in order to "hide" them in selection list
+ List filteredRepos = Lists.newArrayList();
+ for (SeafRepo repo : repos) {
+ if (!repo.encrypted)
+ filteredRepos.add(repo);
+ }
+ getReposAdapter().setRepos(filteredRepos);
+ showListOrEmptyText(filteredRepos.size());
+ }
+
+ private CloudLibraryAccountAdapter getAccountAdapter() {
+ if (mAccountAdapter == null) {
+ mAccountAdapter = new CloudLibraryAccountAdapter(mActivity);
+ }
+ return mAccountAdapter;
+ }
+
+ private CloudLibraryAdapter getReposAdapter() {
+ if (mReposAdapter == null) {
+ mReposAdapter = new CloudLibraryAdapter(onlyShowWritableRepos, encryptedRepoId);
+ }
+ return mReposAdapter;
+ }
+
+ private DirentsAdapter getDirentsAdapter() {
+ if (mDirentsAdapter == null) {
+ mDirentsAdapter = new DirentsAdapter();
+ }
+ return mDirentsAdapter;
+ }
+
+ private void showListOrEmptyText(int listSize) {
+ if (listSize == 0) {
+ mFoldersListView.setVisibility(View.GONE);
+ mEmptyText.setVisibility(View.VISIBLE);
+ } else {
+ mFoldersListView.setVisibility(View.VISIBLE);
+ mEmptyText.setVisibility(View.GONE);
+ }
+ }
+
+ private void setListAdapter(BaseAdapter adapter) {
+ mFoldersListView.setAdapter(adapter);
+ }
+
+ private DataManager getDataManager() {
+ if (mDataManager == null) {
+ mDataManager = new DataManager(mAccount);
+ }
+ return mDataManager;
+ }
+
+ private void setAccount(Account account) {
+ mAccount = account;
+ mDataManager = new DataManager(account);
+ }
+
+ private AccountManager getAccountManager() {
+ if (mAccountManager == null) {
+ mAccountManager = new AccountManager(getActivity());
+ }
+ return mAccountManager;
+ }
+
+ private NavContext getNavContext() {
+ if (mNavContext == null) {
+ mNavContext = new NavContext();
+ }
+ return mNavContext;
+ }
+
+ /**
+ * Sets the current directory's text.
+ */
+ private void setCurrentDirText(String text) {
+ mCurrentFolderText.setText(text);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ getActivity().finish();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getActivity().finish();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (isRemoving()) {
+ mCursor.close();
+ mCursor = null;
+ }
+ }
+
+ private class LoadAccountsTask extends AsyncTask {
+ private List accounts;
+ private Exception err;
+ private AccountManager accountManager;
+ private boolean forwardIfOnlyOneAccount;
+
+ public LoadAccountsTask(AccountManager accountManager, boolean forwardIfOnlyOneAccount) {
+ this.accountManager = accountManager;
+ this.forwardIfOnlyOneAccount = forwardIfOnlyOneAccount;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ showLoading(true);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ accounts = accountManager.getAccountList();
+ } catch (Exception e) {
+ err = e;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void v) {
+ showLoading(false);
+ if (err != null || accounts == null) {
+ setErrorMessage(R.string.load_accounts_fail);
+ if (err != null) {
+ Log.d(DEBUG_TAG, "failed to load accounts: " + err.getMessage());
+ }
+ return;
+ }
+
+ if (accounts.size() == 1 && forwardIfOnlyOneAccount) {
+ // Only 1 account. Go to next step.
+ setAccount(accounts.get(0));
+ chooseRepo();
+ return;
+ }
+
+ CloudLibraryAccountAdapter adapter = getAccountAdapter();
+ adapter.clear();
+ for (Account account : accounts) {
+ adapter.add(account);
+ }
+ adapter.notifyDataSetChanged();
+ showListOrEmptyText(accounts.size());
+ }
+ }
+
+ private class LoadReposTask extends AsyncTask {
+ private List repos;
+ private SeafException err;
+ private DataManager dataManager;
+
+ public LoadReposTask(DataManager dataManager) {
+ this.dataManager = dataManager;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ repos = dataManager.getReposFromServer();
+ } catch (SeafException e) {
+ err = e;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ showLoading(true);
+ }
+
+ @Override
+ protected void onPostExecute(Void v) {
+ if (mStep != STEP_CHOOSE_REPO) {
+ return;
+ }
+ showLoading(false);
+ if (err != null || repos == null) {
+ setErrorMessage(R.string.load_libraries_fail);
+ Log.d(DEBUG_TAG, "failed to load repos: " + (err != null ? err.getMessage() : " no error present"));
+ return;
+ }
+ updateAdapterWithRepos(repos);
+ }
+ }
+
+ private class LoadFolderTask extends AsyncTask {
+ private String repoID, dirPath;
+ private SeafException err;
+ private DataManager dataManager;
+ private List dirents;
+
+ public LoadFolderTask(String repoID, String dirPath, DataManager dataManager) {
+ this.repoID = repoID;
+ this.dirPath = dirPath;
+ this.dataManager = dataManager;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ showLoading(true);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ dirents = dataManager.getDirentsFromServer(repoID, dirPath);
+ } catch (SeafException e) {
+ err = e;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void v) {
+ if (mStep != STEP_CHOOSE_DIR) {
+ return;
+ }
+ getDirentsAdapter().clearDirents();
+ showLoading(false);
+ if (err != null) {
+ int retCode = err.getCode();
+ if (retCode == SeafConnection.HTTP_STATUS_REPO_PASSWORD_REQUIRED) {
+ showPasswordDialog();
+ } else if (retCode == HttpURLConnection.HTTP_NOT_FOUND) {
+ final String message = String.format(getString(R.string.op_exception_folder_deleted), dirPath);
+ mActivity.showShortToast(mActivity, message);
+ } else {
+ Log.d(DEBUG_TAG, "failed to load dirents: " + err.getMessage());
+ err.printStackTrace();
+ setErrorMessage(R.string.load_dir_fail);
+ }
+ return;
+ }
+
+ if (dirents == null) {
+ Log.d(DEBUG_TAG, "failed to load dirents: no error present");
+ setErrorMessage(R.string.load_dir_fail);
+ return;
+ }
+ updateAdapterWithDirents(dirents);
+ }
+ }
+
+ public void loadAvatarUrls(int avatarSize) {
+ List avatars;
+ if (!Utils.isNetworkOn() || !avatarManager.isNeedToLoadNewAvatars()) {
+ avatars = avatarManager.getAvatarList();
+ if (avatars == null) {
+ return;
+ }
+ mAccountAdapter.setAvatars((ArrayList) avatars);
+ mAccountAdapter.notifyDataSetChanged();
+ return;
+ }
+ LoadAvatarUrlsTask task = new LoadAvatarUrlsTask(avatarSize);
+ ConcurrentAsyncTask.execute(task);
+ }
+
+ private class LoadAvatarUrlsTask extends AsyncTask> {
+
+ private List avatars;
+ private int avatarSize;
+ private SeafConnection httpConnection;
+
+ public LoadAvatarUrlsTask(int avatarSize) {
+ this.avatarSize = avatarSize;
+ this.avatars = Lists.newArrayList();
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+ avatars = avatarManager.getAvatarList();
+ List acts = avatarManager.getAccountsWithoutAvatars();
+ List newAvatars = new ArrayList(acts.size());
+ for (Account account : acts) {
+ httpConnection = new SeafConnection(account);
+ String avatarRawData = null;
+ try {
+ avatarRawData = httpConnection.getAvatar(account.getEmail(), avatarSize);
+ } catch (SeafException e) {
+ e.printStackTrace();
+ return avatars;
+ }
+ Avatar avatar = avatarManager.parseAvatar(avatarRawData);
+ if (avatar == null)
+ continue;
+
+ avatar.setSignature(account.getSignature());
+ avatars.add(avatar);
+ newAvatars.add(avatar);
+ }
+ avatarManager.saveAvatarList(newAvatars);
+ return avatars;
+ }
+
+ @Override
+ protected void onPostExecute(List avatars) {
+ if (avatars == null) {
+ return;
+ }
+ mAccountAdapter.setAvatars((ArrayList) avatars);
+ mAccountAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private void setErrorMessage(int resID) {
+ mErrorText.setVisibility(View.VISIBLE);
+ mErrorText.setText(getString(resID));
+ }
+
+ private void clearError() {
+ mErrorText.setVisibility(View.GONE);
+ }
+
+ private void showLoading(boolean loading) {
+ clearError();
+ if (loading) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ mActivity, android.R.anim.fade_in));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ mActivity, android.R.anim.fade_out));
+ mProgressContainer.setVisibility(View.VISIBLE);
+ mListContainer.setVisibility(View.INVISIBLE);
+ } else {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ mActivity, android.R.anim.fade_out));
+ mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ mActivity, android.R.anim.fade_in));
+ mProgressContainer.setVisibility(View.GONE);
+ mListContainer.setVisibility(View.VISIBLE);
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupConfigActivity.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupConfigActivity.java
new file mode 100644
index 000000000..909e65249
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupConfigActivity.java
@@ -0,0 +1,229 @@
+package com.seafile.seadroid2.folderbackup;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.TextUtils;
+import android.widget.Toast;
+import com.google.gson.Gson;
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.SettingsManager;
+import com.seafile.seadroid2.account.Account;
+import com.seafile.seadroid2.data.SeafRepo;
+import com.seafile.seadroid2.folderbackup.selectfolder.SelectBackupFolderFragment;
+import com.seafile.seadroid2.folderbackup.selectfolder.StringTools;
+import com.seafile.seadroid2.ui.activity.BaseActivity;
+import com.seafile.seadroid2.ui.activity.SeafilePathChooserActivity;
+import com.seafile.seadroid2.ui.fragment.SettingsFragment;
+import com.seafile.seadroid2.util.Utils;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FolderBackupConfigActivity extends BaseActivity {
+
+ public String DEBUG_TAG = "FolderBackupConfigActivity";
+ public static final String BACKUP_SELECT_REPO = "backup_select_repo";
+ public static final String BACKUP_SELECT_PATHS = "backup_select_paths";
+ public static final String BACKUP_SELECT_PATHS_SWITCH = "backup_select_paths_switch";
+ private ViewPager mViewPager;
+ private SelectBackupFolderFragment mBucketsFragment;
+ private CloudLibraryChooserFragment mCloudLibFragment;
+ private SeafRepo mSeafRepo;
+ private Account mAccount;
+ private boolean isChooseFolderPage;
+ private boolean isChooseLibPage;
+ private FolderBackupDBHelper databaseHelper;
+ private FolderBackupService mBackupService;
+ private List selectFolderPaths;
+ private Activity mActivity;
+ private String originalBackupPaths;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ setContentView(R.layout.folder_backup_activity_layout);
+ if (getSupportActionBar() != null)
+ getSupportActionBar().hide();
+
+ isChooseFolderPage = getIntent().getBooleanExtra(SettingsFragment.FOLDER_BACKUP_REMOTE_PATH, false);
+ isChooseLibPage = getIntent().getBooleanExtra(SettingsFragment.FOLDER_BACKUP_REMOTE_LIBRARY, false);
+ mViewPager = (ViewPager) findViewById(R.id.cuc_pager);
+ FragmentManager fm = getSupportFragmentManager();
+ mViewPager.setAdapter(new FolderBackupConfigAdapter(fm));
+ mViewPager.setOffscreenPageLimit(2);
+ databaseHelper = FolderBackupDBHelper.getDatabaseHelper();
+ Intent bindIntent = new Intent(this, FolderBackupService.class);
+ bindService(bindIntent, mBackupConnection, Context.BIND_AUTO_CREATE);
+ mActivity = this;
+ originalBackupPaths = SettingsManager.instance().getBackupPaths();
+
+ if (isChooseFolderPage && !TextUtils.isEmpty(originalBackupPaths)) {
+ selectFolderPaths = StringTools.getJsonToList(originalBackupPaths);
+ }
+ }
+
+ private ServiceConnection mBackupConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder binder) {
+ FolderBackupService.FileBackupBinder fileBackupBinder = (FolderBackupService.FileBackupBinder) binder;
+ mBackupService = fileBackupBinder.getService();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mBackupService = null;
+ }
+
+ };
+
+ public void saveBackupLibrary(Account account, SeafRepo seafRepo) {
+ mSeafRepo = seafRepo;
+ mAccount = account;
+ }
+
+ public FolderBackupDBHelper getDatabaseHelper() {
+ return databaseHelper;
+ }
+
+ public void setFolderPathList(List selectFileList) {
+ this.selectFolderPaths = selectFileList;
+ }
+
+ public List getSelectFolderPath() {
+ return selectFolderPaths;
+ }
+
+ public void saveRepoConfig() {
+ if (isChooseLibPage) {
+ Intent intent = new Intent();
+ // update cloud library data
+ if (mSeafRepo != null && mAccount != null) {
+ intent.putExtra(SeafilePathChooserActivity.DATA_REPO_NAME, mSeafRepo.name);
+ intent.putExtra(SeafilePathChooserActivity.DATA_REPO_ID, mSeafRepo.id);
+ intent.putExtra(SeafilePathChooserActivity.DATA_ACCOUNT, mAccount);
+ intent.putExtra(BACKUP_SELECT_REPO, true);
+ SettingsManager.instance().saveBackupEmail(mAccount.getEmail());
+ try {
+ RepoConfig repoConfig = databaseHelper.getRepoConfig(mAccount.getEmail());
+ if (repoConfig != null) {
+ databaseHelper.updateRepoConfig(mAccount.getEmail(), mSeafRepo.getID(), mSeafRepo.getName());
+ } else {
+ databaseHelper.saveRepoConfig(mAccount.getEmail(), mSeafRepo.getID(), mSeafRepo.getName());
+ }
+ Toast.makeText(mActivity, mActivity.getString(R.string.folder_backup_select_repo_update), Toast.LENGTH_SHORT).show();
+ } catch (Exception e) {
+ Utils.utilsLogInfo(true, "=saveRepoConfig=======================" + e.toString());
+ }
+
+ }
+ setResult(RESULT_OK, intent);
+ boolean automaticBackup = SettingsManager.instance().isFolderAutomaticBackup();
+ if (automaticBackup && mBackupService != null) {
+ mBackupService.folderBackup(mAccount.getEmail());
+ }
+ }
+ }
+
+ public void saveFolderConfig() {
+ if (isChooseFolderPage) {
+ String backupEmail = SettingsManager.instance().getBackupEmail();
+ String strJsonPath = new Gson().toJson(selectFolderPaths);
+
+ if ((TextUtils.isEmpty(originalBackupPaths) && !TextUtils.isEmpty(strJsonPath)) ||
+ !originalBackupPaths.equals(strJsonPath)) {
+ mBackupService.startFolderMonitor(selectFolderPaths);
+ Utils.utilsLogInfo(false, "----------Restart monitoring FolderMonitor");
+ }
+ if (!TextUtils.isEmpty(originalBackupPaths) && TextUtils.isEmpty(strJsonPath)) {
+ mBackupService.stopFolderMonitor();
+ }
+ SettingsManager.instance().saveBackupPaths(strJsonPath);
+ Intent intent = new Intent();
+ if (selectFolderPaths != null) {
+ intent.putStringArrayListExtra(BACKUP_SELECT_PATHS, (ArrayList) selectFolderPaths);
+ intent.putExtra(BACKUP_SELECT_PATHS_SWITCH, true);
+ }
+ setResult(RESULT_OK, intent);
+ boolean folderAutomaticBackup = SettingsManager.instance().isFolderAutomaticBackup();
+ if (folderAutomaticBackup && mBackupService != null) {
+ mBackupService.folderBackup(backupEmail);
+ }
+ }
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mBucketsFragment != null && mBucketsFragment.onBackPressed()) {
+ return;
+ }
+ setResult(RESULT_CANCELED);
+ super.onBackPressed();
+ }
+
+ public boolean isChooseDirPage() {
+ return isChooseFolderPage;
+ }
+
+ class FolderBackupConfigAdapter extends FragmentStatePagerAdapter {
+
+ public FolderBackupConfigAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+
+ if (isChooseLibPage) {
+ return position == 0 ? new CloudLibraryChooserFragment() : null;
+ }
+ if (isChooseFolderPage) {
+ switch (position) {
+ case 0:
+ mBucketsFragment = new SelectBackupFolderFragment();
+ return mBucketsFragment;
+ default:
+ return null;
+ }
+
+ }
+ switch (position) {
+ case 0:
+ mCloudLibFragment = new CloudLibraryChooserFragment();
+ return mCloudLibFragment;
+ case 1:
+ mBucketsFragment = new SelectBackupFolderFragment();
+ return mBucketsFragment;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ if (isChooseLibPage || isChooseFolderPage)
+ return 1;
+ else
+ return 2;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mBackupService != null) {
+ unbindService(mBackupConnection);
+ mBackupService = null;
+ }
+ super.onDestroy();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupDBHelper.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupDBHelper.java
new file mode 100644
index 000000000..7d5e1c2d6
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupDBHelper.java
@@ -0,0 +1,202 @@
+package com.seafile.seadroid2.folderbackup;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.seafile.seadroid2.SeadroidApplication;
+
+import java.io.File;
+
+public class FolderBackupDBHelper extends SQLiteOpenHelper {
+
+ private SQLiteDatabase database = null;
+ private static FolderBackupDBHelper dbHelper = null;
+ // version.
+ public static final int DATABASE_VERSION = 1;
+ public static final String DATABASE_NAME = "folder_backup.db";
+ // backup file table
+ private static final String SAVE_FOLDER_BACKUP_FILE_TABLE_NAME = "FolderBackupInfo";
+ private static final String FOLDER_BACKUP_COLUMN_ID = "id";
+ private static final String FOLDER_BACKUP_COLUMN_REPO_ID = "repo_id";
+ private static final String FOLDER_BACKUP_COLUMN_REPO_NAME = "repo_name";
+ private static final String FOLDER_BACKUP_COLUMN_PARENT_FOLDER = "parent_folder";
+ private static final String FOLDER_BACKUP_COLUMN_FILE_NAME = "file_name";
+ private static final String FOLDER_BACKUP_COLUMN_FILE_PATH = "file_path";
+ private static final String FOLDER_BACKUP_COLUMN_FILE_SIZE = "file_size";
+ // repo config table
+ private static final String REPO_CONFIG_TABLE_NAME = "RepoConfig";
+ private static final String REPO_CONFIG_COLUMN_ID = "id";
+ private static final String REPO_CONFIG_MAIL = "mail";
+ private static final String REPO_CONFIG_REPO_ID = "repo_id";
+ private static final String REPO_CONFIG_REPO_NAME = "repo_name";
+
+ private static final String SQL_CREATE_REPO_CONFIG_TABLE =
+ "CREATE TABLE " + REPO_CONFIG_TABLE_NAME + " ("
+ + REPO_CONFIG_COLUMN_ID + " INTEGER PRIMARY KEY, "
+ + REPO_CONFIG_MAIL + " TEXT NOT NULL, "
+ + REPO_CONFIG_REPO_ID + " TEXT NOT NULL, "
+ + REPO_CONFIG_REPO_NAME + " TEXT NOT NULL);";
+
+ private static final String SQL_CREATE_AUTO_BACKUP_FOLDER_TABLE = "CREATE TABLE "
+ + SAVE_FOLDER_BACKUP_FILE_TABLE_NAME
+ + " ("
+ + FOLDER_BACKUP_COLUMN_ID
+ + " INTEGER PRIMARY KEY, "
+ + FOLDER_BACKUP_COLUMN_REPO_ID
+ + " TEXT NOT NULL, "
+ + FOLDER_BACKUP_COLUMN_REPO_NAME
+ + " TEXT NOT NULL, "
+ + FOLDER_BACKUP_COLUMN_PARENT_FOLDER
+ + " TEXT NOT NULL, "
+ + FOLDER_BACKUP_COLUMN_FILE_NAME
+ + " TEXT NOT NULL, "
+ + FOLDER_BACKUP_COLUMN_FILE_PATH
+ + " TEXT NOT NULL, "
+ + FOLDER_BACKUP_COLUMN_FILE_SIZE
+ + " TEXT NOT NULL);";
+
+ public static synchronized FolderBackupDBHelper getDatabaseHelper() {
+ if (dbHelper != null)
+ return dbHelper;
+ dbHelper = new FolderBackupDBHelper(SeadroidApplication.getAppContext());
+ dbHelper.database = dbHelper.getWritableDatabase();
+ return dbHelper;
+ }
+
+ private FolderBackupDBHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createFolderBackupTable(db);
+ }
+
+ private void createFolderBackupTable(SQLiteDatabase db) {
+ db.execSQL(SQL_CREATE_AUTO_BACKUP_FOLDER_TABLE);
+ db.execSQL(SQL_CREATE_REPO_CONFIG_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + SAVE_FOLDER_BACKUP_FILE_TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + SQL_CREATE_REPO_CONFIG_TABLE + ";");
+ onCreate(db);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ onUpgrade(db, oldVersion, newVersion);
+ }
+
+ public void saveFileBackupInfo(FolderBackupInfo info) {
+ ContentValues values = new ContentValues();
+ values.put(FOLDER_BACKUP_COLUMN_REPO_ID, info.repoID);
+ values.put(FOLDER_BACKUP_COLUMN_REPO_NAME, info.repoName);
+ values.put(FOLDER_BACKUP_COLUMN_PARENT_FOLDER, info.parentFolder);
+ values.put(FOLDER_BACKUP_COLUMN_FILE_NAME, info.fileName);
+ values.put(FOLDER_BACKUP_COLUMN_FILE_PATH, info.filePath);
+ values.put(FOLDER_BACKUP_COLUMN_FILE_SIZE, info.fileSize);
+ database.insert(SAVE_FOLDER_BACKUP_FILE_TABLE_NAME, null, values);
+ }
+
+ public void saveRepoConfig(String email, String repoID, String repoName) {
+ ContentValues values = new ContentValues();
+ values.put(REPO_CONFIG_MAIL, email);
+ values.put(REPO_CONFIG_REPO_ID, repoID);
+ values.put(REPO_CONFIG_REPO_NAME, repoName);
+ database.insert(REPO_CONFIG_TABLE_NAME, null, values);
+ }
+
+ public void updateRepoConfig(String email, String repoID, String repoName) {
+ removeRepoConfig(email);
+ saveRepoConfig(email, repoID, repoName);
+ }
+
+ public RepoConfig getRepoConfig(String email) {
+ String[] projection = {
+ REPO_CONFIG_REPO_ID,
+ REPO_CONFIG_REPO_NAME
+ };
+ String selectClause = String.format("%s = ? ",
+ REPO_CONFIG_MAIL);
+ String[] selectArgs = {email};
+ Cursor c = database.query(
+ REPO_CONFIG_TABLE_NAME,
+ projection,
+ selectClause,
+ selectArgs,
+ null,
+ null,
+ null);
+ if (!c.moveToFirst()) {
+ c.close();
+ return null;
+ }
+ RepoConfig item = cursorToRepoConfigInfo(c, email);
+ c.close();
+ return item;
+ }
+
+ public FolderBackupInfo getBackupFileInfo(String repoID, String filePath, String fileSize) {
+ String[] projection = {
+ FOLDER_BACKUP_COLUMN_REPO_ID,
+ FOLDER_BACKUP_COLUMN_REPO_NAME,
+ FOLDER_BACKUP_COLUMN_PARENT_FOLDER,
+ FOLDER_BACKUP_COLUMN_FILE_NAME,
+ FOLDER_BACKUP_COLUMN_FILE_PATH,
+ FOLDER_BACKUP_COLUMN_FILE_SIZE
+ };
+ String selectClause = String.format("%s = ? and %s = ? and %s = ?",
+ FOLDER_BACKUP_COLUMN_REPO_ID,
+ FOLDER_BACKUP_COLUMN_FILE_PATH,
+ FOLDER_BACKUP_COLUMN_FILE_SIZE);
+ String[] selectArgs = {repoID, filePath, fileSize};
+ Cursor c = database.query(
+ SAVE_FOLDER_BACKUP_FILE_TABLE_NAME,
+ projection,
+ selectClause,
+ selectArgs,
+ null,
+ null,
+ null);
+ if (!c.moveToFirst()) {
+ c.close();
+ return null;
+ }
+ FolderBackupInfo item = cursorToBackupUpdateInfo(c);
+ c.close();
+ return item;
+ }
+
+ public void removeRepoConfig(String email) {
+ String whereClause = String.format("%s = ?", REPO_CONFIG_MAIL);
+ String[] params = {email};
+ database.delete(REPO_CONFIG_TABLE_NAME, whereClause, params);
+ }
+
+ private FolderBackupInfo cursorToBackupUpdateInfo(Cursor c) {
+ String repoID = c.getString(0);
+ String repoName = c.getString(1);
+ String parentDir = c.getString(2);
+ String fileName = c.getString(3);
+ String filePath = c.getString(4);
+ String fileSize = c.getString(5);
+ if (!new File(filePath).exists()) {
+ filePath = null;
+ }
+ FolderBackupInfo info = new FolderBackupInfo(repoID, repoName,
+ parentDir, fileName, filePath, fileSize);
+ return info;
+ }
+
+ private RepoConfig cursorToRepoConfigInfo(Cursor c, String email) {
+ String repoID = c.getString(0);
+ String repoName = c.getString(1);
+ RepoConfig info = new RepoConfig(repoID, repoName, email);
+ return info;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupEvent.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupEvent.java
new file mode 100644
index 000000000..aff13b103
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupEvent.java
@@ -0,0 +1,13 @@
+package com.seafile.seadroid2.folderbackup;
+
+public class FolderBackupEvent {
+ private String backupInfo;
+
+ public FolderBackupEvent(String backupInfo) {
+ this.backupInfo = backupInfo;
+ }
+
+ public String getBackupInfo() {
+ return backupInfo;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupInfo.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupInfo.java
new file mode 100644
index 000000000..e71798d5f
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupInfo.java
@@ -0,0 +1,51 @@
+package com.seafile.seadroid2.folderbackup;
+
+import com.google.common.base.Objects;
+
+import java.io.Serializable;
+
+public class FolderBackupInfo implements Serializable {
+ private volatile int hashCode = 0;
+ public String repoID;
+ public String repoName;
+ public String parentFolder;
+ public String filePath;
+ public String fileName;
+ public String fileSize;
+
+ public FolderBackupInfo(String repoID, String repoName, String parentPath,
+ String fileName, String filePath, String fileSize) {
+ this.repoID = repoID;
+ this.repoName = repoName;
+ this.parentFolder = parentPath;
+ this.fileName = fileName;
+ this.filePath = filePath;
+ this.fileSize = fileSize;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || (obj.getClass() != this.getClass()))
+ return false;
+
+ FolderBackupInfo that = (FolderBackupInfo) obj;
+ if (that.repoID == null || that.repoName == null ||
+ that.parentFolder == null || that.filePath == null) {
+ return false;
+ }
+ return that.repoID.equals(this.repoID) &&
+ that.repoName.equals(this.repoName) &&
+ that.parentFolder.equals(this.parentFolder) &&
+ that.filePath.equals(this.filePath);
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = Objects.hashCode(repoID, repoName, parentFolder, filePath);
+ }
+ return hashCode;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupService.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupService.java
new file mode 100644
index 000000000..d48f8e74d
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/FolderBackupService.java
@@ -0,0 +1,325 @@
+package com.seafile.seadroid2.folderbackup;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.seafile.seadroid2.SeadroidApplication;
+import com.seafile.seadroid2.SettingsManager;
+import com.seafile.seadroid2.account.Account;
+import com.seafile.seadroid2.account.AccountManager;
+import com.seafile.seadroid2.data.DataManager;
+import com.seafile.seadroid2.folderbackup.selectfolder.FileBean;
+import com.seafile.seadroid2.folderbackup.selectfolder.FileTools;
+import com.seafile.seadroid2.folderbackup.selectfolder.StringTools;
+import com.seafile.seadroid2.transfer.TransferManager;
+import com.seafile.seadroid2.transfer.TransferService;
+import com.seafile.seadroid2.transfer.UploadTaskManager;
+import com.seafile.seadroid2.util.CameraSyncStatus;
+import com.seafile.seadroid2.util.ConcurrentAsyncTask;
+import com.seafile.seadroid2.util.Utils;
+
+import org.apache.commons.io.monitor.FileAlterationListener;
+import org.apache.commons.io.monitor.FileAlterationMonitor;
+import org.apache.commons.io.monitor.FileAlterationObserver;
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class FolderBackupService extends Service {
+ private static final String DEBUG_TAG = "FolderBackupService";
+ private Map fileUploaded = new HashMap<>();
+ private final IBinder mBinder = new FileBackupBinder();
+ private TransferService txService = null;
+ private DataManager dataManager;
+ private FolderBackupDBHelper databaseHelper;
+ private AccountManager accountManager;
+ private Account currentAccount;
+ private RepoConfig repoConfig;
+ private List backupPathsList;
+ private FolderReceiver mFolderReceiver;
+ private FileAlterationMonitor fileMonitor;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public class FileBackupBinder extends Binder {
+ public FolderBackupService getService() {
+ return FolderBackupService.this;
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ databaseHelper = FolderBackupDBHelper.getDatabaseHelper();
+ Intent bIntent = new Intent(this, TransferService.class);
+ accountManager = new AccountManager(this);
+ bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE);
+ if (mFolderReceiver == null) {
+ mFolderReceiver = new FolderReceiver();
+ }
+ IntentFilter filter = new IntentFilter(TransferManager.BROADCAST_ACTION);
+ LocalBroadcastManager.getInstance(this).registerReceiver(mFolderReceiver, filter);
+ String backupPaths = SettingsManager.instance().getBackupPaths();
+ if (!TextUtils.isEmpty(backupPaths)) {
+ List pathsList = StringTools.getJsonToList(backupPaths);
+ if (pathsList != null) {
+ startFolderMonitor(pathsList);
+ }
+ }
+ }
+
+ public void startFolderMonitor(List backupPaths) {
+ List fileAlterationObserverList = new ArrayList<>();
+ FolderMonitor folderFileMonitor;
+ FileAlterationObserver folderFileObserver;
+ for (String str : backupPaths) {
+ folderFileMonitor = new FolderMonitor();
+ folderFileObserver = new FileAlterationObserver(str);
+ folderFileObserver.addListener(folderFileMonitor);
+ fileAlterationObserverList.add(folderFileObserver);
+ }
+ fileMonitor = new FileAlterationMonitor(1000l, fileAlterationObserverList);
+ try {
+ fileMonitor.start();
+ } catch (Exception e) {
+ Log.w(DEBUG_TAG, "failed to start file monitor");
+ throw new RuntimeException("failed to start file monitor");
+ }
+ }
+
+ public void stopFolderMonitor() {
+ if (fileMonitor != null) {
+ try {
+ fileMonitor.stop();
+ } catch (Exception e) {
+ Log.w(DEBUG_TAG, "failed to stop file monitor");
+ throw new RuntimeException("failed to stop file monitor");
+ }
+ }
+ }
+
+ public void folderBackup(String email) {
+ fileUploaded.clear();
+ if (databaseHelper == null) {
+ databaseHelper = FolderBackupDBHelper.getDatabaseHelper();
+ }
+ if (!TextUtils.isEmpty(email)) {
+ try {
+ repoConfig = databaseHelper.getRepoConfig(email);
+ } catch (Exception e) {
+ repoConfig = null;
+ }
+ }
+ String backupPaths = SettingsManager.instance().getBackupPaths();
+ if (repoConfig == null || TextUtils.isEmpty(backupPaths)) {
+ return;
+ }
+ if (accountManager == null) {
+ accountManager = new AccountManager(this);
+ }
+ currentAccount = accountManager.getCurrentAccount();
+ backupPathsList = StringTools.getJsonToList(backupPaths);
+ dataManager = new DataManager(currentAccount);
+ if (!StringTools.checkFolderUploadNetworkAvailable()) {
+ SeadroidApplication.getInstance().setScanUploadStatus(CameraSyncStatus.NETWORK_UNAVAILABLE);
+ return;
+ }
+ ConcurrentAsyncTask.execute(new backupFolderTask());
+
+ }
+
+ class backupFolderTask extends AsyncTask {
+
+ @Override
+ protected String doInBackground(Void... params) {
+ for (String str : backupPathsList) {
+ String[] split = str.split("/");
+ isFolder(split[split.length - 1] + "/", str);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String FilePath) {
+ Utils.utilsLogInfo(false, "----------" + FilePath);
+ }
+ }
+
+ private void isFolder(String parentPath, String filePath) {
+ List fileBeanList = new ArrayList<>();
+ FileBean fileBean;
+ File file = FileTools.getFileByPath(filePath);
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ fileBean = new FileBean(files[i].getAbsolutePath());
+ fileBeanList.add(fileBean);
+ }
+ }
+ if (fileBeanList == null || fileBeanList.size() == 0) return;
+ for (FileBean fb : fileBeanList) {
+ if (fb.isDir()) {
+ isFolder(parentPath + fb.getFileName() + "/", fb.getFilePath());
+ } else {
+ Utils.utilsLogInfo(false, "=relative_path==============" + parentPath + "--------" + fb.getFilePath());
+
+ FolderBackupInfo fileInfo = databaseHelper.getBackupFileInfo(repoConfig.getRepoID(),
+ fb.getFilePath(), fb.getSimpleSize() + "");
+ if (fileInfo != null && !TextUtils.isEmpty(fileInfo.filePath)) {
+ Utils.utilsLogInfo(false, "===============" + fileInfo.filePath);
+ } else {
+
+ int taskID = txService.addTaskToSourceQue(Utils.TRANSFER_FOLDER_TAG, currentAccount, repoConfig.getRepoID(),
+ repoConfig.getRepoName(), parentPath, fb.getFilePath(), false, true);
+ if (taskID != 0) {
+ FolderBackupInfo dirInfo = new FolderBackupInfo(repoConfig.getRepoID(), repoConfig.getRepoName(),
+ parentPath, fb.getFileName(), fb.getFilePath(), fb.getSimpleSize() + "");
+ fileUploaded.put(taskID + "", dirInfo);
+ }
+ }
+
+ }
+ }
+
+ }
+
+ ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ TransferService.TransferBinder binder = (TransferService.TransferBinder) service;
+ synchronized (this) {
+ txService = binder.getService();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ synchronized (this) {
+ txService = null;
+ }
+ }
+ };
+
+
+ @Override
+ public void onDestroy() {
+ if (txService != null) {
+ unbindService(mConnection);
+ txService = null;
+ }
+ if (mFolderReceiver != null) {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mFolderReceiver);
+ }
+ stopFolderMonitor();
+ super.onDestroy();
+ }
+
+ class FolderMonitor implements FileAlterationListener {
+
+ public FolderMonitor() {
+
+ }
+
+ @Override
+ public void onStart(FileAlterationObserver observer) {
+
+ }
+
+ @Override
+ public void onDirectoryCreate(File directory) {
+ backupFolders();
+ }
+
+ @Override
+ public void onDirectoryChange(File directory) {
+
+ }
+
+ @Override
+ public void onDirectoryDelete(File directory) {
+
+ }
+
+ @Override
+ public void onFileCreate(File file) {
+ backupFolders();
+ }
+
+ @Override
+ public void onFileChange(File file) {
+
+ }
+
+ @Override
+ public void onFileDelete(File file) {
+
+ }
+
+ @Override
+ public void onStop(FileAlterationObserver observer) {
+
+ }
+ }
+
+ public void backupFolders() {
+ String backupEmail = SettingsManager.instance().getBackupEmail();
+ if (backupEmail != null) {
+ folderBackup(backupEmail);
+ }
+ }
+
+ private class FolderReceiver extends BroadcastReceiver {
+
+ private FolderReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ String type = intent.getStringExtra("type");
+ if (type.equals(UploadTaskManager.BROADCAST_FILE_UPLOAD_SUCCESS)) {
+ int taskID = intent.getIntExtra("taskID", 0);
+ onFileBackedUp(taskID);
+ }
+ }
+
+ }
+
+ private void onFileBackedUp(int taskID) {
+ if (fileUploaded != null) {
+ FolderBackupInfo uploadInfo = fileUploaded.get(taskID + "");
+ if (databaseHelper == null) {
+ databaseHelper = FolderBackupDBHelper.getDatabaseHelper();
+ }
+ if (uploadInfo != null) {
+ databaseHelper.saveFileBackupInfo(uploadInfo);
+ }
+ EventBus.getDefault().post(new FolderBackupEvent("folderBackup"));
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/RepoConfig.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/RepoConfig.java
new file mode 100644
index 000000000..c642fe461
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/RepoConfig.java
@@ -0,0 +1,72 @@
+package com.seafile.seadroid2.folderbackup;
+
+import com.google.common.base.Objects;
+import com.seafile.seadroid2.SettingsManager;
+
+import java.io.Serializable;
+
+public class RepoConfig implements Serializable {
+ private String email;
+ private String repoID;
+ private String repoName;
+
+ public RepoConfig(String repoID, String repoName, String email) {
+ this.repoID = repoID;
+ this.repoName = repoName;
+ this.email = email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setRepoID(String repoID) {
+ this.repoID = repoID;
+ }
+
+ public String getRepoID() {
+ return repoID;
+ }
+
+ public void setRepoName(String repoName) {
+ this.repoName = repoName;
+ }
+
+ public String getRepoName() {
+ return repoName;
+ }
+
+ public boolean canLocalDecrypt() {
+ return SettingsManager.instance().isEncryptEnabled();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || (obj.getClass() != this.getClass()))
+ return false;
+
+ RepoConfig that = (RepoConfig) obj;
+ if (that.repoID == null || that.repoName == null || that.email == null) {
+ return false;
+ }
+
+ return that.email.equals(this.email) && that.repoID.equals(this.repoID) &&
+ that.repoName.equals(this.repoName);
+ }
+
+ private volatile int hashCode = 0;
+
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = Objects.hashCode(email, repoID, repoName);
+ }
+ return hashCode;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/BeanListManager.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/BeanListManager.java
new file mode 100644
index 000000000..3b3db394e
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/BeanListManager.java
@@ -0,0 +1,184 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.SeadroidApplication;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.functions.Function;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+public class BeanListManager {
+ public static final int TypeAddTabbar = 0;
+ public static final int TypeDelTabbar = 1;
+ public static final int TypeInitTabbar = 2;
+
+ public static void upDataFileBeanListByAsyn(Activity at, List selectFilePath,
+ List fileBeanList, FileListAdapter fileListAdapter,
+ String path, List fileTypes, int sortType) {
+
+ if (fileBeanList == null) {
+ fileBeanList = new ArrayList<>();
+ } else if (fileBeanList.size() != 0) {
+ fileBeanList.clear();
+ }
+
+ Observable.just(fileBeanList).map(new Function, List>() {
+ @Override
+ public List apply(List fileBeanList) throws Throwable {
+ FileBean fileBean;
+ File file = FileTools.getFileByPath(path);
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ fileBean = new FileBean(files[i].getAbsolutePath());
+ if (selectFilePath != null && selectFilePath.size() > 0) {
+ for (String str : selectFilePath) {
+ if (fileBean.getFilePath().equals(str)) {
+ fileBean.setChecked(true);
+ }
+ }
+ }
+ fileBeanList.add(fileBean);
+ }
+ }
+ sortFileBeanList(fileBeanList, sortType);
+ return fileBeanList;
+ }
+ }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer>() {
+ public ProgressDialog pg;
+
+ @Override
+ public void onSubscribe(@NonNull Disposable d) {
+ pg = new ProgressDialog(at);
+ pg.show();
+ }
+
+ @Override
+ public void onNext(@NonNull List fileBeans) {
+ if (fileListAdapter != null) {
+ fileListAdapter.updateListData(fileBeans);
+ fileListAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onError(@NonNull Throwable e) {
+ pg.dismiss();
+ }
+
+ @Override
+ public void onComplete() {
+ pg.dismiss();
+ }
+ });
+ }
+
+ public static void sortFileBeanList(List fileBeanList, int sortType) {
+ Collections.sort(fileBeanList, new Comparator() {
+ @Override
+ public int compare(FileBean file1, FileBean file2) {
+
+ if (file1.isDir() && file2.isFile())
+ return -1;
+ if (file1.isFile() && file2.isDir())
+ return 1;
+
+ switch (sortType) {
+ case Constants.SORT_NAME_ASC:
+ return file1.getFileName().compareToIgnoreCase(file2.getFileName());
+ case Constants.SORT_NAME_DESC:
+ return file2.getFileName().compareToIgnoreCase(file1.getFileName());
+ case Constants.SORT_TIME_ASC:
+ long diff = file1.getModifyTime() - file2.getModifyTime();
+ if (diff > 0)
+ return 1;
+ else if (diff == 0)
+ return 0;
+ else
+ return -1;
+ case Constants.SORT_TIME_DESC:
+ diff = file2.getModifyTime() - file1.getModifyTime();
+ if (diff > 0)
+ return 1;
+ else if (diff == 0)
+ return 0;
+ else
+ return -1;
+ case Constants.SORT_SIZE_ASC:
+ diff = file1.getSimpleSize() - file2.getSimpleSize();
+ if (diff > 0)
+ return 1;
+ else if (diff == 0)
+ return 0;
+ else
+ return -1;
+ case Constants.SORT_SIZE_DESC:
+ diff = file2.getSimpleSize() - file1.getSimpleSize();
+ if (diff > 0)
+ return 1;
+ else if (diff == 0)
+ return 0;
+ else
+ return -1;
+ default:
+ return 0;
+ }
+ }
+ });
+ }
+
+ public static void getTabbarFileBeanList(List tabbarList,
+ String path, List allPathsList) {
+ if (allPathsList.contains(path)) {
+ tabbarList.add(0, new TabbarFileBean(path,
+ SeadroidApplication.getAppContext().getString(R.string.internal_storage)));
+ return;
+ }
+ }
+
+ public static List upDataTabbarFileBeanList(List tabbarList,
+ TabbarFileListAdapter tabbarAdapter,
+ String path, int type, List allPathsList) {
+ switch (type) {
+ case TypeAddTabbar:
+ tabbarList.add(new TabbarFileBean(path));
+ break;
+ case TypeDelTabbar:
+ for (int i = tabbarList.size() - 1; i >= 0; i--) {
+ if (tabbarList.get(i).getFilePath().length() > path.length()) {
+ tabbarList.remove(i);
+ } else {
+ break;
+ }
+ }
+ break;
+ case TypeInitTabbar:
+ if (tabbarList == null) {
+ tabbarList = new ArrayList<>();
+ } else {
+ tabbarList.clear();
+ }
+ getTabbarFileBeanList(tabbarList, path, allPathsList);
+ break;
+ }
+ if (tabbarAdapter != null) {
+ tabbarAdapter.updateListData(tabbarList);
+ tabbarAdapter.notifyDataSetChanged();
+ }
+ return tabbarList;
+ }
+}
+
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/Constants.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/Constants.java
new file mode 100644
index 000000000..9fe4cc619
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/Constants.java
@@ -0,0 +1,96 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.os.Environment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Constants {
+
+ public static String DEFAULT_ROOTPATH;
+ public static String PATH_ANRROID_DATA;
+ public static String PATH_ANRROID_OBB;
+ public static final int SORT_NAME_ASC = 0;
+ public static final int SORT_NAME_DESC = 1;
+ public static final int SORT_TIME_ASC = 2;
+ public static final int SORT_TIME_DESC = 3;
+ public static final int SORT_SIZE_ASC = 4;
+ public static final int SORT_SIZE_DESC = 5;
+ public static final int TYPE_CUSTOM_VIEW_NULL = -1;
+ public static Map mimeTypeMap = null;
+
+ static {
+ DEFAULT_ROOTPATH = Environment.getExternalStorageDirectory().getAbsolutePath();
+ PATH_ANRROID_DATA = DEFAULT_ROOTPATH + "/Android/data";
+ PATH_ANRROID_OBB = DEFAULT_ROOTPATH + "/Android/obb";
+ }
+
+ static {
+ mimeTypeMap = new HashMap<>();
+ mimeTypeMap.put("apk", "application/vnd.android.package-archive");
+ mimeTypeMap.put("asf", "video/x-ms-asf");
+ mimeTypeMap.put("avi", "video/x-msvideo");
+ mimeTypeMap.put("bin", "application/octet-stream");
+ mimeTypeMap.put("bmp", "image/bmp");
+ mimeTypeMap.put("c", "text/plain");
+ mimeTypeMap.put("class", "application/octet-stream");
+ mimeTypeMap.put("conf", "text/plain");
+ mimeTypeMap.put("cpp", "text/plain");
+ mimeTypeMap.put("doc", "application/msword");
+ mimeTypeMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
+ mimeTypeMap.put("xls", "application/vnd.ms-excel");
+ mimeTypeMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ mimeTypeMap.put("exe", "application/octet-stream");
+ mimeTypeMap.put("gif", "image/gif");
+ mimeTypeMap.put("gtar", "application/x-gtar");
+ mimeTypeMap.put("gz", "application/x-gzip");
+ mimeTypeMap.put("h", "text/plain");
+ mimeTypeMap.put("htm", "text/html");
+ mimeTypeMap.put("html", "text/html");
+ mimeTypeMap.put("jar", "application/java-archive");
+ mimeTypeMap.put("java", "text/plain");
+ mimeTypeMap.put("jpeg", "image/jpeg");
+ mimeTypeMap.put("jpg", "image/jpeg");
+ mimeTypeMap.put("js", "application/x-javascript");
+ mimeTypeMap.put("log", "text/plain");
+ mimeTypeMap.put("m3u", "audio/x-mpegurl");
+ mimeTypeMap.put("m4a", "audio/mp4a-latm");
+ mimeTypeMap.put("m4b", "audio/mp4a-latm");
+ mimeTypeMap.put("m4p", "audio/mp4a-latm");
+ mimeTypeMap.put("m4u", "video/vnd.mpegurl");
+ mimeTypeMap.put("m4v", "video/x-m4v");
+ mimeTypeMap.put("mov", "video/quicktime");
+ mimeTypeMap.put("mp2", "audio/x-mpeg");
+ mimeTypeMap.put("mp3", "audio/mpeg");
+ mimeTypeMap.put("mp4", "video/mp4");
+ mimeTypeMap.put("mpc", "application/vnd.mpohun.certificate");
+ mimeTypeMap.put("mpe", "video/mpeg");
+ mimeTypeMap.put("mpeg", "video/mpeg");
+ mimeTypeMap.put("mpg", "video/mpeg");
+ mimeTypeMap.put("mpg4", "video/mp4");
+ mimeTypeMap.put("mpga", "audio/mpeg");
+ mimeTypeMap.put("msg", "application/vnd.ms-outlook");
+ mimeTypeMap.put("ogg", "audio/ogg");
+ mimeTypeMap.put("pdf", "application/pdf");
+ mimeTypeMap.put("png", "image/png");
+ mimeTypeMap.put("pps", "application/vnd.ms-powerpoint");
+ mimeTypeMap.put("ppt", "application/vnd.ms-powerpoint");
+ mimeTypeMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
+ mimeTypeMap.put("prop", "text/plain");
+ mimeTypeMap.put("rc", "text/plain");
+ mimeTypeMap.put("rmvb", "audio/x-pn-realaudio");
+ mimeTypeMap.put("rtf", "application/rtf");
+ mimeTypeMap.put("sh", "text/plain");
+ mimeTypeMap.put("tar", "application/x-tar");
+ mimeTypeMap.put("tgz", "application/x-compressed");
+ mimeTypeMap.put("txt", "text/plain");
+ mimeTypeMap.put("wav", "audio/x-wav");
+ mimeTypeMap.put("wma", "audio/x-ms-wma");
+ mimeTypeMap.put("wmv", "audio/x-ms-wmv");
+ mimeTypeMap.put("wps", "application/vnd.ms-works");
+ mimeTypeMap.put("xml", "text/plain");
+ mimeTypeMap.put("z", "application/x-compress");
+ mimeTypeMap.put("zip", "application/x-zip-compressed");
+ mimeTypeMap.put("", "*/*");
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileBean.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileBean.java
new file mode 100644
index 000000000..780bee7e5
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileBean.java
@@ -0,0 +1,153 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.util.Utils;
+
+import java.io.Serializable;
+
+
+public class FileBean implements Serializable {
+ private String filePath;
+ private boolean dir;
+ private boolean file;
+ private String fileName;
+ private String fileExtension;
+ private int fileImgType;
+ private String parentPath;
+ private String parentName;
+ private int childrenFileNumber;
+ private int childrenDirNumber;
+ private String size;
+ private boolean visible;
+ private boolean checked;
+ private long modifyTime;
+ private long simpleSize;
+
+ public FileBean(String filePath) {
+ this.filePath = filePath;
+ visible = false;
+ checked = false;
+ if (FileTools.isFile(filePath)) {
+ file = true;
+ dir = false;
+ } else {
+ file = false;
+ dir = true;
+ }
+ fileName = FileTools.getFileName(filePath);
+ fileExtension = FileTools.getFileExtension(filePath);
+ fileImgType = setImageResourceByExtension(fileExtension);
+ parentPath = FileTools.getParentPath(filePath);
+ parentName = FileTools.getDirName(filePath);
+ childrenFileNumber = FileTools.getChildrenNumber(filePath)[0];
+ childrenDirNumber = FileTools.getChildrenNumber(filePath)[1];
+ modifyTime = FileTools.getFileLastModified(filePath);
+ if (file) {
+ size = FileTools.getSize(filePath);
+ simpleSize = FileTools.getSimpleSize(filePath);
+ }
+ }
+
+ public int setImageResourceByExtension(String extension) {
+ int resourceId;
+ switch (extension) {
+ default:
+ if (dir) {
+ resourceId = R.drawable.folder;
+ } else {
+ resourceId = Utils.getFileIconSuffix(extension);
+ }
+ break;
+ }
+ return resourceId;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ public void setChecked(boolean checked) {
+ this.checked = checked;
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public boolean isDir() {
+ return dir;
+ }
+
+ public boolean isFile() {
+ return file;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public String getFileExtension() {
+ return fileExtension;
+ }
+
+ public int getFileImgType() {
+ return fileImgType;
+ }
+
+ public String getParentPath() {
+ return parentPath;
+ }
+
+ public String getParentName() {
+ return parentName;
+ }
+
+ public int getChildrenFileNumber() {
+ return childrenFileNumber;
+ }
+
+ public int getChildrenDirNumber() {
+ return childrenDirNumber;
+ }
+
+ public String getSize() {
+ return size;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public boolean isChecked() {
+ return checked;
+ }
+
+ public long getModifyTime() {
+ return modifyTime;
+ }
+
+ public long getSimpleSize() {
+ return simpleSize;
+ }
+
+ public void setIsDir(Boolean dir) {
+ this.dir = dir;
+ }
+
+ public void setIsFile(Boolean file) {
+ this.file = file;
+ }
+
+ public void setSize(String size) {
+ this.size = size;
+ }
+
+ public void setSimpleSize(long simpleSize) {
+ this.simpleSize = simpleSize;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListAdapter.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListAdapter.java
new file mode 100644
index 000000000..2ce0f6a31
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListAdapter.java
@@ -0,0 +1,89 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.seafile.seadroid2.R;
+
+import java.util.List;
+
+public class FileListAdapter extends RecyclerView.Adapter {
+ private List mListData;
+ private Context mContext;
+ private OnFileItemClickListener onItemClickListener;
+
+ public void setOnItemClickListener(OnFileItemClickListener onItemClickListener) {
+ this.onItemClickListener = onItemClickListener;
+ }
+
+ public FileListAdapter(Activity context, List listData) {
+ mListData = listData;
+ mContext = context;
+ }
+
+ @Override
+ public FileListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(mContext).inflate(R.layout.item_files_list, parent, false);
+ FileListViewHolder fileListViewHolder = new FileListViewHolder(view);
+ return fileListViewHolder;
+ }
+
+ @SuppressLint("StringFormatMatches")
+ @Override
+ public void onBindViewHolder(final FileListViewHolder holder, int positon) {
+ final FileBean fileBean = mListData.get(positon);
+ holder.checkBoxFile.setChecked(fileBean.isChecked());
+ holder.tvFileName.setText(fileBean.getFileName());
+ holder.imgvFiletype.setImageResource(fileBean.getFileImgType());
+ boolean isFile = fileBean.isFile();
+ if (isFile) {
+ holder.tvFileDetail.setText(String.format(mContext.getString(R.string.folder_file_item_size), fileBean.getSize()));
+ } else {
+ holder.tvFileDetail.setText(String.format(mContext.getString(R.string.folder_file_item_describe),
+ fileBean.getChildrenFileNumber(), fileBean.getChildrenDirNumber()));
+ }
+ if (!isFile) {
+ holder.checkBoxFile.setVisibility(View.VISIBLE);
+ } else {
+ holder.checkBoxFile.setVisibility(View.GONE);
+ }
+
+ holder.checkBoxFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onItemClickListener != null) {
+ onItemClickListener.checkBoxClick(holder.checkBoxFile, holder.getAdapterPosition());
+ }
+ }
+ });
+
+ holder.layoutRoot.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onItemClickListener != null) {
+ onItemClickListener.itemClick(holder.getAdapterPosition());
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mListData == null) {
+ return 0;
+ } else {
+ return mListData.size();
+ }
+ }
+
+ public void updateListData(List mListData) {
+ this.mListData = mListData;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListViewHolder.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListViewHolder.java
new file mode 100644
index 000000000..0f0f2db61
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileListViewHolder.java
@@ -0,0 +1,27 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.seafile.seadroid2.R;
+
+
+public class FileListViewHolder extends RecyclerView.ViewHolder {
+ protected ImageView imgvFiletype;
+ protected TextView tvFileName, tvFileDetail;
+ protected CheckBox checkBoxFile;
+ protected RelativeLayout layoutRoot;
+
+ public FileListViewHolder(View itemView) {
+ super(itemView);
+ layoutRoot = (RelativeLayout) itemView.findViewById(R.id.ll_root);
+ imgvFiletype = (ImageView) itemView.findViewById(R.id.iv_file_type_fileitem);
+ tvFileName = (TextView) itemView.findViewById(R.id.tv_file_name_fileitem);
+ tvFileDetail = (TextView) itemView.findViewById(R.id.tv_file_detail_fileitem);
+ checkBoxFile = (CheckBox) itemView.findViewById(R.id.checkbox_file_fileitem);
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileTools.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileTools.java
new file mode 100644
index 000000000..af62a50d5
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/FileTools.java
@@ -0,0 +1,229 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class FileTools {
+ public static File getFileByPath(String path) {
+ return StringTools.isEmpty(path) ? null : new File(path);
+ }
+
+ public static boolean rename(String filePath, String newName) {
+ return rename(getFileByPath(filePath), newName);
+ }
+
+ public static boolean rename(File file, String newName) {
+ if (file == null) {
+ return false;
+ } else if (!file.exists()) {
+ return false;
+ } else if (StringTools.isEmpty(newName)) {
+ return false;
+ } else if (newName.equals(file.getName())) {
+ return true;
+ } else {
+ File newFile = new File(file.getParent() + File.separator + newName);
+ return !newFile.exists() && file.renameTo(newFile);
+ }
+ }
+
+ public static int[] getChildrenNumber(String path) {
+ return getChildrenNumber(getFileByPath(path));
+ }
+
+ public static int[] getChildrenNumber(File file) {
+ File[] files = file.listFiles();
+ int[] numbers = new int[]{0, 0};
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isFile()) {
+ numbers[0]++;
+ } else {
+ numbers[1]++;
+ }
+ }
+ }
+ return numbers;
+ }
+
+ public static boolean isDir(String dirPath) {
+ return isDir(getFileByPath(dirPath));
+ }
+
+ public static boolean isDir(File file) {
+ return file != null && file.exists() && file.isDirectory();
+ }
+
+ public static boolean isFile(String filePath) {
+ return isFile(getFileByPath(filePath));
+ }
+
+ public static boolean isFile(File file) {
+ return file != null && file.exists() && file.isFile();
+ }
+
+ public static String getDirName(String filePath) {
+ if (StringTools.isEmpty(filePath)) {
+ return "";
+ } else {
+ int lastSep1 = filePath.lastIndexOf(File.separator);
+ if (lastSep1 == -1) {
+ return "";
+ } else {
+ int lastSep2 = filePath.substring(0, lastSep1).lastIndexOf(File.separator);
+ return filePath.substring(lastSep2 + 1, lastSep1);
+ }
+ }
+ }
+
+ public static String getParentPath(File file) {
+ return file == null ? "" : getParentPath(file.getAbsolutePath());
+ }
+
+ public static String getParentPath(String filePath) {
+ if (StringTools.isEmpty(filePath)) {
+ return "";
+ } else {
+ int lastSep = filePath.lastIndexOf(File.separator);
+ return lastSep == -1 ? "" : filePath.substring(0, lastSep);
+ }
+ }
+
+ public static String getFileName(String filePath) {
+ if (StringTools.isEmpty(filePath)) {
+ return "";
+ } else {
+ int lastSep = filePath.lastIndexOf(File.separator);
+ return lastSep == -1 ? filePath : filePath.substring(lastSep + 1);
+ }
+ }
+
+ public static String getFileNameNoExtension(String filePath) {
+ if (StringTools.isEmpty(filePath)) {
+ return "";
+ } else {
+ int lastPoi = filePath.lastIndexOf(46);
+ int lastSep = filePath.lastIndexOf(File.separator);
+ if (lastSep == -1) {
+ return lastPoi == -1 ? filePath : filePath.substring(0, lastPoi);
+ } else {
+ return lastPoi != -1 && lastSep <= lastPoi ? filePath.substring(lastSep + 1, lastPoi) : filePath.substring(lastSep + 1);
+ }
+ }
+ }
+
+ public static String getFileExtension(String filePath) {
+ if (StringTools.isEmpty(filePath)) {
+ return "";
+ } else {
+ int lastPoi = filePath.lastIndexOf(46);
+ int lastSep = filePath.lastIndexOf(File.separator);
+ return lastPoi != -1 && lastSep < lastPoi ? filePath.substring(lastPoi + 1) : "";
+ }
+ }
+
+ public static long getFileLastModified(String filePath) {
+ return getFileLastModified(getFileByPath(filePath));
+ }
+
+ public static long getFileLastModified(File file) {
+ return file == null ? -1L : file.lastModified();
+ }
+
+ public static Long getSimpleSize(String filePath) {
+ return getSimpleSize(getFileByPath(filePath));
+ }
+
+ public static Long getSimpleSize(File file) {
+
+ return StringTools.getOnlyNumber(getSize(file));
+ }
+
+ public static String getSize(String filePath) {
+ return getSize(getFileByPath(filePath));
+ }
+
+ public static String getSize(File file) {
+ if (file == null) {
+ return "";
+ } else {
+ return file.isDirectory() ? getDirSize(file) : getFileSize(file);
+ }
+ }
+
+ private static String getDirSize(File dir) {
+ long len = getDirLength(dir);
+ return len == -1L ? "" : byte2FitMemorySize(len, 3);
+ }
+
+ private static String getFileSize(File file) {
+ long len = getFileLength(file);
+ return len == -1L ? "" : byte2FitMemorySize(len, 3);
+ }
+
+ private static long getFileLength(File file) {
+ return !isFile(file) ? -1L : file.length();
+ }
+
+ private static long getDirLength(File dir) {
+ if (!isDir(dir)) {
+ return 0L;
+ } else {
+ long len = 0L;
+ File[] files = dir.listFiles();
+ if (files != null && files.length > 0) {
+ File[] var4 = files;
+ int var5 = files.length;
+
+ for (int var6 = 0; var6 < var5; ++var6) {
+ File file = var4[var6];
+ if (file.isDirectory()) {
+ len += getDirLength(file);
+ } else {
+ len += file.length();
+ }
+ }
+ }
+ return len;
+ }
+ }
+
+ public static String byte2FitMemorySize(long byteSize, int precision) {
+ if (precision < 0) {
+ throw new IllegalArgumentException("precision shouldn't be less than zero!");
+ } else if (byteSize < 0L) {
+ throw new IllegalArgumentException("byteSize shouldn't be less than zero!");
+ } else if (byteSize < 1024L) {
+ return String.format("%." + precision + "fB", (double) byteSize);
+ } else if (byteSize < 1048576L) {
+ return String.format("%." + precision + "fKB", (double) byteSize / 1024.0D);
+ } else {
+ return byteSize < 1073741824L ? String.format("%." + precision + "fMB", (double) byteSize / 1048576.0D) : String.format("%." + precision + "fGB", (double) byteSize / 1.073741824E9D);
+ }
+ }
+
+ public static List getAllPaths(Context context) {
+ Method mMethodGetPaths = null;
+ String[] paths = null;
+ StorageManager mStorageManager = (StorageManager) context
+ .getSystemService(context.STORAGE_SERVICE);//storage
+ try {
+ mMethodGetPaths = mStorageManager.getClass().getMethod("getVolumePaths");
+ paths = (String[]) mMethodGetPaths.invoke(mStorageManager);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (paths != null) {
+ return Arrays.asList(paths);
+ }
+ return new ArrayList();
+ }
+
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/OnFileItemClickListener.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/OnFileItemClickListener.java
new file mode 100644
index 000000000..edd6acbb6
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/OnFileItemClickListener.java
@@ -0,0 +1,8 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.view.View;
+
+public interface OnFileItemClickListener {
+ void itemClick(int position);
+ void checkBoxClick(View view, int position);
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectBackupFolderFragment.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectBackupFolderFragment.java
new file mode 100644
index 000000000..ae1fc9d74
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectBackupFolderFragment.java
@@ -0,0 +1,194 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.folderbackup.FolderBackupConfigActivity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class SelectBackupFolderFragment extends Fragment {
+
+ private RecyclerView mTabbarFileRecyclerView, mFileRecyclerView;
+ private SelectOptions mSelectOptions;
+ private List allPathsList;
+ private List mShowFileTypes;
+ private int mSortType;
+ private List mFileList;
+ private List mTabbarFileList;
+ private String mCurrentPath;
+ private FileListAdapter mFileListAdapter;
+ private TabbarFileListAdapter mTabbarFileListAdapter;
+ private FolderBackupConfigActivity mActivity;
+ private boolean chooseDirPage;
+ private Button mButton;
+ private List selectPaths;
+ private String initialPath;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ mActivity = (FolderBackupConfigActivity) getActivity();
+ View rootView = getActivity().getLayoutInflater().inflate(R.layout.folder_selection_fragment, null);
+ mTabbarFileRecyclerView = (RecyclerView) rootView.findViewById(R.id.rcv_tabbar_files_list);
+ mFileRecyclerView = (RecyclerView) rootView.findViewById(R.id.rcv_files_list);
+ mButton = (Button) rootView.findViewById(R.id.bt_dir_click_to_finish);
+ mFileRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
+ LinearLayoutManager.VERTICAL, false));
+ mFileListAdapter = new FileListAdapter(getActivity(), mFileList);
+ mFileRecyclerView.setAdapter(mFileListAdapter);
+ mTabbarFileRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
+ LinearLayoutManager.HORIZONTAL, false));
+ mTabbarFileListAdapter = new TabbarFileListAdapter(getActivity(), mTabbarFileList);
+ mTabbarFileRecyclerView.setAdapter(mTabbarFileListAdapter);
+ chooseDirPage = mActivity.isChooseDirPage();
+ init();
+ initData();
+ return rootView;
+ }
+
+ private void init() {
+ if (chooseDirPage) {
+ mButton.setVisibility(View.VISIBLE);
+ } else {
+ mButton.setVisibility(View.GONE);
+ }
+ selectPaths = mActivity.getSelectFolderPath();
+ if (selectPaths == null) {
+ selectPaths = new ArrayList<>();
+ }
+
+ mButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mActivity.saveFolderConfig();
+ mActivity.finish();
+ }
+ });
+
+ mFileListAdapter.setOnItemClickListener(new OnFileItemClickListener() {
+ @Override
+ public void itemClick(int position) {
+ FileBean item = mFileList.get(position);
+ if (item.isFile()) {
+ Toast.makeText(getActivity(), getActivity().getString(R.string.selection_file_type), Toast.LENGTH_SHORT).show();
+ } else {
+ mCurrentPath = item.getFilePath();
+ refreshFileAndTabbar(BeanListManager.TypeAddTabbar);
+ }
+ }
+
+ @Override
+ public void checkBoxClick(View view, int position) {
+ FileBean item = mFileList.get(position);
+ for (FileBean fb : mFileList) {
+ if (item.equals(fb)) {
+ if (fb.isChecked()) {
+ for (int i = 0; i < selectPaths.size(); i++) {
+ if (item.getFilePath().equals(selectPaths.get(i))) {
+ selectPaths.remove(i);
+ i--;
+ }
+ }
+ fb.setChecked(false);
+
+ } else {
+ selectPaths.add(item.getFilePath());
+ fb.setChecked(true);
+ }
+ mActivity.setFolderPathList(selectPaths);
+ }
+ }
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ mFileListAdapter.updateListData(mFileList);
+ mFileListAdapter.notifyDataSetChanged();
+ }
+ });
+ }
+ });
+
+ mTabbarFileListAdapter.setOnItemClickListener(new OnFileItemClickListener() {
+ @Override
+ public void itemClick(int position) {
+ TabbarFileBean item = mTabbarFileList.get(position);
+ mCurrentPath = item.getFilePath();
+
+ if (mTabbarFileList.size() > 1) {
+ refreshFileAndTabbar(BeanListManager.TypeDelTabbar);
+ }
+ }
+
+ @Override
+ public void checkBoxClick(View view, int position) {
+
+ }
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ getActivity().finish();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getActivity().finish();
+ }
+
+ private void initData() {
+ mSelectOptions = SelectOptions.getResetInstance(getActivity());
+ allPathsList= initRootPath(getActivity());
+ mShowFileTypes = Arrays.asList(mSelectOptions.getShowFileTypes());
+ mSortType = mSelectOptions.getSortType();
+ mFileList = new ArrayList<>();
+ mTabbarFileList = new ArrayList<>();
+ refreshFileAndTabbar(BeanListManager.TypeInitTabbar);
+ }
+
+ private List initRootPath(Activity activity) {
+ List allPaths = FileTools.getAllPaths(activity);
+ mCurrentPath = mSelectOptions.rootPath;
+ if (mCurrentPath == null) {
+ if (allPaths.isEmpty()) {
+ mCurrentPath = Constants.DEFAULT_ROOTPATH;
+ } else {
+ mCurrentPath = allPaths.get(0);
+ }
+ }
+ initialPath =mCurrentPath;
+ return allPaths;
+ }
+
+ private void refreshFileAndTabbar(int tabbarType) {
+ BeanListManager.upDataFileBeanListByAsyn(getActivity(), selectPaths, mFileList, mFileListAdapter,
+ mCurrentPath, mShowFileTypes, mSortType);
+ BeanListManager.upDataTabbarFileBeanList(mTabbarFileList, mTabbarFileListAdapter,
+ mCurrentPath, tabbarType, allPathsList);
+ }
+
+ public boolean onBackPressed() {
+ if (mCurrentPath.equals(initialPath)||allPathsList.contains(mCurrentPath)){
+ return false;
+ }else {
+ mCurrentPath=FileTools.getParentPath(mCurrentPath);
+ refreshFileAndTabbar(BeanListManager.TypeDelTabbar);
+ return true;
+ }
+ }
+
+}
+
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectOptions.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectOptions.java
new file mode 100644
index 000000000..9264ad74e
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/SelectOptions.java
@@ -0,0 +1,119 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v4.content.ContextCompat;
+import android.view.View;
+
+import com.seafile.seadroid2.R;
+
+import java.util.List;
+
+public class SelectOptions {
+
+ private static SelectOptions mSelectOptions;
+ public Integer requestCode;
+ public Integer frameLayoutID;
+ private Context mContext;
+ public String[] mShowFileTypes;
+ public String[] mSelectFileTypes;
+ public Integer mSortType;
+ public Boolean mSingle;
+ public Integer mMaxCount;
+ public Fragment mToolbarFragment;
+ public Fragment mMoreChooseFragment;
+ public Boolean onlyShowImages;
+ public Boolean onlyShowVideos;
+ public Boolean needMoreOptions;
+ public Boolean needMoreChoose;
+ public String[] optionsName;
+ public String[] MoreChooseItemName;
+ public boolean[] optionsNeedCallBack;
+ public boolean[] toolbarViewNeedCallBack;
+ public boolean[] moreChooseItemNeedCallBack;
+ public onToolbarListener[] toolbarListeners;
+ public String rootPath;
+ public String toolbarMainTitle;
+ public String toolbarSubtitleTitle;
+ public Integer toolbarBG;
+ public Integer toolbarMainTitleColor;
+ public Integer toolbarSubtitleColor;
+ public Integer toolbarOptionColor;
+ public Integer toolbarOptionSize;
+ public FragmentManager fragmentManager;
+ public Boolean showToolBarFragment;
+ public Integer typeLoadCustomView;
+
+ public static SelectOptions getInstance() {
+ if (mSelectOptions == null) {
+ mSelectOptions = new SelectOptions();
+ }
+ return mSelectOptions;
+ }
+
+ public static SelectOptions getResetInstance(Context context) {
+ mSelectOptions = getInstance();
+ mSelectOptions.mContext = context;
+ mSelectOptions.reset();
+ return mSelectOptions;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public String[] getShowFileTypes() {
+ if (mShowFileTypes == null) {
+ return new String[]{};
+ }
+ return mShowFileTypes;
+ }
+
+ public int getSortType() {
+ if (mSortType == null) {
+ return Constants.SORT_NAME_ASC;
+ }
+ return mSortType;
+ }
+
+ private void reset() {
+ requestCode = 100;
+ frameLayoutID = null;
+ mShowFileTypes = null;
+ mSelectFileTypes = null;
+ mSortType = Constants.SORT_NAME_ASC;
+ mSingle = true;
+ mMaxCount = 1;
+ mToolbarFragment = null;
+ mMoreChooseFragment = null;
+ onlyShowImages = false;
+ onlyShowVideos = false;
+ needMoreOptions = false;
+ needMoreChoose = false;
+ optionsName = null;
+ MoreChooseItemName = null;
+ optionsNeedCallBack = null;
+ toolbarViewNeedCallBack = null;
+ moreChooseItemNeedCallBack = null;
+ toolbarListeners = null;
+ rootPath = null;
+ toolbarMainTitle = "";
+ toolbarSubtitleTitle = null;
+ toolbarBG = ContextCompat.getColor(mContext, R.color.fancy_orange);
+ toolbarMainTitleColor = Color.WHITE;
+ toolbarSubtitleColor = Color.WHITE;
+ toolbarOptionColor = Color.WHITE;
+ toolbarOptionSize = 18;
+ fragmentManager = null;
+ showToolBarFragment = true;
+ typeLoadCustomView = Constants.TYPE_CUSTOM_VIEW_NULL;
+ }
+
+ public interface onToolbarListener {
+ void onClick(View view, String currentPath, List fileBeanList,
+ List callBackData, TabbarFileListAdapter tabbarAdapter,
+ FileListAdapter fileAdapter, List callBackFileBeanList);
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/StringTools.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/StringTools.java
new file mode 100644
index 000000000..03f418ab5
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/StringTools.java
@@ -0,0 +1,55 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.seafile.seadroid2.SettingsManager;
+import com.seafile.seadroid2.util.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StringTools {
+
+ public static boolean isEmpty(String s) {
+ return s == null || s.trim().isEmpty() ? true : false;
+ }
+
+ public static Long getOnlyNumber(String s) {
+ String temp = s.replaceAll("[^0-9]", "");
+ if (temp.equals("")) {
+ return -1L;
+ }
+ Long number = Long.valueOf(temp);
+ if (number == null) {
+ return -1L;
+ }
+ return Long.valueOf(temp);
+ }
+
+ public static List getJsonToList(String strJson) {
+ List list = new ArrayList();
+ if (TextUtils.isEmpty(strJson)) {
+ return list;
+ }
+ Gson gson = new Gson();
+ list = gson.fromJson(strJson, new TypeToken>() {
+ }.getType());
+ return list;
+
+ }
+
+ public static boolean checkFolderUploadNetworkAvailable() {
+ if (!Utils.isNetworkOn()) {
+ return false;
+ }
+ // user does not allow mobile connections
+ if (!Utils.isWiFiOn() && !SettingsManager.instance().isFolderBackupDataPlanAllowed()) {
+ return false;
+ }
+ // Wi-Fi or 2G/3G/4G connections available
+ return true;
+ }
+
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileBean.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileBean.java
new file mode 100644
index 000000000..5a50e8353
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileBean.java
@@ -0,0 +1,43 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+
+public class TabbarFileBean {
+
+ private String filePath;
+ private String fileName;
+ private String fileNameNoExtension;
+ private String parentPath;
+ private String parentName;
+
+ public TabbarFileBean(String filePath) {
+ this.filePath = filePath;
+ fileName = FileTools.getFileName(filePath);
+ fileNameNoExtension = FileTools.getFileNameNoExtension(filePath);
+ parentPath = FileTools.getParentPath(filePath);
+ parentName = FileTools.getDirName(filePath);
+ }
+
+ public TabbarFileBean(String filePath, String fileName) {
+ this.filePath = filePath;
+ this.fileName = fileName;
+ fileNameNoExtension = fileName;
+ parentPath = FileTools.getParentPath(filePath);
+ parentName = FileTools.getDirName(filePath);
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public String getParentPath() {
+ return parentPath;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileListAdapter.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileListAdapter.java
new file mode 100644
index 000000000..69cc53701
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileListAdapter.java
@@ -0,0 +1,62 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.app.Activity;
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.seafile.seadroid2.R;
+
+import java.util.List;
+
+public class TabbarFileListAdapter extends RecyclerView.Adapter {
+
+ private List mListData;
+ private Context mContext;
+ private OnFileItemClickListener onItemClickListener;
+
+ public void setOnItemClickListener(OnFileItemClickListener onItemClickListener) {
+ this.onItemClickListener = onItemClickListener;
+ }
+
+ public TabbarFileListAdapter(Activity context, List listData) {
+ mListData = listData;
+ mContext = context;
+ }
+
+ @Override
+ public TabbarFileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(mContext).inflate(R.layout.item_tabbar_files_list, parent, false);
+ return new TabbarFileViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final TabbarFileViewHolder holder, int positon) {
+ final TabbarFileBean entity = mListData.get(positon);
+ String fileName = entity.getFileName().replaceAll("[^\\p{Print}]", "");
+ holder.tvName.setText(fileName);
+ holder.llRoot.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (onItemClickListener != null) {
+ onItemClickListener.itemClick(holder.getAdapterPosition());
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mListData == null) {
+ return 0;
+ } else {
+ return mListData.size();
+ }
+ }
+
+ public void updateListData(List mListData) {
+ this.mListData = mListData;
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileViewHolder.java b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileViewHolder.java
new file mode 100644
index 000000000..8d5640c7d
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/folderbackup/selectfolder/TabbarFileViewHolder.java
@@ -0,0 +1,21 @@
+package com.seafile.seadroid2.folderbackup.selectfolder;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.seafile.seadroid2.R;
+
+
+public class TabbarFileViewHolder extends RecyclerView.ViewHolder {
+
+ protected TextView tvName;
+ protected LinearLayout llRoot;
+
+ public TabbarFileViewHolder(View itemView) {
+ super(itemView);
+ llRoot = (LinearLayout) itemView.findViewById(R.id.ll_root);
+ tvName = (TextView) itemView.findViewById(R.id.btn_item_tabbar);
+ }
+}
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/DownloadTask.java b/app/src/main/java/com/seafile/seadroid2/transfer/DownloadTask.java
index bfa9fb264..91af019a3 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/DownloadTask.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/DownloadTask.java
@@ -25,7 +25,7 @@ public class DownloadTask extends TransferTask {
public DownloadTask(int taskID, Account account, String repoName, String repoID, String path,
DownloadStateListener downloadStateListener) {
- super(taskID, account, repoName, repoID, path);
+ super("", taskID, account, repoName, repoID, path);
this.downloadStateListener = downloadStateListener;
}
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/TransferManager.java b/app/src/main/java/com/seafile/seadroid2/transfer/TransferManager.java
index 859fa1889..3a6924645 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/TransferManager.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/TransferManager.java
@@ -4,9 +4,11 @@
import com.google.common.collect.Lists;
import com.seafile.seadroid2.SeadroidApplication;
+import com.seafile.seadroid2.SettingsManager;
import com.seafile.seadroid2.data.CameraSyncEvent;
import com.seafile.seadroid2.util.CameraSyncStatus;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
+import com.seafile.seadroid2.util.Utils;
import org.greenrobot.eventbus.EventBus;
@@ -46,6 +48,11 @@ public abstract class TransferManager {
*/
protected List waitingList = Lists.newArrayList();
+ private int folderBackupWaitingNumber;
+ private int folderBackupTotalNumber;
+ private int cameraUploadWaitingNumber;
+ private int cameraUploadTotalNumber;
+
protected synchronized TransferTask getTask(int taskID) {
return allTaskList.get(taskID);
}
@@ -97,13 +104,52 @@ public synchronized void doNext() {
TransferTask task = waitingList.remove(0);
transferringList.add(task);
- SeadroidApplication.getInstance().setCameraUploadNumber(waitingList.size(), allTaskList.size());
- SeadroidApplication.getInstance().setScanUploadStatus(CameraSyncStatus.UPLOADING);
- EventBus.getDefault().post(new CameraSyncEvent("upload"));
+ int scanUploadStatus = SeadroidApplication.getInstance().getScanUploadStatus();
+ getWaitingNumber();
+ getTotalNumber();
+ if (scanUploadStatus > 0) {
+ SeadroidApplication.getInstance().setCameraUploadNumber(cameraUploadWaitingNumber, cameraUploadTotalNumber);
+ SeadroidApplication.getInstance().setScanUploadStatus(CameraSyncStatus.UPLOADING);
+ EventBus.getDefault().post(new CameraSyncEvent("upload"));
+ }
+ if (SettingsManager.instance().isFolderAutomaticBackup()) {
+ SeadroidApplication.getInstance().setFolderBackupNumber(folderBackupTotalNumber, folderBackupWaitingNumber);
+ }
+
+
ConcurrentAsyncTask.execute(task);
}
}
+ public void getWaitingNumber() {
+ cameraUploadWaitingNumber = 0;
+ folderBackupWaitingNumber = 0;
+
+ for (TransferTask waitTask : waitingList) {
+ if (waitTask.getSource().equals(Utils.TRANSFER_PHOTO_TAG)) {
+ cameraUploadWaitingNumber++;
+ }
+ if (waitTask.getSource().equals(Utils.TRANSFER_FOLDER_TAG)) {
+ folderBackupWaitingNumber++;
+ }
+ }
+ }
+
+ public void getTotalNumber() {
+ cameraUploadTotalNumber = 0;
+ folderBackupTotalNumber = 0;
+
+ for (TransferTask allTask : allTaskList.values()) {
+ if (allTask.getSource().equals(Utils.TRANSFER_PHOTO_TAG)) {
+ cameraUploadTotalNumber++;
+ }
+ if (allTask.getSource().equals(Utils.TRANSFER_FOLDER_TAG)) {
+ folderBackupTotalNumber++;
+ }
+
+ }
+ }
+
protected void cancel(int taskID) {
TransferTask task = getTask(taskID);
if (task != null) {
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/TransferService.java b/app/src/main/java/com/seafile/seadroid2/transfer/TransferService.java
index f14dcc803..79fc80e43 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/TransferService.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/TransferService.java
@@ -5,9 +5,11 @@
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
+
import com.seafile.seadroid2.account.Account;
import com.seafile.seadroid2.notification.DownloadNotificationProvider;
import com.seafile.seadroid2.notification.UploadNotificationProvider;
+import com.seafile.seadroid2.util.Utils;
import java.util.List;
@@ -90,7 +92,11 @@ public IBinder onBind(Intent intent) {
* @return
*/
public int addTaskToUploadQue(Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal) {
- return uploadTaskManager.addTaskToQue(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal,false);
+ return uploadTaskManager.addTaskToQue("", account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, false);
+ }
+
+ public int addTaskToSourceQue(String source, Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal) {
+ return uploadTaskManager.addTaskToQue(source, account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, false);
}
/**
@@ -110,7 +116,7 @@ public int addTaskToUploadQue(Account account, String repoID, String repoName, S
*/
public int addTaskToUploadQueBlock(Account account, String repoID, String repoName, String dir,
String filePath, boolean isUpdate, boolean isCopyToLocal) {
- return uploadTaskManager.addTaskToQue(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, true);
+ return uploadTaskManager.addTaskToQue("", account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, true);
}
/**
@@ -131,6 +137,11 @@ public int addUploadTask(Account account, String repoID, String repoName, String
return addTaskToUploadQue(account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal);
}
+ public int addCameraUploadTask(Account account, String repoID, String repoName, String dir,
+ String filePath, boolean isUpdate, boolean isCopyToLocal) {
+ return addTaskToSourceQue(Utils.TRANSFER_PHOTO_TAG, account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal);
+ }
+
public UploadTaskInfo getUploadTaskInfo(int taskID) {
return (UploadTaskInfo) uploadTaskManager.getTaskInfo(taskID);
}
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/TransferTask.java b/app/src/main/java/com/seafile/seadroid2/transfer/TransferTask.java
index c02b8fa68..048ffb8b4 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/TransferTask.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/TransferTask.java
@@ -22,8 +22,9 @@ public abstract class TransferTask extends AsyncTask {
protected long totalSize, finished;
protected TaskState state;
protected SeafException err;
+ protected String source;
- public TransferTask(int taskID, Account account, String repoName, String repoID, String path) {
+ public TransferTask(String source, int taskID, Account account, String repoName, String repoID, String path) {
this.account = account;
this.repoName = repoName;
this.repoID = repoID;
@@ -33,6 +34,7 @@ public TransferTask(int taskID, Account account, String repoName, String repoID,
// The size of the file would be known in the first progress update
this.totalSize = -1;
this.taskID = taskID;
+ this.source = source;
}
protected void cancel() {
@@ -53,6 +55,10 @@ public int getTaskID() {
return taskID;
}
+ public String getSource() {
+ return source;
+ }
+
public TaskState getState() {
return state;
}
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/UploadTask.java b/app/src/main/java/com/seafile/seadroid2/transfer/UploadTask.java
index ff55ec78e..8f0d5e62e 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/UploadTask.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/UploadTask.java
@@ -28,14 +28,15 @@ public class UploadTask extends TransferTask {
private boolean isCopyToLocal; // false to turn off copy operation
private boolean byBlock;
private UploadStateListener uploadStateListener;
+ private String source;
private DataManager dataManager;
public static final int HTTP_ABOVE_QUOTA = 443;
- public UploadTask(int taskID, Account account, String repoID, String repoName,
+ public UploadTask(String source, int taskID, Account account, String repoID, String repoName,
String dir, String filePath, boolean isUpdate, boolean isCopyToLocal, boolean byBlock,
UploadStateListener uploadStateListener) {
- super(taskID, account, repoName, repoID, filePath);
+ super(source, taskID, account, repoName, repoID, filePath);
this.dir = dir;
this.isUpdate = isUpdate;
this.isCopyToLocal = isCopyToLocal;
@@ -44,6 +45,7 @@ public UploadTask(int taskID, Account account, String repoID, String repoName,
this.totalSize = new File(filePath).length();
this.finished = 0;
this.dataManager = new DataManager(account);
+ this.source = source;
}
public UploadTaskInfo getTaskInfo() {
diff --git a/app/src/main/java/com/seafile/seadroid2/transfer/UploadTaskManager.java b/app/src/main/java/com/seafile/seadroid2/transfer/UploadTaskManager.java
index ebce9c544..ae347542d 100644
--- a/app/src/main/java/com/seafile/seadroid2/transfer/UploadTaskManager.java
+++ b/app/src/main/java/com/seafile/seadroid2/transfer/UploadTaskManager.java
@@ -25,12 +25,12 @@ public class UploadTaskManager extends TransferManager implements UploadStateLis
private static UploadNotificationProvider mNotifyProvider;
- public int addTaskToQue(Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal, boolean byBlock) {
+ public int addTaskToQue(String source, Account account, String repoID, String repoName, String dir, String filePath, boolean isUpdate, boolean isCopyToLocal, boolean byBlock) {
if (repoID == null || repoName == null)
return 0;
// create a new one to avoid IllegalStateException
- UploadTask task = new UploadTask(++notificationID, account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, byBlock, this);
+ UploadTask task = new UploadTask(source, ++notificationID, account, repoID, repoName, dir, filePath, isUpdate, isCopyToLocal, byBlock, this);
addTaskToQue(task);
return task.getTaskID();
}
@@ -54,7 +54,7 @@ public void retry(int taskID) {
UploadTask task = (UploadTask) getTask(taskID);
if (task == null || !task.canRetry())
return;
- addTaskToQue(task.getAccount(), task.getRepoID(), task.getRepoName(), task.getDir(), task.getPath(), task.isUpdate(), task.isCopyToLocal(),false);
+ addTaskToQue(task.getSource(), task.getAccount(), task.getRepoID(), task.getRepoName(), task.getDir(), task.getPath(), task.isUpdate(), task.isCopyToLocal(),false);
}
private void notifyProgress(int taskID) {
diff --git a/app/src/main/java/com/seafile/seadroid2/ui/activity/BrowserActivity.java b/app/src/main/java/com/seafile/seadroid2/ui/activity/BrowserActivity.java
index 33b2f1320..160099055 100644
--- a/app/src/main/java/com/seafile/seadroid2/ui/activity/BrowserActivity.java
+++ b/app/src/main/java/com/seafile/seadroid2/ui/activity/BrowserActivity.java
@@ -31,6 +31,7 @@
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -46,6 +47,8 @@
import com.seafile.seadroid2.SettingsManager;
import com.seafile.seadroid2.account.Account;
import com.seafile.seadroid2.account.AccountManager;
+import com.seafile.seadroid2.folderbackup.FolderBackupService;
+import com.seafile.seadroid2.folderbackup.FolderBackupService.FileBackupBinder;
import com.seafile.seadroid2.cameraupload.CameraUploadManager;
import com.seafile.seadroid2.cameraupload.MediaObserverService;
import com.seafile.seadroid2.data.CheckUploadServiceEvent;
@@ -158,6 +161,7 @@ public class BrowserActivity extends BaseActivity
private DataManager dataManager = null;
private TransferService txService = null;
+ private FolderBackupService mFolderBackupService = null;
private TransferReceiver mTransferReceiver;
private AccountManager accountManager;
private int currentPosition = 0;
@@ -229,11 +233,19 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = new AccountManager(this);
- EventBus.getDefault().register(this);
+
// restart service should it have been stopped for some reason
Intent mediaObserver = new Intent(this, MediaObserverService.class);
startService(mediaObserver);
+ Intent dIntent = new Intent(this, FolderBackupService.class);
+ startService(dIntent);
+ Log.d(DEBUG_TAG, "----start FolderBackupService");
+
+ Intent dirIntent = new Intent(this, FolderBackupService.class);
+ bindService(dirIntent, folderBackupConnection, Context.BIND_AUTO_CREATE);
+ Log.d(DEBUG_TAG, "----try bind FolderBackupService");
+
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = getIntent().getAction();
@@ -812,10 +824,31 @@ public void onServiceDisconnected(ComponentName arg0) {
}
};
+ ServiceConnection folderBackupConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ FileBackupBinder binder = (FileBackupBinder) service;
+ mFolderBackupService = binder.getService();
+ Log.d(DEBUG_TAG, "-----bind FolderBackupService");
+ boolean dirAutomaticUpload = SettingsManager.instance().isFolderAutomaticBackup();
+ String backupEmail = SettingsManager.instance().getBackupEmail();
+ if (dirAutomaticUpload && mFolderBackupService != null && !TextUtils.isEmpty(backupEmail)) {
+ mFolderBackupService.folderBackup(backupEmail);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ mFolderBackupService = null;
+ }
+ };
+
@Override
public void onStart() {
Log.d(DEBUG_TAG, "onStart");
super.onStart();
+ EventBus.getDefault().register(this);
+
if (android.os.Build.VERSION.SDK_INT < 14
&& SettingsManager.instance().isGestureLockRequired()) {
Intent intent = new Intent(this, UnlockGesturePasswordActivity.class);
@@ -875,6 +908,7 @@ protected void onNewIntent(Intent intent) {
protected void onStop() {
Log.d(DEBUG_TAG, "onStop");
super.onStop();
+ EventBus.getDefault().unregister(this);
if (mTransferReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mTransferReceiver);
@@ -884,11 +918,15 @@ protected void onStop() {
@Override
protected void onDestroy() {
Log.d(DEBUG_TAG, "onDestroy is called");
- EventBus.getDefault().unregister(this);
+
if (txService != null) {
unbindService(mConnection);
txService = null;
}
+ if (mFolderBackupService != null) {
+ unbindService(folderBackupConnection);
+ mFolderBackupService = null;
+ }
super.onDestroy();
}
@@ -2455,5 +2493,11 @@ public void onEvent(CheckUploadServiceEvent result) {
Log.d(DEBUG_TAG, "FileMonitorService============false ");
}
+ if (!Utils.isServiceRunning(BrowserActivity.this, "com.seafile.seadroid2.folderbackup.FolderBackupService")) {
+ monitorIntent = new Intent(this, FolderBackupService.class);
+ startService(monitorIntent);
+ Log.d(DEBUG_TAG, "FolderBackupService============false ");
+ }
+
}
}
diff --git a/app/src/main/java/com/seafile/seadroid2/ui/fragment/SettingsFragment.java b/app/src/main/java/com/seafile/seadroid2/ui/fragment/SettingsFragment.java
index 3bfe7cbc2..fdf9d7cdb 100644
--- a/app/src/main/java/com/seafile/seadroid2/ui/fragment/SettingsFragment.java
+++ b/app/src/main/java/com/seafile/seadroid2/ui/fragment/SettingsFragment.java
@@ -21,6 +21,8 @@
import com.bumptech.glide.Glide;
import com.google.common.collect.Maps;
+import com.hjq.permissions.OnPermissionCallback;
+import com.hjq.permissions.XXPermissions;
import com.seafile.seadroid2.R;
import com.seafile.seadroid2.SeadroidApplication;
import com.seafile.seadroid2.SeafException;
@@ -36,6 +38,11 @@
import com.seafile.seadroid2.data.DatabaseHelper;
import com.seafile.seadroid2.data.ServerInfo;
import com.seafile.seadroid2.data.StorageManager;
+import com.seafile.seadroid2.folderbackup.FolderBackupConfigActivity;
+import com.seafile.seadroid2.folderbackup.FolderBackupDBHelper;
+import com.seafile.seadroid2.folderbackup.FolderBackupEvent;
+import com.seafile.seadroid2.folderbackup.RepoConfig;
+import com.seafile.seadroid2.folderbackup.selectfolder.StringTools;
import com.seafile.seadroid2.gesturelock.LockPatternUtils;
import com.seafile.seadroid2.ui.activity.BrowserActivity;
import com.seafile.seadroid2.ui.activity.CreateGesturePasswordActivity;
@@ -46,6 +53,7 @@
import com.seafile.seadroid2.ui.dialog.ClearPasswordTaskDialog;
import com.seafile.seadroid2.ui.dialog.SwitchStorageTaskDialog;
import com.seafile.seadroid2.ui.dialog.TaskDialog.TaskDialogListener;
+import com.seafile.seadroid2.util.CameraSyncStatus;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
import com.seafile.seadroid2.util.Utils;
@@ -65,9 +73,12 @@ public class SettingsFragment extends CustomPreferenceFragment {
public static final String CAMERA_UPLOAD_BOTH_PAGES = "com.seafile.seadroid2.camera.upload";
public static final String CAMERA_UPLOAD_REMOTE_LIBRARY = "com.seafile.seadroid2.camera.upload.library";
+ public static final String FOLDER_BACKUP_REMOTE_PATH = "com.seafile.seadroid2.folder.backup.path";
public static final String CAMERA_UPLOAD_LOCAL_DIRECTORIES = "com.seafile.seadroid2.camera.upload.directories";
public static final String CONTACTS_UPLOAD_REMOTE_LIBRARY = "com.seafile.seadroid2.contacts.upload.library";
+ public static final String FOLDER_BACKUP_REMOTE_LIBRARY = "com.seafile.seadroid2.folder.backup.library";
public static final int CHOOSE_CAMERA_UPLOAD_REQUEST = 2;
+ public static final int CHOOSE_BACKUP_UPLOAD_REQUEST = 5;
// public static final int CHOOSE_CONTACTS_UPLOAD_REQUEST = 3;
// Account Info
private static Map accountInfoMap = Maps.newHashMap();
@@ -97,7 +108,17 @@ public class SettingsFragment extends CustomPreferenceFragment {
// private Preference cContactsRepoBackUp;
// private Preference cContactsRepoRecovery;
private long mMtime;
- private Preference cUploadRepoState;
+
+ private PreferenceCategory cFolderBackupCategory;
+ private Preference cBackupRepoState;
+ private Preference cBackupFolderMode;
+ private Preference cBackupFolderRepo;
+ private Preference cBackupFolderPref;
+ private Preference cBackupFolderState;
+ private Account act;
+ private List backupSelectPaths;
+ private FolderBackupDBHelper databaseHelper;
+ private RepoConfig selectRepoConfig;
@Override
public void onAttach(Activity activity) {
@@ -110,21 +131,25 @@ public void onAttach(Activity activity) {
accountMgr = new AccountManager(mActivity);
cameraManager = new CameraUploadManager(mActivity.getApplicationContext());
// contactsManager = new ContactsUploadManager(mActivity.getApplicationContext());
- Account act = accountMgr.getCurrentAccount();
+ act = accountMgr.getCurrentAccount();
dataMgr = new DataManager(act);
+ databaseHelper = FolderBackupDBHelper.getDatabaseHelper();
}
public void onCreate(Bundle savedInstanceState) {
Log.d(DEBUG_TAG, "onCreate");
super.onCreate(savedInstanceState);
- EventBus.getDefault().register(this);
+
settingsMgr.registerSharedPreferencesListener(settingsListener);
Account account = accountMgr.getCurrentAccount();
if (!Utils.isNetworkOn()) {
mActivity.showShortToast(mActivity, R.string.network_down);
return;
}
-
+ String backupPaths = SettingsManager.instance().getBackupPaths();
+ if (!TextUtils.isEmpty(backupPaths)) {
+ backupSelectPaths = StringTools.getJsonToList(backupPaths);
+ }
ConcurrentAsyncTask.execute(new RequestAccountInfoTask(), account);
}
@@ -132,7 +157,7 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public void onDestroy() {
super.onDestroy();
- EventBus.getDefault().unregister(this);
+
Log.d(DEBUG_TAG, "onDestroy()");
settingsMgr.unregisterSharedPreferencesListener(settingsListener);
}
@@ -269,11 +294,54 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
cPrivacyCategory.removePreference(clientEncPref);
}
}
+
+ cFolderBackupCategory = (PreferenceCategory) findPreference(SettingsManager.FOLDER_BACKUP_CATEGORY_KEY);
// Camera Upload
cUploadCategory = (PreferenceCategory) findPreference(SettingsManager.CAMERA_UPLOAD_CATEGORY_KEY);
cUploadAdvancedScreen = (PreferenceScreen) findPreference(SettingsManager.CAMERA_UPLOAD_ADVANCED_SCREEN_KEY);
cUploadAdvancedCategory = (PreferenceCategory) findPreference(SettingsManager.CAMERA_UPLOAD_ADVANCED_CATEGORY_KEY);
+ findPreference(SettingsManager.FOLDER_BACKUP_SWITCH_KEY).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+
+ if (newValue instanceof Boolean) {
+ boolean isChecked = (Boolean) newValue;
+ if (!isChecked) {
+ cFolderBackupCategory.removePreference(cBackupFolderMode);
+ cFolderBackupCategory.removePreference(cBackupFolderRepo);
+ cFolderBackupCategory.removePreference(cBackupFolderPref);
+ cFolderBackupCategory.removePreference(cBackupFolderState);
+ SettingsManager.instance().saveFolderAutomaticBackup(false);
+ } else {
+ XXPermissions.with(getActivity()).permission("android.permission.MANAGE_EXTERNAL_STORAGE").request(new OnPermissionCallback() {
+
+ @Override
+ public void onGranted(List permissions, boolean all) {
+ if (all) {
+ SettingsManager.instance().saveFolderAutomaticBackup(true);
+ refreshCameraUploadView();
+
+ }
+ }
+
+ @Override
+ public void onDenied(List permissions, boolean never) {
+ if (never) {
+ Toast.makeText(getActivity(), mActivity.getString(R.string.authorization_storage_permission), Toast.LENGTH_LONG).show();
+ XXPermissions.startPermissionActivity(getActivity(), permissions);
+ } else {
+ Toast.makeText(getActivity(), mActivity.getString(R.string.get_storage_permission_failed), Toast.LENGTH_LONG).show();
+ ((CheckBoxPreference) findPreference(SettingsManager.FOLDER_BACKUP_SWITCH_KEY)).setChecked(false);
+ }
+ }
+ });
+ }
+ return true;
+ }
+ return false;
+ }
+ });
findPreference(SettingsManager.CAMERA_UPLOAD_SWITCH_KEY).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -309,9 +377,45 @@ public boolean onPreferenceClick(Preference preference) {
return true;
}
});
+ // Change backup folder
+ cBackupFolderMode = findPreference(SettingsManager.FOLDER_BACKUP_MODE);
+ cBackupFolderRepo = findPreference(SettingsManager.FOLDER_BACKUP_LIBRARY_KEY);
+ cBackupFolderPref = findPreference(SettingsManager.SELECTED_BACKUP_FOLDERS_KEY);
+ cBackupFolderState = findPreference(SettingsManager.FOLDER_BACKUP_STATE);
+ cBackupFolderMode.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ showWifiDialog();
+ return true;
+ }
+ });
+ cBackupFolderRepo.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+
+ // choose remote library
+ Intent intent = new Intent(mActivity, FolderBackupConfigActivity.class);
+ intent.putExtra(FOLDER_BACKUP_REMOTE_LIBRARY, true);
+ startActivityForResult(intent, CHOOSE_BACKUP_UPLOAD_REQUEST);
+ return true;
+ }
+ });
+ cBackupFolderPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
- cUploadRepoState = findPreference(SettingsManager.CAMERA_UPLOAD_STATE);
- cUploadRepoState.setSummary(Utils.getUploadStateShow(getActivity()));
+ // choose remote folder path
+ Intent intent = new Intent(mActivity, FolderBackupConfigActivity.class);
+ intent.putExtra(FOLDER_BACKUP_REMOTE_PATH, true);
+ startActivityForResult(intent, CHOOSE_BACKUP_UPLOAD_REQUEST);
+ return true;
+ }
+ });
+
+ cBackupRepoState = findPreference(SettingsManager.CAMERA_UPLOAD_STATE);
+ if (cameraManager.isCameraUploadEnabled()) {
+ cBackupRepoState.setSummary(Utils.getUploadStateShow(getActivity()));
+ }
// Contacts Upload
// cContactsCategory = (PreferenceCategory) findPreference(SettingsManager.CONTACTS_UPLOAD_CATEGORY_KEY);
@@ -613,12 +717,46 @@ private void updateStorageLocationSummary() {
private void refreshCameraUploadView() {
Account camAccount = cameraManager.getCameraAccount();
+ String backupEmail = SettingsManager.instance().getBackupEmail();
if (camAccount != null && settingsMgr.getCameraUploadRepoName() != null) {
cUploadRepoPref.setSummary(camAccount.getSignature()
+ "/" + settingsMgr.getCameraUploadRepoName());
}
((CheckBoxPreference) findPreference(SettingsManager.CAMERA_UPLOAD_SWITCH_KEY)).setChecked(cameraManager.isCameraUploadEnabled());
+ ((CheckBoxPreference) findPreference(SettingsManager.FOLDER_BACKUP_SWITCH_KEY)).setChecked(SettingsManager.instance().isFolderAutomaticBackup());
+
+ if (SettingsManager.instance().isFolderAutomaticBackup()) {
+ cFolderBackupCategory.addPreference(cBackupFolderMode);
+ cFolderBackupCategory.addPreference(cBackupFolderRepo);
+ cFolderBackupCategory.addPreference(cBackupFolderPref);
+ cFolderBackupCategory.addPreference(cBackupFolderState);
+ if (backupSelectPaths == null || backupSelectPaths.size() == 0) {
+ cBackupFolderPref.setSummary("0");
+ } else {
+ cBackupFolderPref.setSummary(backupSelectPaths.size() + "");
+ }
+ Utils.utilsLogInfo(true, "=refreshCameraUploadView=======================" + backupEmail);
+ if (!TextUtils.isEmpty(backupEmail)) {
+ try {
+ selectRepoConfig = databaseHelper.getRepoConfig(backupEmail);
+ } catch (Exception e) {
+ Utils.utilsLogInfo(true, "=refreshCameraUploadView=======================" + e.toString());
+ }
+
+ }
+ if (selectRepoConfig != null && !TextUtils.isEmpty(selectRepoConfig.getRepoName())) {
+ cBackupFolderRepo.setSummary(backupEmail + "/" + selectRepoConfig.getRepoName());
+ } else {
+ cBackupFolderRepo.setSummary(getActivity().getString(R.string.folder_backup_select_repo_hint));
+ }
+
+ } else {
+ cFolderBackupCategory.removePreference(cBackupFolderMode);
+ cFolderBackupCategory.removePreference(cBackupFolderRepo);
+ cFolderBackupCategory.removePreference(cBackupFolderPref);
+ cFolderBackupCategory.removePreference(cBackupFolderState);
+ }
if (cameraManager.isCameraUploadEnabled()) {
cUploadCategory.addPreference(cUploadRepoPref);
@@ -715,6 +853,26 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
} else if (resultCode == Activity.RESULT_CANCELED) {
+ }
+ refreshCameraUploadView();
+ break;
+ case CHOOSE_BACKUP_UPLOAD_REQUEST:
+ if (resultCode == Activity.RESULT_OK) {
+ if (data == null) {
+ return;
+ }
+ final boolean pathOn = data.getBooleanExtra(FolderBackupConfigActivity.BACKUP_SELECT_PATHS_SWITCH, false);
+ final ArrayList pathListExtra = data.getStringArrayListExtra(FolderBackupConfigActivity.BACKUP_SELECT_PATHS);
+ if (pathOn && pathListExtra != null) {
+ if (backupSelectPaths == null) {
+ backupSelectPaths = new ArrayList<>();
+ } else {
+ backupSelectPaths.clear();
+ }
+ backupSelectPaths.addAll(pathListExtra);
+ cBackupFolderPref.setSummary(pathListExtra.size() + "");
+ }
+
}
refreshCameraUploadView();
break;
@@ -864,14 +1022,59 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
}
};
+ private void showWifiDialog() {
+
+ String[] buckModes = {"WIFI", getActivity().getString(R.string.folder_backup_mode)};
+ new AlertDialog.Builder(getActivity())
+ .setCancelable(false)
+ .setTitle(getActivity().getString(R.string.folder_backup_mode_title))
+ .setPositiveButton(getActivity().getString(R.string.ok), null)
+ .setSingleChoiceItems(buckModes, 0, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+
+ Toast.makeText(getActivity(), buckModes[i], Toast.LENGTH_SHORT).show();
+ if (i == 0) {
+ SettingsManager.instance().saveFolderBackupDataPlanAllowed(false);
+ } else {
+ SettingsManager.instance().saveFolderBackupDataPlanAllowed(true);
+ }
+ }
+ })
+ .show();
+
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventBus.getDefault().unregister(this);
+ }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(CameraSyncEvent result) {
+ int scanUploadStatus = SeadroidApplication.getInstance().getScanUploadStatus();
+ if (cameraManager.isCameraUploadEnabled() && scanUploadStatus > 0) {
+ if (scanUploadStatus == CameraSyncStatus.SCAN_END) {
+ SeadroidApplication.getInstance().setScanUploadStatus(CameraSyncStatus.NORMAL);
+ }
+ cBackupRepoState.setSummary(Utils.getUploadStateShow(getActivity()));
+ }
- cUploadRepoState.setSummary(Utils.getUploadStateShow(getActivity()));
+ }
- Log.d(DEBUG_TAG, "==========" + result.getLogInfo());
- Utils.utilsLogInfo(true,"==========" + result.getLogInfo());
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEvent(FolderBackupEvent result) {
+ int totalBackup = SeadroidApplication.getInstance().getTotalBackup();
+ int waitingBackup = SeadroidApplication.getInstance().getWaitingBackup();
+ cBackupFolderState.setSummary(getActivity().getString(R.string.uploaded) + " " + (totalBackup - waitingBackup) + " / " + totalBackup);
}
}
diff --git a/app/src/main/java/com/seafile/seadroid2/util/CameraSyncStatus.java b/app/src/main/java/com/seafile/seadroid2/util/CameraSyncStatus.java
index 1e8f9fab9..3d3f9038e 100644
--- a/app/src/main/java/com/seafile/seadroid2/util/CameraSyncStatus.java
+++ b/app/src/main/java/com/seafile/seadroid2/util/CameraSyncStatus.java
@@ -1,6 +1,7 @@
package com.seafile.seadroid2.util;
public class CameraSyncStatus {
+ public final static int NORMAL = 0;
public final static int SCANNING = 1;
public final static int NETWORK_UNAVAILABLE = 2;
public final static int UPLOADING = 3;
diff --git a/app/src/main/java/com/seafile/seadroid2/util/CrashHandler.java b/app/src/main/java/com/seafile/seadroid2/util/CrashHandler.java
new file mode 100644
index 000000000..898c9243f
--- /dev/null
+++ b/app/src/main/java/com/seafile/seadroid2/util/CrashHandler.java
@@ -0,0 +1,140 @@
+package com.seafile.seadroid2.util;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.seafile.seadroid2.R;
+import com.seafile.seadroid2.SeadroidApplication;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CrashHandler implements UncaughtExceptionHandler {
+ public static final String TAG = "CrashHandler";
+ private UncaughtExceptionHandler mDefaultHandler;
+ private static CrashHandler INSTANCE = new CrashHandler();
+ private Context mContext;
+ private Map infos = new HashMap();
+ private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+
+ private CrashHandler() {
+ }
+
+ public static CrashHandler getInstance() {
+ return INSTANCE;
+ }
+
+ public void init(Context context) {
+ mContext = context;
+ mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(this);
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ if (myHandleException(ex) && mDefaultHandler != null) {
+ Utils.utilsLogInfo(false, "=============uncaughtException");
+ Toast.makeText(mContext, mContext.getString(R.string.UncaughtExceptionHandler_err), Toast.LENGTH_SHORT).show();
+ mDefaultHandler.uncaughtException(thread, ex);
+ } else {
+ Utils.utilsLogInfo(false, "===else==========uncaughtException");
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "error : ", e);
+ }
+ android.os.Process.killProcess(android.os.Process.myPid());
+ System.exit(1);
+ }
+ }
+
+ public boolean myHandleException(Throwable ex) {
+ if (ex == null) {
+ return false;
+ }
+ collectDeviceInfo(mContext);
+ saveCrashInfo2File(ex);
+ return true;
+ }
+
+ public void collectDeviceInfo(Context ctx) {
+ try {
+ PackageManager pm = ctx.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+ if (pi != null) {
+ String versionName = pi.versionName == null ? "null" : pi.versionName;
+ String versionCode = pi.versionCode + "";
+ infos.put("versionName", versionName);
+ infos.put("versionCode", versionCode);
+ infos.put("phoneModelInfo", "phoneModelInfo-------" + SeafileLog.getDeviceBrand() + "/" + SeafileLog.getSystemModel() + "/" + SeafileLog.getSystemVersion());
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "an error occured when collect package info", e);
+ }
+ Field[] fields = Build.class.getDeclaredFields();
+ for (Field field : fields) {
+ try {
+ field.setAccessible(true);
+ infos.put(field.getName(), field.get(null).toString());
+ } catch (Exception e) {
+ Log.e(TAG, "an error occured when collect crash info", e);
+ }
+ }
+ }
+
+ private String saveCrashInfo2File(Throwable ex) {
+
+ StringBuffer sb = new StringBuffer();
+ for (Map.Entry entry : infos.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ sb.append(key + "=" + value + "\n");
+ }
+
+ Writer writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ ex.printStackTrace(printWriter);
+ Throwable cause = ex.getCause();
+ while (cause != null) {
+ cause.printStackTrace(printWriter);
+ cause = cause.getCause();
+ }
+ printWriter.close();
+ String result = writer.toString();
+ sb.append(result);
+ try {
+ long timestamp = System.currentTimeMillis();
+ String time = formatter.format(new Date());
+ String fileName = Utils.EXCEPTION_TYPE_CRASH + time + "-" + timestamp + ".log";
+
+ File[] externalMediaDirs = SeadroidApplication.getAppContext().getExternalMediaDirs();
+ String rootPath = externalMediaDirs[0].getAbsolutePath();
+ File dirsFile = new File(rootPath + "/Seafile/");
+ if (!dirsFile.exists()) {
+ dirsFile.mkdirs();
+ }
+ FileOutputStream fos = new FileOutputStream(rootPath + "/Seafile/" + fileName);
+ fos.write(sb.toString().getBytes());
+ fos.close();
+ return fileName;
+ } catch (Exception e) {
+ Log.e(TAG, "an error occured while writing file...", e);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/seafile/seadroid2/util/Utils.java b/app/src/main/java/com/seafile/seadroid2/util/Utils.java
index a14fd52f7..e79a6ae52 100644
--- a/app/src/main/java/com/seafile/seadroid2/util/Utils.java
+++ b/app/src/main/java/com/seafile/seadroid2/util/Utils.java
@@ -89,6 +89,8 @@ public class Utils {
// public static final String NOGROUP = "$nogroup";
public static final String PERSONAL_REPO = "personal_repo";
public static final String SHARED_REPO = "shared_repo";
+ public static final String TRANSFER_PHOTO_TAG = "camera_upload";
+ public static final String TRANSFER_FOLDER_TAG = "folder_backup";
private static final String DEBUG_TAG = "Utils";
private static final String HIDDEN_PREFIX = ".";
private static HashMap suffixIconMap = null;
@@ -300,6 +302,23 @@ public static int getFileIcon(String name) {
return getResIdforMimetype(mime);
}
+ public static int getFileIconSuffix(String suffix) {
+ if (suffix.length() == 0) {
+ return R.drawable.file;
+ }
+
+ HashMap map = getSuffixIconMap();
+ Integer i = map.get(suffix);
+ if (i != null)
+ return i;
+
+ if (suffix.equals("flv")) {
+ return R.drawable.file_video;
+ }
+ String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(suffix);
+ return getResIdforMimetype(mime);
+ }
+
public static boolean isViewableImage(String name) {
String suffix = name.substring(name.lastIndexOf('.') + 1).toLowerCase();
if (suffix.length() == 0)
@@ -486,6 +505,9 @@ public static String getFileMimeType(File file) {
}
public static void copyFile(File src, File dst) throws IOException {
+ if (src == null || dst == null) {
+ return;
+ }
InputStream in = new BufferedInputStream(new FileInputStream(src));
OutputStream out = new BufferedOutputStream(new FileOutputStream(dst));
@@ -994,5 +1016,6 @@ public static void utilsLogInfo(boolean b, String info) {
Log.d(DEBUG_TAG, info);
}
}
+ public static final String EXCEPTION_TYPE_CRASH = "crash_exception";
}
diff --git a/app/src/main/res/drawable-xhdpi/ic_tabbar_partition.png b/app/src/main/res/drawable-xhdpi/ic_tabbar_partition.png
new file mode 100644
index 000000000..93dec392d
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_tabbar_partition.png differ
diff --git a/app/src/main/res/drawable/bottom_line.xml b/app/src/main/res/drawable/bottom_line.xml
new file mode 100644
index 000000000..a0e88ca37
--- /dev/null
+++ b/app/src/main/res/drawable/bottom_line.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bg_settings_section_folder_backup.xml b/app/src/main/res/layout/bg_settings_section_folder_backup.xml
new file mode 100644
index 000000000..7b2291262
--- /dev/null
+++ b/app/src/main/res/layout/bg_settings_section_folder_backup.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cuc_local_directory_fragment.xml b/app/src/main/res/layout/cuc_local_directory_fragment.xml
index 70124d727..77271671f 100644
--- a/app/src/main/res/layout/cuc_local_directory_fragment.xml
+++ b/app/src/main/res/layout/cuc_local_directory_fragment.xml
@@ -40,20 +40,24 @@
android:textSize="@dimen/tv_subtitle_txt_size"/>
-
-
-
+