1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2025-01-10 20:15:54 +00:00

Added support for all versioning types in the app UI. Addressing #718 (#896)

* Merge branch 'Fix718'
Merged fixes for issues #718, added functionality for all the file versioning types in the app UI.

* Basic theme is working

* GOt dialog working with setREsult.

* Addressed issues raised and fixed orientation change bug.
Adressed issues radied by nutomic:
- Changed the String concatenation so it uses String.format.
- Made members variables private that were not previously private by mistake.
- Changed name from FolderPathTextView to FolderPathTextViewFragment.
- Fixed typo in NumberPickerFragment
- Now uses TImeUnit for time conversion
- Changed from DialogFragment to a dialog themed Activity inorder to have correct themeing.

About the orientation change bug. When the dialog themed activity was open and the orientation was changed, it would cause the background activity(The FolderActivity) to
recreate itself. In the process of recreating itself it would reset the mFolder variable and then reinitialise it in the OnApiChange listener. However the result from the
Dialog was returned before the mFolder had been reinitialised and was still null, so inbetween the result being returned and the mFolder variable being reinitialised the new file versioning
configuration had to be stored and then applied to the mFolder variable in the onApiChange listener. The file versioning configuration is temporarily stored in the mVersioning variable which is
an instance of Folder.versioning.
This error only occurred when using an Activity as a dialog and using the startActivityForResult method for initialisling the activity. Before when using a dialogfragment and an interface to callback to
the parent activity, everything happend in the correct order.

* Fixed versioning dialog lag.
The initial updateFragmentView() is called in onCreate() and the
fragment is only updated when a new file versioning type is selected.

* Removed FolderPathTextViewFragment
The fragment is removed, and a helper method has been added to FolderPickerActivity to create an intent
to the the FolderPickerActivity for result.
This commit is contained in:
Jessie Chatham Spencer 2017-06-15 08:44:44 +00:00 committed by Felix Ableitner
parent 941f9aa0c8
commit 0c1abb3c9c
23 changed files with 963 additions and 153 deletions

View file

@ -28,6 +28,7 @@ dependencies {
compile 'com.google.guava:guava:20.0'
compile 'com.annimon:stream:1.1.7'
compile 'com.android.volley:volley:1.0.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support:support-annotations:25.3.1'
}

View file

@ -132,6 +132,14 @@
<action android:name="com.nutomic.syncthingandroid.action.STOP" />
</intent-filter>
</receiver>
<activity android:name=".activities.VersioningDialogActivity"
android:label="@string/file_versioning"
android:theme="@style/Theme.Syncthing.Dialog"
android:parentActivityName=".activities.FolderActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.nutomic.syncthingandroid.activities.FolderActivity" />
</activity>
</application>
</manifest>
</manifest>

View file

