1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-23 04:41:16 +00:00

Merge pull request #461 from veniosg/master

Update device details styling
This commit is contained in:
Felix Ableitner 2015-10-15 21:16:31 +02:00
commit 8929d86401
87 changed files with 840 additions and 550 deletions

View file

@ -3,7 +3,7 @@ package com.nutomic.syncthingandroid.test.activities;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import com.nutomic.syncthingandroid.activities.MainActivity; import com.nutomic.syncthingandroid.activities.MainActivity;
import com.nutomic.syncthingandroid.fragments.DevicesFragment; import com.nutomic.syncthingandroid.fragments.DeviceListFragment;
import com.nutomic.syncthingandroid.fragments.FolderListFragment; import com.nutomic.syncthingandroid.fragments.FolderListFragment;
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder; import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
import com.nutomic.syncthingandroid.test.MockSyncthingService; import com.nutomic.syncthingandroid.test.MockSyncthingService;
@ -20,7 +20,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
getActivity().onServiceConnected(null, new SyncthingServiceBinder(mService)); getActivity().onServiceConnected(null, new SyncthingServiceBinder(mService));
assertTrue(mService.containsListenerInstance(MainActivity.class)); assertTrue(mService.containsListenerInstance(MainActivity.class));
assertTrue(mService.containsListenerInstance(FolderListFragment.class)); assertTrue(mService.containsListenerInstance(FolderListFragment.class));
assertTrue(mService.containsListenerInstance(DevicesFragment.class)); assertTrue(mService.containsListenerInstance(DeviceListFragment.class));
} }
} }

View file

@ -67,7 +67,8 @@
android:value="splitActionBarWhenNarrow" /> android:value="splitActionBarWhenNarrow" />
</activity> </activity>
<activity android:name=".activities.RestartActivity" <activity android:name=".activities.RestartActivity"
android:theme="@style/Theme.Syncthing.Translucent"/> android:theme="@style/Theme.Syncthing.Translucent"
android:launchMode="singleTop"/>
<service android:name=".syncthing.SyncthingService" /> <service android:name=".syncthing.SyncthingService" />

View file

@ -33,7 +33,7 @@ import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.DevicesFragment; import com.nutomic.syncthingandroid.fragments.DeviceListFragment;
import com.nutomic.syncthingandroid.fragments.DrawerFragment; import com.nutomic.syncthingandroid.fragments.DrawerFragment;
import com.nutomic.syncthingandroid.fragments.FolderListFragment; import com.nutomic.syncthingandroid.fragments.FolderListFragment;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.RestApi;
@ -45,8 +45,8 @@ import static java.lang.Math.min;
/** /**
* Shows {@link FolderListFragment} and * Shows {@link FolderListFragment} and
* {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and * {@link DeviceListFragment} in different tabs, and
* {@link com.nutomic.syncthingandroid.fragments.DrawerFragment} in the navigation drawer. * {@link DrawerFragment} in the navigation drawer.
*/ */
public class MainActivity extends SyncthingActivity public class MainActivity extends SyncthingActivity
implements SyncthingService.OnApiChangeListener { implements SyncthingService.OnApiChangeListener {
@ -161,9 +161,9 @@ public class MainActivity extends SyncthingActivity
public Fragment getItem(int position) { public Fragment getItem(int position) {
switch (position) { switch (position) {
case 0: case 0:
return mFolderFragment; return mFolderListFragment;
case 1: case 1:
return mDevicesFragment; return mDeviceListFragment;
default: default:
return null; return null;
} }
@ -187,9 +187,9 @@ public class MainActivity extends SyncthingActivity
} }
}; };
private FolderListFragment mFolderFragment; private FolderListFragment mFolderListFragment;
private DevicesFragment mDevicesFragment; private DeviceListFragment mDeviceListFragment;
private DrawerFragment mDrawerFragment; private DrawerFragment mDrawerFragment;
@ -216,16 +216,16 @@ public class MainActivity extends SyncthingActivity
if (savedInstanceState != null) { if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
mFolderFragment = (FolderListFragment) fm.getFragment( mFolderListFragment = (FolderListFragment) fm.getFragment(
savedInstanceState, FolderListFragment.class.getName()); savedInstanceState, FolderListFragment.class.getName());
mDevicesFragment = (DevicesFragment) fm.getFragment( mDeviceListFragment = (DeviceListFragment) fm.getFragment(
savedInstanceState, DevicesFragment.class.getName()); savedInstanceState, DeviceListFragment.class.getName());
mDrawerFragment = (DrawerFragment) fm.getFragment( mDrawerFragment = (DrawerFragment) fm.getFragment(
savedInstanceState, DrawerFragment.class.getName()); savedInstanceState, DrawerFragment.class.getName());
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab")); mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
} else { } else {
mFolderFragment = new FolderListFragment(); mFolderListFragment = new FolderListFragment();
mDevicesFragment = new DevicesFragment(); mDeviceListFragment = new DeviceListFragment();
mDrawerFragment = new DrawerFragment(); mDrawerFragment = new DrawerFragment();
} }
@ -247,8 +247,8 @@ public class MainActivity extends SyncthingActivity
} }
if (getService() != null) { if (getService() != null) {
getService().unregisterOnApiChangeListener(this); getService().unregisterOnApiChangeListener(this);
getService().unregisterOnApiChangeListener(mFolderFragment); getService().unregisterOnApiChangeListener(mFolderListFragment);
getService().unregisterOnApiChangeListener(mDevicesFragment); getService().unregisterOnApiChangeListener(mDeviceListFragment);
} }
} }
@ -256,8 +256,8 @@ public class MainActivity extends SyncthingActivity
public void onServiceConnected(ComponentName componentName, IBinder iBinder) { public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder); super.onServiceConnected(componentName, iBinder);
getService().registerOnApiChangeListener(this); getService().registerOnApiChangeListener(this);
getService().registerOnApiChangeListener(mFolderFragment); getService().registerOnApiChangeListener(mFolderListFragment);
getService().registerOnApiChangeListener(mDevicesFragment); getService().registerOnApiChangeListener(mDeviceListFragment);
} }
/** /**
@ -267,10 +267,10 @@ public class MainActivity extends SyncthingActivity
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
// Avoid crash if called during startup. // Avoid crash if called during startup.
if (mFolderFragment != null && mDevicesFragment != null && mDrawerFragment != null) { if (mFolderListFragment != null && mDeviceListFragment != null && mDrawerFragment != null) {
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, FolderListFragment.class.getName(), mFolderFragment); fm.putFragment(outState, FolderListFragment.class.getName(), mFolderListFragment);
fm.putFragment(outState, DevicesFragment.class.getName(), mDevicesFragment); fm.putFragment(outState, DeviceListFragment.class.getName(), mDeviceListFragment);
fm.putFragment(outState, DrawerFragment.class.getName(), mDrawerFragment); fm.putFragment(outState, DrawerFragment.class.getName(), mDrawerFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem()); outState.putInt("currentTab", mViewPager.getCurrentItem());
} }

View file

@ -3,10 +3,9 @@ package com.nutomic.syncthingandroid.activities;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.view.View;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.DeviceSettingsFragment; import com.nutomic.syncthingandroid.fragments.DeviceFragment;
import com.nutomic.syncthingandroid.fragments.FolderFragment; import com.nutomic.syncthingandroid.fragments.FolderFragment;
import com.nutomic.syncthingandroid.fragments.SettingsFragment; import com.nutomic.syncthingandroid.fragments.SettingsFragment;
@ -27,7 +26,7 @@ public class SettingsActivity extends SyncthingActivity {
* edited or a new one created. * edited or a new one created.
* <p/> * <p/>
* If this is false, {@link FolderFragment#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). * {@link DeviceFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
*/ */
public static final String EXTRA_IS_CREATE = "create"; public static final String EXTRA_IS_CREATE = "create";
@ -50,7 +49,7 @@ public class SettingsActivity extends SyncthingActivity {
mFragment = new SettingsFragment(); mFragment = new SettingsFragment();
break; break;
case ACTION_NODE_SETTINGS_FRAGMENT: case ACTION_NODE_SETTINGS_FRAGMENT:
mFragment = new DeviceSettingsFragment(); mFragment = new DeviceFragment();
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) { if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set"); throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
} }
@ -87,16 +86,4 @@ public class SettingsActivity extends SyncthingActivity {
public boolean getIsCreate() { public boolean getIsCreate() {
return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false); return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
} }
/**
* Used for the QR code scanner in DeviceSettingsFragment.
*
* Instead of the cast, an interface could be used (if there are multiple fragments using this).
*/
public void onClick(View view) {
if (mFragment instanceof DeviceSettingsFragment) {
((DeviceSettingsFragment) mFragment).onClick(view);
}
}
} }

