1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-12-22 19:01:30 +00:00

Changed code style to use spaces instead of tabs.

It is impossible to add a custom code style in Android Studio that is
stored in the project repository. So change style to something that's
easy to use in practice now rather than later.
This commit is contained in:
Felix Ableitner 2014-08-18 11:30:03 +02:00
parent c3b027e8b5
commit b1749ce7cb
49 changed files with 3611 additions and 3668 deletions

View file

@ -41,6 +41,6 @@ screenshots: *link to file* (only for UI problems)
Always welcome.
Code should follow the [Android Code Style Guidelines](https://source.android.com/source/code-style.html#java-language-rules), with the exception that we use tabs, not spaces for indentation.
Code should follow the [Android Code Style Guidelines](https://source.android.com/source/code-style.html#java-language-rules). This can be done automatically in Android Studio.
Lint warnings should be fixed. If that's not possible, they should be ignored as specifically as possible.

View file

@ -1,12 +1,12 @@
buildscript {
repositories {
mavenCentral()
}
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'com.alexvasilkov:android_sign:0.2+'
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'com.alexvasilkov:android_sign:0.2+'
}
}
import java.util.regex.Pattern
@ -15,126 +15,126 @@ apply plugin: 'com.android.application'
apply plugin: 'android_sign'
repositories {
mavenCentral()
mavenCentral()
}
dependencies {
compile 'com.android.support:appcompat-v7:19.1.+'
compile project(':android-support-v4-preferencefragment')
compile 'com.android.support:appcompat-v7:19.1.+'
compile project(':android-support-v4-preferencefragment')
}
preBuild {
dependsOn 'buildNative'
dependsOn 'buildNative'
}
android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
compileSdkVersion 19
buildToolsVersion "19.1.0"
defaultConfig {
versionCode getVersionCodeFromManifest()
}
defaultConfig {
versionCode getVersionCodeFromManifest()
}
sourceSets {
main {
jniLibs.srcDir file("libs/")
}
}
sourceSets {
main {
jniLibs.srcDir file("libs/")
}
}
signingConfigs {
release {
// Android Studio does not pass environment variables.
// This means you have to use the command line for release builds.
def ks = System.getenv("KEYSTORE")
def ka = System.getenv("KEY_ALIAS")
if (ks != null && ka != null) {
storeFile file(ks)
keyAlias ka
}
}
}
signingConfigs {
release {
// Android Studio does not pass environment variables.
// This means you have to use the command line for release builds.
def ks = System.getenv("KEYSTORE")
def ka = System.getenv("KEY_ALIAS")
if (ks != null && ka != null) {
storeFile file(ks)
keyAlias ka
}
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
}
release {
signingConfig signingConfigs.release
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
}
release {
signingConfig signingConfigs.release
}
}
productFlavors {
x86 {
versionCode Integer.parseInt("4" + defaultConfig.versionCode)
ndk {
abiFilter "x86"
}
}
armeabi_v7a {
versionCode Integer.parseInt("3" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi-v7a"
}
}
armeabi {
versionCode Integer.parseInt("2" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi"
}
}
mips {
versionCode Integer.parseInt("1" + defaultConfig.versionCode)
ndk {
abiFilter "mips"
}
}
fat {
versionCode Integer.parseInt("0" + defaultConfig.versionCode)
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
productFlavors {
x86 {
versionCode Integer.parseInt("4" + defaultConfig.versionCode)
ndk {
abiFilter "x86"
}
}
armeabi_v7a {
versionCode Integer.parseInt("3" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi-v7a"
}
}
armeabi {
versionCode Integer.parseInt("2" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi"
}
}
mips {
versionCode Integer.parseInt("1" + defaultConfig.versionCode)
ndk {
abiFilter "mips"
}
}
fat {
versionCode Integer.parseInt("0" + defaultConfig.versionCode)
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
def getVersionCodeFromManifest() {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return Integer.parseInt(matcher.group(1))
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return Integer.parseInt(matcher.group(1))
}
task buildNative(type: Exec) {
outputs.upToDateWhen { false }
executable = './build-syncthing.sh'
outputs.upToDateWhen { false }
executable = './build-syncthing.sh'
}
task copyNative(type: Copy) {
def lib_dir = "libs/"
new File(lib_dir).mkdirs()
def st_dir = "bin/";
from st_dir + 'syncthing-x86', st_dir + 'syncthing-armeabi', st_dir + 'syncthing-armeabi-v7a';
into lib_dir
rename('syncthing-x86', 'x86/libsyncthing.so')
def lib_dir = "libs/"
new File(lib_dir).mkdirs()
def st_dir = "bin/";
from st_dir + 'syncthing-x86', st_dir + 'syncthing-armeabi', st_dir + 'syncthing-armeabi-v7a';
into lib_dir
rename('syncthing-x86', 'x86/libsyncthing.so')
rename('syncthing-armeabi-v7a', 'armeabi-v7a/libsyncthing.so')
rename('syncthing-armeabi', 'armeabi/libsyncthing.so')
rename('syncthing-armeabi', 'armeabi/libsyncthing.so')
}
buildNative.finalizedBy copyNative
task cleanBin(type: Delete) {
delete 'bin/'
delete 'bin/'
}
copyNative.finalizedBy cleanBin
task cleanNative(type: Delete) {
delete 'bin/'
delete 'build/'
delete 'libs/'
delete 'ext/syncthing/bin/'
delete 'ext/syncthing/pkg/'
delete 'bin/'
delete 'build/'
delete 'libs/'
delete 'ext/syncthing/bin/'
delete 'ext/syncthing/pkg/'
}
clean.dependsOn cleanNative

View file

@ -1,75 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.nutomic.syncthingandroid"
android:versionCode="33"
android:versionName="0.4.19"
tools:ignore="GradleOverrides" >
xmlns:tools="http://schemas.android.com/tools"
package="com.nutomic.syncthingandroid"
android:versionCode="33"
android:versionName="0.4.19"
tools:ignore="GradleOverrides" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".activities.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".activities.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.WebGuiActivity"
android:label="@string/web_gui_title" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity
android:name=".activities.SettingsActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.WebGuiActivity"
android:label="@string/web_gui_title">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<activity android:name=".activities.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".gui.MainActivity" />
</activity>
<service android:name=".syncthing.SyncthingService" />
<service android:name=".syncthing.SyncthingService" />
<activity
android:name=".activities.FolderPickerActivity"
android:label="@string/folder_picker_title" >
<meta-data
android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
<receiver android:name=".syncthing.NetworkReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
<receiver android:name=".syncthing.BatteryReceiver" >
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
<activity
android:name=".activities.FolderPickerActivity"
android:label="@string/folder_picker_title">
<meta-data
android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
<receiver android:name=".syncthing.NetworkReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<receiver android:name=".syncthing.BatteryReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
<receiver android:name=".syncthing.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>

View file

@ -32,42 +32,42 @@ import java.util.Arrays;
* Activity that allows selecting a directory in the local file system.
*/
public class FolderPickerActivity extends SyncthingActivity
implements AdapterView.OnItemClickListener, SyncthingService.OnApiChangeListener {
implements AdapterView.OnItemClickListener, SyncthingService.OnApiChangeListener {
private static final String TAG = "FolderPickerActivity";
private static final String TAG = "FolderPickerActivity";
public static final String EXTRA_INITIAL_DIRECTORY = "initial_directory";
public static final String EXTRA_INITIAL_DIRECTORY = "initial_directory";
public static final String EXTRA_RESULT_DIRECTORY = "result_directory";
public static final String EXTRA_RESULT_DIRECTORY = "result_directory";
private ListView mListView;
private ListView mListView;
private FileAdapter mAdapter;
private FileAdapter mAdapter;
private File mLocation;
private File mLocation;
@Override
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.folder_picker_activity);
mListView = (ListView) findViewById(android.R.id.list);
mListView.setOnItemClickListener(this);
mListView.setEmptyView(findViewById(android.R.id.empty));
mAdapter = new FileAdapter(this);
mListView.setAdapter(mAdapter);
mListView = (ListView) findViewById(android.R.id.list);
mListView.setOnItemClickListener(this);
mListView.setEmptyView(findViewById(android.R.id.empty));
mAdapter = new FileAdapter(this);
mListView.setAdapter(mAdapter);
mLocation = new File(getIntent().getStringExtra(EXTRA_INITIAL_DIRECTORY));
refresh();
mLocation = new File(getIntent().getStringExtra(EXTRA_INITIAL_DIRECTORY));
refresh();
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnApiChangeListener(this);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnApiChangeListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@ -78,116 +78,116 @@ public class FolderPickerActivity extends SyncthingActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create_folder:
final EditText et = new EditText(this);
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.create_folder)
.setView(et)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
createFolder(et.getText().toString());
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT);
}
});
dialog.show();
return true;
case R.id.select:
Intent intent = new Intent()
.putExtra(EXTRA_RESULT_DIRECTORY, mLocation.getAbsolutePath());
setResult(Activity.RESULT_OK, intent);
finish();
return true;
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
switch (item.getItemId()) {
case R.id.create_folder:
final EditText et = new EditText(this);
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.create_folder)
.setView(et)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
createFolder(et.getText().toString());
}
}
)
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT);
}
});
dialog.show();
return true;
case R.id.select:
Intent intent = new Intent()
.putExtra(EXTRA_RESULT_DIRECTORY, mLocation.getAbsolutePath());
setResult(Activity.RESULT_OK, intent);
finish();
return true;
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Creates a new folder with the given name and enters it.
*/
private void createFolder(String name) {
File newFolder = new File(mLocation, name);
newFolder.mkdir();
mLocation = newFolder;
refresh();
}
/**
* Creates a new folder with the given name and enters it.
*/
private void createFolder(String name) {
File newFolder = new File(mLocation, name);
newFolder.mkdir();
mLocation = newFolder;
refresh();
}
/**
* Refreshes the ListView to show the contents of the folder in {@code }mLocation.peek()}.
*/
private void refresh() {
mAdapter.clear();
File[] contents = mLocation.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
});
Arrays.sort(contents);
for (File f : contents) {
mAdapter.add(f);
}
}
/**
* Refreshes the ListView to show the contents of the folder in {@code }mLocation.peek()}.
*/
private void refresh() {
mAdapter.clear();
File[] contents = mLocation.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory();
}
});
Arrays.sort(contents);
for (File f : contents) {
mAdapter.add(f);
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
mLocation = mAdapter.getItem(i);
refresh();
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
mLocation = mAdapter.getItem(i);
refresh();
}
private class FileAdapter extends ArrayAdapter<File> {
private class FileAdapter extends ArrayAdapter<File> {
public FileAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1);
}
public FileAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
}
TextView title = (TextView) convertView.findViewById(android.R.id.text1);
title.setText(getItem(position).getName());
return convertView;
}
}
TextView title = (TextView) convertView.findViewById(android.R.id.text1);
title.setText(getItem(position).getName());
return convertView;
}
}
@Override
public void onBackPressed() {
if (!mLocation.equals(Environment.getExternalStorageDirectory())) {
mLocation = mLocation.getParentFile();
refresh();
}
else {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
@Override
public void onBackPressed() {
if (!mLocation.equals(Environment.getExternalStorageDirectory())) {
mLocation = mLocation.getParentFile();
refresh();
} else {
setResult(Activity.RESULT_CANCELED);
finish();
}
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
setResult(Activity.RESULT_CANCELED);
SyncthingService.showDisabledDialog(this);
finish();
}
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
setResult(Activity.RESULT_CANCELED);
SyncthingService.showDisabledDialog(this);
finish();
}
}
}

View file

@ -37,253 +37,254 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
* {@link com.nutomic.syncthingandroid.fragments.LocalNodeInfoFragment} in the navigation drawer.
*/
public class MainActivity extends SyncthingActivity
implements SyncthingService.OnApiChangeListener {
implements SyncthingService.OnApiChangeListener {
private AlertDialog mLoadingDialog;
private AlertDialog mLoadingDialog;
/**
* Causes population of repo and node lists, unlocks info drawer.
*/
@Override
@SuppressLint("InflateParams")
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
if (currentState == SyncthingService.State.DISABLED) {
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
SyncthingService.showDisabledDialog(this);
}
else if (mLoadingDialog == null) {
final SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
/**
* Causes population of repo and node lists, unlocks info drawer.
*/
@Override
@SuppressLint("InflateParams")
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE && !isFinishing()) {
if (currentState == SyncthingService.State.DISABLED) {
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
SyncthingService.showDisabledDialog(this);
} else if (mLoadingDialog == null) {
final SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
LayoutInflater inflater = getLayoutInflater();
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
loadingText.setText((getService().isFirstStart())
? R.string.web_gui_creating_key
: R.string.api_loading);
LayoutInflater inflater = getLayoutInflater();
View dialogLayout = inflater.inflate(R.layout.loading_dialog, null);
TextView loadingText = (TextView) dialogLayout.findViewById(R.id.loading_text);
loadingText.setText((getService().isFirstStart())
? R.string.web_gui_creating_key
: R.string.api_loading);
mLoadingDialog = new AlertDialog.Builder(this)
.setCancelable(false)
.setView(dialogLayout)
.show();
mLoadingDialog = new AlertDialog.Builder(this)
.setCancelable(false)
.setView(dialogLayout)
.show();
// Make sure the first start dialog is shown on top.
if (prefs.getBoolean("first_start", true)) {
new AlertDialog.Builder(this)
.setTitle(R.string.welcome_title)
.setMessage(R.string.welcome_text)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
prefs.edit().putBoolean("first_start", false).commit();
}
})
.show();
}
}
return;
}
// Make sure the first start dialog is shown on top.
if (prefs.getBoolean("first_start", true)) {
new AlertDialog.Builder(this)
.setTitle(R.string.welcome_title)
.setMessage(R.string.welcome_text)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
prefs.edit().putBoolean("first_start", false).commit();
}
})
.show();
}
}
return;
}
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
mDrawerLayout.setDrawerListener(mDrawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
private final FragmentPagerAdapter mSectionsPagerAdapter =
new FragmentPagerAdapter(getSupportFragmentManager()) {
private final FragmentPagerAdapter mSectionsPagerAdapter =
new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
switch (position) {
case 0: return mRepositoriesFragment;
case 1: return mNodesFragment;
default: return null;
}
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return mRepositoriesFragment;
case 1:
return mNodesFragment;
default:
return null;
}
}
@Override
public int getCount() {
return 2;
}
@Override
public int getCount() {
return 2;
}
};
};
private ReposFragment mRepositoriesFragment;
private ReposFragment mRepositoriesFragment;
private NodesFragment mNodesFragment;
private NodesFragment mNodesFragment;
private LocalNodeInfoFragment mLocalNodeInfoFragment;
private LocalNodeInfoFragment mLocalNodeInfoFragment;
private ViewPager mViewPager;
private ViewPager mViewPager;
private ActionBarDrawerToggle mDrawerToggle;
private ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout mDrawerLayout;
private DrawerLayout mDrawerLayout;
/**
* Initializes tab navigation.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar actionBar = getSupportActionBar();
/**
* Initializes tab navigation.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setContentView(R.layout.main_activity);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setContentView(R.layout.main_activity);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
TabListener tabListener = new TabListener() {
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
TabListener tabListener = new TabListener() {
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
};
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
};
actionBar.addTab(actionBar.newTab()
.setText(R.string.repositories_fragment_title)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.nodes_fragment_title)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.repositories_fragment_title)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.nodes_fragment_title)
.setTabListener(tabListener));
if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager();
mRepositoriesFragment = (ReposFragment) fm.getFragment(
savedInstanceState, ReposFragment.class.getName());
mNodesFragment = (NodesFragment) fm.getFragment(
savedInstanceState, NodesFragment.class.getName());
mLocalNodeInfoFragment = (LocalNodeInfoFragment) fm.getFragment(
savedInstanceState, LocalNodeInfoFragment.class.getName());
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
}
else {
mRepositoriesFragment = new ReposFragment();
mNodesFragment = new NodesFragment();
mLocalNodeInfoFragment = new LocalNodeInfoFragment();
}
if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager();
mRepositoriesFragment = (ReposFragment) fm.getFragment(
savedInstanceState, ReposFragment.class.getName());
mNodesFragment = (NodesFragment) fm.getFragment(
savedInstanceState, NodesFragment.class.getName());
mLocalNodeInfoFragment = (LocalNodeInfoFragment) fm.getFragment(
savedInstanceState, LocalNodeInfoFragment.class.getName());
mViewPager.setCurrentItem(savedInstanceState.getInt("currentTab"));
} else {
mRepositoriesFragment = new ReposFragment();
mNodesFragment = new NodesFragment();
mLocalNodeInfoFragment = new LocalNodeInfoFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.drawer, mLocalNodeInfoFragment)
.commit();
mDrawerToggle = mLocalNodeInfoFragment.new Toggle(this, mDrawerLayout,
R.drawable.ic_drawer);
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.drawer, mLocalNodeInfoFragment)
.commit();
mDrawerToggle = mLocalNodeInfoFragment.new Toggle(this, mDrawerLayout,
R.drawable.ic_drawer);
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
}
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnApiChangeListener(mRepositoriesFragment);
getService().registerOnApiChangeListener(mNodesFragment);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnApiChangeListener(mRepositoriesFragment);
getService().registerOnApiChangeListener(mNodesFragment);
}
/**
* Saves fragment states.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Avoid crash if called during startup.
if (mRepositoriesFragment != null && mNodesFragment != null &&
mLocalNodeInfoFragment != null) {
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, ReposFragment.class.getName(), mRepositoriesFragment);
fm.putFragment(outState, NodesFragment.class.getName(), mNodesFragment);
fm.putFragment(outState, LocalNodeInfoFragment.class.getName(), mLocalNodeInfoFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem());
}
}
/**
* Saves fragment states.
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Avoid crash if called during startup.
if (mRepositoriesFragment != null && mNodesFragment != null &&
mLocalNodeInfoFragment != null) {
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, ReposFragment.class.getName(), mRepositoriesFragment);
fm.putFragment(outState, NodesFragment.class.getName(), mNodesFragment);
fm.putFragment(outState, LocalNodeInfoFragment.class.getName(), mLocalNodeInfoFragment);
outState.putInt("currentTab", mViewPager.getCurrentItem());
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/**
* Shows menu only once syncthing service is running, and shows "share" option only when
* drawer is open.
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean drawerOpen = mDrawerLayout.isDrawerOpen(findViewById(R.id.drawer));
menu.findItem(R.id.share_node_id).setVisible(drawerOpen);
return true;
}
/**
* Shows menu only once syncthing service is running, and shows "share" option only when
* drawer is open.
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean drawerOpen = mDrawerLayout.isDrawerOpen(findViewById(R.id.drawer));
menu.findItem(R.id.share_node_id).setVisible(drawerOpen);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mLocalNodeInfoFragment.onOptionsItemSelected(item) ||
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mLocalNodeInfoFragment.onOptionsItemSelected(item) ||
mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case R.id.add_repo:
Intent intent = new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
case R.id.add_node:
intent = new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
case R.id.web_gui:
startActivity(new Intent(this, WebGuiActivity.class));
return true;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
switch (item.getItemId()) {
case R.id.add_repo:
Intent intent = new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
case R.id.add_node:
intent = new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
case R.id.web_gui:
startActivity(new Intent(this, WebGuiActivity.class));
return true;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
}

View file

@ -14,75 +14,74 @@ import com.nutomic.syncthingandroid.fragments.SettingsFragment;
*/
public class SettingsActivity extends SyncthingActivity {
public static final String ACTION_APP_SETTINGS_FRAGMENT = "app_settings_fragment";
public static final String ACTION_APP_SETTINGS_FRAGMENT = "app_settings_fragment";
public static final String ACTION_NODE_SETTINGS_FRAGMENT = "node_settings_fragment";
public static final String ACTION_NODE_SETTINGS_FRAGMENT = "node_settings_fragment";
public static final String ACTION_REPO_SETTINGS_FRAGMENT = "repo_settings_fragment";
public static final String ACTION_REPO_SETTINGS_FRAGMENT = "repo_settings_fragment";
/**
* Must be set for {@link #ACTION_NODE_SETTINGS_FRAGMENT} and
* {@link #ACTION_REPO_SETTINGS_FRAGMENT} to determine if an existing repo/node should be
* edited or a new one created.
*
* If this is false, {@link com.nutomic.syncthingandroid.fragments.RepoSettingsFragment#EXTRA_REPO_ID} or
* {@link com.nutomic.syncthingandroid.fragments.NodeSettingsFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
*/
public static final String EXTRA_IS_CREATE = "create";
/**
* Must be set for {@link #ACTION_NODE_SETTINGS_FRAGMENT} and
* {@link #ACTION_REPO_SETTINGS_FRAGMENT} to determine if an existing repo/node should be
* edited or a new one created.
* <p/>
* If this is false, {@link com.nutomic.syncthingandroid.fragments.RepoSettingsFragment#EXTRA_REPO_ID} or
* {@link com.nutomic.syncthingandroid.fragments.NodeSettingsFragment#EXTRA_NODE_ID} must be set (according to the selected fragment).
*/
public static final String EXTRA_IS_CREATE = "create";
private Fragment mFragment;
private Fragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState != null) {
mFragment = fm.getFragment(savedInstanceState,
savedInstanceState.getString("fragment_name"));
}
else {
switch (getIntent().getAction()) {
case ACTION_APP_SETTINGS_FRAGMENT:
setTitle(R.string.settings_title);
mFragment = new SettingsFragment();
break;
case ACTION_NODE_SETTINGS_FRAGMENT:
mFragment = new NodeSettingsFragment();
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
break;
case ACTION_REPO_SETTINGS_FRAGMENT:
mFragment = new RepoSettingsFragment();
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
break;
default:
throw new IllegalArgumentException(
"You must provide the requested fragment type as an extra.");
}
}
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState != null) {
mFragment = fm.getFragment(savedInstanceState,
savedInstanceState.getString("fragment_name"));
} else {
switch (getIntent().getAction()) {
case ACTION_APP_SETTINGS_FRAGMENT:
setTitle(R.string.settings_title);
mFragment = new SettingsFragment();
break;
case ACTION_NODE_SETTINGS_FRAGMENT:
mFragment = new NodeSettingsFragment();
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
break;
case ACTION_REPO_SETTINGS_FRAGMENT:
mFragment = new RepoSettingsFragment();
if (!getIntent().hasExtra(EXTRA_IS_CREATE)) {
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
break;
default:
throw new IllegalArgumentException(
"You must provide the requested fragment type as an extra.");
}
}
fm.beginTransaction()
.replace(android.R.id.content, mFragment)
.commit();
}
fm.beginTransaction()
.replace(android.R.id.content, mFragment)
.commit();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String fragmentClassName = mFragment.getClass().getName();
outState.putString("fragment_name", fragmentClassName);
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, fragmentClassName, mFragment);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String fragmentClassName = mFragment.getClass().getName();
outState.putString("fragment_name", fragmentClassName);
FragmentManager fm = getSupportFragmentManager();
fm.putFragment(outState, fragmentClassName, mFragment);
}
public boolean getIsCreate() {
return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
}
public boolean getIsCreate() {
return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
}
}