@ -3,7 +3,6 @@ package com.nutomic.syncthingandroid.activities;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
@ -27,14 +26,17 @@ import android.widget.Toast;
import com.google.common.base.Objects;
import com.google.gson.Gson;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.dialog.KeepVersionsDialogFragment;
import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.service.SyncthingService;
import com.nutomic.syncthingandroid.util.TextWatcherAdapter;
import org.w3c.dom.Text;
import java.util.List;
import java.util.Random;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginEnd;
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginStart;
@ -42,7 +44,6 @@ import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.nutomic.syncthingandroid.service.SyncthingService.State.ACTIVE;
import static java.lang.String.valueOf;
/**
* Shows folder details and allows changing them.
@ -59,15 +60,13 @@ public class FolderActivity extends SyncthingActivity
public static final String EXTRA_DEVICE_ID =
"com.nutomic.syncthingandroid.activities.FolderActivity.DEVICE_ID";
private static final int DIRECTORY_REQUEST_CODE = 234;
private static final String TAG = "EditFolderFragment";
public static final String KEEP_VERSIONS_DIALOG_TAG = "KeepVersionsDialogFragment";
private static final String IS_SHOWING_DELETE_DIALOG = "DELETE_FOLDER_DIALOG_STATE";
private static final String IS_SHOW_DISCARD_DIALOG = "DISCARD_FOLDER_DIALOG_STATE";
private static final int FILE_VERSIONING_DIALOG_REQUEST = 3454;
private Folder mFolder;
private EditText mLabelView;
@ -75,7 +74,8 @@ public class FolderActivity extends SyncthingActivity
private TextView mPathView;
private SwitchCompat mFolderMasterView;
private ViewGroup mDevicesContainer;
private TextView mVersioningKeepView;
private TextView mVersioningDescriptionView;
private TextView mVersioningTypeView;
private boolean mIsCreateMode;
private boolean mFolderNeedsToUpdate;
@ -83,7 +83,7 @@ public class FolderActivity extends SyncthingActivity
private Dialog mDeleteDialog;
private Dialog mDiscardDialog;
private final KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment();
private Folder.Versioning mVersioning;
private final TextWatcher mTextWatcher = new TextWatcherAdapter() {
@Override
@ -117,33 +117,6 @@ public class FolderActivity extends SyncthingActivity
}
};
private final KeepVersionsDialogFragment.OnValueChangeListener mOnValueChangeListener =
new KeepVersionsDialogFragment.OnValueChangeListener() {
@Override
public void onValueChange(int intValue) {
if (intValue == 0) {
mFolder.versioning = new Folder.Versioning();
mVersioningKeepView.setText(R.string.off);
} else {
mFolder.versioning.type = "simple";
mFolder.versioning.params.put("keep", valueOf(intValue));
mVersioningKeepView.setText(valueOf(intValue));
}
mFolderNeedsToUpdate = true;
}
};
private final View.OnClickListener mPathViewClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FolderActivity.this, FolderPickerActivity.class);
if (!TextUtils.isEmpty(mFolder.path)) {
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, mFolder.path);
}
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -155,14 +128,16 @@ public class FolderActivity extends SyncthingActivity
mLabelView = (EditText) findViewById(R.id.label);
mIdView = (EditText) findViewById(R.id.id);
mPathView = (TextView) findViewById(R.id.directory);
mPathView = (TextView) findViewById(R.id.directoryTextView);
mFolderMasterView = (SwitchCompat) findViewById(R.id.master);
mVersioningKeepView = (TextView) findViewById(R.id.versioningKeep);
mVersioningDescriptionView = (TextView) findViewById(R.id.versioningDescription);
mVersioningTypeView = (TextView) findViewById(R.id.versioningType);
mDevicesContainer = (ViewGroup) findViewById(R.id.devicesContainer);
mPathView.setOnClickListener(mPathViewClickListener);
findViewById(R.id.versioningContainer).setOnClickListener(v ->
mKeepVersionsDialogFragment.show(getFragmentManager(), KEEP_VERSIONS_DIALOG_TAG));
mPathView.setOnClickListener(view ->
startActivityForResult(FolderPickerActivity.createIntent(this, mFolder.path), FolderPickerActivity.DIRECTORY_REQUEST_CODE));
findViewById(R.id.versioningContainer).setOnClickListener(v -> showVersioningDialog());
if (mIsCreateMode) {
if (savedInstanceState != null) {
@ -186,6 +161,33 @@ public class FolderActivity extends SyncthingActivity
showDeleteDialog();
}
}
if (savedInstanceState != null){
if (savedInstanceState.getBoolean(IS_SHOWING_DELETE_DIALOG)){
showDeleteDialog();
}
}
}
private void showVersioningDialog() {
Intent intent = new Intent(this, VersioningDialogActivity.class);
intent.putExtras(getVersioningBundle());
startActivityForResult(intent, FILE_VERSIONING_DIALOG_REQUEST);
}
private Bundle getVersioningBundle() {
Bundle bundle = new Bundle();
for (Map.Entry<String, String> entry: mFolder.versioning.params.entrySet()){
bundle.putString(entry.getKey(), entry.getValue());
}
if (TextUtils.isEmpty(mFolder.versioning.type)){
bundle.putString("type", "none");
} else{
bundle.putString("type", mFolder.versioning.type);
}
return bundle;
}
@Override
@ -262,20 +264,32 @@ public class FolderActivity extends SyncthingActivity
mFolderNeedsToUpdate = true;
}
attemptToApplyVersioningConfig();
updateViewsAndSetListeners();
}
// If the FolderActivity gets recreated after the VersioningDialogActivity is closed, then the result from the VersioningDialogActivity will be received before
// the mFolder variable has been recreated, so the versioning config will be stored in the mVersioning variable until the mFolder variable has been
// recreated in the onApiChange(). This has been observed to happen after the screen orientation has changed while the VersioningDialogActivity was open.
private void attemptToApplyVersioningConfig() {
if (mFolder != null && mVersioning != null){
mFolder.versioning = mVersioning;
mVersioning = null;
}
}
private void updateViewsAndSetListeners() {
mLabelView.removeTextChangedListener(mTextWatcher);
mIdView.removeTextChangedListener(mTextWatcher);
mPathView.removeTextChangedListener(mTextWatcher);
mFolderMasterView.setOnCheckedChangeListener(null);
mKeepVersionsDialogFragment.setOnValueChangeListener(null);
// Update views
mLabelView.setText(mFolder.label);
mIdView.setText(mFolder.id);
mPathView.setText(mFolder.path);
updateVersioningDescription();
mFolderMasterView.setChecked(Objects.equal(mFolder.type, "readonly"));
List<Device> devicesList = getApi().getDevices(false);
@ -288,22 +302,11 @@ public class FolderActivity extends SyncthingActivity
}
}
boolean versioningEnabled = Objects.equal(mFolder.versioning.type, "simple");
int versions = 0;
if (versioningEnabled) {
versions = Integer.valueOf(mFolder.versioning.params.get("keep"));
mVersioningKeepView.setText(valueOf(versions));
} else {
mVersioningKeepView.setText(R.string.off);
}
mKeepVersionsDialogFragment.setValue(versions);
// Keep state updated
mLabelView.addTextChangedListener(mTextWatcher);
mIdView.addTextChangedListener(mTextWatcher);
mPathView.addTextChangedListener(mTextWatcher);
mFolderMasterView.setOnCheckedChangeListener(mCheckedListener);
mKeepVersionsDialogFragment.setOnValueChangeListener(mOnValueChangeListener);
}
@Override
@ -365,10 +368,12 @@ public class FolderActivity extends SyncthingActivity
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && requestCode == FolderPickerActivity.DIRECTORY_REQUEST_CODE) {
mFolder.path = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
mPathView.setText(mFolder.path);
mFolderNeedsToUpdate = true;
} else if (resultCode == Activity.RESULT_OK && requestCode == FILE_VERSIONING_DIALOG_REQUEST) {
updateVersioning(data.getExtras());
}
}
@ -460,4 +465,64 @@ public class FolderActivity extends SyncthingActivity
.setNegativeButton(android.R.string.cancel, null)
.create();
}
private void updateVersioning(Bundle arguments) {
if (mFolder != null){
mVersioning = mFolder.versioning;
} else {
mVersioning = new Folder.Versioning();
}
String type = arguments.getString("type");
arguments.remove("type");
if (type.equals("none")){
mVersioning = new Folder.Versioning();
} else {
for (String key : arguments.keySet()) {
mVersioning.params.put(key, arguments.getString(key));
}
mVersioning.type = type;
}
attemptToApplyVersioningConfig();
updateVersioningDescription();
mFolderNeedsToUpdate = true;
}
private void updateVersioningDescription() {
if (mFolder == null){
return;
}
if (TextUtils.isEmpty(mFolder.versioning.type)) {
setVersioningDescription(getString(R.string.none), "");
return;
}
switch (mFolder.versioning.type) {
case "simple":
setVersioningDescription(getString(R.string.type_simple),
getString(R.string.simple_versioning_info, mFolder.versioning.params.get("keep")));
break;
case "trashcan":
setVersioningDescription(getString(R.string.type_trashcan),
getString(R.string.trashcan_versioning_info, mFolder.versioning.params.get("cleanoutDays")));
break;
case "staggered":
int maxAge = (int) TimeUnit.SECONDS.toDays(Long.valueOf(mFolder.versioning.params.get("maxAge")));
setVersioningDescription(getString(R.string.type_staggered),
getString(R.string.staggered_versioning_info, maxAge, mFolder.versioning.params.get("versionsPath")));
break;
case "external":
setVersioningDescription(getString(R.string.type_external),
getString(R.string.external_versioning_info, mFolder.versioning.params.get("command")));
break;
}
}
private void setVersioningDescription(String type, String description) {
mVersioningTypeView.setText(type);
mVersioningDescriptionView.setText(description);
}
}

View file

@ -14,6 +14,7 @@ import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -50,6 +51,8 @@ public class FolderPickerActivity extends SyncthingActivity
public static final String EXTRA_RESULT_DIRECTORY =
"com.nutomic.syncthingandroid.activities.FolderPickerActivity.RESULT_DIRECTORY";
public static final int DIRECTORY_REQUEST_CODE = 234;
private ListView mListView;
private FileAdapter mFilesAdapter;
@ -61,6 +64,16 @@ public class FolderPickerActivity extends SyncthingActivity
*/
private File mLocation;
public static Intent createIntent(Context context, String currentPath) {
Intent intent = new Intent(context, FolderPickerActivity.class);
if (!TextUtils.isEmpty(currentPath)) {
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, currentPath);
}
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View file