View file

@ -0,0 +1,425 @@
package com.nutomic.syncthingandroid.fragments;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.SwitchCompat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
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.TextView;
import android.widget.Toast;
import com.nutomic.syncthingandroid.R;
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.BarcodeIntentIntegrator;
import com.nutomic.syncthingandroid.util.BarcodeIntentResult;
import com.nutomic.syncthingandroid.util.Compression;
import com.nutomic.syncthingandroid.util.TextWatcherAdapter;
import java.util.List;
import java.util.Map;
import static android.text.TextUtils.isEmpty;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static com.nutomic.syncthingandroid.syncthing.SyncthingService.State.ACTIVE;
import static com.nutomic.syncthingandroid.util.Compression.METADATA;
/**
* Shows device details and allows changing them.
*/
public class DeviceFragment extends Fragment implements
SyncthingActivity.OnServiceConnectedListener, RestApi.OnReceiveConnectionsListener,
SyncthingService.OnApiChangeListener, RestApi.OnDeviceIdNormalizedListener,
View.OnClickListener {
public static final String EXTRA_NODE_ID = "device_id";
private static final String TAG = "DeviceSettingsFragment";
private static final String DYNAMIC_ADDRESSES = "dynamic";
private SyncthingService mSyncthingService;
private RestApi.Device mDevice;
private View mIdContainer;
private EditText mIdView;
private View mQrButton;
private EditText mNameView;
private EditText mAddressesView;
private TextView mCurrentAddressView;
private TextView mCompressionValueView;
private SwitchCompat mIntroducerView;
private TextView mSyncthingVersionView;
private View mCompressionContainer;
private boolean mIsCreateMode;
private boolean mDeviceNeedsToUpdate;
private DialogInterface.OnClickListener mCompressionEntrySelectedListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Compression compression = Compression.fromIndex(which);
// Don't pop the restart dialog unless the value is actually different.
if (compression != Compression.fromValue(getActivity(), mDevice.compression)) {
mDeviceNeedsToUpdate = true;
mDevice.compression = compression.getValue(getActivity());
mCompressionValueView.setText(compression.getTitle(getActivity()));
}
}
};
private TextWatcher mIdTextWatcher = new TextWatcherAdapter() {
@Override
public void afterTextChanged(Editable s) {
if (!s.toString().equals(mDevice.deviceID)) {
mDeviceNeedsToUpdate = true;
mDevice.deviceID = s.toString();
}
}
};
private TextWatcher mNameTextWatcher = new TextWatcherAdapter() {
@Override
public void afterTextChanged(Editable s) {
if (!s.toString().equals(mDevice.name)) {
mDeviceNeedsToUpdate = true;
mDevice.name = s.toString();
}
}
};
private TextWatcher mAddressesTextWatcher = new TextWatcherAdapter() {
@Override
public void afterTextChanged(Editable s) {
if (!s.toString().equals(mDevice.addresses)) {
mDeviceNeedsToUpdate = true;
mDevice.addresses = persistableAddresses(s);
}
}
};
private CompoundButton.OnCheckedChangeListener mIntroducerCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mDevice.introducer != isChecked) {
mDeviceNeedsToUpdate = true;
mDevice.introducer = isChecked;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity();
mIsCreateMode = activity.getIsCreate();
activity.registerOnServiceConnectedListener(this);
activity.setTitle(mIsCreateMode ? R.string.add_device : R.string.edit_device);
setHasOptionsMenu(true);
if (mIsCreateMode) {
if (savedInstanceState != null) {
mDevice = (RestApi.Device) savedInstanceState.getSerializable("device");
}
if (mDevice == null) {
initDevice();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mSyncthingService != null) {
mSyncthingService.unregisterOnApiChangeListener(this);
}
}
@Override
public void onPause() {
super.onPause();
// We don't want to update every time a TextView's character changes,
// so we hold off until the view stops being visible to the user.
if (mDeviceNeedsToUpdate) {
updateDevice();
}
}
/**
* 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("device", mDevice);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_device, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mIdContainer = view.findViewById(R.id.idContainer);
mIdView = (EditText) view.findViewById(R.id.id);
mQrButton = view.findViewById(R.id.qrButton);
mNameView = (EditText) view.findViewById(R.id.name);
mAddressesView = (EditText) view.findViewById(R.id.addresses);
mCurrentAddressView = (TextView) view.findViewById(R.id.currentAddress);
mCompressionContainer = view.findViewById(R.id.compressionContainer);
mCompressionValueView = (TextView) view.findViewById(R.id.compressionValue);
mIntroducerView = (SwitchCompat) view.findViewById(R.id.introducer);
mSyncthingVersionView = (TextView) view.findViewById(R.id.syncthingVersion);
mQrButton.setOnClickListener(this);
mCompressionContainer.setOnClickListener(this);
if (!mIsCreateMode) {
prepareEditMode();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mIdView.removeTextChangedListener(mIdTextWatcher);
mNameView.removeTextChangedListener(mNameTextWatcher);
mAddressesView.removeTextChangedListener(mAddressesTextWatcher);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
/**
* Sets version and current address of the device.
* <p/>
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
boolean viewsExist = mSyncthingVersionView != null && mCurrentAddressView != null;
if (viewsExist && connections.containsKey(mDevice.deviceID)) {
mCurrentAddressView.setVisibility(VISIBLE);
mSyncthingVersionView.setVisibility(VISIBLE);
mCurrentAddressView.setText(connections.get(mDevice.deviceID).address);
mSyncthingVersionView.setText(connections.get(mDevice.deviceID).clientVersion);
}
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != ACTIVE) {
getActivity().finish();
return;
}
if (!mIsCreateMode) {
List<RestApi.Device> devices = mSyncthingService.getApi().getDevices(false);
for (int i = 0; i < devices.size(); i++) {
if (devices.get(i).deviceID.equals(
getActivity().getIntent().getStringExtra(EXTRA_NODE_ID))) {
mDevice = devices.get(i);
break;
}
}
if (mDevice == null) {
Log.w(TAG, "Device not found in API update");
getActivity().finish();
return;
}
}
mSyncthingService.getApi().getConnections(this);
updateViewsAndSetListeners();
}
private void updateViewsAndSetListeners() {
// Update views
mIdView.setText(mDevice.deviceID);
mNameView.setText((mDevice.name));
mAddressesView.setText(displayableAddresses());
mCompressionValueView.setText(Compression.fromValue(getActivity(), mDevice.compression).getTitle(getActivity()));
mIntroducerView.setChecked(mDevice.introducer);
// Keep state updated
mIdView.addTextChangedListener(mIdTextWatcher);
mNameView.addTextChangedListener(mNameTextWatcher);
mAddressesView.addTextChangedListener(mAddressesTextWatcher);
mIntroducerView.setOnCheckedChangeListener(mIntroducerCheckedChangeListener);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.device_settings, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreateMode);
menu.findItem(R.id.share_device_id).setVisible(!mIsCreateMode);
menu.findItem(R.id.delete).setVisible(!mIsCreateMode);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (isEmpty(mDevice.deviceID)) {
Toast.makeText(getActivity(), R.string.device_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (isEmpty(mDevice.name)) {
Toast.makeText(getActivity(), R.string.device_name_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editDevice(mDevice, getActivity(), this);
getActivity().finish();
return true;
case R.id.share_device_id:
RestApi.shareDeviceId(getActivity(), mDevice.deviceID);
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_device_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteDevice(mDevice, getActivity());
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Receives value of scanned QR code and sets it as device ID.
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
BarcodeIntentResult scanResult = BarcodeIntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null) {
mDevice.deviceID = scanResult.getContents();
mIdView.setText(mDevice.deviceID);
}
}
/**
* Callback for {@link RestApi#editDevice}.
* Displays an error toast if error message present.
*/
@Override
public void onDeviceIdNormalized(String normalizedId, String error) {
if (error != null) {
Toast.makeText(getActivity(), error, Toast.LENGTH_LONG).show();
}
}
private void initDevice() {
mDevice = new RestApi.Device();
mDevice.name = "";
mDevice.deviceID = "";
mDevice.addresses = "dynamic";
mDevice.compression = METADATA.getValue(getActivity());
mDevice.introducer = false;
}
private void prepareEditMode() {
getActivity().getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mIdView.setEnabled(false);
mQrButton.setVisibility(GONE);
mIdContainer.setOnClickListener(this);
}
/**
* Sends the updated device info if in edit mode.
*/
private void updateDevice() {
if (!mIsCreateMode && mDeviceNeedsToUpdate) {
mSyncthingService.getApi().editDevice(mDevice, getActivity(), this);
}
}
private String persistableAddresses(CharSequence userInput) {
return isEmpty(userInput) ? DYNAMIC_ADDRESSES : userInput.toString();
}
private String displayableAddresses() {
return DYNAMIC_ADDRESSES.equals(mDevice.addresses) ? "" : mDevice.addresses;
}
@Override
public void onClick(View v) {
if (v.equals(mCompressionContainer)) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.compression)
.setSingleChoiceItems(R.array.compress_entries,
Compression.fromValue(getActivity(), mDevice.compression).getIndex(),
mCompressionEntrySelectedListener)
.show();
} else if (v.equals(mQrButton)){
BarcodeIntentIntegrator integrator = new BarcodeIntentIntegrator(DeviceFragment.this);
integrator.initiateScan();
} else if (v.equals(mIdContainer)) {
mSyncthingService.getApi().copyDeviceId(mDevice.deviceID);
}
}
}