View file

@ -19,71 +19,70 @@ import java.util.LinkedList;
*/
public class SyncthingActivity extends ActionBarActivity implements ServiceConnection {
private SyncthingService mSyncthingService;
private SyncthingService mSyncthingService;
private LinkedList<OnServiceConnectedListener> mServiceConnectedListeners = new LinkedList<>();
private LinkedList<OnServiceConnectedListener> mServiceConnectedListeners = new LinkedList<>();
/**
* To be used for Fragments.
*/
public interface OnServiceConnectedListener {
public void onServiceConnected();
}
/**
* To be used for Fragments.
*/
public interface OnServiceConnectedListener {
public void onServiceConnected();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindService(new Intent(this, SyncthingService.class),
this, Context.BIND_AUTO_CREATE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindService(new Intent(this, SyncthingService.class),
this, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(this);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) iBinder;
mSyncthingService = binder.getService();
for (OnServiceConnectedListener listener : mServiceConnectedListeners) {
listener.onServiceConnected();
}
mServiceConnectedListeners.clear();
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
SyncthingServiceBinder binder = (SyncthingServiceBinder) iBinder;
mSyncthingService = binder.getService();
for (OnServiceConnectedListener listener : mServiceConnectedListeners) {
listener.onServiceConnected();
}
mServiceConnectedListeners.clear();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mSyncthingService = null;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mSyncthingService = null;
}
/**
* Used for Fragments to use the Activity's service connection.
*/
public void registerOnServiceConnectedListener(OnServiceConnectedListener listener) {
if (mSyncthingService != null) {
listener.onServiceConnected();
}
else {
mServiceConnectedListeners.addLast(listener);
}
}
/**
* Used for Fragments to use the Activity's service connection.
*/
public void registerOnServiceConnectedListener(OnServiceConnectedListener listener) {
if (mSyncthingService != null) {
listener.onServiceConnected();
} else {
mServiceConnectedListeners.addLast(listener);
}
}
/**
* Returns service object (or null if not bound).
*/
public SyncthingService getService() {
return mSyncthingService;
}
/**
* Returns service object (or null if not bound).
*/
public SyncthingService getService() {
return mSyncthingService;
}
/**
* Returns RestApi instance, or null if SyncthingService is not yet connected.
*/
public RestApi getApi() {
return (getService() != null)
? getService().getApi()
: null;
}
/**
* Returns RestApi instance, or null if SyncthingService is not yet connected.
*/
public RestApi getApi() {
return (getService() != null)
? getService().getApi()
: null;
}
}

View file

@ -17,56 +17,56 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
*/
public class WebGuiActivity extends SyncthingActivity implements SyncthingService.OnWebGuiAvailableListener {
private WebView mWebView;
private WebView mWebView;
private View mLoadingView;
private View mLoadingView;
/**
* Hides the loading screen and shows the WebView once it is fully loaded.
*/
private final WebViewClient mWebViewClient = new WebViewClient() {
/**
* Hides the loading screen and shows the WebView once it is fully loaded.
*/
private final WebViewClient mWebViewClient = new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
mWebView.setVisibility(View.VISIBLE);
mLoadingView.setVisibility(View.GONE);
}
};
@Override
public void onPageFinished(WebView view, String url) {
mWebView.setVisibility(View.VISIBLE);
mLoadingView.setVisibility(View.GONE);
}
};
/**
* Initialize WebView.
*
* Ignore lint javascript warning as js is loaded only from our known, local service.
*/
@Override
@SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Initialize WebView.
* <p/>
* Ignore lint javascript warning as js is loaded only from our known, local service.
*/
@Override
@SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.web_gui_activity);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.web_gui_activity);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mLoadingView = findViewById(R.id.loading);
ProgressBar pb = (ProgressBar) mLoadingView.findViewById(R.id.progress);
pb.setIndeterminate(true);
mLoadingView = findViewById(R.id.loading);
ProgressBar pb = (ProgressBar) mLoadingView.findViewById(R.id.progress);
pb.setIndeterminate(true);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(mWebViewClient);
}
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(mWebViewClient);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnWebGuiAvailableListener(WebGuiActivity.this);
}
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
super.onServiceConnected(componentName, iBinder);
getService().registerOnWebGuiAvailableListener(WebGuiActivity.this);
}
/**
* Loads and shows WebView, hides loading view.
*/
@Override
public void onWebGuiAvailable() {
mWebView.loadUrl(getApi().getUrl());
}
/**
* Loads and shows WebView, hides loading view.
*/
@Override
public void onWebGuiAvailable() {
mWebView.loadUrl(getApi().getUrl());
}
}

View file

@ -26,152 +26,153 @@ import java.util.TimerTask;
* Displays information about the local node.
*/
public class LocalNodeInfoFragment extends Fragment
implements RestApi.OnReceiveSystemInfoListener, RestApi.OnReceiveConnectionsListener {
implements RestApi.OnReceiveSystemInfoListener, RestApi.OnReceiveConnectionsListener {
private TextView mNodeId;
private TextView mNodeId;
private TextView mCpuUsage;
private TextView mCpuUsage;
private TextView mRamUsage;
private TextView mRamUsage;
private TextView mDownload;
private TextView mDownload;
private TextView mUpload;
private TextView mUpload;
private TextView mAnnounceServer;
private TextView mAnnounceServer;
private Timer mTimer;
private Timer mTimer;
private MainActivity mActivity;
private MainActivity mActivity;
/**
* Starts polling for status when opened, stops when closed.
*/
public class Toggle extends ActionBarDrawerToggle {
public Toggle(Activity activity, DrawerLayout drawerLayout, int drawerImageRes) {
super(activity, drawerLayout, drawerImageRes, R.string.app_name, R.string.system_info);
}
/**
* Starts polling for status when opened, stops when closed.
*/
public class Toggle extends ActionBarDrawerToggle {
public Toggle(Activity activity, DrawerLayout drawerLayout, int drawerImageRes) {
super(activity, drawerLayout, drawerImageRes, R.string.app_name, R.string.system_info);
}
@Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
mTimer.cancel();
mTimer = null;
mActivity.getSupportActionBar().setTitle(R.string.app_name);
mActivity.supportInvalidateOptionsMenu();
}
@Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
mTimer.cancel();
mTimer = null;
mActivity.getSupportActionBar().setTitle(R.string.app_name);
mActivity.supportInvalidateOptionsMenu();
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
LocalNodeInfoFragment.this.onDrawerOpened();
}
};
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
LocalNodeInfoFragment.this.onDrawerOpened();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.local_node_info_fragment, container, false);
mNodeId = (TextView) view.findViewById(R.id.node_id);
mCpuUsage = (TextView) view.findViewById(R.id.cpu_usage);
mRamUsage = (TextView) view.findViewById(R.id.ram_usage);
mDownload = (TextView) view.findViewById(R.id.download);
mUpload = (TextView) view.findViewById(R.id.upload);
mAnnounceServer = (TextView) view.findViewById(R.id.announce_server);
;
return view;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.local_node_info_fragment, container, false);
mNodeId = (TextView) view.findViewById(R.id.node_id);
mCpuUsage = (TextView) view.findViewById(R.id.cpu_usage);
mRamUsage = (TextView) view.findViewById(R.id.ram_usage);
mDownload = (TextView) view.findViewById(R.id.download);
mUpload = (TextView) view.findViewById(R.id.upload);
mAnnounceServer = (TextView) view.findViewById(R.id.announce_server);
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mActivity = (MainActivity) getActivity();
return view;
}
if (savedInstanceState != null && savedInstanceState.getBoolean("active")) {
onDrawerOpened();
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mActivity = (MainActivity) getActivity();
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("active", mTimer != null);
}
if (savedInstanceState != null && savedInstanceState.getBoolean("active")) {
onDrawerOpened();
}
}
private void onDrawerOpened() {
// FIXME: never called
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateGui();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("active", mTimer != null);
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
mActivity.getSupportActionBar().setTitle(R.string.system_info);
mActivity.supportInvalidateOptionsMenu();
}
private void onDrawerOpened() {
// FIXME: never called
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateGui();
}
/**
* Invokes status callbacks.
*/
private void updateGui() {
if (mActivity.getApi() != null) {
mActivity.getApi().getSystemInfo(this);
mActivity.getApi().getConnections(this);
}
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
mActivity.getSupportActionBar().setTitle(R.string.system_info);
mActivity.supportInvalidateOptionsMenu();
}
/**
* Populates views with status received via {@link RestApi#getSystemInfo}.
*/
@Override
public void onReceiveSystemInfo(RestApi.SystemInfo info) {
if (getActivity() == null)
return;
/**
* Invokes status callbacks.
*/
private void updateGui() {
if (mActivity.getApi() != null) {
mActivity.getApi().getSystemInfo(this);
mActivity.getApi().getConnections(this);
}
}
mNodeId.setText(info.myID);
mNodeId.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
mActivity.getApi().copyNodeId(mNodeId.getText().toString());
view.performClick();
return true;
}
});
mCpuUsage.setText(new DecimalFormat("0.00").format(info.cpuPercent) + "%");
mRamUsage.setText(RestApi.readableFileSize(mActivity, info.sys));
if (info.extAnnounceOK) {
mAnnounceServer.setText("Online");
mAnnounceServer.setTextColor(getResources().getColor(R.color.text_green));
}
else {
mAnnounceServer.setText("Offline");
mAnnounceServer.setTextColor(getResources().getColor(R.color.text_red));
}
}
/**
* Populates views with status received via {@link RestApi#getSystemInfo}.
*/
@Override
public void onReceiveSystemInfo(RestApi.SystemInfo info) {
if (getActivity() == null)
return;
/**
* Populates views with status received via {@link RestApi#getConnections}.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
RestApi.Connection c = connections.get(RestApi.LOCAL_NODE_CONNECTIONS);
mDownload.setText(RestApi.readableTransferRate(mActivity, c.InBits));
mUpload.setText(RestApi.readableTransferRate(mActivity, c.OutBits));
}
mNodeId.setText(info.myID);
mNodeId.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
mActivity.getApi().copyNodeId(mNodeId.getText().toString());
view.performClick();
return true;
}
});
mCpuUsage.setText(new DecimalFormat("0.00").format(info.cpuPercent) + "%");
mRamUsage.setText(RestApi.readableFileSize(mActivity, info.sys));
if (info.extAnnounceOK) {
mAnnounceServer.setText("Online");
mAnnounceServer.setTextColor(getResources().getColor(R.color.text_green));
} else {
mAnnounceServer.setText("Offline");
mAnnounceServer.setTextColor(getResources().getColor(R.color.text_red));
}
}
/**
* Shares the local node ID when "share" is clicked.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_node_id:
RestApi.shareNodeId(getActivity(), mNodeId.getText().toString());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Populates views with status received via {@link RestApi#getConnections}.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
RestApi.Connection c = connections.get(RestApi.LOCAL_NODE_CONNECTIONS);
mDownload.setText(RestApi.readableTransferRate(mActivity, c.InBits));
mUpload.setText(RestApi.readableTransferRate(mActivity, c.OutBits));
}
/**
* Shares the local node ID when "share" is clicked.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_node_id:
RestApi.shareNodeId(getActivity(), mNodeId.getText().toString());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View file

@ -1,6 +1,5 @@
package com.nutomic.syncthingandroid.fragments;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@ -29,259 +28,253 @@ import java.util.Map;
* Shows node details and allows changing them.
*/
public class NodeSettingsFragment extends PreferenceFragment implements
SyncthingActivity.OnServiceConnectedListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener, RestApi.OnReceiveConnectionsListener,
SyncthingService.OnApiChangeListener, RestApi.OnNodeIdNormalizedListener {
SyncthingActivity.OnServiceConnectedListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener, RestApi.OnReceiveConnectionsListener,
SyncthingService.OnApiChangeListener, RestApi.OnNodeIdNormalizedListener {
public static final String EXTRA_NODE_ID = "node_id";
public static final String EXTRA_NODE_ID = "node_id";
private static final int SCAN_QR_REQUEST_CODE = 235;
private static final int SCAN_QR_REQUEST_CODE = 235;
private SyncthingService mSyncthingService;
private SyncthingService mSyncthingService;
private RestApi.Node mNode;
private RestApi.Node mNode;
private Preference mNodeId;
private Preference mNodeId;
private EditTextPreference mName;
private EditTextPreference mName;
private EditTextPreference mAddresses;
private EditTextPreference mAddresses;
private Preference mVersion;
private Preference mVersion;
private Preference mCurrentAddress;
private Preference mCurrentAddress;
private boolean mIsCreate;
private boolean mIsCreate;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
mIsCreate = ((SettingsActivity) getActivity()).getIsCreate();
setHasOptionsMenu(true);
mIsCreate = ((SettingsActivity) getActivity()).getIsCreate();
setHasOptionsMenu(true);
if (mIsCreate) {
addPreferencesFromResource(R.xml.node_settings_create);
}
else {
addPreferencesFromResource(R.xml.node_settings_edit);
}
if (mIsCreate) {
addPreferencesFromResource(R.xml.node_settings_create);
} else {
addPreferencesFromResource(R.xml.node_settings_edit);
}
mNodeId = findPreference("node_id");
mNodeId.setOnPreferenceChangeListener(this);
mName = (EditTextPreference) findPreference("name");
mName.setOnPreferenceChangeListener(this);
mAddresses = (EditTextPreference) findPreference("addresses");
mAddresses.setOnPreferenceChangeListener(this);
if (!mIsCreate) {
mVersion = findPreference("version");
mVersion.setSummary("?");
mCurrentAddress = findPreference("current_address");
mCurrentAddress.setSummary("?");
}
}
mNodeId = findPreference("node_id");
mNodeId.setOnPreferenceChangeListener(this);
mName = (EditTextPreference) findPreference("name");
mName.setOnPreferenceChangeListener(this);
mAddresses = (EditTextPreference) findPreference("addresses");
mAddresses.setOnPreferenceChangeListener(this);
if (!mIsCreate) {
mVersion = findPreference("version");
mVersion.setSummary("?");
mCurrentAddress = findPreference("current_address");
mCurrentAddress.setSummary("?");
}
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
if (getActivity() == null || getActivity().isFinishing())
return;
if (getActivity() == null || getActivity().isFinishing())
return;
if (mIsCreate) {
getActivity().setTitle(R.string.add_node);
mNode = new RestApi.Node();
mNode.Name = "";
mNode.NodeID = "";
mNode.Addresses = "dynamic";
((EditTextPreference) mNodeId).setText(mNode.NodeID);
}
else {
getActivity().setTitle(R.string.edit_node);
List<RestApi.Node> nodes = mSyncthingService.getApi().getNodes();
for (int i = 0; i < nodes.size(); i++) {
if (nodes.get(i).NodeID.equals(
getActivity().getIntent().getStringExtra(EXTRA_NODE_ID))) {
mNode = nodes.get(i);
break;
}
}
mNodeId.setOnPreferenceClickListener(this);
}
mSyncthingService.getApi().getConnections(NodeSettingsFragment.this);
if (mIsCreate) {
getActivity().setTitle(R.string.add_node);
mNode = new RestApi.Node();
mNode.Name = "";
mNode.NodeID = "";
mNode.Addresses = "dynamic";
((EditTextPreference) mNodeId).setText(mNode.NodeID);
} else {
getActivity().setTitle(R.string.edit_node);
List<RestApi.Node> nodes = mSyncthingService.getApi().getNodes();
for (int i = 0; i < nodes.size(); i++) {
if (nodes.get(i).NodeID.equals(
getActivity().getIntent().getStringExtra(EXTRA_NODE_ID))) {
mNode = nodes.get(i);
break;
}
}
mNodeId.setOnPreferenceClickListener(this);
}
mSyncthingService.getApi().getConnections(NodeSettingsFragment.this);
mNodeId.setSummary(mNode.NodeID);
mName.setText((mNode.Name));
mName.setSummary(mNode.Name);
mAddresses.setText(mNode.Addresses);
mAddresses.setSummary(mNode.Addresses);
}
mNodeId.setSummary(mNode.NodeID);
mName.setText((mNode.Name));
mName.setSummary(mNode.Name);
mAddresses.setText(mNode.Addresses);
mAddresses.setSummary(mNode.Addresses);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.node_settings, menu);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.node_settings, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreate);
menu.findItem(R.id.share_node_id).setVisible(!mIsCreate);
menu.findItem(R.id.delete).setVisible(!mIsCreate);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreate);
menu.findItem(R.id.share_node_id).setVisible(!mIsCreate);
menu.findItem(R.id.delete).setVisible(!mIsCreate);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mNode.NodeID.equals("")) {
Toast.makeText(getActivity(), R.string.node_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (mNode.Name.equals("")) {
Toast.makeText(getActivity(), R.string.node_name_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editNode(mNode, this);
return true;
case R.id.share_node_id:
RestApi.shareNodeId(getActivity(), mNode.NodeID);
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_node_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteNode(mNode, getActivity());
getActivity().finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mNode.NodeID.equals("")) {
Toast.makeText(getActivity(), R.string.node_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (mNode.Name.equals("")) {
Toast.makeText(getActivity(), R.string.node_name_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editNode(mNode, this);
return true;
case R.id.share_node_id:
RestApi.shareNodeId(getActivity(), mNode.NodeID);
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_node_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteNode(mNode, getActivity());
getActivity().finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
if (preference.equals(mNodeId)) {
mNode.NodeID = (String) o;
nodeUpdated();
return true;
}
else if (preference.equals(mName)) {
mNode.Name = (String) o;
nodeUpdated();
return true;
}
else if (preference.equals(mAddresses)) {
mNode.Addresses = (String) o;
nodeUpdated();
return true;
}
return false;
}
if (preference.equals(mNodeId)) {
mNode.NodeID = (String) o;
nodeUpdated();
return true;
} else if (preference.equals(mName)) {
mNode.Name = (String) o;
nodeUpdated();
return true;
} else if (preference.equals(mAddresses)) {
mNode.Addresses = (String) o;
nodeUpdated();
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mNodeId)) {
mSyncthingService.getApi().copyNodeId(mNode.NodeID);
return true;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mNodeId)) {
mSyncthingService.getApi().copyNodeId(mNode.NodeID);
return true;
}
return false;
}
/**
* Sets version and current address of the node.
*
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
if (connections.containsKey(mNode.NodeID)) {
mVersion.setSummary(connections.get(mNode.NodeID).ClientVersion);
mCurrentAddress.setSummary(connections.get(mNode.NodeID).Address);
}
}
/**
* Sets version and current address of the node.
* <p/>
* NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes.
*/
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
if (connections.containsKey(mNode.NodeID)) {
mVersion.setSummary(connections.get(mNode.NodeID).ClientVersion);
mCurrentAddress.setSummary(connections.get(mNode.NodeID).Address);
}
}
/**
* Sends the updated node info if in edit mode.
*/
private void nodeUpdated() {
if (!mIsCreate) {
mSyncthingService.getApi().editNode(mNode, this);
}
}
/**
* Sends the updated node info if in edit mode.
*/
private void nodeUpdated() {
if (!mIsCreate) {
mSyncthingService.getApi().editNode(mNode, this);
}
}
/**
* Sends QR code scanning intent when clicking the qrcode icon.
*/
public void onClick(View view) {
Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
try {
startActivityForResult(intentScan, SCAN_QR_REQUEST_CODE);
}
catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), R.string.no_qr_scanner_installed,
Toast.LENGTH_LONG).show();
}
}
/**
* Sends QR code scanning intent when clicking the qrcode icon.
*/
public void onClick(View view) {
Intent intentScan = new Intent("com.google.zxing.client.android.SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
try {
startActivityForResult(intentScan, SCAN_QR_REQUEST_CODE);
} catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), R.string.no_qr_scanner_installed,
Toast.LENGTH_LONG).show();
}
}
/**
* Receives value of scanned QR code and sets it as node ID.
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SCAN_QR_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
mNode.NodeID = data.getStringExtra("SCAN_RESULT");
((EditTextPreference) mNodeId).setText(mNode.NodeID);
mNodeId.setSummary(mNode.NodeID);
}
}
/**
* Receives value of scanned QR code and sets it as node ID.
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SCAN_QR_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
mNode.NodeID = data.getStringExtra("SCAN_RESULT");
((EditTextPreference) mNodeId).setText(mNode.NodeID);
mNodeId.setSummary(mNode.NodeID);
}
}
/**
* Callback for {@link RestApi#editNode(RestApi.Node, RestApi.OnNodeIdNormalizedListener)}.
* Displays an error message if present, or finishes the Activity on success in edit mode.
*
* @param normalizedId The normalized node ID, or null on error.
* @param error An error message, or null on success.
*/
@Override
public void onNodeIdNormalized(String normalizedId, String error) {
if (error != null) {
Toast.makeText(getActivity(), error, Toast.LENGTH_LONG).show();
}
else if (mIsCreate) {
getActivity().finish();
}
}
/**
* Callback for {@link RestApi#editNode(RestApi.Node, RestApi.OnNodeIdNormalizedListener)}.
* Displays an error message if present, or finishes the Activity on success in edit mode.
*
* @param normalizedId The normalized node ID, or null on error.
* @param error An error message, or null on success.
*/
@Override
public void onNodeIdNormalized(String normalizedId, String error) {
if (error != null) {
Toast.makeText(getActivity(), error, Toast.LENGTH_LONG).show();
} else if (mIsCreate) {
getActivity().finish();
}
}
}