@ -0,0 +1,134 @@
package com.nutomic.syncthingandroid.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.dialog.ExternalVersioningFragment;
import com.nutomic.syncthingandroid.fragments.dialog.NoVersioningFragment;
import com.nutomic.syncthingandroid.fragments.dialog.SimpleVersioningFragment;
import com.nutomic.syncthingandroid.fragments.dialog.StaggeredVersioningFragment;
import com.nutomic.syncthingandroid.fragments.dialog.TrashCanVersioningFragment;
import java.util.Arrays;
import java.util.List;
public class VersioningDialogActivity extends AppCompatActivity {
private Fragment mCurrentFragment;
private List<String> mTypes = Arrays.asList("none", "trashcan", "simple", "staggered", "external");
private Bundle mArguments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_versioning_dialog);
if (savedInstanceState != null) {
mArguments = savedInstanceState.getBundle("arguments");
} else {
mArguments = getIntent().getExtras();
}
updateFragmentView(mTypes.indexOf(getIntent().getExtras().getString("type")));
initiateFinishBtn();
initiateSpinner();
}
private void initiateFinishBtn() {
Button finishBtn = (Button) findViewById(R.id.finish_btn);
finishBtn.setOnClickListener(v -> {
saveConfiguration();
finish();
});
}
private void saveConfiguration() {
Intent intent = new Intent();
intent.putExtras(mCurrentFragment.getArguments());
setResult(Activity.RESULT_OK, intent);
}
private void initiateSpinner() {
Spinner versioningTypeSpinner = (Spinner) findViewById(R.id.versioningTypeSpinner);
versioningTypeSpinner.setSelection(mTypes.indexOf(getIntent().getExtras().getString("type")));
versioningTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position != mTypes.indexOf(getIntent().getExtras().getString("type"))) {
updateVersioningType(position);
updateFragmentView(position);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
private void updateVersioningType(int position) {
mArguments.putString("type", mTypes.get(position));
}
private void updateFragmentView(int selection) {
if (mCurrentFragment != null){
mArguments = mCurrentFragment.getArguments();
}
mCurrentFragment = getFragment(selection);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//This Activtiy (VersioningDialogActivity) contains all the file versioning parameters that have been passed from the FolderActivity in the intent extras, so we simply
// pass that to the currentfragment.
mCurrentFragment.setArguments(mArguments);
transaction.replace(R.id.versioningFragmentContainer, mCurrentFragment);
transaction.commit();
}
private Fragment getFragment(int selection) {
Fragment fragment = null;
switch (selection) {
case 0:
fragment = new NoVersioningFragment();
break;
case 1:
fragment = new TrashCanVersioningFragment();
break;
case 2:
fragment = new SimpleVersioningFragment();
break;
case 3:
fragment = new StaggeredVersioningFragment();
break;
case 4:
fragment = new ExternalVersioningFragment();
break;
}
return fragment;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBundle("arguments", mCurrentFragment.getArguments());
}
@Override
public void onBackPressed() {
saveConfiguration();
super.onBackPressed();
}
}