View file

@ -23,7 +23,7 @@ import java.util.TimerTask;
/** /**
* Displays a list of all existing devices. * Displays a list of all existing devices.
*/ */
public class DevicesFragment extends ListFragment implements SyncthingService.OnApiChangeListener, public class DeviceListFragment extends ListFragment implements SyncthingService.OnApiChangeListener,
ListView.OnItemClickListener { ListView.OnItemClickListener {
private DevicesAdapter mAdapter; private DevicesAdapter mAdapter;
@ -96,7 +96,7 @@ public class DevicesFragment extends ListFragment implements SyncthingService.On
Intent intent = new Intent(getActivity(), SettingsActivity.class); Intent intent = new Intent(getActivity(), SettingsActivity.class);
intent.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT); intent.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT);
intent.putExtra(SettingsActivity.EXTRA_IS_CREATE, false); intent.putExtra(SettingsActivity.EXTRA_IS_CREATE, false);
intent.putExtra(DeviceSettingsFragment.EXTRA_NODE_ID, mAdapter.getItem(i).deviceID); intent.putExtra(DeviceFragment.EXTRA_NODE_ID, mAdapter.getItem(i).deviceID);
startActivity(intent); startActivity(intent);
} }

View file

@ -1,330 +0,0 @@
package com.nutomic.syncthingandroid.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.support.v4.app.Fragment;
import android.support.v4.preference.PreferenceFragment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.nutomic.syncthingandroid.R;
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.BarcodeIntentIntegrator;
import com.nutomic.syncthingandroid.util.BarcodeIntentResult;
import java.util.List;
import java.util.Map;
/**
* Shows device details and allows changing them.
*/
public class DeviceSettingsFragment extends PreferenceFragment implements
SyncthingActivity.OnServiceConnectedListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener, RestApi.OnReceiveConnectionsListener,
SyncthingService.OnApiChangeListener, RestApi.OnDeviceIdNormalizedListener {
public static final String EXTRA_NODE_ID = "device_id";
private static final String TAG = "DeviceSettingsFragment";
private static final int SCAN_QR_REQUEST_CODE = 235;
private SyncthingService mSyncthingService;
private RestApi.Device mDevice;
private Preference mDeviceId;
private EditTextPreference mName;
private EditTextPreference mAddresses;
private ListPreference mCompression;
private CheckBoxPreference mIntroducer;
private Preference mVersion;
private Preference mCurrentAddress;
private boolean mIsCreate;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
mIsCreate = ((SettingsActivity) getActivity()).getIsCreate();
setHasOptionsMenu(true);
if (mIsCreate) {
addPreferencesFromResource(R.xml.device_settings_create);
} else {
addPreferencesFromResource(R.xml.device_settings_edit);
}
mDeviceId = findPreference("device_id");
mDeviceId.setOnPreferenceChangeListener(this);
mName = (EditTextPreference) findPreference("name");
mName.setOnPreferenceChangeListener(this);
mAddresses = (EditTextPreference) findPreference("addresses");
mAddresses.setOnPreferenceChangeListener(this);
mCompression = (ListPreference) findPreference("compression");
mCompression.setOnPreferenceChangeListener(this);
mIntroducer = (CheckBoxPreference) findPreference("introducer");
mIntroducer.setOnPreferenceChangeListener(this);
if (!mIsCreate) {
mVersion = findPreference("version");
mVersion.setSummary("?");
mCurrentAddress = findPreference("current_address");
mCurrentAddress.setSummary("?");
}
if (mIsCreate) {
if (savedInstanceState != null) {
mDevice = (RestApi.Device) savedInstanceState.getSerializable("device");
}
if (mDevice == null) {
mDevice = new RestApi.Device();
mDevice.name = "";
mDevice.deviceID = "";
mDevice.addresses = "dynamic";
mDevice.compression = "metadata";
mDevice.introducer = false;
((EditTextPreference) mDeviceId).setText(mDevice.deviceID);
}
}
}
/**
* 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("device", mDevice);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mSyncthingService.unregisterOnApiChangeListener(this);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
if (mIsCreate)
getActivity().setTitle(R.string.add_device);
else {
RestApi.Device device = null;
getActivity().setTitle(R.string.edit_device);
List<RestApi.Device> devices = mSyncthingService.getApi().getDevices(false);
for (int i = 0; i < devices.size(); i++) {
if (devices.get(i).deviceID.equals(
getActivity().getIntent().getStringExtra(EXTRA_NODE_ID))) {
device = devices.get(i);
break;
}
}
if (device == null) {
Log.w(TAG, "Device not found in API update");
getActivity().finish();
return;
}
mDevice = device;
mDeviceId.setOnPreferenceClickListener(this);
}
mSyncthingService.getApi().getConnections(DeviceSettingsFragment.this);
mDeviceId.setSummary(mDevice.deviceID);
mName.setText((mDevice.name));
mName.setSummary(mDevice.name);
mAddresses.setText(mDevice.addresses);
mAddresses.setSummary(mDevice.addresses);
mCompression.setValue(mDevice.compression);
mCompression.setSummary(mDevice.compression);
mIntroducer.setChecked(mDevice.introducer);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.device_settings, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreate);
menu.findItem(R.id.share_device_id).setVisible(!mIsCreate);
menu.findItem(R.id.delete).setVisible(!mIsCreate);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mDevice.deviceID.equals("")) {
Toast.makeText(getActivity(), R.string.device_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (mDevice.name.equals("")) {
Toast.makeText(getActivity(), R.string.device_name_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editDevice(mDevice, getActivity(), this);
getActivity().finish();
return true;
case R.id.share_device_id:
RestApi.shareDeviceId(getActivity(), mDevice.deviceID);
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_device_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteDevice(mDevice, getActivity());
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
if (preference instanceof ListPreference) {
ListPreference pref = (ListPreference) preference;
pref.setSummary((String) o);
}
if (preference.equals(mDeviceId)) {
mDevice.deviceID = (String) o;
deviceUpdated();
return true;
} else if (preference.equals(mName)) {
mDevice.name = (String) o;
deviceUpdated();
return true;
} else if (preference.equals(mAddresses)) {
mDevice.addresses = (String) o;
deviceUpdated();
return true;
} else if (preference.equals(mCompression)) {
mDevice.compression = (String) o;
deviceUpdated();
return true;
} else if (preference.equals(mIntroducer)) {
mDevice.introducer = (Boolean) o;
deviceUpdated();
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDeviceId)) {
mSyncthingService.getApi().copyDeviceId(mDevice.deviceID);
return true;
}
return false;
}
/**
* Sets version and current address of the device.
*
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
if (mVersion == null || mCurrentAddress == null)
return;
if (connections.containsKey(mDevice.deviceID)) {
mVersion.setSummary(connections.get(mDevice.deviceID).clientVersion);
mCurrentAddress.setSummary(connections.get(mDevice.deviceID).address);
}
}
/**
* Sends the updated device info if in edit mode.
*/
private void deviceUpdated() {
if (!mIsCreate) {
mSyncthingService.getApi().editDevice(mDevice, getActivity(), this);
}
}
/**
* Sends QR code scanning intent when clicking the QR code icon.
*/
public void onClick(View view) {
BarcodeIntentIntegrator integrator = new BarcodeIntentIntegrator(this);
integrator.initiateScan();
}
/**
* Receives value of scanned QR code and sets it as device ID.
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
BarcodeIntentResult scanResult = BarcodeIntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null) {
mDevice.deviceID = scanResult.getContents();
((EditTextPreference) mDeviceId).setText(mDevice.deviceID);
mDeviceId.setSummary(mDevice.deviceID);
}
}
/**
* Callback for {@link RestApi#editDevice}.
* Displays an error toast if error message present.
*/
@Override
public void onDeviceIdNormalized(String normalizedId, String error) {
if (error != null) {
Toast.makeText(getActivity(), error, Toast.LENGTH_LONG).show();
}
}
}