View file

@ -21,80 +21,79 @@ import java.util.TimerTask;
* Displays a list of all existing nodes.
*/
public class NodesFragment extends ListFragment implements SyncthingService.OnApiChangeListener,
ListView.OnItemClickListener {
ListView.OnItemClickListener {
private NodesAdapter mAdapter;
private NodesAdapter mAdapter;
private Timer mTimer;
private Timer mTimer;
@Override
public void onResume() {
super.onResume();
setListShown(true);
}
@Override
public void onResume() {
super.onResume();
setListShown(true);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE)
return;
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE)
return;
initAdapter();
}
initAdapter();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initAdapter();
}
initAdapter();
}
private void initAdapter() {
SyncthingActivity activity = (SyncthingActivity) getActivity();
if (activity == null || activity.getApi() == null)
return;
private void initAdapter() {
SyncthingActivity activity = (SyncthingActivity) getActivity();
if (activity == null || activity.getApi() == null)
return;
mAdapter = new NodesAdapter(activity);
mAdapter.add(activity.getApi().getNodes());
setListAdapter(mAdapter);
setEmptyText(getString(R.string.nodes_list_empty));
getListView().setOnItemClickListener(this);
}
mAdapter = new NodesAdapter(activity);
mAdapter.add(activity.getApi().getNodes());
setListAdapter(mAdapter);
setEmptyText(getString(R.string.nodes_list_empty));
getListView().setOnItemClickListener(this);
}
private void updateList() {
if (mAdapter == null || getView() == null)
return;
private void updateList() {
if (mAdapter == null || getView() == null)
return;
MainActivity activity = (MainActivity) getActivity();
mAdapter.updateConnections(activity.getApi(), getListView());
}
MainActivity activity = (MainActivity) getActivity();
mAdapter.updateConnections(activity.getApi(), getListView());
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateList();
}
if (isVisibleToUser) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateList();
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
}
else if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
} else if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
intent.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT);
intent.putExtra(SettingsActivity.EXTRA_IS_CREATE, false);
intent.putExtra(NodeSettingsFragment.EXTRA_NODE_ID, mAdapter.getItem(i).NodeID);
startActivity(intent);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
intent.setAction(SettingsActivity.ACTION_NODE_SETTINGS_FRAGMENT);
intent.putExtra(SettingsActivity.EXTRA_IS_CREATE, false);
intent.putExtra(NodeSettingsFragment.EXTRA_NODE_ID, mAdapter.getItem(i).NodeID);
startActivity(intent);
}
}

View file

@ -31,275 +31,265 @@ import java.util.List;
* Shows repo details and allows changing them.
*/
public class RepoSettingsFragment extends PreferenceFragment
implements SyncthingActivity.OnServiceConnectedListener,
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
SyncthingService.OnApiChangeListener {
implements SyncthingActivity.OnServiceConnectedListener,
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
SyncthingService.OnApiChangeListener {
private static final int DIRECTORY_REQUEST_CODE = 234;
private static final int DIRECTORY_REQUEST_CODE = 234;
/**
* The ID of the repo to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
* set to false.
*/
public static final String EXTRA_REPO_ID = "repo_id";
/**
* The ID of the repo to be edited. To be used with {@link com.nutomic.syncthingandroid.activities.SettingsActivity#EXTRA_IS_CREATE}
* set to false.
*/
public static final String EXTRA_REPO_ID = "repo_id";
private static final String KEY_NODE_SHARED = "node_shared";
private static final String KEY_NODE_SHARED = "node_shared";
private SyncthingService mSyncthingService;
private SyncthingService mSyncthingService;
private RestApi.Repo mRepo;
private RestApi.Repo mRepo;
private EditTextPreference mRepoId;
private EditTextPreference mRepoId;
private Preference mDirectory;
private Preference mDirectory;
private CheckBoxPreference mRepoMaster;
private CheckBoxPreference mRepoMaster;
private PreferenceScreen mNodes;
private PreferenceScreen mNodes;
private CheckBoxPreference mVersioning;
private CheckBoxPreference mVersioning;
private EditTextPreference mVersioningKeep;
private EditTextPreference mVersioningKeep;
private boolean mIsCreate;
private boolean mIsCreate;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity();
activity.registerOnServiceConnectedListener(this);
mIsCreate = activity.getIsCreate();
setHasOptionsMenu(true);
SettingsActivity activity = (SettingsActivity) getActivity();
activity.registerOnServiceConnectedListener(this);
mIsCreate = activity.getIsCreate();
setHasOptionsMenu(true);
if (mIsCreate) {
addPreferencesFromResource(R.xml.repo_settings_create);
}
else {
addPreferencesFromResource(R.xml.repo_settings_edit);
}
if (mIsCreate) {
addPreferencesFromResource(R.xml.repo_settings_create);
} else {
addPreferencesFromResource(R.xml.repo_settings_edit);
}
mRepoId = (EditTextPreference) findPreference("repo_id");
mRepoId.setOnPreferenceChangeListener(this);
mDirectory = findPreference("directory");
mDirectory.setOnPreferenceClickListener(this);
mRepoMaster = (CheckBoxPreference) findPreference("repo_master");
mRepoMaster.setOnPreferenceChangeListener(this);
mNodes = (PreferenceScreen) findPreference("nodes");
mNodes.setOnPreferenceClickListener(this);
mVersioning = (CheckBoxPreference) findPreference("versioning");
mVersioning.setOnPreferenceChangeListener(this);
mVersioningKeep = (EditTextPreference) findPreference("versioning_keep");
mVersioningKeep.setOnPreferenceChangeListener(this);
}
mRepoId = (EditTextPreference) findPreference("repo_id");
mRepoId.setOnPreferenceChangeListener(this);
mDirectory = findPreference("directory");
mDirectory.setOnPreferenceClickListener(this);
mRepoMaster = (CheckBoxPreference) findPreference("repo_master");
mRepoMaster.setOnPreferenceChangeListener(this);
mNodes = (PreferenceScreen) findPreference("nodes");
mNodes.setOnPreferenceClickListener(this);
mVersioning = (CheckBoxPreference) findPreference("versioning");
mVersioning.setOnPreferenceChangeListener(this);
mVersioningKeep = (EditTextPreference) findPreference("versioning_keep");
mVersioningKeep.setOnPreferenceChangeListener(this);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE) {
getActivity().finish();
return;
}
if (getActivity() == null || getActivity().isFinishing())
return;
if (getActivity() == null || getActivity().isFinishing())
return;
if (mIsCreate) {
getActivity().setTitle(R.string.create_repo);
mRepo = new RestApi.Repo();
mRepo.ID = "";
mRepo.Directory = "";
mRepo.Nodes = new ArrayList<RestApi.Node>();
mRepo.Versioning = new RestApi.Versioning();
}
else {
getActivity().setTitle(R.string.edit_repo);
List<RestApi.Repo> repos = mSyncthingService.getApi().getRepos();
for (int i = 0; i < repos.size(); i++) {
if (repos.get(i).ID.equals(
getActivity().getIntent().getStringExtra(EXTRA_REPO_ID))) {
mRepo = repos.get(i);
break;
}
}
}
if (mIsCreate) {
getActivity().setTitle(R.string.create_repo);
mRepo = new RestApi.Repo();
mRepo.ID = "";
mRepo.Directory = "";
mRepo.Nodes = new ArrayList<RestApi.Node>();
mRepo.Versioning = new RestApi.Versioning();
} else {
getActivity().setTitle(R.string.edit_repo);
List<RestApi.Repo> repos = mSyncthingService.getApi().getRepos();
for (int i = 0; i < repos.size(); i++) {
if (repos.get(i).ID.equals(
getActivity().getIntent().getStringExtra(EXTRA_REPO_ID))) {
mRepo = repos.get(i);
break;
}
}
}
mRepoId.setText(mRepo.ID);
mRepoId.setSummary(mRepo.ID);
mDirectory.setSummary(mRepo.Directory);
mRepoMaster.setChecked(mRepo.ReadOnly);
List<RestApi.Node> nodesList = mSyncthingService.getApi().getNodes();
for (RestApi.Node n : nodesList) {
ExtendedCheckBoxPreference cbp = new ExtendedCheckBoxPreference(getActivity(), n);
cbp.setTitle(n.Name);
cbp.setKey(KEY_NODE_SHARED);
cbp.setOnPreferenceChangeListener(RepoSettingsFragment.this);
cbp.setChecked(false);
for (RestApi.Node n2 : mRepo.Nodes) {
if (n2.NodeID.equals(n.NodeID)) {
cbp.setChecked(true);
}
}
mNodes.addPreference(cbp);
}
mVersioning.setChecked(mRepo.Versioning instanceof RestApi.SimpleVersioning);
if (mVersioning.isChecked()) {
mVersioningKeep.setText(mRepo.Versioning.getParams().get("keep"));
mVersioningKeep.setSummary(mRepo.Versioning.getParams().get("keep"));
mVersioningKeep.setEnabled(true);
}
else {
mVersioningKeep.setEnabled(false);
}
}
mRepoId.setText(mRepo.ID);
mRepoId.setSummary(mRepo.ID);
mDirectory.setSummary(mRepo.Directory);
mRepoMaster.setChecked(mRepo.ReadOnly);
List<RestApi.Node> nodesList = mSyncthingService.getApi().getNodes();
for (RestApi.Node n : nodesList) {
ExtendedCheckBoxPreference cbp = new ExtendedCheckBoxPreference(getActivity(), n);
cbp.setTitle(n.Name);
cbp.setKey(KEY_NODE_SHARED);
cbp.setOnPreferenceChangeListener(RepoSettingsFragment.this);
cbp.setChecked(false);
for (RestApi.Node n2 : mRepo.Nodes) {
if (n2.NodeID.equals(n.NodeID)) {
cbp.setChecked(true);
}
}
mNodes.addPreference(cbp);
}
mVersioning.setChecked(mRepo.Versioning instanceof RestApi.SimpleVersioning);
if (mVersioning.isChecked()) {
mVersioningKeep.setText(mRepo.Versioning.getParams().get("keep"));
mVersioningKeep.setSummary(mRepo.Versioning.getParams().get("keep"));
mVersioningKeep.setEnabled(true);
} else {
mVersioningKeep.setEnabled(false);
}
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.repo_settings, menu);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.repo_settings, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreate);
menu.findItem(R.id.delete).setVisible(!mIsCreate);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.create).setVisible(mIsCreate);
menu.findItem(R.id.delete).setVisible(!mIsCreate);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mRepo.ID.equals("")) {
Toast.makeText(getActivity(), R.string.repo_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (mRepo.Directory.equals("")) {
Toast.makeText(getActivity(), R.string.repo_path_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editRepo(mRepo, true, getActivity());
getActivity().finish();
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_repo_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteRepo(mRepo, getActivity());
getActivity().finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.create:
if (mRepo.ID.equals("")) {
Toast.makeText(getActivity(), R.string.repo_id_required, Toast.LENGTH_LONG)
.show();
return true;
}
if (mRepo.Directory.equals("")) {
Toast.makeText(getActivity(), R.string.repo_path_required, Toast.LENGTH_LONG)
.show();
return true;
}
mSyncthingService.getApi().editRepo(mRepo, true, getActivity());
getActivity().finish();
return true;
case R.id.delete:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.delete_repo_confirm)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mSyncthingService.getApi().deleteRepo(mRepo, getActivity());
getActivity().finish();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
return true;
case android.R.id.home:
getActivity().finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setSummary((String) o);
}
if (preference.equals(mRepoId)) {
mRepo.ID = (String) o;
repoUpdated();
return true;
}
else if (preference.equals(mDirectory)) {
mRepo.Directory = (String) o;
repoUpdated();
return true;
}
else if (preference.equals(mRepoMaster)) {
mRepo.ReadOnly = (Boolean) o;
repoUpdated();
return true;
}
else if (preference.getKey().equals(KEY_NODE_SHARED)) {
ExtendedCheckBoxPreference pref = (ExtendedCheckBoxPreference) preference;
RestApi.Node node = (RestApi.Node) pref.getObject();
if ((Boolean) o) {
mRepo.Nodes.add(node);
}
else {
for (RestApi.Node n : mRepo.Nodes) {
if (n.NodeID.equals(node.NodeID)) {
mRepo.Nodes.remove(n);
}
}
}
repoUpdated();
return true;
}
else if (preference.equals(mVersioning)) {
mVersioningKeep.setEnabled((Boolean) o);
if ((Boolean) o) {
RestApi.SimpleVersioning v = new RestApi.SimpleVersioning();
mRepo.Versioning = v;
v.setParams(5);
mVersioningKeep.setText("5");
mVersioningKeep.setSummary("5");
}
else {
mRepo.Versioning = new RestApi.Versioning();
}
repoUpdated();
return true;
}
else if (preference.equals(mVersioningKeep)) {
((RestApi.SimpleVersioning) mRepo.Versioning)
.setParams(Integer.parseInt((String) o));
repoUpdated();
return true;
}
if (preference.equals(mRepoId)) {
mRepo.ID = (String) o;
repoUpdated();
return true;
} else if (preference.equals(mDirectory)) {
mRepo.Directory = (String) o;
repoUpdated();
return true;
} else if (preference.equals(mRepoMaster)) {
mRepo.ReadOnly = (Boolean) o;
repoUpdated();
return true;
} else if (preference.getKey().equals(KEY_NODE_SHARED)) {
ExtendedCheckBoxPreference pref = (ExtendedCheckBoxPreference) preference;
RestApi.Node node = (RestApi.Node) pref.getObject();
if ((Boolean) o) {
mRepo.Nodes.add(node);
} else {
for (RestApi.Node n : mRepo.Nodes) {
if (n.NodeID.equals(node.NodeID)) {
mRepo.Nodes.remove(n);
}
}
}
repoUpdated();
return true;
} else if (preference.equals(mVersioning)) {
mVersioningKeep.setEnabled((Boolean) o);
if ((Boolean) o) {
RestApi.SimpleVersioning v = new RestApi.SimpleVersioning();
mRepo.Versioning = v;
v.setParams(5);
mVersioningKeep.setText("5");
mVersioningKeep.setSummary("5");
} else {
mRepo.Versioning = new RestApi.Versioning();
}
repoUpdated();
return true;
} else if (preference.equals(mVersioningKeep)) {
((RestApi.SimpleVersioning) mRepo.Versioning)
.setParams(Integer.parseInt((String) o));
repoUpdated();
return true;
}
return false;
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDirectory)) {
Intent intent = new Intent(getActivity(), FolderPickerActivity.class)
.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY,
(mRepo.Directory.length() != 0)
? mRepo.Directory
: Environment.getExternalStorageDirectory().getAbsolutePath());
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
}
else if (preference.equals(mNodes) && mSyncthingService.getApi().getNodes().isEmpty()) {
Toast.makeText(getActivity(), R.string.no_nodes, Toast.LENGTH_SHORT)
.show();
}
return false;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference.equals(mDirectory)) {
Intent intent = new Intent(getActivity(), FolderPickerActivity.class)
.putExtra(FolderPickerActivity.EXTRA_INITIAL_DIRECTORY,
(mRepo.Directory.length() != 0)
? mRepo.Directory
: Environment.getExternalStorageDirectory().getAbsolutePath()
);
startActivityForResult(intent, DIRECTORY_REQUEST_CODE);
} else if (preference.equals(mNodes) && mSyncthingService.getApi().getNodes().isEmpty()) {
Toast.makeText(getActivity(), R.string.no_nodes, Toast.LENGTH_SHORT)
.show();
}
return false;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
mRepo.Directory = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
mDirectory.setSummary(mRepo.Directory);
repoUpdated();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == DIRECTORY_REQUEST_CODE) {
mRepo.Directory = data.getStringExtra(FolderPickerActivity.EXTRA_RESULT_DIRECTORY);
mDirectory.setSummary(mRepo.Directory);
repoUpdated();
}
}
private void repoUpdated() {
if (!mIsCreate) {
mSyncthingService.getApi().editRepo(mRepo, false, getActivity());
}
}
private void repoUpdated() {
if (!mIsCreate) {
mSyncthingService.getApi().editRepo(mRepo, false, getActivity());
}
}
}

