diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/FirstStartActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/FirstStartActivity.java
index 6d418646..05178b04 100644
--- a/app/src/main/java/com/nutomic/syncthingandroid/activities/FirstStartActivity.java
+++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/FirstStartActivity.java
@@ -1,83 +1,309 @@
package com.nutomic.syncthingandroid.activities;
-import android.Manifest;
+import android.annotation.SuppressLint;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.Manifest;
+import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Html;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.SyncthingApp;
+import com.nutomic.syncthingandroid.service.Constants;
import javax.inject.Inject;
-public class FirstStartActivity extends Activity implements Button.OnClickListener {
+public class FirstStartActivity extends Activity {
private static String TAG = "FirstStartActivity";
+ private static final int REQUEST_COARSE_LOCATION = 141;
private static final int REQUEST_WRITE_STORAGE = 142;
+ private static final int SLIDE_POS_LOCATION_PERMISSION = 1;
+
+ private ViewPager mViewPager;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private LinearLayout mDotsLayout;
+ private TextView[] mDots;
+ private int[] mLayouts;
+ private Button mBackButton;
+ private Button mNextButton;
@Inject SharedPreferences mPreferences;
/**
* Handles activity behaviour depending on {@link #isFirstStart()} and {@link #haveStoragePermission()}.
*/
+ @SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SyncthingApp) getApplication()).component().inject(this);
- if (!isFirstStart()) {
+ /**
+ * Recheck storage permission. If it has been revoked after the user
+ * completed the welcome slides, displays the slides again.
+ */
+ if (!mPreferences.getBoolean(Constants.PREF_FIRST_START, true) &&
+ haveStoragePermission()) {
startApp();
return;
}
- // Show first start UI.
+ // Make notification bar transparent (API level 21+)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+
+ // Show first start welcome wizard UI.
setContentView(R.layout.activity_first_start);
- Button cont = findViewById(R.id.cont);
- cont.setOnClickListener(this);
+ mViewPager = (ViewPager) findViewById(R.id.view_pager);
+ mDotsLayout = (LinearLayout) findViewById(R.id.layoutDots);
+ mBackButton = (Button) findViewById(R.id.btn_back);
+ mNextButton = (Button) findViewById(R.id.btn_next);
+
+ mViewPager.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ // Consume the event to prevent swiping through the slides.
+ v.performClick();
+ return true;
+ }
+ });
+
+ // Layouts of all welcome slides
+ mLayouts = new int[]{
+ R.layout.activity_firststart_slide1,
+ R.layout.activity_firststart_slide2,
+ R.layout.activity_firststart_slide3};
+
+ // Add bottom dots
+ addBottomDots(0);
+
+ // Make notification bar transparent
+ changeStatusBarColor();
+
+ mViewPagerAdapter = new ViewPagerAdapter();
+ mViewPager.setAdapter(mViewPagerAdapter);
+ mViewPager.addOnPageChangeListener(mViewPagerPageChangeListener);
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onBtnBackClick();
+ }
+ });
+
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onBtnNextClick();
+ }
+ });
}
- private boolean isFirstStart() {
- return mPreferences.getBoolean("first_start", true);
+ public void onBtnBackClick() {
+ int current = getItem(-1);
+ if (current >= 0) {
+ // Move to previous slider.
+ mViewPager.setCurrentItem(current);
+ if (current == 0) {
+ mBackButton.setVisibility(View.GONE);
+ }
+ }
}
- private void startApp() {
- if (!haveStoragePermission()) {
- requestStoragePermission();
- /**
- * startApp will be called in {@link #onRequestPermissionsResult()}
- * after permission was granted.
- */
- return;
+ public void onBtnNextClick() {
+ // Check if we are allowed to advance to the next slide.
+ if (mViewPager.getCurrentItem() == SLIDE_POS_LOCATION_PERMISSION) {
+ // As the storage permission is a prerequisite to run syncthing, refuse to continue without it.
+ if (!haveStoragePermission()) {
+ Toast.makeText(this, R.string.toast_write_storage_permission_required,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
}
- boolean isFirstStart = isFirstStart();
- if (isFirstStart) {
+ int current = getItem(+1);
+ if (current < mLayouts.length) {
+ // Move to next slide.
+ mViewPager.setCurrentItem(current);
+ mBackButton.setVisibility(View.VISIBLE);
+ } else {
+ // Start the app after "mNextButton" was hit on the last slide.
Log.v(TAG, "User completed first start UI.");
- mPreferences.edit().putBoolean("first_start", false).apply();
+ mPreferences.edit().putBoolean(Constants.PREF_FIRST_START, false).apply();
+ startApp();
+ }
+ }
+
+ private void addBottomDots(int currentPage) {
+ mDots = new TextView[mLayouts.length];
+
+ int[] colorsActive = getResources().getIntArray(R.array.array_dot_active);
+ int[] colorsInactive = getResources().getIntArray(R.array.array_dot_inactive);
+
+ mDotsLayout.removeAllViews();
+ for (int i = 0; i < mDots.length; i++) {
+ mDots[i] = new TextView(this);
+ mDots[i].setText(Html.fromHtml("•"));
+ mDots[i].setTextSize(35);
+ mDots[i].setTextColor(colorsInactive[currentPage]);
+ mDotsLayout.addView(mDots[i]);
}
- // In case start_into_web_gui option is enabled, start both activities so that back
- // navigation works as expected.
+ if (mDots.length > 0)
+ mDots[currentPage].setTextColor(colorsActive[currentPage]);
+ }
+
+ private int getItem(int i) {
+ return mViewPager.getCurrentItem() + i;
+ }
+
+ // ViewPager change listener
+ ViewPager.OnPageChangeListener mViewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
+
+ @Override
+ public void onPageSelected(int position) {
+ addBottomDots(position);
+
+ // Change the next button text from next to finish on last slide.
+ mNextButton.setText(getString((position == mLayouts.length - 1) ? R.string.finish : R.string.cont));
+ }
+
+ @Override
+ public void onPageScrolled(int arg0, float arg1, int arg2) {
+
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int arg0) {
+
+ }
+ };
+
+ /**
+ * Making notification bar transparent
+ */
+ private void changeStatusBarColor() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.setStatusBarColor(Color.TRANSPARENT);
+ }
+ }
+
+ /**
+ * View pager adapter
+ */
+ public class ViewPagerAdapter extends PagerAdapter {
+ private LayoutInflater layoutInflater;
+
+ public ViewPagerAdapter() {
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ View view = layoutInflater.inflate(mLayouts[position], container, false);
+
+ /* Slide: storage permission */
+ Button btnGrantStoragePerm = (Button) view.findViewById(R.id.btnGrantStoragePerm);
+ if (btnGrantStoragePerm != null) {
+ btnGrantStoragePerm.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ requestStoragePermission();
+ }
+ });
+ }
+
+ /* Slide: location permission */
+ Button btnGrantLocationPerm = (Button) view.findViewById(R.id.btnGrantLocationPerm);
+ if (btnGrantLocationPerm != null) {
+ btnGrantLocationPerm.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ requestLocationPermission();
+ }
+ });
+ }
+
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public int getCount() {
+ return mLayouts.length;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object obj) {
+ return view == obj;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ View view = (View) object;
+ container.removeView(view);
+ }
+ }
+
+ /**
+ * Preconditions:
+ * Storage permission has been granted.
+ */
+ private void startApp() {
+ Boolean doInitialKeyGeneration = !Constants.getConfigFile(this).exists();
Intent mainIntent = new Intent(this, MainActivity.class);
- mainIntent.putExtra(MainActivity.EXTRA_KEY_GENERATION_IN_PROGRESS, isFirstStart);
- Intent webIntent = new Intent(this, WebGuiActivity.class);
- if (mPreferences.getBoolean("start_into_web_gui", false)) {
- startActivities(new Intent[] {mainIntent, webIntent});
+ mainIntent.putExtra(MainActivity.EXTRA_KEY_GENERATION_IN_PROGRESS, doInitialKeyGeneration);
+
+ /**
+ * In case start_into_web_gui option is enabled, start both activities
+ * so that back navigation works as expected.
+ */
+ if (mPreferences.getBoolean(Constants.PREF_START_INTO_WEB_GUI, false)) {
+ startActivities(new Intent[] {mainIntent, new Intent(this, WebGuiActivity.class)});
} else {
startActivity(mainIntent);
}
finish();
}
+ /**
+ * Permission check and request functions
+ */
+ private void requestLocationPermission() {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
+ REQUEST_COARSE_LOCATION);
+ }
+
private boolean haveStoragePermission() {
int permissionState = ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
@@ -90,23 +316,26 @@ public class FirstStartActivity extends Activity implements Button.OnClickListen
REQUEST_WRITE_STORAGE);
}
- @Override
- public void onClick(View v) {
- startApp();
- }
-
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode) {
+ case REQUEST_COARSE_LOCATION:
+ if (grantResults.length == 0 ||
+ grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "User denied ACCESS_COARSE_LOCATION permission.");
+ } else {
+ Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_SHORT).show();
+ Log.i(TAG, "User granted ACCESS_COARSE_LOCATION permission.");
+ }
+ break;
case REQUEST_WRITE_STORAGE:
if (grantResults.length == 0 ||
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
- Toast.makeText(this, R.string.toast_write_storage_permission_required,
- Toast.LENGTH_LONG).show();
- this.finish();
+ Log.i(TAG, "User denied WRITE_EXTERNAL_STORAGE permission.");
} else {
- startApp();
+ Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_SHORT).show();
+ Log.i(TAG, "User granted WRITE_EXTERNAL_STORAGE permission.");
}
break;
default:
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 54e7dfbe..45222a00 100644
--- a/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java
+++ b/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java
@@ -10,11 +10,13 @@ public class Constants {
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so";
+ public static final String PREF_FIRST_START = "first_start";
public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background";
public static final String PREF_SYNC_ONLY_WIFI = "sync_only_wifi";
public static final String PREF_SYNC_ONLY_WIFI_SSIDS = "sync_only_wifi_ssids_set";
public static final String PREF_SYNC_ONLY_CHARGING = "sync_only_charging";
public static final String PREF_RESPECT_BATTERY_SAVING = "respect_battery_saving";
+ public static final String PREF_START_INTO_WEB_GUI = "start_into_web_gui";
public static final String PREF_USE_ROOT = "use_root";
public static final String PREF_NOTIFICATION_TYPE = "notification_type";
public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables";
diff --git a/app/src/main/res/drawable-hdpi/ic_location.png b/app/src/main/res/drawable-hdpi/ic_location.png
new file mode 100644
index 00000000..285b40de
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_storage.png b/app/src/main/res/drawable-hdpi/ic_storage.png
new file mode 100644
index 00000000..e42d552a
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_syncthing_logo.png b/app/src/main/res/drawable-hdpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..86088742
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/drawable-ldpi/ic_location.png b/app/src/main/res/drawable-ldpi/ic_location.png
new file mode 100644
index 00000000..f4281a5c
Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-ldpi/ic_storage.png b/app/src/main/res/drawable-ldpi/ic_storage.png
new file mode 100644
index 00000000..fdd90170
Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-ldpi/ic_syncthing_logo.png b/app/src/main/res/drawable-ldpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..8f64308a
Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_location.png b/app/src/main/res/drawable-mdpi/ic_location.png
new file mode 100644
index 00000000..4a443b25
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_storage.png b/app/src/main/res/drawable-mdpi/ic_storage.png
new file mode 100644
index 00000000..e727a975
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_syncthing_logo.png b/app/src/main/res/drawable-mdpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..f8d14c05
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_location.png b/app/src/main/res/drawable-xhdpi/ic_location.png
new file mode 100644
index 00000000..02d1acea
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_storage.png b/app/src/main/res/drawable-xhdpi/ic_storage.png
new file mode 100644
index 00000000..7f4b154c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_syncthing_logo.png b/app/src/main/res/drawable-xhdpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..b8aad5f0
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_location.png b/app/src/main/res/drawable-xxhdpi/ic_location.png
new file mode 100644
index 00000000..c2f2903f
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_storage.png b/app/src/main/res/drawable-xxhdpi/ic_storage.png
new file mode 100644
index 00000000..6abfc061
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_syncthing_logo.png b/app/src/main/res/drawable-xxhdpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..402cb1f9
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_location.png b/app/src/main/res/drawable-xxxhdpi/ic_location.png
new file mode 100644
index 00000000..200567d9
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_location.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_storage.png b/app/src/main/res/drawable-xxxhdpi/ic_storage.png
new file mode 100644
index 00000000..fb0e777d
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_storage.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_syncthing_logo.png b/app/src/main/res/drawable-xxxhdpi/ic_syncthing_logo.png
new file mode 100644
index 00000000..7425cda1
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_syncthing_logo.png differ
diff --git a/app/src/main/res/layout/activity_first_start.xml b/app/src/main/res/layout/activity_first_start.xml
index 4d2bdf23..5b9c1662 100644
--- a/app/src/main/res/layout/activity_first_start.xml
+++ b/app/src/main/res/layout/activity_first_start.xml
@@ -1,44 +1,56 @@
+ tools:context="com.nutomic.syncthingandroid.activities.FirstStartActivity"
+ tools:showIn="@layout/activity_first_start">
-
+
-
+
+
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_firststart_slide1.xml b/app/src/main/res/layout/activity_firststart_slide1.xml
new file mode 100644
index 00000000..fac7957a
--- /dev/null
+++ b/app/src/main/res/layout/activity_firststart_slide1.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_firststart_slide2.xml b/app/src/main/res/layout/activity_firststart_slide2.xml
new file mode 100644
index 00000000..8b2d05fb
--- /dev/null
+++ b/app/src/main/res/layout/activity_firststart_slide2.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_firststart_slide3.xml b/app/src/main/res/layout/activity_firststart_slide3.xml
new file mode 100644
index 00000000..6a66d948
--- /dev/null
+++ b/app/src/main/res/layout/activity_firststart_slide3.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml
index eb2df79e..ff218c55 100644
--- a/app/src/main/res/layout/activity_share.xml
+++ b/app/src/main/res/layout/activity_share.xml
@@ -46,6 +46,7 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:ems="10"
+ android:hint="@string/folder_label"
android:inputType="text|textMultiLine"
app:layout_constraintBottom_toTopOf="@+id/folders"
app:layout_constraintHorizontal_bias="0.0"
diff --git a/app/src/main/res/layout/fragment_external_versioning.xml b/app/src/main/res/layout/fragment_external_versioning.xml
index 82ebab19..50051b48 100644
--- a/app/src/main/res/layout/fragment_external_versioning.xml
+++ b/app/src/main/res/layout/fragment_external_versioning.xml
@@ -17,6 +17,7 @@
android:id="@+id/commandTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:hint="@string/command"
android:ems="10"
android:inputType="textPersonName"
/>
@@ -25,4 +26,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/external_versioning_description" />
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 5809d461..91367a74 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -10,4 +10,38 @@
#ff33b5e5
#ff99cc00
#cccccc
+
+
+
+ #3395ff
+ #20d2bb
+ #c873f4
+
+
+
+ #2278d4
+ #14a895
+ #a854d4
+
+
+
+ #93c6fd
+ #8cf9eb
+ #e4b5fc
+
+
+
+ - @color/dot_light_screen1
+ - @color/dot_light_screen2
+ - @color/dot_light_screen3
+
+
+
+
+ - @color/dot_dark_screen1
+ - @color/dot_dark_screen2
+ - @color/dot_dark_screen3
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 9a86ef66..f0b0a61a 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -2,4 +2,12 @@
72dp
4dp
+
+
+ 30dp
+ 20dp
+ 120dp
+ 30sp
+ 16sp
+ 40dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 10b07804..2c991df1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -11,16 +11,29 @@
Welcome to Syncthing for Android
-
+
+
+ Introduction
Syncthing is an open-source file synchronization application.\n\
To share data with other devices, you need to add their unique device IDs to the device list. Afterwards you can select which folders to share with which devices.\n\
Please report any problems you encounter via Github.
- Continue
+
+ Storage Permission
+ Syncthing needs to access your storage to do file synchronization.
+
+
+ Location Permission
+ Syncthing can be configured to run on selected wifi networks. Android requires applications to have location permissions to be able look up active WiFi network name, as you can sometimes infer users location from the name of the network they are connected to. If you want to use this feature, press the button above to give the required location permissions to Syncthing. Otherwise you can skip this step.
+ Back
+ Continue
+ Finish
Example
Error
+ Grant permission
+ Permission granted
Accept
@@ -699,7 +712,6 @@ Please report any problems you encounter via Github.
Maximum Age
Clean out after
File Versioning
- Finish
None
Versions Path
Command