View file

@ -0,0 +1,39 @@
package com.nutomic.syncthingandroid.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.NumberPicker;
import com.nutomic.syncthingandroid.R;
/**
* Simply displays a numberpicker and allows easy access to configure it with the public functions.
*/
public class NumberPickerFragment extends Fragment {
private NumberPicker mNumberPicker;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mNumberPicker = (NumberPicker) inflater.inflate(R.layout.numberpicker_fragment, container, false);
mNumberPicker.setWrapSelectorWheel(false);
return mNumberPicker;
}
public void setOnValueChangedLisenter(NumberPicker.OnValueChangeListener onValueChangeListener){
mNumberPicker.setOnValueChangedListener(onValueChangeListener);
}
public void updateNumberPicker(int maxValue, int minValue, int currentValue){
mNumberPicker.setMaxValue(maxValue);
mNumberPicker.setMinValue(minValue);
mNumberPicker.setValue(currentValue);
}
}

View file

@ -0,0 +1,74 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.nutomic.syncthingandroid.R;
/**
* Contains the configuration options for external file versioning.
*/
public class ExternalVersioningFragment extends Fragment {
private View mView;
private Bundle mArguments;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_external_versioning, container, false);
mArguments = getArguments();
fillArguments();
initateTextView();
return mView;
}
private void fillArguments() {
if (missingParameters()){
mArguments.putString("command", "");
}
}
private boolean missingParameters() {
return !mArguments.containsKey("command");
}
private void initateTextView() {
TextView commandTextView = (TextView) mView.findViewById(R.id.commandTextView);
commandTextView.setText(getCommand());
commandTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence command, int start, int before, int count) {
updateCommand(command.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
private void updateCommand(String command) {
mArguments.putString("command", command);
}
private String getCommand() {
return mArguments.containsKey("command") ? mArguments.getString("command") : "" ;
}
}

View file

@ -1,88 +0,0 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.widget.FrameLayout.LayoutParams;
import android.widget.NumberPicker;
import com.nutomic.syncthingandroid.R;
import static android.view.Gravity.CENTER;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class KeepVersionsDialogFragment extends DialogFragment {
private static String KEEP_VERSION_VALUE = "KEEP_VERSION_KEY";
private OnValueChangeListener mOnValueChangeListener;
private NumberPicker mNumberPickerView;
private int mValue;
private final DialogInterface.OnClickListener mDialogButtonListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
mValue = mNumberPickerView.getValue();
if (mOnValueChangeListener != null)
mOnValueChangeListener.onValueChange(mValue);
break;
}
}
};
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (savedInstanceState != null) {
setValue(savedInstanceState.getInt(KEEP_VERSION_VALUE));
}
mNumberPickerView = createNumberPicker();
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.keep_versions)
.setView(mNumberPickerView)
.setPositiveButton(android.R.string.ok, mDialogButtonListener)
.setNegativeButton(android.R.string.cancel, null)
.create();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEEP_VERSION_VALUE, mNumberPickerView.getValue());
}
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) {
mOnValueChangeListener = onValueChangeListener;
}
public void setValue(int value) {
this.mValue = value;
if (mNumberPickerView != null) {
mNumberPickerView.setValue(value);
}
}
private NumberPicker createNumberPicker() {
NumberPicker picker = new NumberPicker(getActivity());
picker.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, CENTER));
picker.setMinValue(0);
picker.setMaxValue(5);
picker.setValue(mValue);
picker.setWrapSelectorWheel(false);
return picker;
}
public interface OnValueChangeListener {
void onValueChange(int value);
}
}