View file

@ -19,80 +19,79 @@ import java.util.TimerTask;
* Displays a list of all existing repositories.
*/
public class ReposFragment extends ListFragment implements SyncthingService.OnApiChangeListener,
AdapterView.OnItemClickListener {
AdapterView.OnItemClickListener {
private ReposAdapter mAdapter;
private ReposAdapter mAdapter;
private Timer mTimer;
private Timer mTimer;
@Override
public void onResume() {
super.onResume();
setListShown(true);
}
@Override
public void onResume() {
super.onResume();
setListShown(true);
}
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE)
return;
@Override
public void onApiChange(SyncthingService.State currentState) {
if (currentState != SyncthingService.State.ACTIVE)
return;
initAdapter();
}
initAdapter();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initAdapter();
}
initAdapter();
}
private void initAdapter() {
MainActivity activity = (MainActivity) getActivity();
if (activity == null || activity.getApi() == null)
return;
private void initAdapter() {
MainActivity activity = (MainActivity) getActivity();
if (activity == null || activity.getApi() == null)
return;
mAdapter = new ReposAdapter(activity);
mAdapter.add(activity.getApi().getRepos());
setListAdapter(mAdapter);
setEmptyText(getString(R.string.repositories_list_empty));
getListView().setOnItemClickListener(this);
}
mAdapter = new ReposAdapter(activity);
mAdapter.add(activity.getApi().getRepos());
setListAdapter(mAdapter);
setEmptyText(getString(R.string.repositories_list_empty));
getListView().setOnItemClickListener(this);
}
private void updateList() {
if (mAdapter == null || getView() == null)
return;
private void updateList() {
if (mAdapter == null || getView() == null)
return;
MainActivity activity = (MainActivity) getActivity();
mAdapter.updateModel(activity.getApi(), getListView());
}
MainActivity activity = (MainActivity) getActivity();
mAdapter.updateModel(activity.getApi(), getListView());
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateList();
}
if (isVisibleToUser) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
updateList();
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
}
else if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
}, 0, SyncthingService.GUI_UPDATE_INTERVAL);
} else if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
.putExtra(RepoSettingsFragment.EXTRA_REPO_ID, mAdapter.getItem(i).ID);
startActivity(intent);
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.setAction(SettingsActivity.ACTION_REPO_SETTINGS_FRAGMENT)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
.putExtra(RepoSettingsFragment.EXTRA_REPO_ID, mAdapter.getItem(i).ID);
startActivity(intent);
}
}

View file

@ -16,139 +16,136 @@ import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
public class SettingsFragment extends PreferenceFragment
implements SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener {
implements SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener {
private static final String SYNCTHING_OPTIONS_KEY = "syncthing_options";
private static final String SYNCTHING_OPTIONS_KEY = "syncthing_options";
private static final String SYNCTHING_GUI_KEY = "syncthing_gui";
private static final String SYNCTHING_GUI_KEY = "syncthing_gui";
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
private CheckBoxPreference mStopNotCharging;
private CheckBoxPreference mStopNotCharging;
private CheckBoxPreference mStopMobileData;
private CheckBoxPreference mStopMobileData;
private Preference mVersion;
private Preference mVersion;
private PreferenceScreen mOptionsScreen;
private PreferenceScreen mOptionsScreen;
private PreferenceScreen mGuiScreen;
private PreferenceScreen mGuiScreen;
private SyncthingService mSyncthingService;
private SyncthingService mSyncthingService;
@Override
public void onApiChange(SyncthingService.State currentState) {
mOptionsScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
mGuiScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
@Override
public void onApiChange(SyncthingService.State currentState) {
mOptionsScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
mGuiScreen.setEnabled(currentState == SyncthingService.State.ACTIVE);
mVersion.setSummary(mSyncthingService.getApi().getVersion());
mVersion.setSummary(mSyncthingService.getApi().getVersion());
if (currentState == SyncthingService.State.ACTIVE) {
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
Preference pref = mOptionsScreen.getPreference(i);
pref.setOnPreferenceChangeListener(SettingsFragment.this);
String value = mSyncthingService.getApi()
.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
applyPreference(pref, value);
}
if (currentState == SyncthingService.State.ACTIVE) {
for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) {
Preference pref = mOptionsScreen.getPreference(i);
pref.setOnPreferenceChangeListener(SettingsFragment.this);
String value = mSyncthingService.getApi()
.getValue(RestApi.TYPE_OPTIONS, pref.getKey());
applyPreference(pref, value);
}
for (int i = 0; i < mGuiScreen.getPreferenceCount(); i++) {
Preference pref = mGuiScreen.getPreference(i);
pref.setOnPreferenceChangeListener(SettingsFragment.this);
String value = mSyncthingService.getApi()
.getValue(RestApi.TYPE_GUI, pref.getKey());
applyPreference(pref, value);
}
}
}
for (int i = 0; i < mGuiScreen.getPreferenceCount(); i++) {
Preference pref = mGuiScreen.getPreference(i);
pref.setOnPreferenceChangeListener(SettingsFragment.this);
String value = mSyncthingService.getApi()
.getValue(RestApi.TYPE_GUI, pref.getKey());
applyPreference(pref, value);
}
}
}
/**
* Applies the given value to the preference.
*
* If pref is an EditTextPreference, setText is used and the value shown as summary. If pref is
* a CheckBoxPreference, setChecked is used (by parsing value as Boolean).
*/
private void applyPreference(Preference pref, String value) {
if (pref instanceof EditTextPreference) {
((EditTextPreference) pref).setText(value);
pref.setSummary(value);
}
else if (pref instanceof CheckBoxPreference) {
((CheckBoxPreference) pref).setChecked(Boolean.parseBoolean(value));
}
}
/**
* Applies the given value to the preference.
* <p/>
* If pref is an EditTextPreference, setText is used and the value shown as summary. If pref is
* a CheckBoxPreference, setChecked is used (by parsing value as Boolean).
*/
private void applyPreference(Preference pref, String value) {
if (pref instanceof EditTextPreference) {
((EditTextPreference) pref).setText(value);
pref.setSummary(value);
} else if (pref instanceof CheckBoxPreference) {
((CheckBoxPreference) pref).setChecked(Boolean.parseBoolean(value));
}
}
/**
* Loads layout, sets version from Rest API.
*
* Manual target API as we manually check if ActionBar is available (for ActionBar back button).
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
/**
* Loads layout, sets version from Rest API.
* <p/>
* Manual target API as we manually check if ActionBar is available (for ActionBar back button).
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
addPreferencesFromResource(R.xml.app_settings);
PreferenceScreen screen = getPreferenceScreen();
mStopNotCharging = (CheckBoxPreference) findPreference("stop_sync_on_mobile_data");
mStopNotCharging.setOnPreferenceChangeListener(this);
mStopMobileData = (CheckBoxPreference) findPreference("stop_sync_while_not_charging");
mStopMobileData.setOnPreferenceChangeListener(this);
mVersion = screen.findPreference(SYNCTHING_VERSION_KEY);
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY);
mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY);
}
addPreferencesFromResource(R.xml.app_settings);
PreferenceScreen screen = getPreferenceScreen();
mStopNotCharging = (CheckBoxPreference) findPreference("stop_sync_on_mobile_data");
mStopNotCharging.setOnPreferenceChangeListener(this);
mStopMobileData = (CheckBoxPreference) findPreference("stop_sync_while_not_charging");
mStopMobileData.setOnPreferenceChangeListener(this);
mVersion = screen.findPreference(SYNCTHING_VERSION_KEY);
mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY);
mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
}
/**
* Handles ActionBar back selected.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(getActivity());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Handles ActionBar back selected.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(getActivity());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Sends the updated value to {@link }RestApi}, and sets it as the summary
* for EditTextPreference.
*/
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
String value = (String) o;
preference.setSummary(value);
EditTextPreference etp = (EditTextPreference) preference;
if (etp.getEditText().getInputType() == InputType.TYPE_CLASS_NUMBER) {
o = Integer.parseInt((String) o);
}
}
/**
* Sends the updated value to {@link }RestApi}, and sets it as the summary
* for EditTextPreference.
*/
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
if (preference instanceof EditTextPreference) {
String value = (String) o;
preference.setSummary(value);
EditTextPreference etp = (EditTextPreference) preference;
if (etp.getEditText().getInputType() == InputType.TYPE_CLASS_NUMBER) {
o = Integer.parseInt((String) o);
}
}
if (preference.equals(mStopNotCharging) || preference.equals(mStopMobileData)) {
mSyncthingService.updateState();
}
else if (mOptionsScreen.findPreference(preference.getKey()) != null) {
mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o,
preference.getKey().equals("ListenAddress"), getActivity());
}
else if (mGuiScreen.findPreference(preference.getKey()) != null) {
mSyncthingService.getApi().setValue(
RestApi.TYPE_GUI, preference.getKey(), o, false, getActivity());
}
if (preference.equals(mStopNotCharging) || preference.equals(mStopMobileData)) {
mSyncthingService.updateState();
} else if (mOptionsScreen.findPreference(preference.getKey()) != null) {
mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o,
preference.getKey().equals("ListenAddress"), getActivity());
} else if (mGuiScreen.findPreference(preference.getKey()) != null) {
mSyncthingService.getApi().setValue(
RestApi.TYPE_GUI, preference.getKey(), o, false, getActivity());
}
return true;
}
return true;
}
}

View file

@ -9,12 +9,12 @@ import android.content.Intent;
*/
public class BatteryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction());
Intent i = new Intent(context, SyncthingService.class);
i.putExtra(DeviceStateHolder.EXTRA_IS_CHARGING, isCharging);
context.startService(i);
}
@Override
public void onReceive(Context context, Intent intent) {
boolean isCharging = Intent.ACTION_POWER_CONNECTED.equals(intent.getAction());
Intent i = new Intent(context, SyncthingService.class);
i.putExtra(DeviceStateHolder.EXTRA_IS_CHARGING, isCharging);
context.startService(i);
}
}

View file

@ -8,7 +8,7 @@ import android.os.BatteryManager;
/**
* Holds information about the current wifi and charging state of the device.
*
* <p/>
* This information is actively read on construction, and then updated from intents that are passed
* to {@link #update(android.content.Intent)}.
*/
@ -17,30 +17,30 @@ public class DeviceStateHolder extends BroadcastReceiver {
/**
* Intent extra containing a boolean saying whether wifi is connected or not.
*/
public static final String EXTRA_HAS_WIFI = "has_wifi";
public static final String EXTRA_HAS_WIFI = "has_wifi";
/**
* Intent extra containging a boolean saying whether the device is
* charging or not (any power source).
*/
public static final String EXTRA_IS_CHARGING = "is_charging";
public static final String EXTRA_IS_CHARGING = "is_charging";
private boolean mIsInitialized = false;
private boolean mIsInitialized = false;
private boolean mIsWifiConnected = false;
private boolean mIsWifiConnected = false;
private boolean mIsCharging = false;
private boolean mIsCharging = false;
private SyncthingService mService;
public DeviceStateHolder(SyncthingService service) {
public DeviceStateHolder(SyncthingService service) {
mService = service;
ConnectivityManager cm = (ConnectivityManager)
ConnectivityManager cm = (ConnectivityManager)
mService.getSystemService(Context.CONNECTIVITY_SERVICE);
mIsWifiConnected = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
}
mIsWifiConnected = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
}
@Override
@Override
public void onReceive(Context context, Intent intent) {
context.unregisterReceiver(this);
int status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
@ -48,16 +48,16 @@ public class DeviceStateHolder extends BroadcastReceiver {
mIsInitialized = true;
}
public boolean isCharging() {
return mIsCharging;
}
public boolean isCharging() {
return mIsCharging;
}
public boolean isWifiConnected() {
public boolean isWifiConnected() {
return mIsWifiConnected;
}
public void update(Intent intent) {
mIsWifiConnected = intent.getBooleanExtra(EXTRA_HAS_WIFI, mIsWifiConnected);
mIsCharging = intent.getBooleanExtra(EXTRA_IS_CHARGING, mIsCharging);
}
public void update(Intent intent) {
mIsWifiConnected = intent.getBooleanExtra(EXTRA_HAS_WIFI, mIsWifiConnected);
mIsCharging = intent.getBooleanExtra(EXTRA_IS_CHARGING, mIsCharging);
}
}

View file

@ -25,60 +25,59 @@ import java.util.LinkedList;
*/
public class GetTask extends AsyncTask<String, Void, String> {
private static final String TAG = "GetTask";
private static final String TAG = "GetTask";
public static final String URI_CONFIG = "/rest/config";
public static final String URI_CONFIG = "/rest/config";
public static final String URI_VERSION = "/rest/version";
public static final String URI_VERSION = "/rest/version";
public static final String URI_SYSTEM = "/rest/system";
public static final String URI_SYSTEM = "/rest/system";
public static final String URI_CONNECTIONS = "/rest/connections";
public static final String URI_CONNECTIONS = "/rest/connections";
public static final String URI_MODEL = "/rest/model";
public static final String URI_MODEL = "/rest/model";
public static final String URI_NODEID = "/rest/nodeid";
public static final String URI_NODEID = "/rest/nodeid";
/**
* params[0] Syncthing hostname
* params[1] URI to call
* params[2] Syncthing API key
* params[3] optional parameter key
* params[4] optional parameter value
*/
@Override
protected String doInBackground(String... params) {
String fullUri = params[0] + params[1];
HttpClient httpclient = new DefaultHttpClient();
if (params.length == 5) {
LinkedList<NameValuePair> urlParams = new LinkedList<>();
urlParams.add(new BasicNameValuePair(params[3], params[4]));
fullUri += "?" + URLEncodedUtils.format(urlParams, "utf-8");
}
HttpGet get = new HttpGet(fullUri);
get.addHeader(new BasicHeader("X-API-Key", params[2]));
/**
* params[0] Syncthing hostname
* params[1] URI to call
* params[2] Syncthing API key
* params[3] optional parameter key
* params[4] optional parameter value
*/
@Override
protected String doInBackground(String... params) {
String fullUri = params[0] + params[1];
HttpClient httpclient = new DefaultHttpClient();
if (params.length == 5) {
LinkedList<NameValuePair> urlParams = new LinkedList<>();
urlParams.add(new BasicNameValuePair(params[3], params[4]));
fullUri += "?" + URLEncodedUtils.format(urlParams, "utf-8");
}
HttpGet get = new HttpGet(fullUri);
get.addHeader(new BasicHeader("X-API-Key", params[2]));
try {
HttpResponse response = httpclient.execute(get);
HttpEntity entity = response.getEntity();
try {
HttpResponse response = httpclient.execute(get);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream is = entity.getContent();
if (entity != null) {
InputStream is = entity.getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
String result = "";
while((line = br.readLine()) != null) {
result += line;
}
br.close();
return result;
}
}
catch (IOException e) {
Log.w(TAG, "Failed to call Rest API at " + fullUri, e);
}
return null;
}
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
br.close();
return result;
}
} catch (IOException e) {
Log.w(TAG, "Failed to call Rest API at " + fullUri, e);
}
return null;
}
}

View file

@ -11,17 +11,17 @@ import android.net.NetworkInfo;
*/
public class NetworkReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean isWifiConnected = (wifiInfo != null && wifiInfo.isConnected()) ||
activeNetworkInfo == null;
Intent i = new Intent(context, SyncthingService.class);
i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, isWifiConnected);
context.startService(i);
}
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean isWifiConnected = (wifiInfo != null && wifiInfo.isConnected()) ||
activeNetworkInfo == null;
Intent i = new Intent(context, SyncthingService.class);
i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, isWifiConnected);
context.startService(i);
}
}