View file

@ -32,6 +32,7 @@ import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.RestApi.SimpleVersioning; import com.nutomic.syncthingandroid.syncthing.RestApi.SimpleVersioning;
import com.nutomic.syncthingandroid.syncthing.RestApi.Versioning; import com.nutomic.syncthingandroid.syncthing.RestApi.Versioning;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.util.TextWatcherAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -76,7 +77,7 @@ public class FolderFragment extends Fragment
private TextView mVersioningKeepView; private TextView mVersioningKeepView;
private boolean mIsCreate; private boolean mIsCreateMode;
private KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment(); private KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment();
@ -154,12 +155,12 @@ public class FolderFragment extends Fragment
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity(); SettingsActivity activity = (SettingsActivity) getActivity();
mIsCreate = activity.getIsCreate(); mIsCreateMode = activity.getIsCreate();
activity.setTitle(isCreatingFolder() ? R.string.create_folder : R.string.edit_folder); activity.setTitle(mIsCreateMode ? R.string.create_folder : R.string.edit_folder);
activity.registerOnServiceConnectedListener(this); activity.registerOnServiceConnectedListener(this);
setHasOptionsMenu(true); setHasOptionsMenu(true);
if (isCreatingFolder()) { if (mIsCreateMode) {
if (savedInstanceState != null) { if (savedInstanceState != null) {
mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder"); mFolder = (RestApi.Folder) savedInstanceState.getSerializable("folder");
} }
@ -169,6 +170,14 @@ public class FolderFragment extends Fragment
} }
} }
@Override
public void onDestroy() {
super.onDestroy();
if (mSyncthingService != null) {
mSyncthingService.unregisterOnApiChangeListener(this);
}
}
/** /**
* Save current settings in case we are in create mode and they aren't yet stored in the config. * Save current settings in case we are in create mode and they aren't yet stored in the config.
*/ */
@ -196,11 +205,50 @@ public class FolderFragment extends Fragment
mPathView.setOnClickListener(mPathViewClickListener); mPathView.setOnClickListener(mPathViewClickListener);
view.findViewById(R.id.versioningContainer).setOnClickListener(mVersioningContainerClickListener); view.findViewById(R.id.versioningContainer).setOnClickListener(mVersioningContainerClickListener);
if (isEditingFolder()) { if (!mIsCreateMode) {
prepareEditMode(); prepareEditMode();
} }
} }
@Override
public void onDestroyView() {
super.onDestroyView();
mIdView.removeTextChangedListener(mIdTextWatcher);
mPathView.removeTextChangedListener(mPathTextWatcher);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != ACTIVE) {
getActivity().finish();
return;
}
if (!mIsCreateMode) {
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();
}
private void updateViewsAndSetListeners() { private void updateViewsAndSetListeners() {
// Update views // Update views
mIdView.setText(mFolder.id); mIdView.setText(mFolder.id);
@ -233,53 +281,6 @@ public class FolderFragment extends Fragment
mKeepVersionsDialogFragment.setOnValueChangeListener(mOnValueChangeListener); 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 @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@ -288,8 +289,8 @@ public class FolderFragment extends Fragment
@Override @Override
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(isCreatingFolder()); menu.findItem(R.id.create).setVisible(mIsCreateMode);
menu.findItem(R.id.delete).setVisible(isEditingFolder()); menu.findItem(R.id.delete).setVisible(!mIsCreateMode);
} }
@Override @Override
@ -324,9 +325,10 @@ public class FolderFragment extends Fragment
case android.R.id.home: case android.R.id.home:
getActivity().finish(); getActivity().finish();
return true; return true;
} default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
@ -355,7 +357,6 @@ public class FolderFragment extends Fragment
private void addEmptyDeviceListView() { private void addEmptyDeviceListView() {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(WRAP_CONTENT, dp(48, getActivity())); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(WRAP_CONTENT, dp(48, getActivity()));
// 72dp margin to align with dividers
int dividerInset = getResources().getDimensionPixelOffset(R.dimen.material_divider_inset); int dividerInset = getResources().getDimensionPixelOffset(R.dimen.material_divider_inset);
int contentInset = getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material); int contentInset = getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material);
setMarginStart(params, dividerInset); setMarginStart(params, dividerInset);
@ -376,28 +377,8 @@ public class FolderFragment extends Fragment
} }
private void updateRepo() { private void updateRepo() {
if (isEditingFolder()) { if (!mIsCreateMode) {
mSyncthingService.getApi().editFolder(mFolder, false, getActivity()); 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) {}
}
} }