View file

@ -0,0 +1,19 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.nutomic.syncthingandroid.R;
public class NoVersioningFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_no_versioning, container, false);
}
}

View file

@ -0,0 +1,61 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.NumberPicker;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.NumberPickerFragment;
/**
* Contains the configuration options for simple file versioning.
*/
public class SimpleVersioningFragment extends Fragment {
private View mView;
private Bundle mArguments;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_simple_versioning, container, false);
mArguments = getArguments();
fillArguments();
updateNumberPicker();
return mView;
}
private void fillArguments() {
if (missingParameters()){
mArguments.putString("keep", "5");
}
}
private boolean missingParameters() {
return !mArguments.containsKey("keep");
}
//a NumberPickerFragment is nested in the fragment_simple_versioning layout, the values for it are update below.
private void updateNumberPicker() {
NumberPickerFragment numberPicker = (NumberPickerFragment) getChildFragmentManager().findFragmentByTag("numberpicker_simple_versioning");
numberPicker.updateNumberPicker(100000, 1, getKeepVersions());
numberPicker.setOnValueChangedLisenter((picker, oldVal, newVal) -> updateKeepVersions((String.valueOf(newVal))));
}
private void updateKeepVersions(String newValue) {
mArguments.putString("keep", newValue);
}
private int getKeepVersions() {
return Integer.valueOf(mArguments.getString("keep"));
}
}