View file

@ -16,37 +16,36 @@ import java.io.IOException;
*/
public class PostTask extends AsyncTask<String, Void, Void> {
private static final String TAG = "PostTask";
private static final String TAG = "PostTask";
public static final String URI_CONFIG = "/rest/config";
public static final String URI_CONFIG = "/rest/config";
public static final String URI_RESTART = "/rest/restart";
public static final String URI_RESTART = "/rest/restart";
public static final String URI_SHUTDOWN = "/rest/shutdown";
public static final String URI_SHUTDOWN = "/rest/shutdown";
/**
* params[0] Syncthing hostname
* params[1] URI to call
* params[2] Syncthing API key
* params[3] The request content (optional)
*/
@Override
protected Void doInBackground(String... params) {
String fullUri = params[0] + params[1];
HttpClient httpclient = new DefaultHttpClient();
HttpPost post = new HttpPost(fullUri);
post.addHeader(new BasicHeader("X-API-Key", params[2]));
/**
* params[0] Syncthing hostname
* params[1] URI to call
* params[2] Syncthing API key
* params[3] The request content (optional)
*/
@Override
protected Void doInBackground(String... params) {
String fullUri = params[0] + params[1];
HttpClient httpclient = new DefaultHttpClient();
HttpPost post = new HttpPost(fullUri);
post.addHeader(new BasicHeader("X-API-Key", params[2]));
try {
if (params.length > 3) {
post.setEntity(new StringEntity(params[3]));
}
httpclient.execute(post);
}
catch (IOException e) {
Log.w(TAG, "Failed to call Rest API at " + fullUri, e);
}
return null;
}
try {
if (params.length > 3) {
post.setEntity(new StringEntity(params[3]));
}
httpclient.execute(post);
} catch (IOException e) {
Log.w(TAG, "Failed to call Rest API at " + fullUri, e);
}
return null;
}
}

View file

@ -43,7 +43,7 @@ public class SyncthingRunnable implements Runnable {
DataOutputStream dos = null;
int ret = 1;
Process process = null;
try {
try {
process = Runtime.getRuntime().exec("sh");
dos = new DataOutputStream(process.getOutputStream());
// Set home directory to data folder for syncthing to use.
@ -57,22 +57,19 @@ public class SyncthingRunnable implements Runnable {
log(process.getInputStream());
ret = process.waitFor();
}
catch(IOException | InterruptedException e) {
} catch (IOException | InterruptedException e) {
Log.e(TAG, "Failed to execute syncthing binary or read output", e);
}
finally {
} finally {
try {
dos.close();
}
catch (IOException e) {
} catch (IOException e) {
Log.w(TAG, "Failed to close shell stream", e);
}
process.destroy();
final int retVal = ret;
if (ret != 0) {
Log.w(TAG_NATIVE, "Syncthing binary crashed with error code " + Integer.toString(retVal));
postCrashDialog(retVal);
Log.w(TAG_NATIVE, "Syncthing binary crashed with error code " + Integer.toString(retVal));
postCrashDialog(retVal);
}
}
}
@ -95,7 +92,8 @@ public class SyncthingRunnable implements Runnable {
int i) {
System.exit(0);
}
})
}
)
.create();
dialog.getWindow()
.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
@ -120,8 +118,7 @@ public class SyncthingRunnable implements Runnable {
while ((line = br.readLine()) != null) {
Log.i(TAG_NATIVE, line);
}
}
catch (IOException e) {
} catch (IOException e) {
// NOTE: This is sometimes called on shutdown, as
// Process.destroy() closes the stream.
Log.w(TAG, "Failed to read syncthing command line output", e);

View file

@ -40,56 +40,56 @@ import java.util.Iterator;
*/
public class SyncthingService extends Service {
private static final String TAG = "SyncthingService";
private static final String TAG = "SyncthingService";
private static final int NOTIFICATION_RUNNING = 1;
private static final int NOTIFICATION_RUNNING = 1;
/**
* Intent action to perform a syncthing restart.
*/
public static final String ACTION_RESTART = "restart";
/**
* Intent action to perform a syncthing restart.
*/
public static final String ACTION_RESTART = "restart";
/**
* Interval in ms at which the GUI is updated (eg {@link }LocalNodeInfoFragment}).
*/
public static final int GUI_UPDATE_INTERVAL = 1000;
/**
* Interval in ms at which the GUI is updated (eg {@link }LocalNodeInfoFragment}).
*/
public static final int GUI_UPDATE_INTERVAL = 1000;
/**
* Interval in ms, at which connections to the web gui are performed on first start
* to find out if it's online.
*/
private static final long WEB_GUI_POLL_INTERVAL = 100;
/**
* Interval in ms, at which connections to the web gui are performed on first start
* to find out if it's online.
*/
private static final long WEB_GUI_POLL_INTERVAL = 100;
/**
* Name of the public key file in the data directory.
*/
private static final String PUBLIC_KEY_FILE = "cert.pem";
/**
* Name of the public key file in the data directory.
*/
private static final String PUBLIC_KEY_FILE = "cert.pem";
/**
* Name of the private key file in the data directory.
*/
private static final String PRIVATE_KEY_FILE = "key.pem";
/**
* Name of the private key file in the data directory.
*/
private static final String PRIVATE_KEY_FILE = "key.pem";
private RestApi mApi;
private RestApi mApi;
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
/**
* Callback for when the Syncthing web interface becomes first available after service start.
*/
public interface OnWebGuiAvailableListener {
public void onWebGuiAvailable();
}
/**
* Callback for when the Syncthing web interface becomes first available after service start.
*/
public interface OnWebGuiAvailableListener {
public void onWebGuiAvailable();
}
private final HashSet<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
new HashSet<>();
private final HashSet<OnWebGuiAvailableListener> mOnWebGuiAvailableListeners =
new HashSet<>();
public interface OnApiChangeListener {
public void onApiChange(State currentState);
}
public interface OnApiChangeListener {
public void onApiChange(State currentState);
}
private final HashSet<WeakReference<OnApiChangeListener>> mOnApiChangeListeners =
new HashSet<>();
private final HashSet<WeakReference<OnApiChangeListener>> mOnApiChangeListeners =
new HashSet<>();
/**
* INIT: Service is starting up and initializing.
@ -97,334 +97,328 @@ public class SyncthingService extends Service {
* ACTIVE: Syncthing binary is up and running.
* DISABLED: Syncthing binary is stopped according to user preferences.
*/
public enum State {
public enum State {
INIT,
STARTING,
ACTIVE,
DISABLED
}
STARTING,
ACTIVE,
DISABLED
}
private State mCurrentState = State.INIT;
private State mCurrentState = State.INIT;
/**
* True if a stop was requested while syncthing is starting, in that case, perform stop in
* {@link PollWebGuiAvailableTask}.
*/
private boolean mStopScheduled = false;
/**
* True if a stop was requested while syncthing is starting, in that case, perform stop in
* {@link PollWebGuiAvailableTask}.
*/
private boolean mStopScheduled = false;
private DeviceStateHolder mDeviceStateHolder;
private DeviceStateHolder mDeviceStateHolder;
/**
/**
* Handles intents, either {@link #ACTION_RESTART}, or intents having
* {@link DeviceStateHolder.EXTRA_HAS_WIFI} or {@link DeviceStateHolder.EXTRA_IS_CHARGING}
* (which are handled by {@link DeviceStateHolder}.
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Just catch the empty intent and return.
if (intent == null) {
}
else if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
new PostTask() {
@Override
protected void onPostExecute(Void aVoid) {
ConfigXml config = new ConfigXml(SyncthingService.this);
mApi = new RestApi(SyncthingService.this,
config.getWebGuiUrl(), config.getApiKey());
mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTask().execute();
}
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
}
else if (mCurrentState != State.INIT) {
mDeviceStateHolder.update(intent);
updateState();
}
return START_STICKY;
}
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Just catch the empty intent and return.
if (intent == null) {
} else if (ACTION_RESTART.equals(intent.getAction()) && mCurrentState == State.ACTIVE) {
new PostTask() {
@Override
protected void onPostExecute(Void aVoid) {
ConfigXml config = new ConfigXml(SyncthingService.this);
mApi = new RestApi(SyncthingService.this,
config.getWebGuiUrl(), config.getApiKey());
mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTask().execute();
}
}.execute(mApi.getUrl(), PostTask.URI_RESTART, mApi.getApiKey());
} else if (mCurrentState != State.INIT) {
mDeviceStateHolder.update(intent);
updateState();
}
return START_STICKY;
}
/**
* Checks according to preferences and charging/wifi state, whether syncthing should be enabled
/**
* Checks according to preferences and charging/wifi state, whether syncthing should be enabled
* or not.
*
* <p/>
* Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is
* called.
*/
public void updateState() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean prefStopMobileData = prefs.getBoolean("stop_sync_on_mobile_data", true);
boolean prefStopNotCharging = prefs.getBoolean("stop_sync_while_not_charging", true);
*/
public void updateState() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean prefStopMobileData = prefs.getBoolean("stop_sync_on_mobile_data", true);
boolean prefStopNotCharging = prefs.getBoolean("stop_sync_while_not_charging", true);
// Start syncthing.
if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
(mDeviceStateHolder.isWifiConnected() || !prefStopMobileData)) {
if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) {
mStopScheduled = false;
return;
}
// Start syncthing.
if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
(mDeviceStateHolder.isWifiConnected() || !prefStopMobileData)) {
if (mCurrentState == State.ACTIVE || mCurrentState == State.STARTING) {
mStopScheduled = false;
return;
}
mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTask().execute();
new Thread(new SyncthingRunnable(this)).start();
}
// Stop syncthing.
else {
if (mCurrentState == State.DISABLED)
return;
mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTask().execute();
new Thread(new SyncthingRunnable(this)).start();
}
// Stop syncthing.
else {
if (mCurrentState == State.DISABLED)
return;
mCurrentState = State.DISABLED;
mCurrentState = State.DISABLED;
// Syncthing is currently started, perform the stop later.
if (mCurrentState == State.STARTING) {
mStopScheduled = true;
} else if (mApi != null) {
mApi.shutdown();
}
}
onApiChange();
}
// Syncthing is currently started, perform the stop later.
if (mCurrentState == State.STARTING) {
mStopScheduled = true;
} else if (mApi != null) {
mApi.shutdown();
}
}
onApiChange();
}
/**
* Polls SYNCTHING_URL until it returns HTTP status OK, then calls all listeners
* in mOnWebGuiAvailableListeners and clears it.
*/
private class PollWebGuiAvailableTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
int status = 0;
HttpClient httpclient = new DefaultHttpClient();
HttpHead head = new HttpHead(mApi.getUrl());
do {
try {
Thread.sleep(WEB_GUI_POLL_INTERVAL);
HttpResponse response = httpclient.execute(head);
// NOTE: status is not really needed, as HttpHostConnectException is thrown
// earlier.
status = response.getStatusLine().getStatusCode();
}
catch (HttpHostConnectException e) {
// We catch this in every call, as long as the service is not online,
// so we ignore and continue.
}
catch (IOException e) {
Log.w(TAG, "Failed to poll for web interface", e);
}
catch (InterruptedException e) {
Log.w(TAG, "Failed to poll for web interface", e);
}
} while(status != HttpStatus.SC_OK);
return null;
}
/**
* Polls SYNCTHING_URL until it returns HTTP status OK, then calls all listeners
* in mOnWebGuiAvailableListeners and clears it.
*/
private class PollWebGuiAvailableTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
int status = 0;
HttpClient httpclient = new DefaultHttpClient();
HttpHead head = new HttpHead(mApi.getUrl());
do {
try {
Thread.sleep(WEB_GUI_POLL_INTERVAL);
HttpResponse response = httpclient.execute(head);
// NOTE: status is not really needed, as HttpHostConnectException is thrown
// earlier.
status = response.getStatusLine().getStatusCode();
} catch (HttpHostConnectException e) {
// We catch this in every call, as long as the service is not online,
// so we ignore and continue.
} catch (IOException e) {
Log.w(TAG, "Failed to poll for web interface", e);
} catch (InterruptedException e) {
Log.w(TAG, "Failed to poll for web interface", e);
}
} while (status != HttpStatus.SC_OK);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if (mStopScheduled) {
mCurrentState = State.DISABLED;
onApiChange();
mApi.shutdown();
mStopScheduled = false;
return;
}
Log.i(TAG, "Web GUI has come online at " + mApi.getUrl());
mCurrentState = State.ACTIVE;
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
listener.onWebGuiAvailable();
}
mOnWebGuiAvailableListeners.clear();
}
}
@Override
protected void onPostExecute(Void aVoid) {
if (mStopScheduled) {
mCurrentState = State.DISABLED;
onApiChange();
mApi.shutdown();
mStopScheduled = false;
return;
}
Log.i(TAG, "Web GUI has come online at " + mApi.getUrl());
mCurrentState = State.ACTIVE;
for (OnWebGuiAvailableListener listener : mOnWebGuiAvailableListeners) {
listener.onWebGuiAvailable();
}
mOnWebGuiAvailableListeners.clear();
}
}
/**
* Move config file, keys, and index files to "official" folder
*
* Intended to bring the file locations in older installs in line with
* newer versions.
*/
private void moveConfigFiles() {
FilenameFilter idxFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".idx.gz");
}
};
/**
* Move config file, keys, and index files to "official" folder
* <p/>
* Intended to bring the file locations in older installs in line with
* newer versions.
*/
private void moveConfigFiles() {
FilenameFilter idxFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".idx.gz");
}
};
if (new File(getApplicationInfo().dataDir, PUBLIC_KEY_FILE).exists()) {
File publicKey = new File(getApplicationInfo().dataDir, PUBLIC_KEY_FILE);
publicKey.renameTo(new File(getFilesDir(), PUBLIC_KEY_FILE));
File privateKey = new File(getApplicationInfo().dataDir, PRIVATE_KEY_FILE);
privateKey.renameTo(new File(getFilesDir(), PRIVATE_KEY_FILE));
File config = new File(getApplicationInfo().dataDir, ConfigXml.CONFIG_FILE);
config.renameTo(new File(getFilesDir(), ConfigXml.CONFIG_FILE));
if (new File(getApplicationInfo().dataDir, PUBLIC_KEY_FILE).exists()) {
File publicKey = new File(getApplicationInfo().dataDir, PUBLIC_KEY_FILE);
publicKey.renameTo(new File(getFilesDir(), PUBLIC_KEY_FILE));
File privateKey = new File(getApplicationInfo().dataDir, PRIVATE_KEY_FILE);
privateKey.renameTo(new File(getFilesDir(), PRIVATE_KEY_FILE));
File config = new File(getApplicationInfo().dataDir, ConfigXml.CONFIG_FILE);
config.renameTo(new File(getFilesDir(), ConfigXml.CONFIG_FILE));
File oldStorageDir = new File(getApplicationInfo().dataDir);
File[] files = oldStorageDir.listFiles(idxFilter);
for (File file : files) {
if (file.isFile()) {
file.renameTo(new File(getFilesDir(), file.getName()));
}
}
}
}
File oldStorageDir = new File(getApplicationInfo().dataDir);
File[] files = oldStorageDir.listFiles(idxFilter);
for (File file : files) {
if (file.isFile()) {
file.renameTo(new File(getFilesDir(), file.getName()));
}
}
}
}
/**
* Creates notification, starts native binary.
*/
@Override
public void onCreate() {
PendingIntent pi = PendingIntent.getActivity(
this, 0, new Intent(this, MainActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
Notification n = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pi)
.setPriority(NotificationCompat.PRIORITY_MIN)
.build();
n.flags |= Notification.FLAG_ONGOING_EVENT;
startForeground(NOTIFICATION_RUNNING, n);
/**
* Creates notification, starts native binary.
*/
@Override
public void onCreate() {
PendingIntent pi = PendingIntent.getActivity(
this, 0, new Intent(this, MainActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
Notification n = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pi)
.setPriority(NotificationCompat.PRIORITY_MIN)
.build();
n.flags |= Notification.FLAG_ONGOING_EVENT;
startForeground(NOTIFICATION_RUNNING, n);
mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this);
registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
new StartupTask().execute();
}
new StartupTask().execute();
}
/**
* Sets up the initial configuration, updates the config when coming from an old
* version, and reads syncthing URL and API key (these are passed internally as
* {@code Pair<String, String>}.
*/
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
@Override
protected Pair<String, String> doInBackground(Void... voids) {
moveConfigFiles();
ConfigXml config = new ConfigXml(SyncthingService.this);
config.updateIfNeeded();
/**
* Sets up the initial configuration, updates the config when coming from an old
* version, and reads syncthing URL and API key (these are passed internally as
* {@code Pair<String, String>}.
*/
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> {
@Override
protected Pair<String, String> doInBackground(Void... voids) {
moveConfigFiles();
ConfigXml config = new ConfigXml(SyncthingService.this);
config.updateIfNeeded();
if (isFirstStart()) {
Log.i(TAG, "App started for the first time. " +
"Copying default config, keys will be generated automatically");
config.createCameraRepo();
}
if (isFirstStart()) {
Log.i(TAG, "App started for the first time. " +
"Copying default config, keys will be generated automatically");
config.createCameraRepo();
}
return new Pair<>(config.getWebGuiUrl(), config.getApiKey());
}
return new Pair<>(config.getWebGuiUrl(), config.getApiKey());
}
@Override
protected void onPostExecute(Pair<String, String> urlAndKey) {
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second);
Log.i(TAG, "Web GUI will be available at " + mApi.getUrl());
@Override
protected void onPostExecute(Pair<String, String> urlAndKey) {
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second);
Log.i(TAG, "Web GUI will be available at " + mApi.getUrl());
// HACK: Make sure there is no syncthing binary left running from an improper
// shutdown (eg Play Store updateIfNeeded).
// NOTE: This will log an exception if syncthing is not actually running.
mApi.shutdown();
// HACK: Make sure there is no syncthing binary left running from an improper
// shutdown (eg Play Store updateIfNeeded).
// NOTE: This will log an exception if syncthing is not actually running.
mApi.shutdown();
updateState();
}
}
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* Stops the native binary.
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Shutting down service");
mApi.shutdown();
}
/**
* Stops the native binary.
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Shutting down service");
mApi.shutdown();
}
/**
* Register a listener for the web gui becoming available..
*
* If the web gui is already available, listener will be called immediately.
* Listeners are unregistered automatically after being called.
*/
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
if (mCurrentState == State.ACTIVE) {
listener.onWebGuiAvailable();
}
else {
mOnWebGuiAvailableListeners.add(listener);
}
}
/**
* Register a listener for the web gui becoming available..
* <p/>
* If the web gui is already available, listener will be called immediately.
* Listeners are unregistered automatically after being called.
*/
public void registerOnWebGuiAvailableListener(OnWebGuiAvailableListener listener) {
if (mCurrentState == State.ACTIVE) {
listener.onWebGuiAvailable();
} else {
mOnWebGuiAvailableListeners.add(listener);
}
}
/**
* Returns true if this service has not been started before (ie config.xml does not exist).
*
* This will return true until the public key file has been generated.
*/
public boolean isFirstStart() {
return !new File(getFilesDir(), PUBLIC_KEY_FILE).exists();
}
/**
* Returns true if this service has not been started before (ie config.xml does not exist).
* <p/>
* This will return true until the public key file has been generated.
*/
public boolean isFirstStart() {
return !new File(getFilesDir(), PUBLIC_KEY_FILE).exists();
}
public RestApi getApi() {
return mApi;
}
public RestApi getApi() {
return mApi;
}
/**
* Register a listener for the syncthing API state changing.
*
* The listener is called immediately with the current state, and again whenever the state
* changes.
*/
public void registerOnApiChangeListener(OnApiChangeListener listener) {
/**
* Register a listener for the syncthing API state changing.
* <p/>
* The listener is called immediately with the current state, and again whenever the state
* changes.
*/
public void registerOnApiChangeListener(OnApiChangeListener listener) {
// Make sure we don't send an invalid state or syncthing might shwow a "disabled" message
// when it's just starting up.
listener.onApiChange(mCurrentState);
mOnApiChangeListeners.add(new WeakReference<OnApiChangeListener>(listener));
}
listener.onApiChange(mCurrentState);
mOnApiChangeListeners.add(new WeakReference<OnApiChangeListener>(listener));
}
/**
* Called to notifiy listeners of an API change.
*
/**
* Called to notifiy listeners of an API change.
* <p/>
* Must only be called from SyncthingService or {@link RestApi}.
*/
public void onApiChange() {
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiChangeListeners.iterator();
*/
public void onApiChange() {
for (Iterator<WeakReference<OnApiChangeListener>> i = mOnApiChangeListeners.iterator();
i.hasNext(); ) {
WeakReference<OnApiChangeListener> listener = i.next();
if (listener.get() != null) {
listener.get().onApiChange(mCurrentState);
}
else {
i.remove();
}
}
}
WeakReference<OnApiChangeListener> listener = i.next();
if (listener.get() != null) {
listener.get().onApiChange(mCurrentState);
} else {
i.remove();
}
}
}
/**
* Dialog to be shown when attempting to start syncthing while it is disabled according
* to settings (because the device is not charging or wifi is disconnected).
*/
public static void showDisabledDialog(final Activity activity) {
new AlertDialog.Builder(activity)
/**
* Dialog to be shown when attempting to start syncthing while it is disabled according
* to settings (because the device is not charging or wifi is disconnected).
*/
public static void showDisabledDialog(final Activity activity) {
new AlertDialog.Builder(activity)
.setTitle(R.string.syncthing_disabled_title)
.setMessage(R.string.syncthing_disabled_message)
.setPositiveButton(R.string.syncthing_disabled_change_settings,
.setMessage(R.string.syncthing_disabled_message)
.setPositiveButton(R.string.syncthing_disabled_change_settings,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
activity.finish();
Intent intent = new Intent(activity, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT);
activity.startActivity(intent);
}
})
.setNegativeButton(R.string.exit,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
activity.finish();
}
}
)
.show()
.setCancelable(false);
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
activity.finish();
Intent intent = new Intent(activity, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS_FRAGMENT);
activity.startActivity(intent);
}
}
)
.setNegativeButton(R.string.exit,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
activity.finish();
}
}
)
.show()
.setCancelable(false);
}
}

