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

Add welcome wizard slides on first start requesting permissions (#1176)

* Prepare welcome wizard related to issues
https://github.com/syncthing/syncthing-android/issues/1129
https://github.com/syncthing/syncthing-android/issues/622

* Add drawables and texts

* Disable scrolling the ViewPager, we have buttons for that

* Add welcome wizard code
Ask for storage and location permission
Location permission is required to run the app
Recheck storage permission on each app start, if revoked the
welcome slides show again.

* Review

* Review - Do not quit the app when storage permission is not granted

* Review - Minor string improvements
This commit is contained in:
Catfriend1 2018-08-01 20:10:31 +02:00 committed by GitHub
parent b50fcf1fa1
commit cdaf8e69c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 556 additions and 71 deletions

View file

@ -1,83 +1,309 @@
package com.nutomic.syncthingandroid.activities; package com.nutomic.syncthingandroid.activities;
import android.Manifest; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Color;
import android.Manifest;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; 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.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View; 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.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.SyncthingApp; import com.nutomic.syncthingandroid.SyncthingApp;
import com.nutomic.syncthingandroid.service.Constants;
import javax.inject.Inject; import javax.inject.Inject;
public class FirstStartActivity extends Activity implements Button.OnClickListener { public class FirstStartActivity extends Activity {
private static String TAG = "FirstStartActivity"; 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 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; @Inject SharedPreferences mPreferences;
/** /**
* Handles activity behaviour depending on {@link #isFirstStart()} and {@link #haveStoragePermission()}. * Handles activity behaviour depending on {@link #isFirstStart()} and {@link #haveStoragePermission()}.
*/ */
@SuppressLint("ClickableViewAccessibility")
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
((SyncthingApp) getApplication()).component().inject(this); ((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(); startApp();
return; 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); setContentView(R.layout.activity_first_start);
Button cont = findViewById(R.id.cont); mViewPager = (ViewPager) findViewById(R.id.view_pager);
cont.setOnClickListener(this); 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() { public void onBtnBackClick() {
return mPreferences.getBoolean("first_start", true); int current = getItem(-1);
if (current >= 0) {
// Move to previous slider.
mViewPager.setCurrentItem(current);
if (current == 0) {
mBackButton.setVisibility(View.GONE);
}
}
} }
private void startApp() { 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()) { if (!haveStoragePermission()) {
requestStoragePermission(); Toast.makeText(this, R.string.toast_write_storage_permission_required,
/** Toast.LENGTH_LONG).show();
* startApp will be called in {@link #onRequestPermissionsResult()}
* after permission was granted.
*/
return; return;
} }
boolean isFirstStart = isFirstStart();
if (isFirstStart) {
Log.v(TAG, "User completed first start UI.");
mPreferences.edit().putBoolean("first_start", false).apply();
} }
// In case start_into_web_gui option is enabled, start both activities so that back int current = getItem(+1);
// navigation works as expected. 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(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("&#8226;"));
mDots[i].setTextSize(35);
mDots[i].setTextColor(colorsInactive[currentPage]);
mDotsLayout.addView(mDots[i]);
}
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); Intent mainIntent = new Intent(this, MainActivity.class);
mainIntent.putExtra(MainActivity.EXTRA_KEY_GENERATION_IN_PROGRESS, isFirstStart); mainIntent.putExtra(MainActivity.EXTRA_KEY_GENERATION_IN_PROGRESS, doInitialKeyGeneration);
Intent webIntent = new Intent(this, WebGuiActivity.class);
if (mPreferences.getBoolean("start_into_web_gui", false)) { /**
startActivities(new Intent[] {mainIntent, webIntent}); * 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 { } else {
startActivity(mainIntent); startActivity(mainIntent);
} }
finish(); 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() { private boolean haveStoragePermission() {
int permissionState = ContextCompat.checkSelfPermission(this, int permissionState = ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE); Manifest.permission.WRITE_EXTERNAL_STORAGE);
@ -90,23 +316,26 @@ public class FirstStartActivity extends Activity implements Button.OnClickListen
REQUEST_WRITE_STORAGE); REQUEST_WRITE_STORAGE);
} }
@Override
public void onClick(View v) {
startApp();
}
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) { @NonNull int[] grantResults) {
switch (requestCode) { 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: case REQUEST_WRITE_STORAGE:
if (grantResults.length == 0 || if (grantResults.length == 0 ||
grantResults[0] != PackageManager.PERMISSION_GRANTED) { grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.toast_write_storage_permission_required, Log.i(TAG, "User denied WRITE_EXTERNAL_STORAGE permission.");
Toast.LENGTH_LONG).show();
this.finish();
} else { } else {
startApp(); Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_SHORT).show();
Log.i(TAG, "User granted WRITE_EXTERNAL_STORAGE permission.");
} }
break; break;
default: default:

View file

@ -10,11 +10,13 @@ public class Constants {
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so"; 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_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 = "sync_only_wifi";
public static final String PREF_SYNC_ONLY_WIFI_SSIDS = "sync_only_wifi_ssids_set"; 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_SYNC_ONLY_CHARGING = "sync_only_charging";
public static final String PREF_RESPECT_BATTERY_SAVING = "respect_battery_saving"; 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_USE_ROOT = "use_root";
public static final String PREF_NOTIFICATION_TYPE = "notification_type"; public static final String PREF_NOTIFICATION_TYPE = "notification_type";
public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables"; public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables";

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,44 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="16dp" tools:context="com.nutomic.syncthingandroid.activities.FirstStartActivity"
tools:context="com.nutomic.syncthingandroid.activities.FirstStartActivity"> tools:showIn="@layout/activity_first_start">
<TextView <android.support.v4.view.ViewPager
android:id="@+id/title" android:id="@+id/view_pager"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent" />
android:text="@string/welcome_title"
android:layout_centerHorizontal="true" <LinearLayout
android:id="@+id/layoutDots"
android:layout_width="match_parent"
android:layout_height="@dimen/dots_height"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/dots_margin_bottom"
android:gravity="center" android:gravity="center"
android:textColor="@color/primary_dark" android:orientation="horizontal">
android:textSize="30sp" </LinearLayout>
android:layout_margin="30dp" />
<TextView <View
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="1dp"
android:text="@string/welcome_text" android:alpha=".5"
android:layout_below="@id/title" android:layout_above="@id/layoutDots"
android:textSize="16sp" android:background="@android:color/white" />
android:lineSpacingExtra="5sp"
android:layout_marginStart="30dp"
android:layout_marginLeft="30dp"
android:layout_marginEnd="30dp"
android:layout_marginRight="30dp" />
<Button <Button
android:id="@+id/cont" android:id="@+id/btn_back"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/cont" android:layout_alignParentBottom="true"
android:background="?android:selectableItemBackground" android:layout_alignParentLeft="true"
android:textColor="@color/accent" android:layout_alignParentStart="true"
android:layout_alignParentEnd="true" android:background="@null"
android:text="@string/back"
android:textColor="@android:color/white"
android:visibility="gone" />
<Button
android:id="@+id/btn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"/> android:layout_alignParentEnd="true"
android:background="@null"
android:text="@string/cont"
android:textColor="@android:color/white" />
</RelativeLayout> </RelativeLayout>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_screen1">
<LinearLayout
android:background="@color/bg_screen1"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_title"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="30sp"
android:layout_margin="30dp" />
<ImageView
android:layout_width="@dimen/img_width_height"
android:layout_height="@dimen/img_width_height"
android:contentDescription="@null"
android:src="@drawable/ic_syncthing_logo" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/introduction"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_title"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:paddingLeft="@dimen/desc_padding"
android:paddingRight="@dimen/desc_padding"
android:text="@string/welcome_text"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_desc" />
</LinearLayout>
</RelativeLayout>

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_screen2">
<LinearLayout
android:background="@color/bg_screen2"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_title"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="30sp"
android:layout_margin="30dp" />
<ImageView
android:layout_width="@dimen/img_width_height"
android:layout_height="@dimen/img_width_height"
android:contentDescription="@null"
android:src="@drawable/ic_storage" />
<Button
android:id="@+id/btnGrantStoragePerm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingEnd="40dp"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:paddingStart="40dp"
android:text="@string/grant_permission"
android:contentDescription="@string/grant_permission"
android:drawableLeft="@android:drawable/ic_menu_save"
android:drawableStart="@android:drawable/ic_menu_save"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/storage_permission_title"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_title"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:paddingLeft="@dimen/desc_padding"
android:paddingRight="@dimen/desc_padding"
android:text="@string/storage_permission_desc"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_desc" />
</LinearLayout>
</RelativeLayout>

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_screen3">
<LinearLayout
android:background="@color/bg_screen3"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_title"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="30sp"
android:layout_margin="30dp" />
<ImageView
android:layout_width="@dimen/img_width_height"
android:layout_height="@dimen/img_width_height"
android:contentDescription="@null"
android:src="@drawable/ic_location" />
<Button
android:id="@+id/btnGrantLocationPerm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingEnd="40dp"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:paddingStart="40dp"
android:text="@string/grant_permission"
android:contentDescription="@string/grant_permission"
android:drawableLeft="@android:drawable/ic_menu_mylocation"
android:drawableStart="@android:drawable/ic_menu_mylocation"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/location_permission_title"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_title"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:paddingLeft="@dimen/desc_padding"
android:paddingRight="@dimen/desc_padding"
android:text="@string/location_permission_desc"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="@dimen/slide_desc" />
</LinearLayout>
</RelativeLayout>

View file

@ -46,6 +46,7 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:ems="10" android:ems="10"
android:hint="@string/folder_label"
android:inputType="text|textMultiLine" android:inputType="text|textMultiLine"
app:layout_constraintBottom_toTopOf="@+id/folders" app:layout_constraintBottom_toTopOf="@+id/folders"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"

View file

@ -17,6 +17,7 @@
android:id="@+id/commandTextView" android:id="@+id/commandTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/command"
android:ems="10" android:ems="10"
android:inputType="textPersonName" android:inputType="textPersonName"
/> />

View file

@ -10,4 +10,38 @@
<color name="text_blue">#ff33b5e5</color> <color name="text_blue">#ff33b5e5</color>
<color name="text_green">#ff99cc00</color> <color name="text_green">#ff99cc00</color>
<color name="light_grey">#cccccc</color> <color name="light_grey">#cccccc</color>
<!-- FirstStartActivity welcome wizard start -->
<!-- Screens background color-->
<color name="bg_screen1">#3395ff</color>
<color name="bg_screen2">#20d2bb</color>
<color name="bg_screen3">#c873f4</color>
<!-- <color name="bg_screen4">#f64c73</color> -->
<!-- dots inactive colors -->
<color name="dot_dark_screen1">#2278d4</color>
<color name="dot_dark_screen2">#14a895</color>
<color name="dot_dark_screen3">#a854d4</color>
<!-- <color name="dot_dark_screen4">#d1395c</color> -->
<!-- dots active colors -->
<color name="dot_light_screen1">#93c6fd</color>
<color name="dot_light_screen2">#8cf9eb</color>
<color name="dot_light_screen3">#e4b5fc</color>
<!-- <color name="dot_light_screen4">#f98da5</color> -->
<array name="array_dot_active">
<item>@color/dot_light_screen1</item>
<item>@color/dot_light_screen2</item>
<item>@color/dot_light_screen3</item>
<!-- <item>@color/dot_light_screen4</item> -->
</array>
<array name="array_dot_inactive">
<item>@color/dot_dark_screen1</item>
<item>@color/dot_dark_screen2</item>
<item>@color/dot_dark_screen3</item>
<!-- <item>@color/dot_dark_screen4</item> -->
</array>
<!-- FirstStartActivity welcome wizard end -->
</resources> </resources>

View file

@ -2,4 +2,12 @@
<resources> <resources>
<dimen name="material_divider_inset">72dp</dimen> <dimen name="material_divider_inset">72dp</dimen>
<dimen name="toolbar_elevation">4dp</dimen> <dimen name="toolbar_elevation">4dp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="dots_height">30dp</dimen>
<dimen name="dots_margin_bottom">20dp</dimen>
<dimen name="img_width_height">120dp</dimen>
<dimen name="slide_title">30sp</dimen>
<dimen name="slide_desc">16sp</dimen>
<dimen name="desc_padding">40dp</dimen>
</resources> </resources>

View file

@ -11,16 +11,29 @@
<!-- Title for dialog displayed on first start --> <!-- Title for dialog displayed on first start -->
<string name="welcome_title">Welcome to Syncthing for Android</string> <string name="welcome_title">Welcome to Syncthing for Android</string>
<!-- Text for dialog displayed on first start --> <!-- Welcome wizard -->
<!-- Slide 1 -->
<string name="introduction">Introduction</string>
<string name="welcome_text">Syncthing is an open-source file synchronization application.\n\ <string name="welcome_text">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\ 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.</string> Please report any problems you encounter via Github.</string>
<string name="cont">Continue</string> <!-- Slide 2 -->
<string name="storage_permission_title">Storage Permission</string>
<string name="storage_permission_desc">Syncthing needs to access your storage to do file synchronization.</string>
<!-- Slide 3 -->
<string name="location_permission_title">Location Permission</string>
<string name="location_permission_desc">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.</string>
<!-- Generic texts used everywhere --> <!-- Generic texts used everywhere -->
<string name="back">Back</string>
<string name="cont">Continue</string>
<string name="finish">Finish</string>
<string name="generic_example">Example</string> <string name="generic_example">Example</string>
<string name="generic_error">Error</string> <string name="generic_error">Error</string>
<string name="grant_permission">Grant permission</string>
<string name="permission_granted">Permission granted</string>
<string name="accept">Accept</string> <string name="accept">Accept</string>
@ -699,7 +712,6 @@ Please report any problems you encounter via Github.</string>
<string name="maximum_age">Maximum Age</string> <string name="maximum_age">Maximum Age</string>
<string name="clean_out_after">Clean out after</string> <string name="clean_out_after">Clean out after</string>
<string name="file_versioning">File Versioning</string> <string name="file_versioning">File Versioning</string>
<string name="finish">Finish</string>
<string name="none"> None</string> <string name="none"> None</string>
<string name="versions_path">Versions Path</string> <string name="versions_path">Versions Path</string>
<string name="command">Command</string> <string name="command">Command</string>