View file

@ -0,0 +1,97 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
import com.nutomic.syncthingandroid.fragments.NumberPickerFragment;
import java.util.concurrent.TimeUnit;
/**
* Contains the configuration options for Staggered file versioning.
*/
public class StaggeredVersioningFragment extends Fragment {
private View mView;
private Bundle mArguments;
private TextView mPathView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_staggered_versioning, container, false);
mArguments = getArguments();
fillArguments();
updateNumberPicker();
initiateVersionsPathTextView();
return mView;
}
private void fillArguments() {
if (missingParameters()) {
mArguments.putString("maxAge", "0");
mArguments.putString("versionsPath", "");
}
}
private boolean missingParameters() {
return !mArguments.containsKey("maxAge");
}
//The maxAge parameter is displayed in days but stored in seconds since Syncthing needs it in seconds.
//A NumberPickerFragment is nested in the fragment_staggered_versioning layout, the values for it are update below.
private void updateNumberPicker() {
NumberPickerFragment numberPicker = (NumberPickerFragment) getChildFragmentManager().findFragmentByTag("numberpicker_staggered_versioning");
numberPicker.updateNumberPicker(100, 0, getMaxAgeInDays());
numberPicker.setOnValueChangedLisenter((picker, oldVal, newVal) -> updatePreference("maxAge", (String.valueOf(TimeUnit.DAYS.toSeconds(newVal)))));
}
private void initiateVersionsPathTextView() {
mPathView = (TextView) mView.findViewById(R.id.directoryTextView);
String currentPath = getVersionsPath();
mPathView.setText(currentPath);
mPathView.setOnClickListener(view ->
startActivityForResult(FolderPickerActivity.createIntent(getContext(), currentPath), FolderPickerActivity.DIRECTORY_REQUEST_CODE));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK && requestCode == FolderPickerActivity.DIRECTORY_REQUEST_CODE ) {
updatePath(data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY));
}
}
private void updatePath(String directory) {
mPathView.setText(directory);
updatePreference("versionsPath", directory);
}
private String getVersionsPath() {
return mArguments.getString("versionsPath");
}
private void updatePreference(String key, String newValue) {
getArguments().putString(key, newValue);
}
private int getMaxAgeInDays() {
return (int) TimeUnit.SECONDS.toDays(Long.valueOf(mArguments.getString("maxAge")));
}
}

View file