View file

@ -4,14 +4,14 @@ import android.os.Binder;
public class SyncthingServiceBinder extends Binder {
private final SyncthingService mService;
private final SyncthingService mService;
public SyncthingServiceBinder(SyncthingService service) {
mService = service;
}
public SyncthingServiceBinder(SyncthingService service) {
mService = service;
}
public SyncthingService getService() {
return mService;
}
public SyncthingService getService() {
return mService;
}
}
}

View file

@ -28,184 +28,180 @@ import javax.xml.transform.stream.StreamResult;
/**
* Provides direct access to the config.xml file in the file system.
*
* <p/>
* This class should only be used if the syncthing API is not available (usually during startup).
*/
public class ConfigXml {
private static final String TAG = "ConfigXml";
private static final String TAG = "ConfigXml";
/**
* File in the config folder that contains configuration.
*/
public static final String CONFIG_FILE = "config.xml";
/**
* File in the config folder that contains configuration.
*/
public static final String CONFIG_FILE = "config.xml";
private File mConfigFile;
private File mConfigFile;
private Document mConfig;
private Document mConfig;
public ConfigXml(Context context) {
mConfigFile = new File(context.getFilesDir(), CONFIG_FILE);
if (!mConfigFile.exists()) {
copyDefaultConfig(context);
}
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
mConfig = db.parse(mConfigFile);
} catch (SAXException | ParserConfigurationException | IOException e) {
throw new RuntimeException("Failed to open config file", e);
}
}
public ConfigXml(Context context) {
mConfigFile = new File(context.getFilesDir(), CONFIG_FILE);
if (!mConfigFile.exists()) {
copyDefaultConfig(context);
}
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
mConfig = db.parse(mConfigFile);
} catch (SAXException | ParserConfigurationException | IOException e) {
throw new RuntimeException("Failed to open config file", e);
}
}
public String getWebGuiUrl() {
return "http://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent();
}
public String getWebGuiUrl() {
return "http://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent();
}
public String getApiKey() {
return getGuiElement().getElementsByTagName("apikey").item(0).getTextContent();
}
public String getApiKey() {
return getGuiElement().getElementsByTagName("apikey").item(0).getTextContent();
}
/**
* Updates the config file.
*
* Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is
* replaced with "194.126.249.5:22025" (as domain resolve is broken).
*
* Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository.
*/
@SuppressWarnings("SdCardPath")
public void updateIfNeeded() {
Log.i(TAG, "Checking for needed config updates");
boolean changed = false;
Element options = (Element) mConfig.getDocumentElement()
.getElementsByTagName("options").item(0);
Element gui = (Element) mConfig.getDocumentElement()
.getElementsByTagName("gui").item(0);
/**
* Updates the config file.
* <p/>
* Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is
* replaced with "194.126.249.5:22025" (as domain resolve is broken).
* <p/>
* Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository.
*/
@SuppressWarnings("SdCardPath")
public void updateIfNeeded() {
Log.i(TAG, "Checking for needed config updates");
boolean changed = false;
Element options = (Element) mConfig.getDocumentElement()
.getElementsByTagName("options").item(0);
Element gui = (Element) mConfig.getDocumentElement()
.getElementsByTagName("gui").item(0);
// Create an API key if it does not exist.
if (gui.getElementsByTagName("apikey").getLength() == 0) {
Log.i(TAG, "Initializing API key with random string");
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 20; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
Element apiKey = mConfig.createElement("apikey");
apiKey.setTextContent(sb.toString());
gui.appendChild(apiKey);
changed = true;
}
// Create an API key if it does not exist.
if (gui.getElementsByTagName("apikey").getLength() == 0) {
Log.i(TAG, "Initializing API key with random string");
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 20; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
Element apiKey = mConfig.createElement("apikey");
apiKey.setTextContent(sb.toString());
gui.appendChild(apiKey);
changed = true;
}
// Hardcode default globalAnnounceServer ip.
Element globalAnnounceServer = (Element)
options.getElementsByTagName("globalAnnounceServer").item(0);
if (globalAnnounceServer.getTextContent().equals("announce.syncthing.net:22025")) {
Log.i(TAG, "Replacing globalAnnounceServer host with ip");
globalAnnounceServer.setTextContent("194.126.249.5:22025");
changed = true;
}
// Hardcode default globalAnnounceServer ip.
Element globalAnnounceServer = (Element)
options.getElementsByTagName("globalAnnounceServer").item(0);
if (globalAnnounceServer.getTextContent().equals("announce.syncthing.net:22025")) {
Log.i(TAG, "Replacing globalAnnounceServer host with ip");
globalAnnounceServer.setTextContent("194.126.249.5:22025");
changed = true;
}
NodeList repos = mConfig.getDocumentElement().getElementsByTagName("repository");
for (int i = 0; i < repos.getLength(); i++) {
Element r = (Element) repos.item(i);
// Set ignorePerms attribute.
if (!r.hasAttribute("ignorePerms") ||
!Boolean.parseBoolean(r.getAttribute("ignorePerms"))) {
Log.i(TAG, "Set 'ignorePerms' on repository " + r.getAttribute("id"));
r.setAttribute("ignorePerms", Boolean.toString(true));
changed = true;
}
NodeList repos = mConfig.getDocumentElement().getElementsByTagName("repository");
for (int i = 0; i < repos.getLength(); i++) {
Element r = (Element) repos.item(i);
// Set ignorePerms attribute.
if (!r.hasAttribute("ignorePerms") ||
!Boolean.parseBoolean(r.getAttribute("ignorePerms"))) {
Log.i(TAG, "Set 'ignorePerms' on repository " + r.getAttribute("id"));
r.setAttribute("ignorePerms", Boolean.toString(true));
changed = true;
}
// Replace /sdcard/ in repository path with proper path.
String dir = r.getAttribute("directory");
if (dir.startsWith("/sdcard")) {
String newDir = dir.replace("/sdcard",
Environment.getExternalStorageDirectory().getAbsolutePath());
r.setAttribute("directory", newDir);
changed = true;
}
}
// Replace /sdcard/ in repository path with proper path.
String dir = r.getAttribute("directory");
if (dir.startsWith("/sdcard")) {
String newDir = dir.replace("/sdcard",
Environment.getExternalStorageDirectory().getAbsolutePath());
r.setAttribute("directory", newDir);
changed = true;
}
}
// Change global announce server port to 22026 for syncthing v0.9.0.
if (globalAnnounceServer.getTextContent().equals("194.126.249.5:22025")) {
Log.i(TAG, "Changing announce server port for v0.9.0");
globalAnnounceServer.setTextContent("194.126.249.5:22026");
changed = true;
}
// Change global announce server port to 22026 for syncthing v0.9.0.
if (globalAnnounceServer.getTextContent().equals("194.126.249.5:22025")) {
Log.i(TAG, "Changing announce server port for v0.9.0");
globalAnnounceServer.setTextContent("194.126.249.5:22026");
changed = true;
}
if (changed) {
saveChanges();
}
}
if (changed) {
saveChanges();
}
}
private Element getGuiElement() {
return (Element) mConfig.getDocumentElement()
.getElementsByTagName("gui").item(0);
}
private Element getGuiElement() {
return (Element) mConfig.getDocumentElement()
.getElementsByTagName("gui").item(0);
}
/**
* Creates a repository for the default camera folder.
*/
public void createCameraRepo() {
File cameraFolder =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
/**
* Creates a repository for the default camera folder.
*/
public void createCameraRepo() {
File cameraFolder =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
Element cameraRepo = mConfig.createElement("repository");
cameraRepo.setAttribute("id", "camera");
cameraRepo.setAttribute("directory", cameraFolder.getAbsolutePath());
cameraRepo.setAttribute("ro", "true");
cameraRepo.setAttribute("ignorePerms", "true");
mConfig.getDocumentElement().appendChild(cameraRepo);
Element cameraRepo = mConfig.createElement("repository");
cameraRepo.setAttribute("id", "camera");
cameraRepo.setAttribute("directory", cameraFolder.getAbsolutePath());
cameraRepo.setAttribute("ro", "true");
cameraRepo.setAttribute("ignorePerms", "true");
mConfig.getDocumentElement().appendChild(cameraRepo);
saveChanges();
}
saveChanges();
}
/**
* Writes updated mConfig back to file.
*/
private void saveChanges() {
try {
Log.i(TAG, "Writing updated config back to file");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(mConfig);
StreamResult streamResult = new StreamResult(mConfigFile);
transformer.transform(domSource, streamResult);
}
catch (TransformerException e) {
Log.w(TAG, "Failed to save updated config", e);
}
}
/**
* Writes updated mConfig back to file.
*/
private void saveChanges() {
try {
Log.i(TAG, "Writing updated config back to file");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(mConfig);
StreamResult streamResult = new StreamResult(mConfigFile);
transformer.transform(domSource, streamResult);
} catch (TransformerException e) {
Log.w(TAG, "Failed to save updated config", e);
}
}
/**
* Copies the default config file from res/raw/config_default.xml to (data folder)/config.xml.
*/
private void copyDefaultConfig(Context context) {
InputStream in = null;
FileOutputStream out = null;
try {
in = context.getResources().openRawResource(R.raw.config_default);
out = new FileOutputStream(mConfigFile);
byte[] buff = new byte[1024];
int read;
/**
* Copies the default config file from res/raw/config_default.xml to (data folder)/config.xml.
*/
private void copyDefaultConfig(Context context) {
InputStream in = null;
FileOutputStream out = null;
try {
in = context.getResources().openRawResource(R.raw.config_default);
out = new FileOutputStream(mConfigFile);
byte[] buff = new byte[1024];
int read;
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
}
catch (IOException e) {
throw new RuntimeException("Failed to write config file", e);
}
finally {
try {
in.close();
out.close();
}
catch (IOException e) {
Log.w(TAG, "Failed to close stream while copying config", e);
}
}
}
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} catch (IOException e) {
throw new RuntimeException("Failed to write config file", e);
} finally {
try {
in.close();
out.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close stream while copying config", e);
}
}
}
}

View file

@ -8,15 +8,15 @@ import android.preference.CheckBoxPreference;
*/
public class ExtendedCheckBoxPreference extends CheckBoxPreference {
private final Object mObject;
private final Object mObject;
public ExtendedCheckBoxPreference(Context context, Object object) {
super(context);
mObject = object;
}
public ExtendedCheckBoxPreference(Context context, Object object) {
super(context);
mObject = object;
}
public Object getObject() {
return mObject;
}
public Object getObject() {
return mObject;
}
}

View file

@ -20,79 +20,77 @@ import java.util.Map;
* Generates item views for node items.
*/
public class NodesAdapter extends ArrayAdapter<RestApi.Node>
implements RestApi.OnReceiveConnectionsListener {
implements RestApi.OnReceiveConnectionsListener {
private Map<String, RestApi.Connection> mConnections =
new HashMap<>();
private Map<String, RestApi.Connection> mConnections =
new HashMap<>();
public NodesAdapter(Context context) {
super(context, R.layout.node_list_item);
}
public NodesAdapter(Context context) {
super(context, R.layout.node_list_item);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.node_list_item, parent, false);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.node_list_item, parent, false);
}
TextView name = (TextView) convertView.findViewById(R.id.name);
TextView status = (TextView) convertView.findViewById(R.id.status);
TextView download = (TextView) convertView.findViewById(R.id.download);
TextView upload = (TextView) convertView.findViewById(R.id.upload);
TextView name = (TextView) convertView.findViewById(R.id.name);
TextView status = (TextView) convertView.findViewById(R.id.status);
TextView download = (TextView) convertView.findViewById(R.id.download);
TextView upload = (TextView) convertView.findViewById(R.id.upload);
name.setText(getItem(position).Name);
final String nodeId = getItem(position).NodeID;
name.setText(getItem(position).Name);
final String nodeId = getItem(position).NodeID;
RestApi.Connection conn = mConnections.get(nodeId);
Resources res = getContext().getResources();
if (conn != null) {
if (conn.Completion == 100) {
status.setText(res.getString(R.string.node_up_to_date));
status.setTextColor(res.getColor(R.color.text_green));
}
else {
status.setText(res.getString(R.string.node_syncing, conn.Completion));
status.setTextColor(res.getColor(R.color.text_blue));
}
download.setText(RestApi.readableTransferRate(getContext(), conn.InBits));
upload.setText(RestApi.readableTransferRate(getContext(), conn.OutBits));
}
else {
download.setText("0 " + res.getStringArray(R.array.transfer_rate_units)[0]);
upload.setText("0 " + res.getStringArray(R.array.transfer_rate_units)[0]);
status.setText(res.getString(R.string.node_disconnected));
status.setTextColor(res.getColor(R.color.text_red));
}
RestApi.Connection conn = mConnections.get(nodeId);
Resources res = getContext().getResources();
if (conn != null) {
if (conn.Completion == 100) {
status.setText(res.getString(R.string.node_up_to_date));
status.setTextColor(res.getColor(R.color.text_green));
} else {
status.setText(res.getString(R.string.node_syncing, conn.Completion));
status.setTextColor(res.getColor(R.color.text_blue));
}
download.setText(RestApi.readableTransferRate(getContext(), conn.InBits));
upload.setText(RestApi.readableTransferRate(getContext(), conn.OutBits));
} else {
download.setText("0 " + res.getStringArray(R.array.transfer_rate_units)[0]);
upload.setText("0 " + res.getStringArray(R.array.transfer_rate_units)[0]);
status.setText(res.getString(R.string.node_disconnected));
status.setTextColor(res.getColor(R.color.text_red));
}
return convertView;
}
return convertView;
}
/**
* Replacement for addAll, which is not implemented on lower API levels.
*/
public void add(List<RestApi.Node> nodes) {
for (RestApi.Node n : nodes) {
add(n);
}
}
/**
* Replacement for addAll, which is not implemented on lower API levels.
*/
public void add(List<RestApi.Node> nodes) {
for (RestApi.Node n : nodes) {
add(n);
}
}
/**
* Requests new connection info for all nodes visible in listView.
*/
public void updateConnections(RestApi api, ListView listView) {
for (int i = 0; i < getCount(); i++) {
if ( i >= listView.getFirstVisiblePosition() &&
i <= listView.getLastVisiblePosition()) {
api.getConnections(this);
}
}
}
/**
* Requests new connection info for all nodes visible in listView.
*/
public void updateConnections(RestApi api, ListView listView) {
for (int i = 0; i < getCount(); i++) {
if (i >= listView.getFirstVisiblePosition() &&
i <= listView.getLastVisiblePosition()) {
api.getConnections(this);
}
}
}
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
mConnections = connections;
notifyDataSetInvalidated();
}
@Override
public void onReceiveConnections(Map<String, RestApi.Connection> connections) {
mConnections = connections;
notifyDataSetInvalidated();
}
}

View file

