diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/activities/MainActivityTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/activities/MainActivityTest.java index 18b075ff..5d29ed43 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/activities/MainActivityTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/activities/MainActivityTest.java @@ -3,7 +3,7 @@ package com.nutomic.syncthingandroid.test.activities; import android.test.ActivityInstrumentationTestCase2; 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.syncthing.SyncthingServiceBinder; import com.nutomic.syncthingandroid.test.MockSyncthingService; @@ -20,7 +20,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 + android:theme="@style/Theme.Syncthing.Translucent" + android:launchMode="singleTop"/> diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java index d725b289..37b37689 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java @@ -33,7 +33,7 @@ import android.view.ViewGroup; import android.widget.TextView; 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.FolderListFragment; import com.nutomic.syncthingandroid.syncthing.RestApi; @@ -45,8 +45,8 @@ import static java.lang.Math.min; /** * Shows {@link FolderListFragment} and - * {@link com.nutomic.syncthingandroid.fragments.DevicesFragment} in different tabs, and - * {@link com.nutomic.syncthingandroid.fragments.DrawerFragment} in the navigation drawer. + * {@link DeviceListFragment} in different tabs, and + * {@link DrawerFragment} in the navigation drawer. */ public class MainActivity extends SyncthingActivity implements SyncthingService.OnApiChangeListener { @@ -161,9 +161,9 @@ public class MainActivity extends SyncthingActivity public Fragment getItem(int position) { switch (position) { case 0: - return mFolderFragment; + return mFolderListFragment; case 1: - return mDevicesFragment; + return mDeviceListFragment; default: 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; @@ -216,16 +216,16 @@ public class MainActivity extends SyncthingActivity if (savedInstanceState != null) { FragmentManager fm = getSupportFragmentManager(); - mFolderFragment = (FolderListFragment) fm.getFragment( + mFolderListFragment = (FolderListFragment) fm.getFragment( savedInstanceState, FolderListFragment.class.getName()); - mDevicesFragment = (DevicesFragment) fm.getFragment( - savedInstanceState, DevicesFragment.class.getName()); + mDeviceListFragment = (DeviceListFragment) fm.getFragment( + savedInstanceState, DeviceListFragment.class.getName()); mDrawerFragment = (DrawerFragment) fm.getFragment( savedInstanceState, DrawerFragment.class.getName()); mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab")); } else { - mFolderFragment = new FolderListFragment(); - mDevicesFragment = new DevicesFragment(); + mFolderListFragment = new FolderListFragment(); + mDeviceListFragment = new DeviceListFragment(); mDrawerFragment = new DrawerFragment(); } @@ -247,8 +247,8 @@ public class MainActivity extends SyncthingActivity } if (getService() != null) { getService().unregisterOnApiChangeListener(this); - getService().unregisterOnApiChangeListener(mFolderFragment); - getService().unregisterOnApiChangeListener(mDevicesFragment); + getService().unregisterOnApiChangeListener(mFolderListFragment); + getService().unregisterOnApiChangeListener(mDeviceListFragment); } } @@ -256,8 +256,8 @@ public class MainActivity extends SyncthingActivity public void onServiceConnected(ComponentName componentName, IBinder iBinder) { super.onServiceConnected(componentName, iBinder); getService().registerOnApiChangeListener(this); - getService().registerOnApiChangeListener(mFolderFragment); - getService().registerOnApiChangeListener(mDevicesFragment); + getService().registerOnApiChangeListener(mFolderListFragment); + getService().registerOnApiChangeListener(mDeviceListFragment); } /** @@ -267,10 +267,10 @@ 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 (mFolderListFragment != null && mDeviceListFragment != null && mDrawerFragment != null) { FragmentManager fm = getSupportFragmentManager(); - fm.putFragment(outState, FolderListFragment.class.getName(), mFolderFragment); - fm.putFragment(outState, DevicesFragment.class.getName(), mDevicesFragment); + fm.putFragment(outState, FolderListFragment.class.getName(), mFolderListFragment); + fm.putFragment(outState, DeviceListFragment.class.getName(), mDeviceListFragment); fm.putFragment(outState, DrawerFragment.class.getName(), mDrawerFragment); outState.putInt("currentTab", mViewPager.getCurrentItem()); } diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java index 17892e81..a1129f6f 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/SettingsActivity.java @@ -3,10 +3,9 @@ package com.nutomic.syncthingandroid.activities; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.view.View; 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.SettingsFragment; @@ -27,7 +26,7 @@ public class SettingsActivity extends SyncthingActivity { * edited or a new one created. *

* 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"; @@ -50,7 +49,7 @@ public class SettingsActivity extends SyncthingActivity { mFragment = new SettingsFragment(); break; case ACTION_NODE_SETTINGS_FRAGMENT: - mFragment = new DeviceSettingsFragment(); + mFragment = new DeviceFragment(); if (!getIntent().hasExtra(EXTRA_IS_CREATE)) { throw new IllegalArgumentException("EXTRA_IS_CREATE must be set"); } @@ -87,16 +86,4 @@ public class SettingsActivity extends SyncthingActivity { public boolean getIsCreate() { 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); - } - } - } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java new file mode 100644 index 00000000..b4e64483 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java @@ -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. + *

+ * NOTE: This is only called once on startup, should be called more often to properly display + * version/address changes. + */ + @Override + public void onReceiveConnections(Map 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 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); + } + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DevicesFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java similarity index 95% rename from src/main/java/com/nutomic/syncthingandroid/fragments/DevicesFragment.java rename to src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java index ba24e923..a6f3be63 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/DevicesFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java @@ -23,7 +23,7 @@ import java.util.TimerTask; /** * 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 { private DevicesAdapter mAdapter; @@ -96,7 +96,7 @@ public class DevicesFragment extends ListFragment implements SyncthingService.On Intent intent = new Intent(getActivity(), SettingsActivity.class); intent.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT); 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); } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceSettingsFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceSettingsFragment.java deleted file mode 100644 index eed7f7f7..00000000 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceSettingsFragment.java +++ /dev/null @@ -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 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 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(); - } - } - -} diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java index afce907e..e9ce9b98 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java @@ -32,6 +32,7 @@ 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 com.nutomic.syncthingandroid.util.TextWatcherAdapter; import java.util.ArrayList; import java.util.List; @@ -76,7 +77,7 @@ public class FolderFragment extends Fragment private TextView mVersioningKeepView; - private boolean mIsCreate; + private boolean mIsCreateMode; private KeepVersionsDialogFragment mKeepVersionsDialogFragment = new KeepVersionsDialogFragment(); @@ -154,12 +155,12 @@ public class FolderFragment extends Fragment super.onCreate(savedInstanceState); SettingsActivity activity = (SettingsActivity) getActivity(); - mIsCreate = activity.getIsCreate(); - activity.setTitle(isCreatingFolder() ? R.string.create_folder : R.string.edit_folder); + mIsCreateMode = activity.getIsCreate(); + activity.setTitle(mIsCreateMode ? R.string.create_folder : R.string.edit_folder); activity.registerOnServiceConnectedListener(this); setHasOptionsMenu(true); - if (isCreatingFolder()) { + if (mIsCreateMode) { if (savedInstanceState != null) { 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. */ @@ -196,11 +205,50 @@ public class FolderFragment extends Fragment mPathView.setOnClickListener(mPathViewClickListener); view.findViewById(R.id.versioningContainer).setOnClickListener(mVersioningContainerClickListener); - if (isEditingFolder()) { + if (!mIsCreateMode) { 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 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() { // Update views mIdView.setText(mFolder.id); @@ -233,53 +281,6 @@ public class FolderFragment extends Fragment 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 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); @@ -288,8 +289,8 @@ public class FolderFragment extends Fragment @Override public void onPrepareOptionsMenu(Menu menu) { - menu.findItem(R.id.create).setVisible(isCreatingFolder()); - menu.findItem(R.id.delete).setVisible(isEditingFolder()); + menu.findItem(R.id.create).setVisible(mIsCreateMode); + menu.findItem(R.id.delete).setVisible(!mIsCreateMode); } @Override @@ -324,8 +325,9 @@ public class FolderFragment extends Fragment case android.R.id.home: getActivity().finish(); return true; + default: + return super.onOptionsItemSelected(item); } - return super.onOptionsItemSelected(item); } @Override @@ -355,7 +357,6 @@ public class FolderFragment extends Fragment private void addEmptyDeviceListView() { 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 contentInset = getResources().getDimensionPixelOffset(R.dimen.abc_action_bar_content_inset_material); setMarginStart(params, dividerInset); @@ -376,28 +377,8 @@ public class FolderFragment extends Fragment } private void updateRepo() { - if (isEditingFolder()) { + if (!mIsCreateMode) { 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) {} - } } diff --git a/src/main/java/com/nutomic/syncthingandroid/util/Compression.java b/src/main/java/com/nutomic/syncthingandroid/util/Compression.java new file mode 100644 index 00000000..28063ecb --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/util/Compression.java @@ -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); + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/util/TextWatcherAdapter.java b/src/main/java/com/nutomic/syncthingandroid/util/TextWatcherAdapter.java new file mode 100644 index 00000000..ee9f2888 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/util/TextWatcherAdapter.java @@ -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) {} +} diff --git a/src/main/java/com/nutomic/syncthingandroid/widget/EnhancedEditText.java b/src/main/java/com/nutomic/syncthingandroid/widget/EnhancedEditText.java new file mode 100644 index 00000000..5df51557 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/widget/EnhancedEditText.java @@ -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.
+ *
+ * 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; + } +} diff --git a/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_active.png new file mode 100644 index 00000000..23f0c445 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_inactive.png new file mode 100644 index 00000000..95aa9c1f Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_cellphone_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_info_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_info_black_24dp_active.png new file mode 100644 index 00000000..e724f48f Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_info_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_info_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_info_black_24dp_inactive.png new file mode 100644 index 00000000..6cc170e6 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_info_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_link_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_link_black_24dp_active.png new file mode 100644 index 00000000..2cc6a5a3 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_link_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_link_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_link_black_24dp_inactive.png new file mode 100644 index 00000000..b0effd93 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_link_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_active.png new file mode 100644 index 00000000..935686e8 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_inactive.png new file mode 100644 index 00000000..3d0ab6e4 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_phonelink_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_qrcode.png b/src/main/res/drawable-hdpi/ic_qrcode.png deleted file mode 100644 index 3f7475ec..00000000 Binary files a/src/main/res/drawable-hdpi/ic_qrcode.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_active.png new file mode 100644 index 00000000..49eb0bbe Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_inactive.png new file mode 100644 index 00000000..1f79549c Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_qrcode_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_active.png new file mode 100644 index 00000000..3da65a17 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_inactive.png new file mode 100644 index 00000000..94ff9526 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_tag_text_outline_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_active.png b/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_active.png new file mode 100644 index 00000000..77961be8 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_active.png differ diff --git a/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_inactive.png b/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_inactive.png new file mode 100644 index 00000000..f64ae589 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_zip_box_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-mdpi/ic_qrcode.png b/src/main/res/drawable-mdpi/ic_qrcode.png deleted file mode 100644 index c20e21bf..00000000 Binary files a/src/main/res/drawable-mdpi/ic_qrcode.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_active.png new file mode 100644 index 00000000..aaaa44af Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_inactive.png new file mode 100644 index 00000000..fb786521 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_cellphone_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_info_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_info_black_24dp_active.png new file mode 100644 index 00000000..c4b44188 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_info_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_info_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_info_black_24dp_inactive.png new file mode 100644 index 00000000..138e36b0 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_info_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_link_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_link_black_24dp_active.png new file mode 100644 index 00000000..b1499f36 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_link_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_link_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_link_black_24dp_inactive.png new file mode 100644 index 00000000..97aaef54 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_link_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_active.png new file mode 100644 index 00000000..5575e46d Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_inactive.png new file mode 100644 index 00000000..008a58ce Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_phonelink_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_qrcode.png b/src/main/res/drawable-xhdpi/ic_qrcode.png deleted file mode 100644 index 34fe1032..00000000 Binary files a/src/main/res/drawable-xhdpi/ic_qrcode.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_active.png new file mode 100644 index 00000000..f4547714 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_inactive.png new file mode 100644 index 00000000..529c0700 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_qrcode_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_active.png new file mode 100644 index 00000000..3fede434 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_inactive.png new file mode 100644 index 00000000..a7bb5637 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_tag_text_outline_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_active.png b/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_active.png new file mode 100644 index 00000000..240f40a6 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_active.png differ diff --git a/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_inactive.png b/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_inactive.png new file mode 100644 index 00000000..ec5db18a Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_zip_box_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_active.png new file mode 100644 index 00000000..1a03caba Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_inactive.png new file mode 100644 index 00000000..2dc0ef75 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_cellphone_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_info_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_info_black_24dp_active.png new file mode 100644 index 00000000..459b0d4a Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_info_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_info_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_info_black_24dp_inactive.png new file mode 100644 index 00000000..3fd178db Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_info_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_link_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_link_black_24dp_active.png new file mode 100644 index 00000000..213d4d52 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_link_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_link_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_link_black_24dp_inactive.png new file mode 100644 index 00000000..daa211f9 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_link_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_active.png new file mode 100644 index 00000000..132fe37e Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_inactive.png new file mode 100644 index 00000000..854c4256 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_phonelink_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode.png b/src/main/res/drawable-xxhdpi/ic_qrcode.png deleted file mode 100644 index 5ead1fb9..00000000 Binary files a/src/main/res/drawable-xxhdpi/ic_qrcode.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_active.png new file mode 100644 index 00000000..5b2884d3 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_inactive.png new file mode 100644 index 00000000..e0c9df0c Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_qrcode_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_active.png new file mode 100644 index 00000000..f70f65ed Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_inactive.png new file mode 100644 index 00000000..77572b06 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_tag_text_outline_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_active.png b/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_active.png new file mode 100644 index 00000000..43dc2405 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_inactive.png b/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_inactive.png new file mode 100644 index 00000000..61e2f21f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_zip_box_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_active.png new file mode 100644 index 00000000..0533b7be Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_inactive.png new file mode 100644 index 00000000..8b9000fb Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_cellphone_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_active.png new file mode 100644 index 00000000..090f8258 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_inactive.png new file mode 100644 index 00000000..35f85460 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_info_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_active.png new file mode 100644 index 00000000..18ba9a5d Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_inactive.png new file mode 100644 index 00000000..1971c1ed Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_link_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_active.png new file mode 100644 index 00000000..10f29d47 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_inactive.png new file mode 100644 index 00000000..b2f04b03 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_phonelink_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_active.png new file mode 100644 index 00000000..a5f05770 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_inactive.png new file mode 100644 index 00000000..e85a4c25 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_qrcode_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_active.png new file mode 100644 index 00000000..bd2545fa Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_inactive.png new file mode 100644 index 00000000..fef8eff9 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_tag_text_outline_black_24dp_inactive.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_active.png b/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_active.png new file mode 100644 index 00000000..c3359f74 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_active.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_inactive.png b/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_inactive.png new file mode 100644 index 00000000..b861d2d7 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_zip_box_black_24dp_inactive.png differ diff --git a/src/main/res/drawable/ic_cellphone_black_24dp.xml b/src/main/res/drawable/ic_cellphone_black_24dp.xml new file mode 100644 index 00000000..52c7da3c --- /dev/null +++ b/src/main/res/drawable/ic_cellphone_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_info_black_24dp.xml b/src/main/res/drawable/ic_info_black_24dp.xml new file mode 100644 index 00000000..81edf070 --- /dev/null +++ b/src/main/res/drawable/ic_info_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_link_black_24dp.xml b/src/main/res/drawable/ic_link_black_24dp.xml new file mode 100644 index 00000000..9bd770ba --- /dev/null +++ b/src/main/res/drawable/ic_link_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_phonelink_black_24dp.xml b/src/main/res/drawable/ic_phonelink_black_24dp.xml new file mode 100644 index 00000000..87c6109a --- /dev/null +++ b/src/main/res/drawable/ic_phonelink_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_qrcode_black_24dp.xml b/src/main/res/drawable/ic_qrcode_black_24dp.xml new file mode 100644 index 00000000..bdf21d93 --- /dev/null +++ b/src/main/res/drawable/ic_qrcode_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_tag_text_outline_black_24dp.xml b/src/main/res/drawable/ic_tag_text_outline_black_24dp.xml new file mode 100644 index 00000000..fdc74823 --- /dev/null +++ b/src/main/res/drawable/ic_tag_text_outline_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/ic_transparent_24dp.xml b/src/main/res/drawable/ic_transparent_24dp.xml new file mode 100644 index 00000000..c4043eb2 --- /dev/null +++ b/src/main/res/drawable/ic_transparent_24dp.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/src/main/res/drawable/ic_zip_box_black_24dp.xml b/src/main/res/drawable/ic_zip_box_black_24dp.xml new file mode 100644 index 00000000..7fce39a7 --- /dev/null +++ b/src/main/res/drawable/ic_zip_box_black_24dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/layout/fragment_device.xml b/src/main/res/layout/fragment_device.xml new file mode 100644 index 00000000..8afb2561 --- /dev/null +++ b/src/main/res/layout/fragment_device.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/fragment_folder.xml b/src/main/res/layout/fragment_folder.xml index c883375a..2f1d8fe4 100644 --- a/src/main/res/layout/fragment_folder.xml +++ b/src/main/res/layout/fragment_folder.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".fragments.FoldersFragment"> + tools:context=".fragments.FolderFragment"> + android:showDividers="middle"> + android:hint="@string/folder_id" + android:imeOptions="actionDone" + android:inputType="textCapWords" /> @@ -40,9 +41,9 @@ android:orientation="vertical"> @@ -50,9 +51,9 @@ \ No newline at end of file + tools:ignore="RtlHardcoded,RtlSymmetry" /> diff --git a/src/main/res/layout/pref_widget_scan_qr_code.xml b/src/main/res/layout/pref_widget_scan_qr_code.xml index 5412c17a..8b06f59d 100644 --- a/src/main/res/layout/pref_widget_scan_qr_code.xml +++ b/src/main/res/layout/pref_widget_scan_qr_code.xml @@ -1,8 +1,8 @@ diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 47455f7f..c546bc97 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -25,18 +25,20 @@ @dimen/abc_action_bar_content_inset_material - - -