From e0d8153b6dcc9333c4ed426ec9bc54a71b5d1922 Mon Sep 17 00:00:00 2001 From: Catfriend1 Date: Sun, 19 Aug 2018 23:22:38 +0200 Subject: [PATCH] Add explanation in UI why syncthing is (not) running (fixes #729) * WIP - Allow expanding the drawer if syncting is not running - Hide syncthing process stats in Drawer if syncthing is not running - Hide menu action button if syncthing is not running (except settings and exit) * WIP - Add RunConditionsMonitor#getRunDecisionExplanation * WIP - Show run status explanation in StatusFragment * Fix duplicate declaration in RunConditionMonitor * Explain all syncthing service statuses instead of only differentiating between ACTIVE and NON-ACTIVE * Remove parts marked "// to-remove" * Remove unused resources * Move syncthing live status from drawer into status tab * Fix handler start and stop in StatusFragments Note: onResume is intentionally not overidden as the fragment is not active after the user left and reentered the app * Fix crash on orientation change * Only update MainActivity.ViewPager when a service state occured This fixes a UI glitch occuring because onResume also results in onServiceStateChanged as the serviceStateChangeListeners are re-registered. * Remove unused strings --- .../activities/MainActivity.java | 148 ++++++---- .../activities/StateDialogActivity.java | 43 +-- .../activities/SyncthingActivity.java | 2 +- .../fragments/DrawerFragment.java | 184 ++++--------- .../fragments/StatusFragment.java | 260 ++++++++++++++++++ .../syncthingandroid/service/Constants.java | 1 + .../syncthingandroid/service/RestApi.java | 5 +- .../service/RunConditionMonitor.java | 72 ++++- .../service/SyncthingService.java | 7 + .../ic_settings_white_24dp.png | Bin 0 -> 743 bytes app/src/main/res/layout/fragment_drawer.xml | 117 -------- app/src/main/res/layout/fragment_status.xml | 6 + app/src/main/res/menu/status_list.xml | 12 + app/src/main/res/values-ar/strings.xml | 2 - app/src/main/res/values-bg/strings.xml | 1 - app/src/main/res/values-ca-rES/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-es-rEC/strings.xml | 2 - app/src/main/res/values-es-rMX/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-fi/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-lt/strings.xml | 2 - app/src/main/res/values-nb/strings.xml | 1 - app/src/main/res/values-nl-rBE/strings.xml | 2 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-nn/strings.xml | 1 - app/src/main/res/values-no/strings.xml | 2 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-sl/strings.xml | 2 - app/src/main/res/values-sr/strings.xml | 2 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 3 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 35 ++- 51 files changed, 518 insertions(+), 420 deletions(-) create mode 100644 app/src/main/java/com/nutomic/syncthingandroid/fragments/StatusFragment.java create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png create mode 100644 app/src/main/res/layout/fragment_status.xml create mode 100644 app/src/main/res/menu/status_list.xml diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java index 03376e83..7597e374 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java @@ -24,7 +24,7 @@ import android.provider.Settings; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewPager; @@ -49,6 +49,7 @@ import com.nutomic.syncthingandroid.SyncthingApp; import com.nutomic.syncthingandroid.fragments.DeviceListFragment; import com.nutomic.syncthingandroid.fragments.DrawerFragment; import com.nutomic.syncthingandroid.fragments.FolderListFragment; +import com.nutomic.syncthingandroid.fragments.StatusFragment; import com.nutomic.syncthingandroid.model.Options; import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; @@ -89,11 +90,13 @@ public class MainActivity extends StateDialogActivity private Dialog mRestartDialog; private boolean mBatteryOptimizationDialogDismissed; + private SyncthingService.State mSyncthingServiceState = SyncthingService.State.INIT; private ViewPager mViewPager; private FolderListFragment mFolderListFragment; private DeviceListFragment mDeviceListFragment; + private StatusFragment mStatusFragment; private DrawerFragment mDrawerFragment; private ActionBarDrawerToggle mDrawerToggle; @@ -105,13 +108,17 @@ public class MainActivity extends StateDialogActivity */ @Override public void onServiceStateChange(SyncthingService.State currentState) { + if (currentState != mSyncthingServiceState) { + mSyncthingServiceState = currentState; + updateViewPager(); + } + switch (currentState) { case STARTING: break; case ACTIVE: showBatteryOptimizationDialogIfNecessary(); mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - mDrawerFragment.requestGuiUpdate(); // Check if the usage reporting minimum delay passed by. Boolean usageReportingDelayPassed = (new Date().getTime() > getFirstStartTime() + USAGE_REPORTING_DIALOG_DELAY); @@ -176,39 +183,6 @@ public class MainActivity extends StateDialogActivity return firstInstallTime; } - private final FragmentPagerAdapter mSectionsPagerAdapter = - new FragmentPagerAdapter(getSupportFragmentManager()) { - - @Override - public Fragment getItem(int position) { - switch (position) { - case 0: - return mFolderListFragment; - case 1: - return mDeviceListFragment; - default: - return null; - } - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getResources().getString(R.string.folders_fragment_title); - case 1: - return getResources().getString(R.string.devices_fragment_title); - default: - return String.valueOf(position); - } - } - }; - /** * Initializes tab navigation. */ @@ -218,6 +192,7 @@ public class MainActivity extends StateDialogActivity setContentView(R.layout.activity_main); mDrawerLayout = findViewById(R.id.drawer_layout); + mViewPager = findViewById(R.id.pager); FragmentManager fm = getSupportFragmentManager(); if (savedInstanceState != null) { @@ -225,18 +200,24 @@ public class MainActivity extends StateDialogActivity savedInstanceState, FolderListFragment.class.getName()); mDeviceListFragment = (DeviceListFragment) fm.getFragment( savedInstanceState, DeviceListFragment.class.getName()); + mStatusFragment = (StatusFragment) fm.getFragment( + savedInstanceState, StatusFragment.class.getName()); mDrawerFragment = (DrawerFragment) fm.getFragment( savedInstanceState, DrawerFragment.class.getName()); - } else { + } + if (mFolderListFragment == null) { mFolderListFragment = new FolderListFragment(); + } + if (mDeviceListFragment == null) { mDeviceListFragment = new DeviceListFragment(); + } + if (mStatusFragment == null) { + mStatusFragment = new StatusFragment(); + } + if (mDrawerFragment == null) { mDrawerFragment = new DrawerFragment(); } - mViewPager = findViewById(R.id.pager); - mViewPager.setAdapter(mSectionsPagerAdapter); - TabLayout tabLayout = findViewById(R.id.tabContainer); - tabLayout.setupWithViewPager(mViewPager); if (savedInstanceState != null) { mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab")); if (savedInstanceState.getBoolean(IS_SHOWING_RESTART_DIALOG)){ @@ -246,6 +227,8 @@ public class MainActivity extends StateDialogActivity if(savedInstanceState.getBoolean(IS_QRCODE_DIALOG_DISPLAYED)) { showQrCodeDialog(savedInstanceState.getString(DEVICEID_KEY), savedInstanceState.getParcelable(QRCODE_BITMAP_KEY)); } + } else { + updateViewPager(); } fm.beginTransaction().replace(R.id.drawer, mDrawerFragment).commit(); @@ -261,6 +244,74 @@ public class MainActivity extends StateDialogActivity onNewIntent(getIntent()); } + /** + * Updates the ViewPager to show tabs depending on the service state. + */ + private void updateViewPager() { + FragmentStatePagerAdapter mSectionsPagerAdapter = + new FragmentStatePagerAdapter(getSupportFragmentManager()) { + + @Override + public Fragment getItem(int position) { + if (mSyncthingServiceState == SyncthingService.State.ACTIVE) { + switch (position) { + case 0: + return mFolderListFragment; + case 1: + return mDeviceListFragment; + case 2: + return mStatusFragment; + default: + return null; + } + } else { + switch (position) { + case 0: + return mStatusFragment; + default: + return null; + } + } + } + + @Override + public int getItemPosition(Object object) { + return this.POSITION_NONE; + } + + @Override + public int getCount() { + return mSyncthingServiceState == SyncthingService.State.ACTIVE ? 3 : 1; + } + + @Override + public CharSequence getPageTitle(int position) { + if (mSyncthingServiceState == SyncthingService.State.ACTIVE) { + switch (position) { + case 0: + return getResources().getString(R.string.folders_fragment_title); + case 1: + return getResources().getString(R.string.devices_fragment_title); + case 2: + return getResources().getString(R.string.status_fragment_title); + default: + return String.valueOf(position); + } + } else { + switch (position) { + case 0: + return getResources().getString(R.string.status_fragment_title); + default: + return String.valueOf(position); + } + } + } + }; + mViewPager.setAdapter(mSectionsPagerAdapter); + TabLayout tabLayout = findViewById(R.id.tabContainer); + tabLayout.setupWithViewPager(mViewPager); + } + @Override public void onResume() { // Check if storage permission has been revoked at runtime. @@ -286,6 +337,8 @@ public class MainActivity extends StateDialogActivity mSyncthingService.unregisterOnServiceStateChangeListener(this); mSyncthingService.unregisterOnServiceStateChangeListener(mFolderListFragment); mSyncthingService.unregisterOnServiceStateChangeListener(mDeviceListFragment); + mSyncthingService.unregisterOnServiceStateChangeListener(mDrawerFragment); + mSyncthingService.unregisterOnServiceStateChangeListener(mStatusFragment); } } @@ -297,6 +350,8 @@ public class MainActivity extends StateDialogActivity syncthingService.registerOnServiceStateChangeListener(this); syncthingService.registerOnServiceStateChangeListener(mFolderListFragment); syncthingService.registerOnServiceStateChangeListener(mDeviceListFragment); + syncthingService.registerOnServiceStateChangeListener(mDrawerFragment); + syncthingService.registerOnServiceStateChangeListener(mStatusFragment); } /** @@ -314,6 +369,7 @@ public class MainActivity extends StateDialogActivity }; putFragment.accept(mFolderListFragment); putFragment.accept(mDeviceListFragment); + putFragment.accept(mStatusFragment); putFragment.accept(mDrawerFragment); outState.putInt("currentTab", mViewPager.getCurrentItem()); @@ -402,18 +458,6 @@ public class MainActivity extends StateDialogActivity super(activity, drawerLayout, R.string.app_name, R.string.app_name); } - @Override - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - mDrawerFragment.onDrawerOpened(); - } - - @Override - public void onDrawerClosed(View view) { - super.onDrawerClosed(view); - mDrawerFragment.onDrawerClosed(); - } - @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, 0); diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/StateDialogActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/StateDialogActivity.java index a9049b5c..2f77e7e7 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/StateDialogActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/StateDialogActivity.java @@ -40,20 +40,12 @@ public abstract class StateDialogActivity extends SyncthingActivity { protected void onResume() { super.onResume(); mIsPaused = false; - switch (mServiceState) { - case DISABLED: - showDisabledDialog(); - break; - default: - break; - } } @Override protected void onPause() { super.onPause(); mIsPaused = true; - dismissDisabledDialog(); dismissLoadingDialog(); } @@ -63,7 +55,6 @@ public abstract class StateDialogActivity extends SyncthingActivity { if (getService() != null) { getService().unregisterOnServiceStateChangeListener(this::onServiceStateChange); } - dismissDisabledDialog(); } private void onServiceStateChange(SyncthingService.State currentState) { @@ -71,50 +62,18 @@ public abstract class StateDialogActivity extends SyncthingActivity { switch (mServiceState) { case INIT: // fallthrough case STARTING: - dismissDisabledDialog(); showLoadingDialog(); break; case ACTIVE: - dismissDisabledDialog(); dismissLoadingDialog(); break; - case DISABLED: - if (!mIsPaused) { - showDisabledDialog(); - } - break; + case DISABLED: // fallthrough case ERROR: // fallthrough default: break; } } - private void showDisabledDialog() { - if (this.isFinishing() && (mDisabledDialog != null)) { - return; - } - mDisabledDialog = new AlertDialog.Builder(this) - .setTitle(R.string.syncthing_disabled_title) - .setMessage(R.string.syncthing_disabled_message) - .setPositiveButton(R.string.syncthing_disabled_change_settings, - (dialogInterface, i) -> { - Intent intent = new Intent(this, SettingsActivity.class); - intent.putExtra(SettingsActivity.EXTRA_OPEN_SUB_PREF_SCREEN, "category_run_conditions"); - startActivity(intent); - } - ) - .setNegativeButton(R.string.exit, - (dialogInterface, i) -> ActivityCompat.finishAffinity(this) - ) - .setCancelable(false) - .show(); - } - - private void dismissDisabledDialog() { - Util.dismissDialogSafe(mDisabledDialog, this); - mDisabledDialog = null; - } - /** * Shows the loading dialog with the correct text ("creating keys" or "loading"). */ diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/SyncthingActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/SyncthingActivity.java index 50867d49..eb9ad5eb 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/SyncthingActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/SyncthingActivity.java @@ -92,7 +92,7 @@ public abstract class SyncthingActivity extends AppCompatActivity implements Ser /** * Returns service object (or null if not bound). */ - SyncthingService getService() { + public SyncthingService getService() { return mSyncthingService; } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java b/app/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java index 493a9320..a6dc68db 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/fragments/DrawerFragment.java @@ -13,75 +13,54 @@ import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.activities.MainActivity; import com.nutomic.syncthingandroid.activities.SettingsActivity; import com.nutomic.syncthingandroid.activities.WebGuiActivity; import com.nutomic.syncthingandroid.http.ImageGetRequest; -import com.nutomic.syncthingandroid.model.Connections; -import com.nutomic.syncthingandroid.model.SystemInfo; -import com.nutomic.syncthingandroid.model.SystemVersion; import com.nutomic.syncthingandroid.service.Constants; import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; -import com.nutomic.syncthingandroid.util.Util; import java.net.URL; -import java.text.NumberFormat; -import java.util.Locale; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; + /** * Displays information about the local device. */ -public class DrawerFragment extends Fragment implements View.OnClickListener { +public class DrawerFragment extends Fragment implements SyncthingService.OnServiceStateChangeListener, + View.OnClickListener { + + private SyncthingService.State mServiceState = SyncthingService.State.INIT; private static final String TAG = "DrawerFragment"; - private TextView mCpuUsage; - private TextView mRamUsage; - private TextView mDownload; - private TextView mUpload; - private TextView mAnnounceServer; private TextView mVersion; + private TextView mDrawerActionShowQrCode; + private TextView mDrawerActionWebGui; + private TextView mDrawerActionRestart; + private TextView mDrawerActionSettings; private TextView mExitButton; - private Timer mTimer; - private MainActivity mActivity; + private SharedPreferences sharedPreferences; - public void onDrawerOpened() { - mTimer = new Timer(); - mTimer.schedule(new TimerTask() { - @Override - public void run() { - updateGui(); - } - - }, 0, Constants.GUI_UPDATE_INTERVAL); + @Override + public void onServiceStateChange(SyncthingService.State currentState) { + mServiceState = currentState; + updateButtons(); } @Override public void onResume() { super.onResume(); - updateExitButtonVisibility(); - } - - public void onDrawerClosed() { - if (mTimer != null) { - mTimer.cancel(); - mTimer = null; - } + updateButtons(); } @Override public void onDestroy() { super.onDestroy(); - onDrawerClosed(); } /** @@ -94,121 +73,57 @@ public class DrawerFragment extends Fragment implements View.OnClickListener { @Override public void onViewCreated(View view, Bundle savedInstanceState) { - mCpuUsage = view.findViewById(R.id.cpu_usage); - mRamUsage = view.findViewById(R.id.ram_usage); - mDownload = view.findViewById(R.id.download); - mUpload = view.findViewById(R.id.upload); - mAnnounceServer = view.findViewById(R.id.announce_server); - mVersion = view.findViewById(R.id.version); - mExitButton = view.findViewById(R.id.drawerActionExit); + mActivity = (MainActivity) getActivity(); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity); - view.findViewById(R.id.drawerActionWebGui) - .setOnClickListener(this); - view.findViewById(R.id.drawerActionRestart) - .setOnClickListener(this); - view.findViewById(R.id.drawerActionSettings) - .setOnClickListener(this); - view.findViewById(R.id.drawerActionShowQrCode) - .setOnClickListener(this); + mVersion = view.findViewById(R.id.version); + mDrawerActionShowQrCode = view.findViewById(R.id.drawerActionShowQrCode); + mDrawerActionWebGui = view.findViewById(R.id.drawerActionWebGui); + mDrawerActionRestart = view.findViewById(R.id.drawerActionRestart); + mDrawerActionSettings = view.findViewById(R.id.drawerActionSettings); + mExitButton = view.findViewById(R.id.drawerActionExit); + + // Show static content. + mVersion.setText(sharedPreferences.getString(Constants.PREF_LAST_BINARY_VERSION, "")); + + // Add listeners to buttons. + mDrawerActionShowQrCode.setOnClickListener(this); + mDrawerActionWebGui.setOnClickListener(this); + mDrawerActionRestart.setOnClickListener(this); + mDrawerActionSettings.setOnClickListener(this); mExitButton.setOnClickListener(this); - updateExitButtonVisibility(); - } - - private void updateExitButtonVisibility() { - boolean alwaysInBackground = alwaysRunInBackground(); - mExitButton.setVisibility(alwaysInBackground ? View.GONE : View.VISIBLE); + updateButtons(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mActivity = (MainActivity) getActivity(); - - if (savedInstanceState != null && savedInstanceState.getBoolean("active")) { - onDrawerOpened(); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean("active", mTimer != null); } /** - * Invokes status callbacks. + * Update action button availability. */ - private void updateGui() { - MainActivity mainActivity = (MainActivity) getActivity(); - if (mainActivity == null) { - return; - } - if (mainActivity.isFinishing()) { - return; - } + private void updateButtons() { + Boolean synthingRunning = mServiceState == SyncthingService.State.ACTIVE; - RestApi mApi = mainActivity.getApi(); - if (mApi != null) { - mApi.getSystemInfo(this::onReceiveSystemInfo); - mApi.getSystemVersion(this::onReceiveSystemVersion); - mApi.getConnections(this::onReceiveConnections); - } - } + // Show buttons if syncthing is running. + mVersion.setVisibility(synthingRunning ? View.VISIBLE : View.GONE); + mDrawerActionShowQrCode.setVisibility(synthingRunning ? View.VISIBLE : View.GONE); + mDrawerActionWebGui.setVisibility(synthingRunning ? View.VISIBLE : View.GONE); + mDrawerActionRestart.setVisibility(synthingRunning ? View.VISIBLE : View.GONE); - /** - * This will not do anything if gui updates are already scheduled. - */ - public void requestGuiUpdate() { - if (mTimer == null) { - updateGui(); - } - } - - /** - * Populates views with status received via {@link RestApi#getSystemInfo}. - */ - private void onReceiveSystemInfo(SystemInfo info) { - if (getActivity() == null) - return; - NumberFormat percentFormat = NumberFormat.getPercentInstance(); - percentFormat.setMaximumFractionDigits(2); - mCpuUsage.setText(percentFormat.format(info.cpuPercent / 100)); - mRamUsage.setText(Util.readableFileSize(mActivity, info.sys)); - int announceTotal = info.discoveryMethods; - int announceConnected = - announceTotal - Optional.fromNullable(info.discoveryErrors).transform(Map::size).or(0); - mAnnounceServer.setText(String.format(Locale.getDefault(), "%1$d/%2$d", - announceConnected, announceTotal)); - int color = (announceConnected > 0) - ? R.color.text_green - : R.color.text_red; - mAnnounceServer.setTextColor(ContextCompat.getColor(getContext(), color)); - } - - /** - * Populates views with status received via {@link RestApi#getSystemInfo}. - */ - private void onReceiveSystemVersion(SystemVersion info) { - if (getActivity() == null) - return; - - mVersion.setText(info.version); - } - - /** - * Populates views with status received via {@link RestApi#getConnections}. - */ - private void onReceiveConnections(Connections connections) { - Connections.Connection c = connections.total; - mDownload.setText(Util.readableTransferRate(mActivity, c.inBits)); - mUpload.setText(Util.readableTransferRate(mActivity, c.outBits)); + // Do not show the exit button if our app runs as a background service. + mExitButton.setVisibility( + sharedPreferences.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false) ? + View.GONE : + View.VISIBLE + ); } /** * Gets QRCode and displays it in a Dialog. */ - private void showQrCode() { RestApi restApi = mActivity.getApi(); if (restApi == null) { @@ -255,9 +170,4 @@ public class DrawerFragment extends Fragment implements View.OnClickListener { break; } } - - private boolean alwaysRunInBackground() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); - return sp.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false); - } } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/fragments/StatusFragment.java b/app/src/main/java/com/nutomic/syncthingandroid/fragments/StatusFragment.java new file mode 100644 index 00000000..3a988951 --- /dev/null +++ b/app/src/main/java/com/nutomic/syncthingandroid/fragments/StatusFragment.java @@ -0,0 +1,260 @@ +package com.nutomic.syncthingandroid.fragments; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.ListFragment; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; + +import com.google.common.base.Optional; +import com.nutomic.syncthingandroid.R; +import com.nutomic.syncthingandroid.activities.MainActivity; +import com.nutomic.syncthingandroid.activities.SettingsActivity; +import com.nutomic.syncthingandroid.activities.SyncthingActivity; +import com.nutomic.syncthingandroid.model.Connections; +import com.nutomic.syncthingandroid.model.SystemInfo; +import com.nutomic.syncthingandroid.model.SystemVersion; +import com.nutomic.syncthingandroid.service.Constants; +import com.nutomic.syncthingandroid.service.RestApi; +import com.nutomic.syncthingandroid.service.SyncthingService; +import com.nutomic.syncthingandroid.util.Util; + +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; +import java.text.NumberFormat; + +/** + * Displays why syncthing is running or disabled. + */ +public class StatusFragment extends ListFragment implements SyncthingService.OnServiceStateChangeListener { + + private static final String TAG = "StatusFragment"; + + private Runnable mRestApiQueryRunnable = new Runnable() { + @Override + public void run() { + onTimerEvent(); + mRestApiQueryHandler.postDelayed(this, Constants.GUI_UPDATE_INTERVAL); + } + }; + + private MainActivity mActivity; + private ArrayAdapter mAdapter; + private SyncthingService.State mServiceState = SyncthingService.State.INIT; + private final Handler mRestApiQueryHandler = new Handler(); + private Boolean mLastVisibleToUser = false; + + /** + * Object that must be locked upon accessing the status holders. + */ + private final Object mStatusHolderLock = new Object(); + + /** + * Status holders, filled on callbacks. + */ + private String mCpuUsage = ""; + private String mRamUsage = ""; + private String mDownload = ""; + private String mUpload = ""; + private String mAnnounceServer = ""; + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) + { + super.setUserVisibleHint(isVisibleToUser); + if (isVisibleToUser && !mLastVisibleToUser) { + // User switched to the current tab, start handler. + mRestApiQueryHandler.post(mRestApiQueryRunnable); + + } else if (!isVisibleToUser && mLastVisibleToUser) { + // User switched away to another tab, stop handler. + mRestApiQueryHandler.removeCallbacks(mRestApiQueryRunnable); + } + mLastVisibleToUser = isVisibleToUser; + } + + @Override + public void onPause() { + mRestApiQueryHandler.removeCallbacks(mRestApiQueryRunnable); + super.onPause(); + } + + @Override + public void onServiceStateChange(SyncthingService.State currentState) { + mServiceState = currentState; + updateStatus(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_status, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1); + setListAdapter(mAdapter); + setHasOptionsMenu(true); + updateStatus(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mActivity = (MainActivity) getActivity(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.status_list, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.open_preferences: + startActivity(new Intent(getContext(), SettingsActivity.class)); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void updateStatus() { + SyncthingActivity syncthingActivity = (SyncthingActivity) getActivity(); + if (syncthingActivity == null || getView() == null || syncthingActivity.isFinishing()) { + return; + } + SyncthingService syncthingService = syncthingActivity.getService(); + if (syncthingService == null) { + return; + } + + // Add status line showing the syncthing service state. + ArrayList statusItems = new ArrayList(); + switch (mServiceState) { + case INIT: + case STARTING: + statusItems.add(getString(R.string.syncthing_starting)); + break; + case ACTIVE: + statusItems.add(getString(R.string.syncthing_running)); + break; + case DISABLED: + statusItems.add(getString(R.string.syncthing_not_running)); + break; + case ERROR: + statusItems.add(getString(R.string.syncthing_has_crashed)); + break; + } + + // Add explanation why syncthing is (not) running. + switch (mServiceState) { + case ACTIVE: + case DISABLED: + statusItems.add(getString(R.string.reason) + "\n" + + "- " + syncthingService.getRunDecisionExplanation().trim().replace("\n", "\n- ")); + default: + break; + } + + // Add status holders refreshed by callbacks to the list. + if (mServiceState == SyncthingService.State.ACTIVE) { + synchronized (mStatusHolderLock) { + if (!TextUtils.isEmpty(mCpuUsage)) { + statusItems.add(getString(R.string.cpu_usage) + ": " + mCpuUsage); + } + if (!TextUtils.isEmpty(mRamUsage)) { + statusItems.add(getString(R.string.ram_usage) + ": " + mRamUsage); + } + if (!TextUtils.isEmpty(mDownload)) { + statusItems.add(getString(R.string.download_title) + ": " + mDownload); + } + if (!TextUtils.isEmpty(mUpload)) { + statusItems.add(getString(R.string.upload_title) + ": " + mUpload); + } + if (!TextUtils.isEmpty(mAnnounceServer)) { + statusItems.add(getString(R.string.announce_server) + ": " + mAnnounceServer); + } + } + } + + // Update list contents. + mAdapter.setNotifyOnChange(false); + mAdapter.clear(); + mAdapter.addAll(statusItems); + mAdapter.notifyDataSetChanged(); + } + + /** + * Invokes status callbacks via syncthing's REST API + * while the user is looking at the current tab. + */ + private void onTimerEvent() { + if (mServiceState != SyncthingService.State.ACTIVE) { + return; + } + MainActivity mainActivity = (MainActivity) getActivity(); + if (mainActivity == null) { + return; + } + if (mainActivity.isFinishing()) { + return; + } + RestApi restApi = mainActivity.getApi(); + if (restApi == null) { + return; + } + Log.v(TAG, "Invoking REST status queries"); + restApi.getSystemInfo(this::onReceiveSystemInfo); + restApi.getConnections(this::onReceiveConnections); + } + + /** + * Populates status holders with status received via {@link RestApi#getSystemInfo}. + */ + private void onReceiveSystemInfo(SystemInfo info) { + if (getActivity() == null) { + return; + } + NumberFormat percentFormat = NumberFormat.getPercentInstance(); + percentFormat.setMaximumFractionDigits(2); + int announceTotal = info.discoveryMethods; + int announceConnected = + announceTotal - Optional.fromNullable(info.discoveryErrors).transform(Map::size).or(0); + synchronized (mStatusHolderLock) { + mCpuUsage = percentFormat.format(info.cpuPercent / 100); + mRamUsage = Util.readableFileSize(mActivity, info.sys); + mAnnounceServer = String.format(Locale.getDefault(), "%1$d/%2$d", announceConnected, announceTotal); + } + updateStatus(); + } + + /** + * Populates status holders with status received via {@link RestApi#getConnections}. + */ + private void onReceiveConnections(Connections connections) { + if (getActivity() == null) { + return; + } + Connections.Connection c = connections.total; + synchronized (mStatusHolderLock) { + mDownload = Util.readableTransferRate(mActivity, c.inBits); + mUpload = Util.readableTransferRate(mActivity, c.outBits); + } + updateStatus(); + } + +} diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java b/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java index 53d54c83..dee6d9e3 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java @@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit; public class Constants { public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so"; + public static final String PREF_LAST_BINARY_VERSION = "lastBinaryVersion"; // Preferences - Run conditions public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background"; diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java b/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java index 65998bdf..58a1bda5 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java @@ -225,8 +225,7 @@ public class RestApi { * Precondition: {@link #mVersion} read from REST */ private void updateDebugFacilitiesCache() { - final String PREF_LAST_BINARY_VERSION = "lastBinaryVersion"; - if (!mVersion.equals(PreferenceManager.getDefaultSharedPreferences(mContext).getString(PREF_LAST_BINARY_VERSION, ""))) { + if (!mVersion.equals(PreferenceManager.getDefaultSharedPreferences(mContext).getString(Constants.PREF_LAST_BINARY_VERSION, ""))) { // First binary launch or binary upgraded case. new GetRequest(mContext, mUrl, GetRequest.URI_DEBUG, mApiKey, null, result -> { try { @@ -243,7 +242,7 @@ public class RestApi { // Store current binary version so we will only store this information again // after a binary update. PreferenceManager.getDefaultSharedPreferences(mContext).edit() - .putString(PREF_LAST_BINARY_VERSION, mVersion) + .putString(Constants.PREF_LAST_BINARY_VERSION, mVersion) .apply(); } catch (Exception e) { Log.w(TAG, "updateDebugFacilitiesCache: Failed to get debug facilities. result=" + result); diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/RunConditionMonitor.java b/app/src/main/java/com/nutomic/syncthingandroid/service/RunConditionMonitor.java index 4b4bfd79..6bd80f58 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/RunConditionMonitor.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/RunConditionMonitor.java @@ -8,6 +8,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SyncStatusObserver; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; @@ -20,6 +21,7 @@ import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.google.common.collect.Lists; +import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.SyncthingApp; import com.nutomic.syncthingandroid.service.ReceiverManager; @@ -58,6 +60,7 @@ public class RunConditionMonitor { private final Context mContext; @Inject SharedPreferences mPreferences; private ReceiverManager mReceiverManager; + private String mRunDecisionExplanation = ""; /** * Sending callback notifications through {@link OnDeviceStateChangedListener} is enabled if not null. @@ -151,10 +154,18 @@ public class RunConditionMonitor { } } + public String getRunDecisionExplanation() { + return mRunDecisionExplanation; + } + /** * Determines if Syncthing should currently run. + * Updates mRunDecisionExplanation. */ private boolean decideShouldRun() { + Resources res = mContext.getResources(); + mRunDecisionExplanation = ""; + // Get run conditions preferences. boolean prefRunOnMobileData= mPreferences.getBoolean(Constants.PREF_RUN_ON_MOBILE_DATA, false); boolean prefRunOnWifi= mPreferences.getBoolean(Constants.PREF_RUN_ON_WIFI, true); @@ -171,12 +182,14 @@ public class RunConditionMonitor { case POWER_SOURCE_AC: if (!isOnAcPower()) { Log.v(TAG, "decideShouldRun: POWER_SOURCE_AC && !isOnAcPower"); + mRunDecisionExplanation = res.getString(R.string.reason_not_on_ac_power); return false; } break; case POWER_SOURCE_BATTERY: if (isOnAcPower()) { Log.v(TAG, "decideShouldRun: POWER_SOURCE_BATTERY && isOnAcPower"); + mRunDecisionExplanation = res.getString(R.string.reason_not_on_battery_power); return false; } break; @@ -189,6 +202,7 @@ public class RunConditionMonitor { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (prefRespectPowerSaving && isPowerSaving()) { Log.v(TAG, "decideShouldRun: prefRespectPowerSaving && isPowerSaving"); + mRunDecisionExplanation = res.getString(R.string.reason_not_while_power_saving); return false; } } @@ -196,35 +210,65 @@ public class RunConditionMonitor { // Android global AutoSync setting. if (prefRespectMasterSync && !ContentResolver.getMasterSyncAutomatically()) { Log.v(TAG, "decideShouldRun: prefRespectMasterSync && !getMasterSyncAutomatically"); + mRunDecisionExplanation = res.getString(R.string.reason_not_while_auto_sync_data_disabled); return false; } // Run on mobile data. - if (prefRunOnMobileData && isMobileDataConnection()) { - Log.v(TAG, "decideShouldRun: prefRunOnMobileData && isMobileDataConnection"); - return true; + if (prefRunOnMobileData) { + if (isMobileDataConnection()) { + Log.v(TAG, "decideShouldRun: prefRunOnMobileData && isMobileDataConnection"); + mRunDecisionExplanation = res.getString(R.string.reason_on_mobile_data); + return true; + } + mRunDecisionExplanation = res.getString(R.string.reason_not_on_mobile_data); } // Run on wifi. - if (prefRunOnWifi && isWifiOrEthernetConnection()) { - if (prefRunOnMeteredWifi) { - // We are on non-metered or metered wifi. Check if wifi whitelist run condition is met. - if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) { - Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && prefRunOnMeteredWifi && wifiWhitelistConditionMet"); - return true; + if (prefRunOnWifi) { + if (isWifiOrEthernetConnection()) { + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_wifi); + if (prefRunOnMeteredWifi) { + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_metered_nonmetered_wifi); + // We are on non-metered or metered wifi. Check if wifi whitelist run condition is met. + if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) { + Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && prefRunOnMeteredWifi && wifiWhitelistConditionMet"); + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_whitelisted_wifi); + return true; + } + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_not_on_whitelisted_wifi); + } else { + // Check if we are on a non-metered wifi. + if (!isMeteredNetworkConnection()) { + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_nonmetered_wifi); + // Check if wifi whitelist run condition is met. + if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) { + Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && !prefRunOnMeteredWifi && !isMeteredNetworkConnection && wifiWhitelistConditionMet"); + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_whitelisted_wifi); + return true; + } + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_not_on_whitelisted_wifi); + } else { + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_not_nonmetered_wifi); + } } } else { - // Check if we are on a non-metered wifi and if wifi whitelist run condition is met. - if (!isMeteredNetworkConnection() && wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) { - Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && !prefRunOnMeteredWifi && !isMeteredNetworkConnection && wifiWhitelistConditionMet"); - return true; - } + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_not_on_wifi); + /** + * if (prefRunOnWifi && !isWifiOrEthernetConnection()) { return false; } + * This is intentionally not returning "false" as the flight mode workaround + * relevant for some phone models needs to be done by the code below. + * ConnectivityManager.getActiveNetworkInfo() returns "null" on those phones which + * results in assuming !isWifiOrEthernetConnection even if the phone is connected + * to wifi during flight mode, see {@link isWifiOrEthernetConnection}. + */ } } // Run in flight mode. if (prefRunInFlightMode && isFlightMode()) { Log.v(TAG, "decideShouldRun: prefRunInFlightMode && isFlightMode"); + mRunDecisionExplanation += "\n" + res.getString(R.string.reason_on_flight_mode); return true; } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java b/app/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java index 6432b5ec..1d893160 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/SyncthingService.java @@ -587,6 +587,13 @@ public class SyncthingService extends Service { return mNotificationHandler; } + public String getRunDecisionExplanation() { + if (mRunConditionMonitor == null) { + return "This should not happen: mRunConditionMonitor is not instantiated."; + } + return mRunConditionMonitor.getRunDecisionExplanation(); + } + /** * Exports the local config and keys to {@link Constants#EXPORT_PATH}. */ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_settings_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..76b5dbad0653f14b9c989011c3aab1eccce1ba4a GIT binary patch literal 743 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1SE5RJ;(=AY$ZW{!9W@a@|LkrWME*L-R1jchNosK55$I5iS|A*lRLp#l zDfE8G+nhWG&0`FW5()y%h1r**e*cQezH0U{*+46&sM&Ra{O(@|{zPQjxbaG@WVI5s zo#AEY{j+%MLp$NOlb_3KJuOK6=<%iOevOEfDr#VOZVeNu2@G4xP=G4;@LWgewKmcP9Bo!J%MOz~LY?6$SW?2z9_&2LpF zOL=E!NGy?@!NJ8b|M+=>yN!o)K7ZeFbL-v3)$?aOvt-INcj+`z<+-$Nmyh~A$%{^f z3!>#ptqz`RS-0fxs}iNT6a7AKy%rXe-Q*wtxp7O-lm4u*gKz5HTKzXjzglj!ZT>-* zD@&|zv0FG_laTLue5j}}rR+x}+qvaOH3I%)6J(Fk0mD2)ynZBGBvDuX< zmJEyj?)3%GDR+bHRbVNPNu81_rFR>&H|@02KN69p}%D& TIYidWg9JQX{an^LB{Ts5i#kD# literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/fragment_drawer.xml b/app/src/main/res/layout/fragment_drawer.xml index 6b58d8df..ab8f7420 100644 --- a/app/src/main/res/layout/fragment_drawer.xml +++ b/app/src/main/res/layout/fragment_drawer.xml @@ -61,123 +61,6 @@ android:layout_marginBottom="4dp" android:background="@drawable/list_divider" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/app/src/main/res/menu/status_list.xml b/app/src/main/res/menu/status_list.xml new file mode 100644 index 00000000..d204c4ce --- /dev/null +++ b/app/src/main/res/menu/status_list.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 905d7cdb..a14e7989 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -135,8 +135,6 @@ - - diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index bd163e46..a035f6ca 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -259,7 +259,6 @@ Syncthing не работи - Промяна на настройките Изход diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml index 81cbab6b..2c70dfe7 100644 --- a/app/src/main/res/values-ca-rES/strings.xml +++ b/app/src/main/res/values-ca-rES/strings.xml @@ -347,7 +347,6 @@ Ens podeu informar dels problemes que trobeu a través de Github. El Syncthing està desactivat - Canvia la configuració Surt diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 05edf8a2..18d7a37f 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -326,7 +326,6 @@ Všechny zaznamenané chyby prosím hlašte přes Github. Syncthing je vypnutý - Změnit nastavení Ukončit diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 959aa9ce..88093385 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -313,7 +313,6 @@ Vær venlig at rapportere ethvert problem, du støder på, via Github. Syncthing er slået fra - Ændre Indstillinger Afslut diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3b593954..f8ebc733 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -362,7 +362,6 @@ Bitte melden Sie auftretende Probleme via GitHub. Syncthing ist deaktiviert - Einstellungen ändern Beenden diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 79cfde20..d3a70898 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -317,7 +317,6 @@ Το Syncthing είναι απενεργοποιημένο - Αλλαγή ρυθμίσεως Έξοδος diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 5338a9f1..a01dffb8 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -121,8 +121,6 @@ - - diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index abb19375..38590a91 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -240,7 +240,6 @@ Syncthing está deshabilitado - Cambiar ajustes Salir diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index bcbec4d6..4097631b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -291,7 +291,6 @@ Syncthing está deshabilitado - Cambiar la configuración Salir diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 5208e28a..50e6bd46 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -288,7 +288,6 @@ Ilmoitathan ystävällisesti kaikista havaitsemistasi ongelmista Githubin kautta Syncthing on poistettu käytöstä - Muuta asetuksia Poistu diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a449c446..6d0eb72b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -348,7 +348,6 @@ S\'il vous plaît, soumettez les problèmes que vous rencontrez via Github.Syncthing est désactivé - Modification des paramètres Quitter diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5a056d07..ff5c9ec8 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -351,7 +351,6 @@ VIGYÁZAT! Más alkalmazások kiolvashatják a backupból a titkos kulcsot, és A Syncthing le van tiltva - Beállítások megváltoztatása Kilépés diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 026e99c1..c070160d 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -298,7 +298,6 @@ Jika ada masalah silakan laporkan lewat Github. Syncthing dimatikan - Ubah Pengaturan Keluar diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 98445c80..c6a66ecf 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -348,7 +348,6 @@ Si prega di segnalare eventuali problemi che si incontrano via Github. Syncthing è disabilitato - Cambia Impostazioni Esci diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 4b237106..b42ebb4d 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -314,7 +314,6 @@ Syncthing は無効になりました - 設定を変更 終了 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 995f6b88..263c1e44 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -313,7 +313,6 @@ Syncthing이 비활성화되었습니다 - 설정 변경 나가기 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 5338a9f1..a01dffb8 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -121,8 +121,6 @@ - - diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 1ed66849..3f8f72e4 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -244,7 +244,6 @@ Syncthing er deaktivert - Endre Innstillinger Avslutt diff --git a/app/src/main/res/values-nl-rBE/strings.xml b/app/src/main/res/values-nl-rBE/strings.xml index 5338a9f1..a01dffb8 100644 --- a/app/src/main/res/values-nl-rBE/strings.xml +++ b/app/src/main/res/values-nl-rBE/strings.xml @@ -121,8 +121,6 @@ - - diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 30a98d67..71146945 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -348,7 +348,6 @@ Als je problemen tegenkomt, meld ze dan via GitHub. Syncthing is uitgeschakeld - Instellingen wijzigen Afsluiten diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index 0934d1be..94a84030 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -244,7 +244,6 @@ Syncthing er ikkje aktivert - Endre innstillingar Avslutt diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index e7f6ff09..4c6e467e 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -97,8 +97,6 @@ - - diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 66787c7f..8a99043e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -321,7 +321,6 @@ Proszę zgłaszać napotkane błędy programu za pośrednictwem serwisu Github.< Syncthing jest wyłączony - Zmień ustawienia Zakończ diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 04f6e442..33cdf97d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -339,7 +339,6 @@ Por favor, nos avise sobre quaisquer problemas que você encontrar via Github.O Syncthing está desabilitado - Alterar configurações Sair diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 256ca864..220e8ae6 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -266,7 +266,6 @@ Reporte, através do Github, quaisquer problemas que encontre, por favor.O Syncthing está desactivado - Alterar configurações Sair diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 1dbc794b..8c891530 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -370,7 +370,6 @@ Vă rugăm să raportați orice problemă întâlniți, prin intermediul GitHub. Syncthing este dezactivat - Schimbă setări Ieșire diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d51fc9dc..ef5e776b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -354,7 +354,6 @@ Syncthing выключен - Изменить настройки Выход diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 4507fc3b..52d997b1 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -229,7 +229,6 @@ Syncthing je zakázaný - Zmeniť Nastavenia Koniec diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 5338a9f1..a01dffb8 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -121,8 +121,6 @@ - - diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 5338a9f1..a01dffb8 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -121,8 +121,6 @@ - - diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index f3881757..6c303bb3 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -365,7 +365,6 @@ Vänligen rapportera eventuella problem du stöter på via Github. Syncthing är inaktiverad - Ändra inställningar Avsluta diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 436bfd89..ff54799d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -264,7 +264,6 @@ Eğer herhangi bir sorunla karşılaşırsan Github aracılığıyla bildir.Syncthing devre dışı - Ayarları Değiştir Çık diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f8854c4a..d5e1e9cc 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -239,9 +239,6 @@ Syncthing заборонено - - - Змінити Налаштування Вихід diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 3ed12229..fc6625fa 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -240,7 +240,6 @@ Đã tắt Syncthing - Thay đổi cài đặt Thoát diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 30602a1e..657b3bd4 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -316,7 +316,6 @@ Syncthing 已禁用 - 变更设置 退出 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 556aae8a..5dcd4122 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -313,7 +313,6 @@ Syncthing 已經停用 - 更改設定 離開 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51453a41..25f02a87 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ Please report any problems you encounter via Github. Error Grant permission Permission granted + Reason: Accept @@ -139,6 +140,13 @@ Please report any problems you encounter via Github. Upload + + Status + Syncthing is starting. + Syncthing is running. + Syncthing is not running. + Syncthing has crashed. + @@ -155,7 +163,7 @@ Please report any problems you encounter via Github. RAM Usage - Announce Server + Configured Announce Server Restart @@ -631,18 +639,29 @@ Please report any problems you encounter via Github. Sub folder + + + + Phone is not running on AC power. + Phone is not running on battery power. + Syncthing is not running as the phone is currently power saving. + Syncthing is not running as Android currently has \'Auto-sync data\' disabled. + Syncthing is running as mobile data is currently connected. + Syncthing is allowed to run on mobile data connection but mobile data isn\'t connected. + Syncthing is allowed to run on WiFi and WiFi is currently connected. + Syncthing is allowed to run on WiFi but WiFi isn\'t connected or the phone is in flight mode. + Syncthing is allowed to run on metered and non-metered WiFi connections. + Syncthing is allowed to run on the current WiFi network. + Syncthing is not running as the current WiFi network\'s name is not whitelisted. + Syncthing is allowed to run on non-metered WiFi connections. The active WiFi connection is non-metered. + Syncthing is not running as you disallowed it to run on metered WiFi connections. + Syncthing is running as you allowed it to run when flight mode is active. + Syncthing is disabled - - - Do you want to change the run conditions? - - - Change Settings - Exit