View file

@ -0,0 +1,57 @@
package com.nutomic.syncthingandroid.util;
import android.content.Context;
import com.nutomic.syncthingandroid.R;
/**
* Device compression attribute helper. This unifies operations between string values as expected by
* Syncthing with string values as displayed to the user and int ordinals as expected by the dialog
* click interface.
*/
public enum Compression {
NONE(0),
METADATA(1),
ALWAYS(2);
private final int index;
Compression(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
public String getValue(Context context) {
return context.getResources().getStringArray(R.array.compress_values)[index];
}
public String getTitle(Context context) {
return context.getResources().getStringArray(R.array.compress_entries)[index];
}
public static Compression fromIndex(int index) {
switch (index) {
case 0:
return NONE;
case 2:
return ALWAYS;
default:
return METADATA;
}
}
public static Compression fromValue(Context context, String value) {
int index = 0;
String[] values = context.getResources().getStringArray(R.array.compress_values);
for (int i = 0; i < values.length; i++) {
if (values[i].equals(value)) {
index = i;
}
}
return fromIndex(index);
}
}

View file

@ -0,0 +1,16 @@
package com.nutomic.syncthingandroid.util;
import android.text.Editable;
import android.text.TextWatcher;
public 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) {}
}

View file

@ -0,0 +1,50 @@
package com.nutomic.syncthingandroid.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;
import static android.view.inputmethod.EditorInfo.IME_FLAG_NO_ENTER_ACTION;
/**
* Apparently EditText blocks touch event propagation to the parent even
* when disabled/not clickable/not focusable. Therefore we have to manually
* check whether we are enabled and either ignore the event or process it normally. <br/>
* <br/>
* This class also blocks the default EditText behaviour of textMultiLine flag enforcing replacement
* of the IME action button with the new line character. This allows rendering soft wraps on single
* line input.
*/
public class EnhancedEditText extends EditText {
public EnhancedEditText(Context context) {
super(context);
}
public EnhancedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EnhancedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled()) {
return super.onTouchEvent(event);
} else {
return false;
}
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection conn = super.onCreateInputConnection(outAttrs);
outAttrs.imeOptions &= ~IME_FLAG_NO_ENTER_ACTION;
return conn;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