@ -18,77 +18,77 @@ import java.util.List;
* Generates item views for repository items.
*/
public class ReposAdapter extends ArrayAdapter<RestApi.Repo>
implements RestApi.OnReceiveModelListener {
implements RestApi.OnReceiveModelListener {
private HashMap<String, RestApi.Model> mModels = new HashMap<>();
private HashMap<String, RestApi.Model> mModels = new HashMap<>();
public ReposAdapter(Context context) {
super(context, R.layout.repo_list_item);
}
public ReposAdapter(Context context) {
super(context, R.layout.repo_list_item);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.repo_list_item, parent, false);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.repo_list_item, parent, false);
}
TextView id = (TextView) convertView.findViewById(R.id.id);
TextView id = (TextView) convertView.findViewById(R.id.id);
TextView state = (TextView) convertView.findViewById(R.id.state);
TextView folder = (TextView) convertView.findViewById(R.id.folder);
TextView items = (TextView) convertView.findViewById(R.id.items);
TextView size = (TextView) convertView.findViewById(R.id.size);
TextView invalid = (TextView) convertView.findViewById(R.id.invalid);
TextView state = (TextView) convertView.findViewById(R.id.state);
TextView folder = (TextView) convertView.findViewById(R.id.folder);
TextView items = (TextView) convertView.findViewById(R.id.items);
TextView size = (TextView) convertView.findViewById(R.id.size);
TextView invalid = (TextView) convertView.findViewById(R.id.invalid);
id.setText(getItem(position).ID);
state.setTextColor(getContext().getResources().getColor(R.color.text_green));
folder.setText((getItem(position).Directory));
RestApi.Model model = mModels.get(getItem(position).ID);
if (model != null) {
state.setText(getContext().getString(R.string.repo_progress_format, model.state,
(model.globalBytes <= 0)
? 100
: (int) ((model.localBytes / (float) model.globalBytes) * 100)));
items.setText(getContext()
.getString(R.string.files, model.localFiles, model.globalFiles));
size.setText(RestApi.readableFileSize(getContext(), model.localBytes) + " / " +
RestApi.readableFileSize(getContext(), model.globalBytes));
invalid.setText(model.invalid);
invalid.setVisibility((model.invalid.equals("")) ? View.GONE : View.VISIBLE);
}
else {
invalid.setVisibility(View.GONE);
}
id.setText(getItem(position).ID);
state.setTextColor(getContext().getResources().getColor(R.color.text_green));
folder.setText((getItem(position).Directory));
RestApi.Model model = mModels.get(getItem(position).ID);
if (model != null) {
state.setText(getContext().getString(R.string.repo_progress_format, model.state,
(model.globalBytes <= 0)
? 100
: (int) ((model.localBytes / (float) model.globalBytes) * 100)
));
items.setText(getContext()
.getString(R.string.files, model.localFiles, model.globalFiles));
size.setText(RestApi.readableFileSize(getContext(), model.localBytes) + " / " +
RestApi.readableFileSize(getContext(), model.globalBytes));
invalid.setText(model.invalid);
invalid.setVisibility((model.invalid.equals("")) ? View.GONE : View.VISIBLE);
} else {
invalid.setVisibility(View.GONE);
}
return convertView;
}
return convertView;
}
/**
* Replacement for addAll, which is not implemented on lower API levels.
*/
public void add(List<RestApi.Repo> nodes) {
for (RestApi.Repo r : nodes) {
add(r);
}
}
/**
* Replacement for addAll, which is not implemented on lower API levels.
*/
public void add(List<RestApi.Repo> nodes) {
for (RestApi.Repo r : nodes) {
add(r);
}
}
/**
* Requests updated model info from the api for all visible items.
*/
public void updateModel(RestApi api, ListView listView) {
for (int i = 0; i < getCount(); i++) {
if ( i >= listView.getFirstVisiblePosition() &&
i <= listView.getLastVisiblePosition()) {
api.getModel(getItem(i).ID, this);
}
}
}
/**
* Requests updated model info from the api for all visible items.
*/
public void updateModel(RestApi api, ListView listView) {
for (int i = 0; i < getCount(); i++) {
if (i >= listView.getFirstVisiblePosition() &&
i <= listView.getLastVisiblePosition()) {
api.getModel(getItem(i).ID, this);
}
}
}
@Override
public void onReceiveModel(String repoId, RestApi.Model model) {
mModels.put(repoId, model);
notifyDataSetChanged();
}
@Override
public void onReceiveModel(String repoId, RestApi.Model model) {
mModels.put(repoId, model);
notifyDataSetChanged();
}
}

View file

@ -1,17 +1,17 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/directory_empty"
android:layout_centerInParent="true"/>
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/directory_empty"
android:layout_centerInParent="true" />
</RelativeLayout>

View file

@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_gravity="center"
android:padding="10dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
android:layout_gravity="center"
android:padding="10dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/loading_text"
android:layout_toRightOf="@id/progress"
android:layout_alignBottom="@id/progress"
android:layout_alignTop="@id/progress"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/loading_text"
android:layout_toRightOf="@id/progress"
android:layout_alignBottom="@id/progress"
android:layout_alignTop="@id/progress"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</RelativeLayout>

View file

@ -1,117 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="10dip"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:orientation="vertical"
android:padding="10dip"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/node_id_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/node_id"
android:lines="1"
android:ellipsize="end"
android:layout_below="@id/node_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/node_id_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/cpu_usage_title"
android:text="@string/cpu_usage"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/node_id"
android:lines="1"
android:ellipsize="end"
android:layout_below="@id/node_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/cpu_usage"
android:layout_alignBottom="@id/cpu_usage_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/ram_usage_title"
android:text="@string/ram_usage"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/cpu_usage_title"
android:text="@string/cpu_usage"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/ram_usage"
android:layout_alignBottom="@id/ram_usage_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/cpu_usage"
android:layout_alignBottom="@id/cpu_usage_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/download_title"
android:text="@string/download_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/download"
android:layout_alignBottom="@id/download_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/ram_usage_title"
android:text="@string/ram_usage"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/upload_title"
android:text="@string/upload_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/ram_usage"
android:layout_alignBottom="@id/ram_usage_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/upload"
android:layout_alignBottom="@id/upload_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/announce_server_title"
android:text="@string/announce_server"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/download_title"
android:text="@string/download_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/announce_server"
android:layout_alignBottom="@id/announce_server_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<TextView
android:id="@+id/download"
android:layout_alignBottom="@id/download_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/upload_title"
android:text="@string/upload_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/upload"
android:layout_alignBottom="@id/upload_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/announce_server_title"
android:text="@string/announce_server"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/announce_server"
android:layout_alignBottom="@id/announce_server_title"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>

View file

@ -1,23 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/drawer"
android:orientation="vertical"
android:layout_gravity="start"
android:layout_width="260dip"
android:layout_height="match_parent"
android:clickable="true"
android:background="@android:color/background_light" />
<LinearLayout
android:id="@+id/drawer"
android:orientation="vertical"
android:layout_gravity="start"
android:layout_width="260dip"
android:layout_height="match_parent"
android:clickable="true"
android:background="@android:color/background_light" />
</android.support.v4.widget.DrawerLayout>
</android.support.v4.widget.DrawerLayout>

View file

@ -1,59 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dip" >
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dip">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/status"
android:lines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/status"
android:lines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/status"
android:layout_alignParentRight="true"
android:layout_alignBottom="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="end" />
<TextView
android:id="@+id/status"
android:layout_alignParentRight="true"
android:layout_alignBottom="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/download_title"
android:text="@string/download_title_colon"
android:layout_below="@id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/download_title"
android:text="@string/download_title_colon"
android:layout_below="@id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/download"
android:layout_alignBaseline="@id/download_title"
android:layout_toRightOf="@id/download_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/download"
android:layout_alignBaseline="@id/download_title"
android:layout_toRightOf="@id/download_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/upload_title"
android:text="@string/upload_title_colon"
android:layout_below="@id/download_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/upload_title"
android:text="@string/upload_title_colon"
android:layout_below="@id/download_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/upload"
android:layout_alignBaseline="@id/upload_title"
android:layout_toRightOf="@id/upload_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/upload"
android:layout_alignBaseline="@id/upload_title"
android:layout_toRightOf="@id/upload_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
>>>>>>> Changed code style to use spaces instead of tabs.
</RelativeLayout>

View file

@ -1,55 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dip" >
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dip">
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/state"
android:lines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/state"
android:lines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/state"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignBottom="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/state"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignBottom="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/folder"
android:layout_below="@id/state"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end" />
<TextView
android:id="@+id/folder"
android:layout_below="@id/state"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end" />
<TextView
android:id="@+id/items"
android:layout_below="@id/folder"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/items"
android:layout_below="@id/folder"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/size"
android:layout_below="@id/items"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/size"
android:layout_below="@id/items"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/invalid"
android:textColor="@color/text_red"
android:layout_below="@id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/invalid"
android:textColor="@color/text_red"
android:layout_below="@id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

View file

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

View file

@ -1,37 +1,36 @@
<?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" >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/loading"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RelativeLayout
android:id="@+id/loading"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progress"
android:layout_centerInParent="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dip" />
<ProgressBar
android:id="@+id/progress"
android:layout_centerInParent="true"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dip" />
<TextView
android:id="@+id/loading_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress"
android:text="@string/web_gui_loading" />
<TextView
android:id="@+id/loading_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress"
android:text="@string/web_gui_loading" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>

View file

@ -1,17 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/select"
android:title="@string/select_folder"
app:showAsAction="ifRoom" />
<item
android:id="@+id/select"
android:title="@string/select_folder"
app:showAsAction="ifRoom" />
<item
android:id="@+id/create_folder"
android:title="@string/create_folder"
app:showAsAction="ifRoom" />
<item
android:id="@+id/create_folder"
android:title="@string/create_folder"
app:showAsAction="ifRoom" />
</menu>

View file

@ -1,32 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/share_node_id"
android:icon="@android:drawable/ic_menu_share"
android:title="@string/share_node_id"
app:showAsAction="ifRoom" />
<item
android:id="@+id/share_node_id"
android:icon="@android:drawable/ic_menu_share"
android:title="@string/share_node_id"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_repo"
android:title="@string/add_repo"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_repo"
android:title="@string/add_repo"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_node"
android:title="@string/add_node"
app:showAsAction="ifRoom" />
<item
android:id="@+id/add_node"
android:title="@string/add_node"
app:showAsAction="ifRoom" />
<item
android:id="@+id/web_gui"
android:title="@string/web_gui_title" />
<item
android:id="@+id/web_gui"
android:title="@string/web_gui_title" />
<item
android:id="@+id/settings"
android:title="@string/settings_title" />
<item
android:id="@+id/settings"
android:title="@string/settings_title" />
</menu>

View file

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/create"
android:title="@string/add"
app:showAsAction="always" />
<item
android:id="@+id/create"
android:title="@string/add"
app:showAsAction="always" />
<item
android:id="@+id/share_node_id"
android:icon="@android:drawable/ic_menu_share"
android:title="@string/share_node_id"
app:showAsAction="ifRoom" />
<item
android:id="@+id/share_node_id"
android:icon="@android:drawable/ic_menu_share"
android:title="@string/share_node_id"
app:showAsAction="ifRoom" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete_node"
app:showAsAction="ifRoom" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete_node"
app:showAsAction="ifRoom" />
</menu>

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/create"
android:title="@string/create"
app:showAsAction="always" />
<item
android:id="@+id/create"
android:title="@string/create"
app:showAsAction="always" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete_repo"
app:showAsAction="ifRoom" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete_repo"
app:showAsAction="ifRoom" />
</menu>

View file

@ -1,18 +1,18 @@
<configuration version="2">
<gui enabled="true">
<address>127.0.0.1:8080</address>
</gui>
<options>
<listenAddress>0.0.0.0:22000</listenAddress>
<globalAnnounceServer>194.126.249.5:22026</globalAnnounceServer>
<globalAnnounceEnabled>true</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled>
<parallelRequests>16</parallelRequests>
<maxSendKbps>0</maxSendKbps>
<rescanIntervalS>60</rescanIntervalS>
<reconnectionIntervalS>60</reconnectionIntervalS>
<maxChangeKbps>1000</maxChangeKbps>
<startBrowser>false</startBrowser>
<upnpEnabled>true</upnpEnabled>
</options>
<gui enabled="true">
<address>127.0.0.1:8080</address>
</gui>
<options>
<listenAddress>0.0.0.0:22000</listenAddress>
<globalAnnounceServer>194.126.249.5:22026</globalAnnounceServer>
<globalAnnounceEnabled>true</globalAnnounceEnabled>
<localAnnounceEnabled>true</localAnnounceEnabled>
<parallelRequests>16</parallelRequests>
<maxSendKbps>0</maxSendKbps>
<rescanIntervalS>60</rescanIntervalS>
<reconnectionIntervalS>60</reconnectionIntervalS>
<maxChangeKbps>1000</maxChangeKbps>
<startBrowser>false</startBrowser>
<upnpEnabled>true</upnpEnabled>
</options>
</configuration>

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar" />
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar" />
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_red">#ffff4444</color>
<color name="text_blue">#ff33b5e5</color>
<color name="text_green">#ff99cc00</color>
</resources>
<color name="text_red">#ffff4444</color>
<color name="text_blue">#ff33b5e5</color>
<color name="text_green">#ff99cc00</color>
</resources>

View file