@ -0,0 +1,57 @@
package com.nutomic.syncthingandroid.fragments.dialog;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.NumberPickerFragment;
/**
* Contains the configuration options for trashcan file versioning.
*/
public class TrashCanVersioningFragment extends Fragment {
private View mView;
private Bundle mArguments;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_trashcan_versioning, container, false);
mArguments = getArguments();
fillArguments();
updateNumberPicker();
return mView;
}
private void fillArguments() {
if (missingParameters()) {
mArguments.putString("cleanoutDays", "0");
}
}
private boolean missingParameters() {
return !mArguments.containsKey("cleanoutDays");
}
//a NumberPickerFragment is nested in the fragment_trashcan_versioning layout, the values for it are update below.
private void updateNumberPicker() {
NumberPickerFragment numberPicker = (NumberPickerFragment) getChildFragmentManager().findFragmentByTag("numberpicker_trashcan_versioning");
numberPicker.updateNumberPicker(100, 0, getCleanoutDays());
numberPicker.setOnValueChangedLisenter((picker, oldVal, newVal) -> updateCleanoutDays((String.valueOf(newVal))));
}
private int getCleanoutDays() {
return Integer.valueOf(mArguments.getString("cleanoutDays"));
}
private void updateCleanoutDays(String newValue) {
mArguments.putString("cleanoutDays", newValue);
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.nutomic.syncthingandroid.activities.VersioningDialogActivity">
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/directoryTextView"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_black_24dp"
android:drawableStart="@drawable/ic_folder_black_24dp"
android:focusable="true"
android:hint="@string/directory"/>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/command"
android:textAlignment="center"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<EditText
android:id="@+id/commandTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
/>
<TextView
android:id="@+id/textView6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/external_versioning_description" />
</LinearLayout>

View file

@ -45,14 +45,14 @@
android:inputType="textCapWords" />
<TextView
android:id="@+id/directory"
android:id="@+id/directoryTextView"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_black_24dp"
android:drawableStart="@drawable/ic_folder_black_24dp"
android:focusable="true"
android:hint="@string/directory" />
android:hint="@string/directory"/>
<LinearLayout
android:id="@+id/devicesContainer"
@ -67,6 +67,7 @@
android:drawableLeft="@drawable/ic_device_hub_black_24dp_active"
android:drawableStart="@drawable/ic_device_hub_black_24dp_active"
android:text="@string/devices" />
</LinearLayout>
<android.support.v7.widget.SwitchCompat
@ -84,27 +85,39 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal">
android:orientation="vertical"
android:gravity="center_vertical">
<TextView
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@null"
android:checked="false"
android:drawableLeft="@drawable/ic_history_black_24dp_active"
android:drawableStart="@drawable/ic_history_black_24dp_active"
android:text="@string/keep_versions" />
android:text="@string/file_versioning" />
<TextView
android:id="@+id/versioningKeep"
android:layout_width="wrap_content"
android:id="@+id/versioningType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/abc_action_bar_content_inset_material"
android:layout_marginRight="@dimen/abc_action_bar_content_inset_material"
android:layout_marginLeft="75dp"
android:layout_marginStart="75dp"
android:layout_marginTop="-20dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/versioningDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:layout_marginStart="75dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/no_versioning_description" />
</LinearLayout>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="500dp"
android:orientation="vertical" >
<TextView
android:id="@+id/textView8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/simple_file_versioning_description"
android:layout_margin="10dp" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/keep_versions"
android:textAlignment="center"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<fragment
android:id="@+id/fragment"
android:name="com.nutomic.syncthingandroid.fragments.NumberPickerFragment"
android:layout_width="match_parent"
android:layout_height="100dp"
android:tag="numberpicker_simple_versioning"
tools:layout="@layout/numberpicker_fragment" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/keep_versions_description"
android:layout_margin="10dp" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textView10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/staggered_versioning_description"
android:layout_margin="10dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/maximum_age"
android:textAlignment="center"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold"
android:layout_margin="10dp"
android:layout_marginBottom="0dp" />
<fragment
android:id="@+id/fragment"
android:name="com.nutomic.syncthingandroid.fragments.NumberPickerFragment"
android:layout_width="match_parent"
android:layout_height="100dp"
android:tag="numberpicker_staggered_versioning"
tools:layout="@layout/numberpicker_fragment" />
<TextView
android:id="@+id/textView11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/maximum_age_description"
android:layout_margin="10dp"
/>
<TextView
android:id="@+id/textView13"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/versions_path"
android:textAlignment="center"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold"
/>
<TextView
android:id="@+id/directoryTextView"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_black_24dp"
android:drawableStart="@drawable/ic_folder_black_24dp"
android:focusable="true"
android:hint="@string/directory"/>
<TextView
android:id="@+id/textView12"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/versions_path_description" />
</LinearLayout>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/textView9"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/trashcan_versioning_description"
android:layout_margin="10dp" />
<TextView
android:id="@+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/clean_out_after"
android:textAlignment="center"
android:textColor="@android:color/black"
android:textSize="18sp"
android:layout_margin="10dp"
android:textStyle="bold"/>
<fragment
android:id="@+id/fragment"
android:name="com.nutomic.syncthingandroid.fragments.NumberPickerFragment"
android:layout_width="match_parent"
android:layout_height="100dp"
android:tag="numberpicker_trashcan_versioning"
tools:layout="@layout/numberpicker_fragment" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cleanout_after_description"
android:layout_margin="10dp" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="?dialogPreferredPadding"
style="@style/Theme.Syncthing.Dialog"
>
<Spinner
android:id="@+id/versioningTypeSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/versioning_types"
style="@style/Widget.AppCompat.Spinner.Underlined"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/versioningFragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/finish_btn"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="right"
android:text="@string/finish"
android:textColor="@color/primary_dark" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<NumberPicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/numberpicker"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View file

@ -271,6 +271,14 @@ Please report any problems you encounter via Github.</string>
<item>None</item>
</string-array>
<string-array name="versioning_types">
<item>None</item>
<item>TrashCan</item>
<item>Simple</item>
<item>Staggered</item>
<item>External</item>
</string-array>
<string name="device_name">Device Name</string>
<string name="listen_address">Sync Protocol Listen Addresses</string>
@ -553,4 +561,38 @@ Please report any problems you encounter via Github.</string>
<string name="folder_size_format">%1$s / %2$s</string>
<string name="appconfig_receiver_background_enabled">Stopping Syncthing is not supported when running in background is enabled.</string>
<!-- Titles used in the file versioning dialog and file versioning button in the folder activity -->
<string name="maximum_age">Maximum Age</string>
<string name="versions_path_title">Versions Path</string>
<string name="clean_out_after">Clean out after</string>
<string name="file_versioning">File Versioning</string>
<string name="finish">Finish</string>
<string name="none"> None</string>
<string name="versions_path">Versions Path</string>
<string name="command">Command</string>
<string name="type">Type</string>
<!-- File versioning dialog descriptions for file versioning types and file versioning configuration options. -->
<string name="keep_versions_description">The number of old versions to keep, per file.</string>
<string name="simple_file_versioning_description">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</string>
<string name="trashcan_versioning_description">Files are moved to .stversions directory when replaced or deleted by Syncthing.</string>
<string name="staggered_versioning_description">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing. Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.\n\nThe following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</string>
<string name="maximum_age_description">The maximum time to keep a version (in days, set to 0 to keep versions forever).</string>
<string name="versions_path_description">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</string>
<string name="cleanout_after_description">The number of days to keep files in the trash can. Zero means forever</string>
<string name="external_versioning_description">The first command line parameter is the folder path and the second parameter is the relative path in the folder.</string>
<string name="no_versioning_description">Select a versioning type to enable versioning.</string>
<!-- Displays the current file versioning type in the Folder activity -->
<string name="type_simple">Type: Simple</string>
<string name="type_trashcan">Type: Trashcan</string>
<string name="type_staggered">Type: Staggered</string>
<string name="type_external">Type: External</string>
<!-- Information displayed about the current file versioning configuration in the folder activity -->
<string name="trashcan_versioning_info" formatted="false">Cleanout after: %1$s</string>
<string name="staggered_versioning_info" formatted="false">Maximum Age: %1$d \nVersions Path: %2$s</string>
<string name="external_versioning_info" formatted="false">Command: %1$s</string>
<string name="simple_versioning_info">Keep versions: %1$s</string>
</resources>