View 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_cellphone_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_cellphone_black_24dp_active" />
</selector>

View 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_info_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_info_black_24dp_active" />
</selector>

View 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_link_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_link_black_24dp_active" />
</selector>

View 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_phonelink_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_phonelink_black_24dp_active" />
</selector>

View 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_qrcode_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_qrcode_black_24dp_active" />
</selector>

View 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_tag_text_outline_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_tag_text_outline_black_24dp_active" />
</selector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="24dp"
android:height="24dp" />
<solid android:color="@android:color/transparent" />
</shape>

View 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_zip_box_black_24dp_inactive" android:state_enabled="false" />
<item android:drawable="@drawable/ic_zip_box_black_24dp_active" />
</selector>

View file

@ -0,0 +1,133 @@
<?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.DeviceFragment">
<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">
<LinearLayout
android:id="@+id/idContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal">
<com.nutomic.syncthingandroid.widget.EnhancedEditText
android:id="@+id/id"
style="@style/Widget.Syncthing.TextView.Label.Details.Field"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="@drawable/ic_cellphone_black_24dp"
android:drawableStart="@drawable/ic_cellphone_black_24dp"
android:hint="@string/device_id"
android:imeOptions="actionNext"
android:inputType="textNoSuggestions|textMultiLine"
android:nextFocusForward="@+id/name"
tools:text="@string/device_id_placeholder" />
<ImageView
android:id="@+id/qrButton"
style="?actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/abc_action_bar_content_inset_material"
android:layout_marginRight="@dimen/abc_action_bar_content_inset_material"
android:contentDescription="@string/scan_qr_code_description"
android:src="@drawable/ic_qrcode_black_24dp" />
</LinearLayout>
<EditText
android:id="@+id/name"
style="@style/Widget.Syncthing.TextView.Label.Details.Field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_tag_text_outline_black_24dp"
android:drawableStart="@drawable/ic_tag_text_outline_black_24dp"
android:hint="@string/name"
android:imeOptions="actionNext"
android:inputType="textCapWords" />
<EditText
android:id="@+id/addresses"
style="@style/Widget.Syncthing.TextView.Label.Details.Field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_link_black_24dp"
android:drawableStart="@drawable/ic_link_black_24dp"
android:hint="@string/addresses"
android:imeOptions="actionDone" />
<LinearLayout
android:id="@+id/compressionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal">
<TextView
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:checked="false"
android:drawableLeft="@drawable/ic_zip_box_black_24dp"
android:drawableStart="@drawable/ic_zip_box_black_24dp"
android:text="@string/compression" />
<TextView
android:id="@+id/compressionValue"
android:layout_width="wrap_content"
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:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:ignore="HardcodedText" />
</LinearLayout>
<android.support.v7.widget.SwitchCompat
android:id="@+id/introducer"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:drawableLeft="@drawable/ic_phonelink_black_24dp"
android:drawableStart="@drawable/ic_phonelink_black_24dp"
android:text="@string/introducer" />
<TextView
android:id="@+id/currentAddress"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_info_black_24dp"
android:drawableStart="@drawable/ic_info_black_24dp"
android:enabled="false"
android:hint="@string/current_address"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/syncthingVersion"
style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_transparent_24dp"
android:drawableStart="@drawable/ic_transparent_24dp"
android:enabled="false"
android:hint="@string/syncthing_version_title"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</ScrollView>