@ -1,234 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Syncthing</string>
<string name="app_name">Syncthing</string>
<!-- MainActivity -->
<!-- MainActivity -->
<!-- Title of the "add repo" menu action -->
<string name="add_repo">Add Repository</string>
<!-- Title of the "add repo" menu action -->
<string name="add_repo">Add Repository</string>
<!-- Title of the "share node id" menu action -->
<string name="share_node_id">Share Node ID</string>
<!-- Title of the "share node id" menu action -->
<string name="share_node_id">Share Node ID</string>
<!-- Shown in the chooser dialog when sharing a Node ID -->
<string name="send_node_id_to">Send Node ID to</string>
<!-- Shown in the chooser dialog when sharing a Node ID -->
<string name="send_node_id_to">Send Node ID to</string>
<!-- Text for RepositoriesFragment and NodesFragment loading view -->
<string name="api_loading">Waiting for API</string>
<!-- Text for RepositoriesFragment and NodesFragment loading view -->
<string name="api_loading">Waiting for API</string>
<!-- RepositoriesFragment -->
<!-- RepositoriesFragment -->
<string name="repositories_fragment_title">Repositories</string>
<string name="repositories_fragment_title">Repositories</string>
<!-- Format string for repository progress. First parameter is status string, second is sync percentage -->
<string name="repo_progress_format">%1$s (%2$d%%)</string>
<!-- Format string for repository progress. First parameter is status string, second is sync percentage -->
<string name="repo_progress_format">%1$s (%2$d%%)</string>
<!-- Shown if no repos exist -->
<string name="repositories_list_empty">No repositories found</string>
<!-- Shown if no repos exist -->
<string name="repositories_list_empty">No repositories found</string>
<!-- Format string for repository file count -->
<string name="files">%1$d / %2$d Files</string>
<!-- Format string for repository file count -->
<string name="files">%1$d / %2$d Files</string>
<!-- NodesFragment -->
<!-- NodesFragment -->
<string name="nodes_fragment_title">Nodes</string>
<string name="nodes_fragment_title">Nodes</string>
<!-- Shown if no nodes exist -->
<string name="nodes_list_empty">No nodes found</string>
<!-- Shown if no nodes exist -->
<string name="nodes_list_empty">No nodes found</string>
<!-- Indicates that a repo is fully synced to the local node -->
<string name="node_up_to_date">Up to Date</string>
<!-- Indicates that a repo is fully synced to the local node -->
<string name="node_up_to_date">Up to Date</string>
<!-- Indicates that the node is currently syncing. Parameter is sync percentage -->
<string name="node_syncing">Syncing (%1$d%%)</string>
<!-- Indicates that the node is currently syncing. Parameter is sync percentage -->
<string name="node_syncing">Syncing (%1$d%%)</string>
<!-- Indicates that there is no connection to the node -->
<string name="node_disconnected">Disconnected</string>
<!-- Indicates that there is no connection to the node -->
<string name="node_disconnected">Disconnected</string>
<!-- Title for current download rate -->
<string name="download_title">Download</string>
<!-- Title for current download rate -->
<string name="download_title">Download</string>
<!-- Title for current upload rate -->
<string name="upload_title">Upload</string>
<!-- Title for current upload rate -->
<string name="upload_title">Upload</string>
<!-- LocalNodeInfoFragment -->
<!-- LocalNodeInfoFragment -->
<!-- ActionBar title shown when the drawer is open -->
<string name="system_info">System Info</string>
<!-- ActionBar title shown when the drawer is open -->
<string name="system_info">System Info</string>
<!-- Same as download_title with a colon and space appended -->
<string name="download_title_colon">Download:\u0020</string>
<!-- Same as download_title with a colon and space appended -->
<string name="download_title_colon">Download:\u0020</string>
<!-- Same as upload_title with a colon and space appended -->
<string name="upload_title_colon">Upload:\u0020</string>
<!-- Same as upload_title with a colon and space appended -->
<string name="upload_title_colon">Upload:\u0020</string>
<!-- Title for current CPU usage -->
<string name="cpu_usage">CPU Usage</string>
<!-- Title for current CPU usage -->
<string name="cpu_usage">CPU Usage</string>
<!-- Title for current RAM usage -->
<string name="ram_usage">RAM Usage</string>
<!-- Title for current RAM usage -->
<string name="ram_usage">RAM Usage</string>
<!-- Title for announce server status -->
<string name="announce_server">Announce Server</string>
<!-- Title for announce server status -->
<string name="announce_server">Announce Server</string>
<!-- RepoSettingsFragment -->
<!-- RepoSettingsFragment -->
<!-- Setting title -->
<string name="repo_id">Repository ID</string>
<!-- Setting title -->
<string name="repo_id">Repository ID</string>
<!-- Setting title -->
<string name="directory">Directory</string>
<!-- Setting title -->
<string name="directory">Directory</string>
<!-- Setting title -->
<string name="repo_master">Repository Master</string>
<!-- Setting title -->
<string name="repo_master">Repository Master</string>
<!-- Setting title -->
<string name="nodes">Nodes</string>
<!-- Setting title -->
<string name="nodes">Nodes</string>
<!-- Setting title -->
<string name="file_versioning">File Versioning</string>
<!-- Setting title -->
<string name="file_versioning">File Versioning</string>
<!-- Setting title -->
<string name="keep_versions">Keep Versions</string>
<!-- Setting title -->
<string name="keep_versions">Keep Versions</string>
<!-- Setting title -->
<string name="delete_repo">Delete Repository</string>
<!-- Setting title -->
<string name="delete_repo">Delete Repository</string>
<!-- Title for RepoSettingsFragment in create mode -->
<string name="create_repo">Create Repository</string>
<!-- Title for RepoSettingsFragment in create mode -->
<string name="create_repo">Create Repository</string>
<!-- Title for RepoSettingsFragment in edit mode -->
<string name="edit_repo">Edit Repository</string>
<!-- Title for RepoSettingsFragment in edit mode -->
<string name="edit_repo">Edit Repository</string>
<!-- Menu item to confirm repo creation -->
<string name="create">Create</string>
<!-- Menu item to confirm repo creation -->
<string name="create">Create</string>
<!-- Dialog shown when attempting to delete a repository -->
<string name="delete_repo_confirm">Do you really want to delete this repository?</string>
<!-- Dialog shown when attempting to delete a repository -->
<string name="delete_repo_confirm">Do you really want to delete this repository?</string>
<!-- Toast shown when trying to create a repository with an empty ID -->
<string name="repo_id_required">The repository ID must not be empty</string>
<!-- Toast shown when trying to create a repository with an empty ID -->
<string name="repo_id_required">The repository ID must not be empty</string>
<!-- Toast shown when trying to create a repository with an empty path -->
<string name="repo_path_required">The repository path must not be empty</string>
<!-- Toast shown when trying to create a repository with an empty path -->
<string name="repo_path_required">The repository path must not be empty</string>
<!-- Toast shown when selecting 'nodes' if no nodes have been added -->
<string name="no_nodes">Please connect a node first.</string>
<!-- Toast shown when selecting 'nodes' if no nodes have been added -->
<string name="no_nodes">Please connect a node first.</string>
<!-- NodeSettingsFragment -->
<!-- NodeSettingsFragment -->
<!-- Setting title -->
<string name="node_id">Node ID</string>
<!-- Setting title -->
<string name="node_id">Node ID</string>
<!-- Setting title -->
<string name="name">Name</string>
<!-- Setting title -->
<string name="name">Name</string>
<!-- Setting title -->
<string name="addresses">Addresses</string>
<!-- Setting title -->
<string name="addresses">Addresses</string>
<!-- Setting title -->
<string name="current_address">Current Address</string>
<!-- Setting title -->
<string name="current_address">Current Address</string>
<!-- Setting title -->
<string name="delete_node">Delete Node</string>
<!-- Setting title -->
<string name="delete_node">Delete Node</string>
<!-- Title for NodeSettingsFragment in create mode -->
<string name="add_node">Add Node</string>
<!-- Title for NodeSettingsFragment in create mode -->
<string name="add_node">Add Node</string>
<!-- Menu item to confirm adding a node -->
<string name="add">Add</string>
<!-- Menu item to confirm adding a node -->
<string name="add">Add</string>
<!-- Title for NodeSettingsFragment in edit mode -->
<string name="edit_node">Edit Node</string>
<!-- Title for NodeSettingsFragment in edit mode -->
<string name="edit_node">Edit Node</string>
<!-- Dialog shown when attempting to delete a node -->
<string name="delete_node_confirm">Do you really want to delete this node?</string>
<!-- Dialog shown when attempting to delete a node -->
<string name="delete_node_confirm">Do you really want to delete this node?</string>
<!-- Toast shown when trying to create a node with an empty ID -->
<string name="node_id_required">The node ID must not be empty</string>
<!-- Toast shown when trying to create a node with an empty ID -->
<string name="node_id_required">The node ID must not be empty</string>
<!-- Toast shown when trying to create a node with an empty name -->
<string name="node_name_required">The node name must not be empty</string>
<!-- Toast shown when trying to create a node with an empty name -->
<string name="node_name_required">The node name must not be empty</string>
<!-- Content description for node ID qr code icon -->
<string name="scan_qr_code_description">Scan QR Code</string>
<!-- Content description for node ID qr code icon -->
<string name="scan_qr_code_description">Scan QR Code</string>
<!-- Text for toast shown if the "scan QR code" intent fails with an ActivityNotFoundException -->
<!-- Text for toast shown if the "scan QR code" intent fails with an ActivityNotFoundException -->
<string name="no_qr_scanner_installed">You have no QR scanner installed or your scanner is not supported. \
Please install a scanner or enter the node ID manually.</string>
<!-- WebGuiActivity -->
<!-- WebGuiActivity -->
<!-- Title of the web gui activity -->
<string name="web_gui_title">Web GUI</string>
<!-- Title of the web gui activity -->
<string name="web_gui_title">Web GUI</string>
<!-- Text for WebGuiActivity loading view -->
<string name="web_gui_loading">Waiting for GUI</string>
<!-- Text for WebGuiActivity loading view -->
<string name="web_gui_loading">Waiting for GUI</string>
<!-- Shown instead of web_gui_loading if the key does not exist and has to be created -->
<string name="web_gui_creating_key">Generating keys. This may take a while.</string>
<!-- Shown instead of web_gui_loading if the key does not exist and has to be created -->
<string name="web_gui_creating_key">Generating keys. This may take a while.</string>
<!-- Title for dialog displayed on first start -->
<string name="welcome_title">First Start</string>
<!-- Title for dialog displayed on first start -->
<string name="welcome_title">First Start</string>
<!-- Text for dialog displayed on first start -->
<!-- Text for dialog displayed on first start -->
<string name="welcome_text">Welcome to Syncthing for Android!\n\n\
This app is currently in Alpha state, and you may experience bugs, performance problems or data loss.\n\n\
There is currently no special handling for mobile data, so it may use up your data volume if active.\n\n\
Please report any problems you encounter.</string>
<!-- SettingsFragment -->
<!-- SettingsFragment -->
<!-- Activity title -->
<string name="settings_title">Settings</string>
<!-- Activity title -->
<string name="settings_title">Settings</string>
<string name="stop_sync_while_not_charging">Stop sync when not charging</string>
<string name="stop_sync_while_not_charging">Stop sync when not charging</string>
<string name="stop_sync_on_mobile_data">Stop sync on mobile data</string>
<string name="stop_sync_on_mobile_data">Stop sync on mobile data</string>
<!-- Settings item that opens issue tracker -->
<string name="report_issue_title">Report Issue</string>
<!-- Settings item that opens issue tracker -->
<string name="report_issue_title">Report Issue</string>
<!-- Summary for the issue tracker settings item -->
<string name="report_issue_summary">Open the Syncthing-Android issue tracker</string>
<!-- Summary for the issue tracker settings item -->
<string name="report_issue_summary">Open the Syncthing-Android issue tracker</string>
<!-- URL of the issue tracker -->
<string name="issue_tracker_url" translatable="false">https://github.com/Nutomic/syncthing-android/issues</string>
<!-- URL of the issue tracker -->
<string name="issue_tracker_url" translatable="false">https://github.com/Nutomic/syncthing-android/issues</string>
<!-- Title of the preference showing upstream version name -->
<string name="syncthing_version_title">Syncthing Version</string>
<!-- Title of the preference showing upstream version name -->
<string name="syncthing_version_title">Syncthing Version</string>
<!-- FolderPickerAcitivity -->
<!-- FolderPickerAcitivity -->
<!-- Activity title -->
<string name="folder_picker_title">Folder Picker</string>
<!-- Activity title -->
<string name="folder_picker_title">Folder Picker</string>
<!-- ListView empty text -->
<string name="directory_empty">Directory is Empty</string>
<!-- ListView empty text -->
<string name="directory_empty">Directory is Empty</string>
<!-- Menu item to create folder -->
<string name="create_folder">Create new Folder</string>
<!-- Menu item to create folder -->
<string name="create_folder">Create new Folder</string>
<!-- Menu item to select the current folder -->
<string name="select_folder">Select Folder</string>
<!-- Menu item to select the current folder -->
<string name="select_folder">Select Folder</string>
<!-- SyncthingService -->
<!-- SyncthingService -->
<!-- Title of the dialog shown when the syncthing binary returns an error -->
<string name="binary_crashed_title">Syncthing Binary Crashed</string>
<!-- Title of the dialog shown when the syncthing binary returns an error -->
<string name="binary_crashed_title">Syncthing Binary Crashed</string>
<!-- Message of the dialog shown when the syncthing binary returns an error -->
<!-- Message of the dialog shown when the syncthing binary returns an error -->
<string name="binary_crashed_message">The syncthing binary has exited with error code %1$d.\n\n
If this error persists, try reinstalling the app and restarting your device.\n\n</string>
@ -238,43 +238,43 @@ If this error persists, try reinstalling the app and restarting your device.\n\n
<!-- Message of the "syncthing disabled" dialog -->
<string name="syncthing_disabled_message">Do you want to change your preferences?</string>
<!-- Button text on the "syncthing disabled" dialog -->
<!-- Button text on the "syncthing disabled" dialog -->
<string name="syncthing_disabled_change_settings">Change Settings</string>
<!-- Button text on the "syncthing disabled" dialog -->
<string name="exit">Exit</string>
<!-- Button text on the "syncthing disabled" dialog -->
<string name="exit">Exit</string>
<!-- RestApi -->
<!-- RestApi -->
<!-- Title of the notification shown when a restart is needed -->
<string name="restart_title">Restart Needed</string>
<!-- Title of the notification shown when a restart is needed -->
<string name="restart_title">Restart Needed</string>
<!-- Text of the notification shown when a restart is needed -->
<string name="restart_notification_text">Click here to restart syncthing now</string>
<!-- Text of the notification shown when a restart is needed -->
<string name="restart_notification_text">Click here to restart syncthing now</string>
<!-- Text for the dismiss button of the restart Activity -->
<string name="restart_later">Restart Later</string>
<!-- Text for the dismiss button of the restart Activity -->
<string name="restart_later">Restart Later</string>
<!-- Shown when a node ID is copied to the clipboard -->
<string name="node_id_copied_to_clipboard">Node ID copied to clipboard</string>
<!-- Shown when a node ID is copied to the clipboard -->
<string name="node_id_copied_to_clipboard">Node ID copied to clipboard</string>
<!-- Strings representing units for file sizes, from smallest to largest -->
<string-array name="file_size_units">
<item>B</item>
<item>KB</item>
<item>MB</item>
<item>GB</item>
<item>TB</item>
</string-array>
<!-- Strings representing units for file sizes, from smallest to largest -->
<string-array name="file_size_units">
<item>B</item>
<item>KB</item>
<item>MB</item>
<item>GB</item>
<item>TB</item>
</string-array>
<!-- Strings representing units for transfer rates, from smallest to largest -->
<string-array name="transfer_rate_units">
<item>b/s</item>
<item>Kb/s</item>
<item>Mb/s</item>
<item>Gb/s</item>
<item>Tb/s</item>
</string-array>
<!-- Strings representing units for transfer rates, from smallest to largest -->
<string-array name="transfer_rate_units">
<item>b/s</item>
<item>Kb/s</item>
<item>Mb/s</item>
<item>Gb/s</item>
<item>Tb/s</item>
</string-array>
</resources>

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@style/Theme.AppCompat" />
</resources>
<style name="AppTheme" parent="@style/Theme.AppCompat" />
</resources>

View file

@ -1,106 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="stop_sync_while_not_charging"
android:title="@string/stop_sync_while_not_charging"
android:defaultValue="true" />
<CheckBoxPreference
android:key="stop_sync_while_not_charging"
android:title="@string/stop_sync_while_not_charging"
android:defaultValue="true" />
<CheckBoxPreference
android:key="stop_sync_on_mobile_data"
android:title="@string/stop_sync_on_mobile_data"
android:defaultValue="true" />
<CheckBoxPreference
android:key="stop_sync_on_mobile_data"
android:title="@string/stop_sync_on_mobile_data"
android:defaultValue="true" />
<PreferenceScreen
android:key="syncthing_options"
android:title="Syncthing Options"
android:persistent="false" >
<PreferenceScreen
android:key="syncthing_options"
android:title="Syncthing Options"
android:persistent="false">
<EditTextPreference
android:key="ListenAddress"
android:title="Sync Protocol Listen Addresses"
android:defaultValue="0.0.0.0:22000" />
<EditTextPreference
android:key="ListenAddress"
android:title="Sync Protocol Listen Addresses"
android:defaultValue="0.0.0.0:22000" />
<EditTextPreference
android:key="MaxSendKbps"
android:title="Outgoing Rate Limit (KiB/s)"
android:numeric="integer" />
<EditTextPreference
android:key="MaxSendKbps"
android:title="Outgoing Rate Limit (KiB/s)"
android:numeric="integer" />
<EditTextPreference
android:key="RescanIntervalS"
android:title="Rescan Interval (s)"
android:numeric="integer" />
<EditTextPreference
android:key="RescanIntervalS"
android:title="Rescan Interval (s)"
android:numeric="integer" />
<EditTextPreference
android:key="ReconnectIntervalS"
android:title="Reconnect Interval (s)"
android:numeric="integer" />
<EditTextPreference
android:key="ReconnectIntervalS"
android:title="Reconnect Interval (s)"
android:numeric="integer" />
<EditTextPreference
android:key="ParallelRequests"
android:title="Max Outstanding Requests"
android:numeric="integer" />
<EditTextPreference
android:key="ParallelRequests"
android:title="Max Outstanding Requests"
android:numeric="integer" />
<EditTextPreference
android:key="MaxChangeKbps"
android:title="Max File Change Rate (KiB/s)"
android:numeric="integer" />
<EditTextPreference
android:key="MaxChangeKbps"
android:title="Max File Change Rate (KiB/s)"
android:numeric="integer" />
<CheckBoxPreference
android:key="GlobalAnnEnabled"
android:title="Global Discovery" />
<CheckBoxPreference
android:key="GlobalAnnEnabled"
android:title="Global Discovery" />
<CheckBoxPreference
android:key="LocalAnnEnabled"
android:title="Local Discovery" />
<CheckBoxPreference
android:key="LocalAnnEnabled"
android:title="Local Discovery" />
<EditTextPreference
android:key="LocalAnnPort"
android:title="Local Discovery Port"
android:numeric="integer" />
<EditTextPreference
android:key="LocalAnnPort"
android:title="Local Discovery Port"
android:numeric="integer" />
<CheckBoxPreference
android:key="UPnPEnabled"
android:title="Enable UPnP" />
<CheckBoxPreference
android:key="UPnPEnabled"
android:title="Enable UPnP" />
</PreferenceScreen>
</PreferenceScreen>
<PreferenceScreen
android:key="syncthing_gui"
android:title="Syncthing GUI"
android:persistent="false" >
<PreferenceScreen
android:key="syncthing_gui"
android:title="Syncthing GUI"
android:persistent="false">
<EditTextPreference
android:key="Address"
android:title="GUI Listen Addresses" />
<EditTextPreference
android:key="Address"
android:title="GUI Listen Addresses" />
<EditTextPreference
android:key="User"
android:title="GUI Authentication User" />
<EditTextPreference
android:key="User"
android:title="GUI Authentication User" />
<EditTextPreference
android:key="Password"
android:title="GUI Authentication Password"
android:inputType="textPassword" />
<EditTextPreference
android:key="Password"
android:title="GUI Authentication Password"
android:inputType="textPassword" />
<CheckBoxPreference
android:key="UseTLS"
android:title="Use HTTPS for GUI"
android:enabled="false" />
<CheckBoxPreference
android:key="UseTLS"
android:title="Use HTTPS for GUI"
android:enabled="false" />
</PreferenceScreen>
</PreferenceScreen>
<Preference
android:title="@string/report_issue_title"
android:summary="@string/report_issue_summary" >
<intent
android:action="android.intent.action.VIEW"
android:data="@string/issue_tracker_url" />
</Preference>
<Preference
android:title="@string/report_issue_title"
android:summary="@string/report_issue_summary">
<intent
android:action="android.intent.action.VIEW"
android:data="@string/issue_tracker_url" />
</Preference>
<Preference
android:key="syncthing_version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
<Preference
android:key="syncthing_version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
</PreferenceScreen>

View file

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
android:persistent="false">
<EditTextPreference
android:key="node_id"
android:title="@string/node_id"
android:widgetLayout="@layout/scan_qr_code_widget" />
<EditTextPreference
android:key="node_id"
android:title="@string/node_id"
android:widgetLayout="@layout/scan_qr_code_widget" />
<EditTextPreference
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:key="addresses"
android:title="@string/addresses" />
<EditTextPreference
android:key="addresses"
android:title="@string/addresses" />
</PreferenceScreen>
</PreferenceScreen>

View file

@ -1,27 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
android:persistent="false">
<Preference
android:key="node_id"
android:title="@string/node_id"/>
<Preference
android:key="node_id"
android:title="@string/node_id" />
<EditTextPreference
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:key="name"
android:title="@string/name" />
<EditTextPreference
android:key="addresses"
android:title="@string/addresses" />
<EditTextPreference
android:key="addresses"
android:title="@string/addresses" />
<Preference
android:key="version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
<Preference
android:key="version"
android:title="@string/syncthing_version_title"
style="?android:preferenceInformationStyle" />
<Preference
android:key="current_address"
android:title="@string/current_address"
style="?android:preferenceInformationStyle" />
<Preference
android:key="current_address"
android:title="@string/current_address"
style="?android:preferenceInformationStyle" />
</PreferenceScreen>

View file

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
android:persistent="false">
<EditTextPreference
android:key="repo_id"
android:title="@string/repo_id" />
<EditTextPreference
android:key="repo_id"
android:title="@string/repo_id" />
<Preference
android:key="directory"
android:title="@string/directory" />
<Preference
android:key="directory"
android:title="@string/directory" />
<CheckBoxPreference
android:key="repo_master"
android:title="@string/repo_master" />
<CheckBoxPreference
android:key="repo_master"
android:title="@string/repo_master" />
<PreferenceScreen
android:key="nodes"
android:title="@string/nodes" />
<PreferenceScreen
android:key="nodes"
android:title="@string/nodes" />
<CheckBoxPreference
android:key="versioning"
android:title="@string/file_versioning" />
<CheckBoxPreference
android:key="versioning"
android:title="@string/file_versioning" />
<EditTextPreference
android:key="versioning_keep"
android:title="@string/keep_versions"
android:inputType="numberDecimal" />
<EditTextPreference
android:key="versioning_keep"
android:title="@string/keep_versions"
android:inputType="numberDecimal" />
</PreferenceScreen>
</PreferenceScreen>

View file

@ -1,34 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:persistent="false" >
android:persistent="false">
<EditTextPreference
android:key="repo_id"
android:title="@string/repo_id"
android:enabled="false"
style="?android:preferenceInformationStyle" />
<EditTextPreference
android:key="repo_id"
android:title="@string/repo_id"
android:enabled="false"
style="?android:preferenceInformationStyle" />
<Preference
android:key="directory"
android:title="@string/directory"
android:enabled="false"
style="?android:preferenceInformationStyle" />
<Preference
android:key="directory"
android:title="@string/directory"
android:enabled="false"
style="?android:preferenceInformationStyle" />
<CheckBoxPreference
android:key="repo_master"
android:title="@string/repo_master" />
<CheckBoxPreference
android:key="repo_master"
android:title="@string/repo_master" />
<PreferenceScreen
android:key="nodes"
android:title="@string/nodes" />
<PreferenceScreen
android:key="nodes"
android:title="@string/nodes" />
<CheckBoxPreference
android:key="versioning"
android:title="File Versioning" />
<CheckBoxPreference
android:key="versioning"
android:title="File Versioning" />
<EditTextPreference
android:key="versioning_keep"
android:title="@string/keep_versions"
android:inputType="numberDecimal" />
<EditTextPreference
android:key="versioning_keep"
android:title="@string/keep_versions"
android:inputType="numberDecimal" />
</PreferenceScreen>