Migrating FolderFragment from preferences to views.
Implementing new form design following material design conventions and current best practices. Ref #404. Upped minSDK to 11. Fixes #448.
|
@ -43,7 +43,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId "com.nutomic.syncthingandroid"
|
||||
minSdkVersion 8
|
||||
minSdkVersion 11
|
||||
targetSdkVersion 22
|
||||
versionCode 72
|
||||
versionName "0.6.6"
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.test.util;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import com.nutomic.syncthingandroid.util.ExtendedCheckBoxPreference;
|
||||
|
||||
public class ExtendedCheckBoxPreferenceTest extends AndroidTestCase {
|
||||
|
||||
public void testExtendedCheckBoxPreference() {
|
||||
Object o = new Object();
|
||||
ExtendedCheckBoxPreference cb = new ExtendedCheckBoxPreference(getContext(), o);
|
||||
assertEquals(cb.getObject(), o);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@ import android.support.v4.app.Fragment;
|
|||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
|
@ -24,7 +25,6 @@ import android.support.v7.app.ActionBar.Tab;
|
|||
import android.support.v7.app.ActionBar.TabListener;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -278,13 +278,11 @@ public class MainActivity extends SyncthingActivity
|
|||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// Avoid crash if called during startup.
|
||||
if (mFolderFragment != null && mDevicesFragment != null &&
|
||||
mDrawerFragment != null) {
|
||||
if (mFolderFragment != null && mDevicesFragment != null && mDrawerFragment != null) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
fm.putFragment(outState, FoldersFragment.class.getName(), mFolderFragment);
|
||||
fm.putFragment(outState, DevicesFragment.class.getName(), mDevicesFragment);
|
||||
fm.putFragment(outState, DrawerFragment.class.getName(),
|
||||
mDrawerFragment);
|
||||
fm.putFragment(outState, DrawerFragment.class.getName(), mDrawerFragment);
|
||||
outState.putInt("currentTab", mViewPager.getCurrentItem());
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +334,7 @@ public class MainActivity extends SyncthingActivity
|
|||
* Closes the drawer. Use when navigating away from activity.
|
||||
*/
|
||||
public void closeDrawer() {
|
||||
mDrawerLayout.closeDrawer(Gravity.START);
|
||||
mDrawerLayout.closeDrawer(GravityCompat.START);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -345,8 +343,8 @@ public class MainActivity extends SyncthingActivity
|
|||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent e) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
if (!mDrawerLayout.isDrawerOpen(Gravity.START))
|
||||
mDrawerLayout.openDrawer(Gravity.START);
|
||||
if (!mDrawerLayout.isDrawerOpen(GravityCompat.START))
|
||||
mDrawerLayout.openDrawer(GravityCompat.START);
|
||||
else
|
||||
closeDrawer();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.view.View;
|
|||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment;
|
||||
import com.nutomic.syncthingandroid.fragments.FolderSettingsFragment;
|
||||
import com.nutomic.syncthingandroid.fragments.FolderFragment;
|
||||
import com.nutomic.syncthingandroid.fragments.SettingsFragment;
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
* {@link #ACTION_REPO_SETTINGS_FRAGMENT} to determine if an existing folder/device should be
|
||||
* edited or a new one created.
|
||||
* <p/>
|
||||
* If this is false, {@link com.nutomic.syncthingandroid.fragments.FolderSettingsFragment#EXTRA_REPO_ID} or
|
||||
* If this is false, {@link FolderFragment#EXTRA_REPO_ID} or
|
||||
* {@link com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
|
||||
*/
|
||||
public static final String EXTRA_IS_CREATE = "create";
|
||||
|
@ -56,7 +56,7 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
}
|
||||
break;
|
||||
case ACTION_REPO_SETTINGS_FRAGMENT:
|
||||
mFragment = new FolderSettingsFragment();
|
||||
mFragment = new FolderFragment();
|
||||
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
|
||||
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
package com.nutomic.syncthingandroid.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.DrawableContainer.DrawableContainerState;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
|
||||
import com.nutomic.syncthingandroid.activities.SettingsActivity;
|
||||
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
|
||||
import com.nutomic.syncthingandroid.fragments.dialog.KeepVersionsDialogFragment;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi.SimpleVersioning;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi.Versioning;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginEnd;
|
||||
import static android.support.v4.view.MarginLayoutParamsCompat.setMarginStart;
|
||||
import static android.view.Gravity.CENTER_VERTICAL;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static com.nutomic.syncthingandroid.syncthing.SyncthingService.State.ACTIVE;
|
||||
import static com.nutomic.syncthingandroid.util.DpConverter.dp;
|
||||
import static java.lang.String.valueOf;
|
||||
|
||||
/**
|
||||
* Shows folder details and allows changing them.
|
||||
*/
|
||||
public class FolderFragment extends Fragment
|
||||
implements SyncthingActivity.OnServiceConnectedListener, SyncthingService.OnApiChangeListener {
|
||||
|
||||
/**
|
||||
* The ID of the folder to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
|
||||
* set to false.
|
||||
*/
|
||||
public static final String EXTRA_REPO_ID = "folder_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 SyncthingService mSyncthingService;
|
||||
|
||||
private RestApi.Folder mFolder;
|
||||
|
||||
private EditText mIdView;
|
||||
|
||||
private TextView mPathView;
|
||||
|
||||
private SwitchCompat mFolderMasterView;
|
||||
|
||||
private ViewGroup mDevicesContainer;
|
||||
|
||||
private TextView mVersioningKeepView;
|
||||
|
||||
private boolean mIsCreate;
|
||||
|
||||
private KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment();
|
||||
|
||||
private TextWatcher mIdTextWatcher = new TextWatcherAdapter() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
mFolder.id = s.toString();
|
||||
updateRepo();
|
||||
}
|
||||
};
|
||||
|
||||
private TextWatcher mPathTextWatcher = new TextWatcherAdapter() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
mFolder.path = s.toString();
|
||||
}
|
||||
};
|
||||
|
||||
private CompoundButton.OnCheckedChangeListener mMasterCheckedListener = new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
|
||||
mFolder.readOnly = isChecked;
|
||||
updateRepo();
|
||||
}
|
||||
};
|
||||
|
||||
private CompoundButton.OnCheckedChangeListener mOnShareChangeListener = new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
|
||||
RestApi.Device device = (RestApi.Device) view.getTag();
|
||||
if (isChecked) {
|
||||
mFolder.deviceIds.add(device.deviceID);
|
||||
} else {
|
||||
mFolder.deviceIds.remove(device.deviceID);
|
||||
}
|
||||
updateRepo();
|
||||
}
|
||||
};
|
||||
|
||||
private KeepVersionsDialogFragment.OnValueChangeListener mOnValueChangeListener = new KeepVersionsDialogFragment.OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(int intValue) {
|
||||
if (intValue == 0) {
|
||||
mFolder.versioning = new Versioning();
|
||||
mVersioningKeepView.setText(R.string.off);
|
||||
} else {
|
||||
mFolder.versioning = new SimpleVersioning();
|
||||
((SimpleVersioning) mFolder.versioning).setParams(intValue);
|
||||
mVersioningKeepView.setText(valueOf(intValue));
|
||||
}
|
||||
updateRepo();
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener mPathViewClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(getActivity(), FolderPickerActivity.class);
|
||||
if (mFolder.path.length() > 0) {
|
||||
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, mFolder.path);
|
||||
}
|
||||
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener mVersioningContainerClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mKeepVersionsDialogFragment.show(getFragmentManager(), KEEP_VERSIONS_DIALOG_TAG);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
SettingsActivity activity = (SettingsActivity) getActivity();
|
||||
mIsCreate = activity.getIsCreate();
|
||||
activity.setTitle(isCreatingFolder() ? R.string.create_folder : R.string.edit_folder);
|
||||
activity.registerOnServiceConnectedListener(this);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
if (isCreatingFolder()) {
|
||||
if (savedInstanceState != null) {
|
||||
mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder");
|
||||
}
|
||||
if (mFolder == null) {
|
||||
initFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current settings in case we are in create mode and they aren't yet stored in the config.
|
||||
*/
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable("folder", mFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_folder, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mIdView = (EditText) view.findViewById(R.id.id);
|
||||
mPathView = (TextView) view.findViewById(R.id.directory);
|
||||
mFolderMasterView = (SwitchCompat) view.findViewById(R.id.master);
|
||||
mVersioningKeepView = (TextView) view.findViewById(R.id.versioningKeep);
|
||||
mDevicesContainer = (ViewGroup) view.findViewById(R.id.devicesContainer);
|
||||
|
||||
mPathView.setOnClickListener(mPathViewClickListener);
|
||||
view.findViewById(R.id.versioningContainer).setOnClickListener(mVersioningContainerClickListener);
|
||||
|
||||
if (isEditingFolder()) {
|
||||
prepareEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateViewsAndSetListeners() {
|
||||
// Update views
|
||||
mIdView.setText(mFolder.id);
|
||||
mPathView.setText(mFolder.path);
|
||||
mFolderMasterView.setChecked(mFolder.readOnly);
|
||||
List<RestApi.Device> devicesList = mSyncthingService.getApi().getDevices(false);
|
||||
|
||||
if (devicesList.isEmpty()) {
|
||||
addEmptyDeviceListView();
|
||||
} else {
|
||||
for (RestApi.Device n : devicesList) {
|
||||
addDeviceViewAndSetListener(n, getLayoutInflater(null));
|
||||
}
|
||||
}
|
||||
|
||||
boolean versioningEnabled = mFolder.versioning instanceof SimpleVersioning;
|
||||
int versions = 0;
|
||||
if (versioningEnabled) {
|
||||
versions = Integer.valueOf(mFolder.versioning.getParams().get("keep"));
|
||||
mVersioningKeepView.setText(valueOf(versions));
|
||||
} else {
|
||||
mVersioningKeepView.setText(R.string.off);
|
||||
}
|
||||
mKeepVersionsDialogFragment.setValue(versions);
|
||||
|
||||
// Keep state updated
|
||||
mIdView.addTextChangedListener(mIdTextWatcher);
|
||||
mPathView.addTextChangedListener(mPathTextWatcher);
|
||||
mFolderMasterView.setOnCheckedChangeListener(mMasterCheckedListener);
|
||||
mKeepVersionsDialogFragment.setOnValueChangeListener(mOnValueChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mIdView.removeTextChangedListener(mIdTextWatcher);
|
||||
mPathView.removeTextChangedListener(mPathTextWatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != ACTIVE) {
|
||||
getActivity().finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEditingFolder()) {
|
||||
List<RestApi.Folder> folders = mSyncthingService.getApi().getFolders();
|
||||
String passedId = getActivity().getIntent().getStringExtra(EXTRA_REPO_ID);
|
||||
for (RestApi.Folder currentFolder : folders) {
|
||||
if (currentFolder.id.equals(passedId)) {
|
||||
mFolder = currentFolder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mFolder == null) {
|
||||
Log.w(TAG, "Folder not found in API update");
|
||||
getActivity().finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateViewsAndSetListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
|
||||
mSyncthingService.registerOnApiChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mSyncthingService != null) {
|
||||
mSyncthingService.unregisterOnApiChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.folder_settings, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
menu.findItem(R.id.create).setVisible(isCreatingFolder());
|
||||
menu.findItem(R.id.delete).setVisible(isEditingFolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.create:
|
||||
if (mFolder.id.length() > 64 || !mFolder.id.matches("[a-zA-Z0-9-_\\.]+")) {
|
||||
Toast.makeText(getActivity(), R.string.folder_id_invalid, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
if (mFolder.path.equals("")) {
|
||||
Toast.makeText(getActivity(), R.string.folder_path_required, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
mSyncthingService.getApi().editFolder(mFolder, true, getActivity());
|
||||
getActivity().finish();
|
||||
return true;
|
||||
case R.id.delete:
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(R.string.delete_folder_confirm)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
mSyncthingService.getApi().deleteFolder(mFolder, getActivity());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
|
||||
mFolder.path = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
|
||||
mPathView.setText(mFolder.path);
|
||||
updateRepo();
|
||||
}
|
||||
}
|
||||
|
||||
private void initFolder() {
|
||||
mFolder = new RestApi.Folder();
|
||||
mFolder.id = "";
|
||||
mFolder.path = "";
|
||||
mFolder.rescanIntervalS = 259200; // Scan every 3 days (in case inotify dropped some changes)
|
||||
mFolder.deviceIds = new ArrayList<>();
|
||||
mFolder.versioning = new Versioning();
|
||||
}
|
||||
|
||||
private void prepareEditMode() {
|
||||
mIdView.clearFocus();
|
||||
mIdView.setFocusable(false);
|
||||
mIdView.setEnabled(false);
|
||||
mPathView.setEnabled(false);
|
||||
}
|
||||
|
||||
private void addEmptyDeviceListView() {
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(WRAP_CONTENT, dp(48, getActivity()));
|
||||
// 72dp margin to align with dividers
|
||||
// 4dp additional margin to align with the rest of the items
|
||||
setMarginStart(params, dp(72 + 4, getActivity()));
|
||||
setMarginEnd(params, dp(16 + 4, getActivity()));
|
||||
TextView emptyView = new TextView(mDevicesContainer.getContext());
|
||||
emptyView.setGravity(CENTER_VERTICAL);
|
||||
emptyView.setText(R.string.devices_list_empty);
|
||||
mDevicesContainer.addView(emptyView, params);
|
||||
}
|
||||
|
||||
private void addDeviceViewAndSetListener(RestApi.Device device, LayoutInflater inflater) {
|
||||
inflater.inflate(R.layout.item_device_form, mDevicesContainer);
|
||||
SwitchCompat deviceView = (SwitchCompat) mDevicesContainer.getChildAt(mDevicesContainer.getChildCount()-1);
|
||||
deviceView.setChecked(mFolder.deviceIds.contains(device.deviceID));
|
||||
deviceView.setText(device.name);
|
||||
deviceView.setTag(device);
|
||||
deviceView.setOnCheckedChangeListener(mOnShareChangeListener);
|
||||
}
|
||||
|
||||
private void updateRepo() {
|
||||
if (isEditingFolder()) {
|
||||
mSyncthingService.getApi().editFolder(mFolder, false, getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEditingFolder() {
|
||||
return !mIsCreate;
|
||||
}
|
||||
|
||||
private boolean isCreatingFolder() {
|
||||
return mIsCreate;
|
||||
}
|
||||
|
||||
private class TextWatcherAdapter implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
}
|
||||
}
|
|
@ -1,341 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.support.v4.preference.PreferenceFragment;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.activities.FolderPickerActivity;
|
||||
import com.nutomic.syncthingandroid.activities.SettingsActivity;
|
||||
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
|
||||
import com.nutomic.syncthingandroid.syncthing.RestApi;
|
||||
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
|
||||
import com.nutomic.syncthingandroid.util.ExtendedCheckBoxPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows folder details and allows changing them.
|
||||
*/
|
||||
public class FolderSettingsFragment extends PreferenceFragment
|
||||
implements SyncthingActivity.OnServiceConnectedListener,
|
||||
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
|
||||
SyncthingService.OnApiChangeListener {
|
||||
|
||||
private static final int DIRECTORY_REQUEST_CODE = 234;
|
||||
|
||||
/**
|
||||
* The ID of the folder to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
|
||||
* set to false.
|
||||
*/
|
||||
public static final String EXTRA_REPO_ID = "folder_id";
|
||||
|
||||
private static final String TAG = "FolderSettingsFragment";
|
||||
|
||||
private static final String KEY_NODE_SHARED = "device_shared";
|
||||
|
||||
private SyncthingService mSyncthingService;
|
||||
|
||||
private RestApi.Folder mFolder;
|
||||
|
||||
private EditTextPreference mFolderId;
|
||||
|
||||
private Preference mDirectory;
|
||||
|
||||
private CheckBoxPreference mFolderMaster;
|
||||
|
||||
private PreferenceScreen mDevices;
|
||||
|
||||
private CheckBoxPreference mVersioning;
|
||||
|
||||
private EditTextPreference mVersioningKeep;
|
||||
|
||||
private boolean mIsCreate;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
SettingsActivity activity = (SettingsActivity) getActivity();
|
||||
activity.registerOnServiceConnectedListener(this);
|
||||
mIsCreate = activity.getIsCreate();
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
if (mIsCreate) {
|
||||
addPreferencesFromResource(R.xml.folder_settings_create);
|
||||
} else {
|
||||
addPreferencesFromResource(R.xml.folder_settings_edit);
|
||||
}
|
||||
|
||||
mFolderId = (EditTextPreference) findPreference("folder_id");
|
||||
mFolderId.setOnPreferenceChangeListener(this);
|
||||
mDirectory = findPreference("directory");
|
||||
mDirectory.setOnPreferenceClickListener(this);
|
||||
mFolderMaster = (CheckBoxPreference) findPreference("folder_master");
|
||||
mFolderMaster.setOnPreferenceChangeListener(this);
|
||||
mDevices = (PreferenceScreen) findPreference("devices");
|
||||
mDevices.setOnPreferenceClickListener(this);
|
||||
mVersioning = (CheckBoxPreference) findPreference("versioning");
|
||||
mVersioning.setOnPreferenceChangeListener(this);
|
||||
mVersioningKeep = (EditTextPreference) findPreference("versioning_keep");
|
||||
mVersioningKeep.setOnPreferenceChangeListener(this);
|
||||
|
||||
if (mIsCreate) {
|
||||
if (savedInstanceState != null) {
|
||||
mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder");
|
||||
}
|
||||
if (mFolder == null) {
|
||||
mFolder = new RestApi.Folder();
|
||||
mFolder.id = "";
|
||||
mFolder.path = "";
|
||||
mFolder.rescanIntervalS = 259200; // Scan every 3 days (in case inotify dropped some changes)
|
||||
mFolder.deviceIds = new ArrayList<>();
|
||||
mFolder.versioning = new RestApi.Versioning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current settings in case we are in create mode and they aren't yet stored in the config.
|
||||
*/
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable("folder", mFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApiChange(SyncthingService.State currentState) {
|
||||
if (currentState != SyncthingService.State.ACTIVE) {
|
||||
getActivity().finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsCreate) {
|
||||
getActivity().setTitle(R.string.create_folder);
|
||||
} else {
|
||||
RestApi.Folder folder = null;
|
||||
getActivity().setTitle(R.string.edit_folder);
|
||||
List<RestApi.Folder> folders = mSyncthingService.getApi().getFolders();
|
||||
for (int i = 0; i < folders.size(); i++) {
|
||||
if (folders.get(i).id.equals(
|
||||
getActivity().getIntent().getStringExtra(EXTRA_REPO_ID))) {
|
||||
folder = folders.get(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (folder == null) {
|
||||
Log.w(TAG, "Folder not found in API update");
|
||||
getActivity().finish();
|
||||
return;
|
||||
}
|
||||
mFolder = folder;
|
||||
}
|
||||
|
||||
mFolderId.setText(mFolder.id);
|
||||
mFolderId.setSummary(mFolder.id);
|
||||
mDirectory.setSummary(mFolder.path);
|
||||
mFolderMaster.setChecked(mFolder.readOnly);
|
||||
List<RestApi.Device> devicesList = mSyncthingService.getApi().getDevices(false);
|
||||
for (RestApi.Device n : devicesList) {
|
||||
ExtendedCheckBoxPreference cbp = new ExtendedCheckBoxPreference(getActivity(), n);
|
||||
// Calling addPreference later causes it to change the checked state.
|
||||
mDevices.addPreference(cbp);
|
||||
cbp.setTitle(n.name);
|
||||
cbp.setKey(KEY_NODE_SHARED);
|
||||
cbp.setOnPreferenceChangeListener(FolderSettingsFragment.this);
|
||||
cbp.setChecked(false);
|
||||
for (String n2 : mFolder.deviceIds) {
|
||||
if (n2.equals(n.deviceID)) {
|
||||
cbp.setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
mVersioning.setChecked(mFolder.versioning instanceof RestApi.SimpleVersioning);
|
||||
if (mVersioning.isChecked()) {
|
||||
mVersioningKeep.setText(mFolder.versioning.getParams().get("keep"));
|
||||
mVersioningKeep.setSummary(mFolder.versioning.getParams().get("keep"));
|
||||
mVersioningKeep.setEnabled(true);
|
||||
} else {
|
||||
mVersioningKeep.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
|
||||
mSyncthingService.registerOnApiChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mSyncthingService.unregisterOnApiChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.folder_settings, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
menu.findItem(R.id.create).setVisible(mIsCreate);
|
||||
menu.findItem(R.id.delete).setVisible(!mIsCreate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.create:
|
||||
if (mFolder.id.length() > 64 || !mFolder.id.matches("[a-zA-Z0-9-_\\.]+")) {
|
||||
Toast.makeText(getActivity(), R.string.folder_id_invalid, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
if (mFolder.path.equals("")) {
|
||||
Toast.makeText(getActivity(), R.string.folder_path_required, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
mSyncthingService.getApi().editFolder(mFolder, true, getActivity());
|
||||
getActivity().finish();
|
||||
return true;
|
||||
case R.id.delete:
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(R.string.delete_folder_confirm)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
mSyncthingService.getApi().deleteFolder(mFolder, getActivity());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
if (preference instanceof EditTextPreference) {
|
||||
EditTextPreference pref = (EditTextPreference) preference;
|
||||
if ((pref.getEditText().getInputType() & InputType.TYPE_CLASS_NUMBER) > 0) {
|
||||
try {
|
||||
o = Integer.parseInt((String) o);
|
||||
o = o.toString();
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "Invalid number: " + o);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pref.setSummary((String) o);
|
||||
}
|
||||
|
||||
if (preference.equals(mFolderId)) {
|
||||
mFolder.id = (String) o;
|
||||
folderUpdated();
|
||||
return true;
|
||||
} else if (preference.equals(mDirectory)) {
|
||||
mFolder.path = (String) o;
|
||||
folderUpdated();
|
||||
return true;
|
||||
} else if (preference.equals(mFolderMaster)) {
|
||||
mFolder.readOnly = (Boolean) o;
|
||||
folderUpdated();
|
||||
return true;
|
||||
} else if (preference.getKey().equals(KEY_NODE_SHARED)) {
|
||||
ExtendedCheckBoxPreference pref = (ExtendedCheckBoxPreference) preference;
|
||||
RestApi.Device device = (RestApi.Device) pref.getObject();
|
||||
if ((Boolean) o) {
|
||||
mFolder.deviceIds.add(device.deviceID);
|
||||
} else {
|
||||
Iterator<String> it = mFolder.deviceIds.iterator();
|
||||
while (it.hasNext()) {
|
||||
String n = it.next();
|
||||
if (n.equals(device.deviceID)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
folderUpdated();
|
||||
return true;
|
||||
} else if (preference.equals(mVersioning)) {
|
||||
mVersioningKeep.setEnabled((Boolean) o);
|
||||
if ((Boolean) o) {
|
||||
RestApi.SimpleVersioning v = new RestApi.SimpleVersioning();
|
||||
mFolder.versioning = v;
|
||||
v.setParams(5);
|
||||
mVersioningKeep.setText("5");
|
||||
mVersioningKeep.setSummary("5");
|
||||
} else {
|
||||
mFolder.versioning = new RestApi.Versioning();
|
||||
}
|
||||
folderUpdated();
|
||||
return true;
|
||||
} else if (preference.equals(mVersioningKeep)) {
|
||||
try {
|
||||
((RestApi.SimpleVersioning) mFolder.versioning)
|
||||
.setParams(Integer.parseInt((String) o));
|
||||
folderUpdated();
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "invalid versioning option: "+ o);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (preference.equals(mDirectory)) {
|
||||
Intent intent = new Intent(getActivity(), FolderPickerActivity.class);
|
||||
if (mFolder.path.length() > 0) {
|
||||
intent.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY, mFolder.path);
|
||||
}
|
||||
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
|
||||
} else if (preference.equals(mDevices) &&
|
||||
mSyncthingService.getApi().getDevices(false).isEmpty()) {
|
||||
Toast.makeText(getActivity(), R.string.no_devices, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
|
||||
mFolder.path = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
|
||||
mDirectory.setSummary(mFolder.path);
|
||||
folderUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
private void folderUpdated() {
|
||||
if (!mIsCreate) {
|
||||
mSyncthingService.getApi().editFolder(mFolder, false, getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -103,7 +103,7 @@ public class FoldersFragment extends ListFragment implements SyncthingService.On
|
|||
Intent intent = new Intent(getActivity(), SettingsActivity.class)
|
||||
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
|
||||
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
|
||||
.putExtra(FolderSettingsFragment.EXTRA_REPO_ID, mAdapter.getItem(i).id);
|
||||
.putExtra(FolderFragment.EXTRA_REPO_ID, mAdapter.getItem(i).id);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package com.nutomic.syncthingandroid.fragments.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
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 OnValueChangeListener mOnValueChangeListener = OnValueChangeListener.NO_OP;
|
||||
|
||||
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();
|
||||
mOnValueChangeListener.onValueChange(mValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
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();
|
||||
}
|
||||
|
||||
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) {
|
||||
if (onValueChangeListener == null) {
|
||||
onValueChangeListener = OnValueChangeListener.NO_OP;
|
||||
}
|
||||
|
||||
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);
|
||||
return picker;
|
||||
}
|
||||
|
||||
public interface OnValueChangeListener {
|
||||
OnValueChangeListener NO_OP = new OnValueChangeListener() {
|
||||
@Override
|
||||
public void onValueChange(int value) {}
|
||||
};
|
||||
|
||||
void onValueChange(int value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
import static android.util.TypedValue.applyDimension;
|
||||
|
||||
public abstract class DpConverter {
|
||||
|
||||
/**
|
||||
* Converts dips to pixels.
|
||||
*
|
||||
* @param dp Number of dps
|
||||
* @param c The context to convert in.
|
||||
* @return Number of pixels that equal dp in context.
|
||||
*/
|
||||
public static int dp(int dp, Context c) {
|
||||
return (int) applyDimension(COMPLEX_UNIT_DIP, dp, c.getResources().getDisplayMetrics());
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.CheckBoxPreference;
|
||||
|
||||
/**
|
||||
* Saves an extra object on construction, which can be retrieved later.
|
||||
*/
|
||||
public class ExtendedCheckBoxPreference extends CheckBoxPreference {
|
||||
|
||||
private final Object mObject;
|
||||
|
||||
public ExtendedCheckBoxPreference(Context context, Object object) {
|
||||
super(context);
|
||||
mObject = object;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return mObject;
|
||||
}
|
||||
|
||||
}
|
BIN
src/main/res/drawable-hdpi/ic_device_hub_black_36dp_active.png
Normal file
After Width: | Height: | Size: 355 B |
BIN
src/main/res/drawable-hdpi/ic_device_hub_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
src/main/res/drawable-hdpi/ic_folder_black_36dp_active.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
src/main/res/drawable-hdpi/ic_folder_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
src/main/res/drawable-hdpi/ic_history_black_36dp_active.png
Normal file
After Width: | Height: | Size: 709 B |
BIN
src/main/res/drawable-hdpi/ic_history_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 657 B |
BIN
src/main/res/drawable-hdpi/ic_lock_black_36dp_active.png
Normal file
After Width: | Height: | Size: 461 B |
BIN
src/main/res/drawable-hdpi/ic_lock_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 418 B |
BIN
src/main/res/drawable-hdpi/ic_vpn_key_black_36dp_active.png
Normal file
After Width: | Height: | Size: 428 B |
BIN
src/main/res/drawable-hdpi/ic_vpn_key_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 376 B |
BIN
src/main/res/drawable-mdpi/ic_device_hub_black_36dp_active.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
src/main/res/drawable-mdpi/ic_device_hub_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
src/main/res/drawable-mdpi/ic_folder_black_36dp_active.png
Normal file
After Width: | Height: | Size: 143 B |
BIN
src/main/res/drawable-mdpi/ic_folder_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 143 B |
BIN
src/main/res/drawable-mdpi/ic_history_black_36dp_active.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
src/main/res/drawable-mdpi/ic_history_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 445 B |
BIN
src/main/res/drawable-mdpi/ic_lock_black_36dp_active.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
src/main/res/drawable-mdpi/ic_lock_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 294 B |
BIN
src/main/res/drawable-mdpi/ic_vpn_key_black_36dp_active.png
Normal file
After Width: | Height: | Size: 287 B |
BIN
src/main/res/drawable-mdpi/ic_vpn_key_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 255 B |
BIN
src/main/res/drawable-xhdpi/ic_device_hub_black_36dp_active.png
Normal file
After Width: | Height: | Size: 430 B |
After Width: | Height: | Size: 408 B |
BIN
src/main/res/drawable-xhdpi/ic_folder_black_36dp_active.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
src/main/res/drawable-xhdpi/ic_folder_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
src/main/res/drawable-xhdpi/ic_history_black_36dp_active.png
Normal file
After Width: | Height: | Size: 927 B |
BIN
src/main/res/drawable-xhdpi/ic_history_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 866 B |
BIN
src/main/res/drawable-xhdpi/ic_lock_black_36dp_active.png
Normal file
After Width: | Height: | Size: 562 B |
BIN
src/main/res/drawable-xhdpi/ic_lock_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 516 B |
BIN
src/main/res/drawable-xhdpi/ic_vpn_key_black_36dp_active.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
src/main/res/drawable-xhdpi/ic_vpn_key_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 447 B |
BIN
src/main/res/drawable-xxhdpi/ic_device_hub_black_36dp_active.png
Normal file
After Width: | Height: | Size: 651 B |
After Width: | Height: | Size: 615 B |
BIN
src/main/res/drawable-xxhdpi/ic_folder_black_36dp_active.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
src/main/res/drawable-xxhdpi/ic_folder_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 285 B |
BIN
src/main/res/drawable-xxhdpi/ic_history_black_36dp_active.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_history_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-xxhdpi/ic_lock_black_36dp_active.png
Normal file
After Width: | Height: | Size: 840 B |
BIN
src/main/res/drawable-xxhdpi/ic_lock_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
src/main/res/drawable-xxhdpi/ic_vpn_key_black_36dp_active.png
Normal file
After Width: | Height: | Size: 769 B |
BIN
src/main/res/drawable-xxhdpi/ic_vpn_key_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 856 B |
After Width: | Height: | Size: 825 B |
BIN
src/main/res/drawable-xxxhdpi/ic_folder_black_36dp_active.png
Normal file
After Width: | Height: | Size: 522 B |
BIN
src/main/res/drawable-xxxhdpi/ic_folder_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 452 B |
BIN
src/main/res/drawable-xxxhdpi/ic_history_black_36dp_active.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_history_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_lock_black_36dp_active.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/res/drawable-xxxhdpi/ic_lock_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 975 B |
BIN
src/main/res/drawable-xxxhdpi/ic_vpn_key_black_36dp_active.png
Normal file
After Width: | Height: | Size: 1,000 B |
BIN
src/main/res/drawable-xxxhdpi/ic_vpn_key_black_36dp_inactive.png
Normal file
After Width: | Height: | Size: 941 B |
5
src/main/res/drawable/ic_device_hub_black_36dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_device_hub_black_36dp_inactive" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_device_hub_black_36dp_active" />
|
||||
</selector>
|
5
src/main/res/drawable/ic_folder_black_36dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_folder_black_36dp_inactive" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_folder_black_36dp_active" />
|
||||
</selector>
|
5
src/main/res/drawable/ic_history_black_36dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_history_black_36dp_inactive" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_history_black_36dp_active" />
|
||||
</selector>
|
5
src/main/res/drawable/ic_lock_black_36dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_lock_black_36dp_inactive" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_lock_black_36dp_active" />
|
||||
</selector>
|
5
src/main/res/drawable/ic_vpn_key_black_36dp.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_vpn_key_black_36dp_inactive" android:state_enabled="false" />
|
||||
<item android:drawable="@drawable/ic_vpn_key_black_36dp_active" />
|
||||
</selector>
|
8
src/main/res/drawable/list_divider_inset.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="72dp">
|
||||
<shape>
|
||||
<solid android:color="@color/divider" />
|
||||
<size android:height="1px" />
|
||||
</shape>
|
||||
</inset>
|
90
src/main/res/layout/fragment_folder.xml
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.FoldersFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="?android:listDivider"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:showDividers="middle"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/id"
|
||||
style="@style/Widget.Syncthing.TextView.Field"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:drawableLeft="@drawable/ic_vpn_key_black_36dp"
|
||||
android:drawableStart="@drawable/ic_vpn_key_black_36dp"
|
||||
android:hint="@string/folder_id" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/directory"
|
||||
style="@style/Widget.Syncthing.TextView.Label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:drawableLeft="@drawable/ic_folder_black_36dp"
|
||||
android:drawableStart="@drawable/ic_folder_black_36dp"
|
||||
android:hint="@string/directory" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/devicesContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/Widget.Syncthing.TextView.Label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:drawableLeft="@drawable/ic_device_hub_black_36dp"
|
||||
android:drawableStart="@drawable/ic_device_hub_black_36dp"
|
||||
android:text="@string/devices" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/master"
|
||||
style="@style/Widget.Syncthing.TextView.Label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:checked="false"
|
||||
android:drawableLeft="@drawable/ic_lock_black_36dp"
|
||||
android:drawableStart="@drawable/ic_lock_black_36dp"
|
||||
android:text="@string/folder_master" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/versioningContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
style="@style/Widget.Syncthing.TextView.Label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false"
|
||||
android:drawableLeft="@drawable/ic_history_black_36dp"
|
||||
android:drawableStart="@drawable/ic_history_black_36dp"
|
||||
android:background="@null"
|
||||
android:text="@string/keep_versions" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/versioningKeep"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:text="0"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
tools:ignore="HardcodedText" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
10
src/main/res/layout/item_device_form.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.SwitchCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/Widget.Syncthing.TextView.Label.DeviceList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="72dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
tools:ignore="RtlHardcoded,RtlSymmetry" />
|
|
@ -5,7 +5,7 @@
|
|||
<color name="primary_light">#B3E5FC</color>
|
||||
<color name="accent">#FF9100</color>
|
||||
<color name="icons">#FFFFFF</color>
|
||||
<color name="divider">#B6B6B6</color>
|
||||
<color name="divider">#1F000000</color>
|
||||
|
||||
<color name="text_red">#ffff4444</color>
|
||||
<color name="text_blue">#ff33b5e5</color>
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
<string name="no">No</string>
|
||||
|
||||
<string name="off">off</string>
|
||||
|
||||
<!-- FoldersFragment -->
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Widget" />
|
||||
<style name="Widget.Syncthing" />
|
||||
|
||||
<!-- Widget styles -->
|
||||
<eat-comment />
|
||||
|
@ -12,6 +14,33 @@
|
|||
<item name="android:divider">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Syncthing.TextView" />
|
||||
|
||||
<style name="Widget.Syncthing.TextView.Label" parent="@android:style/Widget.TextView">
|
||||
<item name="android:background">?selectableItemBackground</item>
|
||||
<item name="android:drawablePadding">24dp</item>
|
||||
<item name="android:gravity">start|center_vertical</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Body1</item>
|
||||
<item name="android:textColor">?attr/editTextColor</item>
|
||||
<item name="android:paddingLeft">16dp</item>
|
||||
<item name="android:paddingRight">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Syncthing.TextView.Label.DeviceList">
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Syncthing.TextView.Field" parent="Widget.Syncthing.TextView.Label">
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:imeOptions">actionDone</item>
|
||||
<item name="android:inputType">textVisiblePassword</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Syncthing.Switch" parent="@style/Widget.AppCompat.CompoundButton.Switch" >
|
||||
<item name="android:paddingLeft">4dp</item>
|
||||
<item name="android:minHeight">48dp</item>
|
||||
</style>
|
||||
|
||||
<!-- Text appearances -->
|
||||
<eat-comment />
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
<item name="colorPrimaryDark">@color/primary_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="actionBarTheme">@style/ThemeOverlay.Syncthing.ActionBar</item>
|
||||
|
||||
<item name="textAppearanceListItemPrimary">@style/TextAppearance.Syncthing.ListItemPrimary</item>
|
||||
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Syncthing.ListItemSecondary</item>
|
||||
<item name="textAppearanceListItemSmall">@style/TextAppearance.Syncthing.ListItemSmall</item>
|
||||
|
||||
<item name="android:listViewStyle">@style/Widget.Syncthing.ListView</item>
|
||||
<item name="android:listDivider">@drawable/list_divider_inset</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Syncthing" parent="Theme.Syncthing.Base"/>
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<EditTextPreference
|
||||
android:persistent="false"
|
||||
android:key="folder_id"
|
||||
android:title="@string/folder_id"
|
||||
android:enabled="false"
|
||||
style="?android:preferenceInformationStyle" />
|
||||
|
||||
<Preference
|
||||
android:persistent="false"
|
||||
android:key="directory"
|
||||
android:title="@string/directory"
|
||||
android:enabled="false"
|
||||
style="?android:preferenceInformationStyle" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:persistent="false"
|
||||
android:key="folder_master"
|
||||
android:title="@string/folder_master" />
|
||||
|
||||
<PreferenceScreen
|
||||
android:persistent="false"
|
||||
android:key="devices"
|
||||
android:title="@string/devices" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:persistent="false"
|
||||
android:key="versioning"
|
||||
android:title="File Versioning" />
|
||||
|
||||
<EditTextPreference
|
||||
android:persistent="false"
|
||||
android:key="versioning_keep"
|
||||
android:title="@string/keep_versions"
|
||||
android:inputType="numberDecimal" />
|
||||
|
||||
</PreferenceScreen>
|