View file

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragments.FoldersFragment"> tools:context=".fragments.FolderFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -12,23 +12,24 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:showDividers="middle" android:showDividers="middle">
tools:ignore="RtlSymmetry">
<EditText <EditText
android:id="@+id/id" android:id="@+id/id"
style="@style/Widget.Syncthing.TextView.Label.Folder.Field" style="@style/Widget.Syncthing.TextView.Label.Details.Field"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_vpn_key_black_24dp" android:drawableLeft="@drawable/ic_vpn_key_black_24dp"
android:drawableStart="@drawable/ic_vpn_key_black_24dp" android:drawableStart="@drawable/ic_vpn_key_black_24dp"
android:hint="@string/folder_id" /> android:hint="@string/folder_id"
android:imeOptions="actionDone"
android:inputType="textCapWords" />
<TextView <TextView
android:id="@+id/directory" android:id="@+id/directory"
style="@style/Widget.Syncthing.TextView.Label.Folder" style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_folder_black_24dp" android:drawableLeft="@drawable/ic_folder_black_24dp"
android:drawableStart="@drawable/ic_folder_black_24dp" android:drawableStart="@drawable/ic_folder_black_24dp"
android:hint="@string/directory" /> android:hint="@string/directory" />
@ -40,9 +41,9 @@
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
style="@style/Widget.Syncthing.TextView.Label.Folder" style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_device_hub_black_24dp" android:drawableLeft="@drawable/ic_device_hub_black_24dp"
android:drawableStart="@drawable/ic_device_hub_black_24dp" android:drawableStart="@drawable/ic_device_hub_black_24dp"
android:text="@string/devices" /> android:text="@string/devices" />
@ -50,9 +51,9 @@
<android.support.v7.widget.SwitchCompat <android.support.v7.widget.SwitchCompat
android:id="@+id/master" android:id="@+id/master"
style="@style/Widget.Syncthing.TextView.Label.Folder" style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="wrap_content"
android:checked="false" android:checked="false"
android:drawableLeft="@drawable/ic_lock_black_24dp" android:drawableLeft="@drawable/ic_lock_black_24dp"
android:drawableStart="@drawable/ic_lock_black_24dp" android:drawableStart="@drawable/ic_lock_black_24dp"
@ -66,14 +67,14 @@
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
style="@style/Widget.Syncthing.TextView.Label.Folder" style="@style/Widget.Syncthing.TextView.Label.Details"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="56dp" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:background="@null"
android:checked="false" android:checked="false"
android:drawableLeft="@drawable/ic_history_black_24dp" android:drawableLeft="@drawable/ic_history_black_24dp"
android:drawableStart="@drawable/ic_history_black_24dp" android:drawableStart="@drawable/ic_history_black_24dp"
android:background="@null"
android:text="@string/keep_versions" /> android:text="@string/keep_versions" />
<TextView <TextView

View file

@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.SwitchCompat xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v7.widget.SwitchCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Syncthing.TextView.Label.Folder.DeviceList" style="@style/Widget.Syncthing.TextView.Label.Details.DeviceList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/material_divider_inset" android:layout_marginLeft="@dimen/material_divider_inset"
android:layout_marginStart="@dimen/material_divider_inset" android:layout_marginStart="@dimen/material_divider_inset"
android:layout_marginRight="@dimen/abc_action_bar_content_inset_material"
android:layout_marginEnd="@dimen/abc_action_bar_content_inset_material"
android:textAppearance="@style/TextAppearance.AppCompat.Caption" android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:ignore="RtlHardcoded,RtlSymmetry" /> tools:ignore="RtlHardcoded,RtlSymmetry" />

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android" <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dip" android:layout_width="wrap_content"
android:layout_height="48dip" android:layout_height="wrap_content"
android:src="@drawable/ic_qrcode" android:src="@drawable/ic_qrcode_black_24dp"
android:onClick="onClick" android:onClick="onClick"
android:contentDescription="@string/scan_qr_code_description" /> android:contentDescription="@string/scan_qr_code_description" />

View file

@ -25,18 +25,20 @@
<item name="android:paddingRight">@dimen/abc_action_bar_content_inset_material</item> <item name="android:paddingRight">@dimen/abc_action_bar_content_inset_material</item>
</style> </style>
<style name="Widget.Syncthing.TextView.Label.Folder"> <style name="Widget.Syncthing.TextView.Label.Details">
<item name="android:textColor">?attr/editTextColor</item> <item name="android:textColor">?attr/editTextColor</item>
<item name="android:minHeight">56dp</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingBottom">8dp</item>
</style> </style>
<style name="Widget.Syncthing.TextView.Label.Folder.DeviceList"> <style name="Widget.Syncthing.TextView.Label.Details.DeviceList">
<item name="android:paddingLeft">4dp</item> <item name="android:paddingLeft">4dp</item>
<item name="android:minHeight">48dp</item>
</style> </style>
<style name="Widget.Syncthing.TextView.Label.Folder.Field"> <style name="Widget.Syncthing.TextView.Label.Details.Field">
<item name="android:background">@null</item> <item name="android:background">@null</item>
<item name="android:imeOptions">actionDone</item>
<item name="android:inputType">textVisiblePassword</item>
</style> </style>
<style name="Widget.Syncthing.Switch" parent="@style/Widget.AppCompat.CompoundButton.Switch" > <style name="Widget.Syncthing.Switch" parent="@style/Widget.AppCompat.CompoundButton.Switch" >

View file

@ -13,6 +13,7 @@
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Syncthing.ListItemSecondary</item> <item name="textAppearanceListItemSecondary">@style/TextAppearance.Syncthing.ListItemSecondary</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.Syncthing.ListItemSmall</item> <item name="textAppearanceListItemSmall">@style/TextAppearance.Syncthing.ListItemSmall</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:listViewStyle">@style/Widget.Syncthing.ListView</item> <item name="android:listViewStyle">@style/Widget.Syncthing.ListView</item>
<item name="android:listDivider">@drawable/list_divider_inset</item> <item name="android:listDivider">@drawable/list_divider_inset</item>
<item name="drawerArrowStyle">@style/Widget.Syncthing.DrawerArrowToggle</item> <item name="drawerArrowStyle">@style/Widget.Syncthing.DrawerArrowToggle</item>

View file

@ -1,32 +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="device_id"
android:title="@string/device_id"
android:widgetLayout="@layout/pref_widget_scan_qr_code" />
<EditTextPreference
android:persistent="false"
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:persistent="false"
android:key="addresses"
android:title="@string/addresses" />
<ListPreference
android:persistent="false"
android:key="compression"
android:entries="@array/compress_entries"
android:entryValues="@array/compress_values"
android:title="@string/compression" />
<CheckBoxPreference
android:persistent="false"
android:key="introducer"
android:title="@string/introducer" />
</PreferenceScreen>

View file

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:persistent="false"
android:key="device_id"
android:title="@string/device_id" />
<EditTextPreference
android:persistent="false"
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:persistent="false"
android:key="addresses"
android:title="@string/addresses" />
<ListPreference
android:persistent="false"
android:key="compression"
android:entries="@array/compress_entries"
android:entryValues="@array/compress_values"
android:title="@string/compression" />
<CheckBoxPreference
android:persistent="false"
android:key="introducer"
android:title="@string/introducer" />
<Preference
android:persistent="false"
android:key="version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
<Preference
android:persistent="false"
android:key="current_address"
android:title="@string/current_address"
style="?android:preferenceInformationStyle" />
</PreferenceScreen>