9 Commits

Author SHA1 Message Date
Felix Ableitner 087b0f4ec1 Version 0.2.1 2018-02-20 14:58:06 +09:00
Felix Ableitner a6cc38f4a1 Imported translations 2018-02-20 14:58:06 +09:00
Felix Ableitner 96a4ec1738 Added preferences with device name and app version 2018-02-20 14:29:26 +09:00
Felix Ableitner 42b77cab12 Added app intro 2018-02-20 03:27:40 +09:00
Felix Ableitner a28e5d704d Reworked add device dialog 2018-02-15 04:05:16 +09:00
Felix Ableitner b924b50ddc Show warning if another Syncthing instance is running (fixes 18) 2018-02-12 16:47:11 +09:00
Felix Ableitner 96503dd9c1 Added device ID and qr code (fixes #14) 2018-02-09 00:38:51 +09:00
Licaon_Kter c83504c155 Some typos (#27) 2018-02-08 17:51:26 +09:00
Felix Ableitner acea170854 Version 0.2.0 2018-02-08 02:52:43 +09:00
34 changed files with 781 additions and 137 deletions
+7 -8
View File
@@ -11,8 +11,8 @@ android {
applicationId "net.syncthing.lite"
minSdkVersion 21
targetSdkVersion 25
versionCode 7
versionName "0.2.0"
versionCode 9
versionName "0.2.1"
multiDexEnabled true
}
sourceSets {
@@ -48,10 +48,10 @@ dependencies {
implementation "org.jetbrains.anko:anko-commons:$anko_version"
implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
kapt "com.android.databinding:compiler:$build_tools_version"
implementation "com.android.support:appcompat-v7:$support_version"
implementation "com.android.support:design:$support_version"
implementation "com.android.support:cardview-v7:$support_version"
implementation ("com.github.Nutomic:syncthing-java:0.2.0") {
implementation "com.android.support:preference-v14:$support_version"
kapt "com.android.databinding:library:1.3.3"
implementation ("com.github.Nutomic:syncthing-java:0.2.1") {
exclude group: 'commons-logging', module:'commons-logging'
exclude group: 'org.apache.httpcomponents', module:'httpclient'
exclude group: 'org.slf4j'
@@ -62,7 +62,6 @@ dependencies {
implementation 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
implementation 'sk.baka.slf4j:slf4j-handroid:1.7.26'
implementation 'com.google.zxing:android-integration:3.3.0'
implementation ('uk.co.markormesher:android-fab:2.0.0') {
exclude group: "org.jetbrains.kotlin"
}
implementation 'com.google.zxing:core:3.3.0'
implementation 'com.github.apl-devs:appintro:v4.2.3'
}
+2
View File
@@ -17,6 +17,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activities.IntroActivity"
android:theme="@style/Theme.Syncthing.NoActionBar"/>
<activity android:name=".activities.FolderBrowserActivity"
android:parentActivityName=".activities.MainActivity"/>
<provider
@@ -13,8 +13,8 @@ import net.syncthing.java.core.utils.PathUtils
import net.syncthing.lite.R
import net.syncthing.lite.adapters.FolderContentsAdapter
import net.syncthing.lite.databinding.ActivityFolderBrowserBinding
import net.syncthing.lite.utils.FileDownloadDialog
import net.syncthing.lite.utils.FileUploadDialog
import net.syncthing.lite.dialogs.FileDownloadDialog
import net.syncthing.lite.dialogs.FileUploadDialog
class FolderBrowserActivity : SyncthingActivity() {
@@ -66,7 +66,7 @@ class FolderBrowserActivity : SyncthingActivity() {
libraryHandler?.syncthingClient { syncthingClient ->
FileUploadDialog(this@FolderBrowserActivity, syncthingClient, intent!!.data,
indexBrowser.folder, indexBrowser.currentPath,
{ showFolderListView(indexBrowser.currentPath) } )
{ showFolderListView(indexBrowser.currentPath) }).show()
}
}
}
@@ -88,7 +88,7 @@ class FolderBrowserActivity : SyncthingActivity() {
binding.progressBar.visibility = View.VISIBLE
} else {
Log.i(TAG, "pulling file = " + fileInfo)
libraryHandler?.syncthingClient { FileDownloadDialog(this, it, fileInfo) }
libraryHandler?.syncthingClient { FileDownloadDialog(this, it, fileInfo).show() }
}
}
}
@@ -0,0 +1,157 @@
package net.syncthing.lite.activities
import android.content.Intent
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.github.paolorotolo.appintro.AppIntro
import com.google.zxing.integration.android.IntentIntegrator
import net.syncthing.java.core.beans.DeviceId
import net.syncthing.lite.R
import net.syncthing.lite.databinding.FragmentIntroOneBinding
import net.syncthing.lite.databinding.FragmentIntroThreeBinding
import net.syncthing.lite.databinding.FragmentIntroTwoBinding
import net.syncthing.lite.fragments.SyncthingFragment
import net.syncthing.lite.utils.FragmentIntentIntegrator
import net.syncthing.lite.utils.Util
import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.intentFor
import java.io.IOException
/**
* Shown when a user first starts the app. Shows some info and helps the user to add their first
* device and folder.
*/
class IntroActivity : AppIntro() {
/**
* Initialize fragments and library parameters.
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Disable continue button on second slide until a valid device ID is entered.
nextButton.setOnClickListener {
val fragment = fragments[pager.currentItem]
if (fragment !is IntroFragmentTwo || fragment.isDeviceIdValid()) {
pager.goToNextSlide()
}
}
addSlide(IntroFragmentOne())
addSlide(IntroFragmentTwo())
addSlide(IntroFragmentThree())
setSeparatorColor(ContextCompat.getColor(this, android.R.color.primary_text_dark))
showSkipButton(true)
isProgressButtonEnabled = true
pager.isPagingEnabled = false
}
override fun onSkipPressed(currentFragment: Fragment) {
onDonePressed(currentFragment)
}
override fun onDonePressed(currentFragment: Fragment) {
defaultSharedPreferences.edit().putBoolean(MainActivity.PREF_IS_FIRST_START, false).apply()
startActivity(intentFor<MainActivity>())
finish()
}
/**
* Display some simple welcome text.
*/
class IntroFragmentOne : SyncthingFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return DataBindingUtil.inflate<FragmentIntroOneBinding>(
inflater, R.layout.fragment_intro_one, container, false).root
}
override fun onLibraryLoaded() {
super.onLibraryLoaded()
context?.let { SyncthingActivity.checkLocalDiscoveryPort(it) }
libraryHandler?.configuration { config ->
config.localDeviceName = Util.getDeviceName()
config.persistLater()
}
}
}
/**
* Display device ID entry field and QR scanner option.
*/
class IntroFragmentTwo : SyncthingFragment() {
private lateinit var binding: FragmentIntroTwoBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_intro_two, container, false)
binding.enterDeviceId!!.scanQrCode.setOnClickListener {
FragmentIntentIntegrator(this@IntroFragmentTwo).initiateScan()
}
binding.enterDeviceId!!.scanQrCode.setImageResource(R.drawable.ic_qr_code_white_24dp)
return binding.root
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent)
if (scanResult?.contents != null && scanResult.contents.isNotBlank()) {
binding.enterDeviceId!!.deviceId.setText(scanResult.contents)
binding.enterDeviceId!!.deviceIdHolder.isErrorEnabled = false
}
}
/**
* Checks if the entered device ID is valid. If yes, imports it and returns true. If not,
* sets an error on the textview and returns false.
*/
fun isDeviceIdValid(): Boolean {
return try {
val deviceId = binding.enterDeviceId!!.deviceId.text.toString()
Util.importDeviceId(libraryHandler, context, deviceId, { })
true
} catch (e: IOException) {
binding.enterDeviceId!!.deviceId.error = getString(R.string.invalid_device_id)
false
}
}
}
/**
* Waits until remote device connects with new folder.
*/
class IntroFragmentThree : SyncthingFragment() {
private lateinit var binding: FragmentIntroThreeBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_intro_three, container, false)
return binding.root
}
override fun onLibraryLoaded() {
super.onLibraryLoaded()
libraryHandler?.library { config, client, _ ->
client.addOnConnectionChangedListener(this::onConnectionChanged)
val deviceId = config.localDeviceId.deviceId
val desc = activity?.getString(R.string.intro_page_three_description, "<b>$deviceId</b>")
binding.description.text = Html.fromHtml(desc)
}
}
private fun onConnectionChanged(deviceId: DeviceId) {
libraryHandler?.library { config, client, _ ->
if (config.folders.isNotEmpty()) {
client.removeOnConnectionChangedListener(this::onConnectionChanged)
(activity as IntroActivity?)?.onDonePressed(this)
}
}
}
}
}
@@ -4,6 +4,7 @@ import android.app.AlertDialog
import android.content.res.Configuration
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.ActionBarDrawerToggle
import android.view.Gravity
import android.view.MenuItem
@@ -11,19 +12,30 @@ import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import net.syncthing.lite.R
import net.syncthing.lite.databinding.ActivityMainBinding
import net.syncthing.lite.dialogs.DeviceIdDialog
import net.syncthing.lite.fragments.DevicesFragment
import net.syncthing.lite.fragments.FoldersFragment
import net.syncthing.lite.fragments.SyncthingFragment
import net.syncthing.lite.fragments.SettingsFragment
import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.intentFor
class MainActivity : SyncthingActivity() {
companion object {
const val PREF_IS_FIRST_START = "net.syncthing.lite.activities.MainActivity.IS_FIRST_START"
}
private lateinit var binding: ActivityMainBinding
private var drawerToggle: ActionBarDrawerToggle? = null
private var currentFragment: SyncthingFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (defaultSharedPreferences.getBoolean(PREF_IS_FIRST_START, true)) {
startActivity(intentFor<IntroActivity>())
finish()
}
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
drawerToggle = ActionBarDrawerToggle(
@@ -67,6 +79,10 @@ class MainActivity : SyncthingActivity() {
when (menuItem.itemId) {
R.id.folders -> setContentFragment(FoldersFragment())
R.id.devices -> setContentFragment(DevicesFragment())
R.id.settings -> setContentFragment(SettingsFragment())
R.id.device_id -> libraryHandler?.configuration { config ->
DeviceIdDialog(this, config.localDeviceId).show()
}
R.id.clear_index -> AlertDialog.Builder(this)
.setTitle(getString(R.string.clear_cache_and_index_title))
.setMessage(getString(R.string.clear_cache_and_index_body))
@@ -79,8 +95,7 @@ class MainActivity : SyncthingActivity() {
return true
}
private fun setContentFragment(fragment: SyncthingFragment) {
currentFragment = fragment
private fun setContentFragment(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(R.id.content_frame, fragment)
@@ -1,6 +1,7 @@
package net.syncthing.lite.activities
import android.app.AlertDialog
import android.content.Context
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.design.widget.Snackbar
@@ -16,6 +17,18 @@ import org.slf4j.impl.HandroidLoggerAdapter
abstract class SyncthingActivity : AppCompatActivity() {
companion object {
fun checkLocalDiscoveryPort(context: Context) {
if (LibraryHandler.isListeningPortTaken) {
AlertDialog.Builder(context)
.setTitle(R.string.other_syncthing_instance_title)
.setMessage(R.string.other_syncthing_instance_message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
}
var libraryHandler: LibraryHandler? = null
private set
private var loadingDialog: AlertDialog? = null
@@ -63,5 +76,7 @@ abstract class SyncthingActivity : AppCompatActivity() {
snackBar = null
}
open fun onLibraryLoaded() {}
open fun onLibraryLoaded() {
checkLocalDiscoveryPort(this)
}
}
@@ -0,0 +1,92 @@
package net.syncthing.lite.dialogs
import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.databinding.DataBindingUtil
import android.graphics.Bitmap
import android.graphics.Color
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import com.google.zxing.BarcodeFormat
import com.google.zxing.WriterException
import com.google.zxing.qrcode.QRCodeWriter
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import net.syncthing.java.core.beans.DeviceId
import net.syncthing.lite.R
import net.syncthing.lite.databinding.DialogDeviceIdBinding
import org.jetbrains.anko.doAsync
class DeviceIdDialog(private val context: Context, private val deviceId: DeviceId) {
private val Tag = "DeviceIdDialog"
private val binding = DataBindingUtil.inflate<DialogDeviceIdBinding>(
LayoutInflater.from(context), R.layout.dialog_device_id, null, false)
fun show() {
generateQrCode()
binding.deviceId.text = deviceId.deviceId
// Make QR code/progress bar views rectangular based on match_parent height.
binding.progressBar.post {
binding.progressBar.minimumHeight = binding.progressBar.width
binding.qrCode.minimumHeight = binding.progressBar.width
}
binding.deviceId.setOnClickListener({ copyDeviceId() })
binding.share.setOnClickListener { }
binding.share.setOnClickListener({ shareDeviceId() })
val qrCodeDialog = AlertDialog.Builder(context)
.setTitle(context.getString(R.string.device_id))
.setView(binding.root)
.setPositiveButton(android.R.string.ok, null)
.create()
qrCodeDialog.show()
}
private fun generateQrCode() {
doAsync {
val writer = QRCodeWriter()
try {
val bitMatrix = writer.encode(deviceId.deviceId, BarcodeFormat.QR_CODE, 512, 512)
val width = bitMatrix.width
val height = bitMatrix.height
val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
for (x in 0 until width) {
for (y in 0 until height) {
bmp.setPixel(x, y, if (bitMatrix.get(x, y)) Color.BLACK else Color.WHITE)
}
}
async(UI) {
binding.progressBar.visibility = View.GONE
binding.qrCode.visibility = View.VISIBLE
binding.qrCode.setImageBitmap(bmp)
}
} catch (e: WriterException) {
Log.w(Tag, e)
}
}
}
private fun copyDeviceId() {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(context.getString(R.string.device_id), deviceId.deviceId)
clipboard.primaryClip = clip
Toast.makeText(context, context.getString(R.string.device_id_copied), Toast.LENGTH_SHORT)
.show()
}
private fun shareDeviceId() {
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.type = "text/plain"
shareIntent.putExtra(Intent.EXTRA_TEXT, deviceId.deviceId)
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_device_id_chooser)))
}
}
@@ -1,6 +1,5 @@
package net.syncthing.lite.utils
package net.syncthing.lite.dialogs
import android.app.AlertDialog
import android.app.ProgressDialog
import android.content.ActivityNotFoundException
import android.content.Context
@@ -21,14 +20,14 @@ import org.jetbrains.anko.newTask
import org.jetbrains.anko.toast
import java.io.File
class FileDownloadDialog(context: Context, syncthingClient: SyncthingClient,
private val fileInfo: FileInfo) : AlertDialog(context) {
class FileDownloadDialog(private val context: Context, private val syncthingClient: SyncthingClient,
private val fileInfo: FileInfo) {
private val Tag = "FileDownloadDialog"
private lateinit var progressDialog: ProgressDialog
private var downloadFileTask: DownloadFileTask? = null
init {
fun show() {
showDialog()
doAsync {
downloadFileTask = DownloadFileTask(context, syncthingClient, fileInfo,
@@ -1,4 +1,4 @@
package net.syncthing.lite.utils
package net.syncthing.lite.dialogs
import android.app.ProgressDialog
import android.content.Context
@@ -9,18 +9,19 @@ import net.syncthing.java.bep.BlockPusher
import net.syncthing.java.client.SyncthingClient
import net.syncthing.lite.R
import net.syncthing.lite.library.UploadFileTask
import net.syncthing.lite.utils.Util
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
class FileUploadDialog(private val context: Context, private val syncthingClient: SyncthingClient,
private val localFile: Uri, private val syncthingFolder: String,
syncthingSubFolder: String,
private val syncthingSubFolder: String,
private val onUploadCompleteListener: () -> Unit) {
private lateinit var progressDialog: ProgressDialog
private var uploadFileTask: UploadFileTask? = null
init {
fun show() {
showDialog()
doAsync {
uploadFileTask = UploadFileTask(context, syncthingClient, localFile, syncthingFolder,
@@ -9,48 +9,40 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.Toast
import com.google.zxing.integration.android.IntentIntegrator
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import net.syncthing.java.core.beans.DeviceId
import net.syncthing.java.core.beans.DeviceInfo
import net.syncthing.lite.R
import net.syncthing.lite.adapters.DevicesAdapter
import net.syncthing.lite.databinding.FragmentDevicesBinding
import net.syncthing.lite.databinding.ViewEnterDeviceIdBinding
import net.syncthing.lite.utils.FragmentIntentIntegrator
import org.apache.commons.lang3.StringUtils.isBlank
import uk.co.markormesher.android_fab.SpeedDialMenuAdapter
import uk.co.markormesher.android_fab.SpeedDialMenuItem
import net.syncthing.lite.utils.Util
import java.io.IOException
import java.security.InvalidParameterException
class DevicesFragment : SyncthingFragment() {
private lateinit var binding: FragmentDevicesBinding
private lateinit var adapter: DevicesAdapter
private var addDeviceDialog: AlertDialog? = null
private var addDeviceDialogBinding: ViewEnterDeviceIdBinding? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_devices, container, false)
binding.list.emptyView = binding.empty
binding.fab.speedDialMenuAdapter = FabMenuAdapter()
binding.addDevice.setOnClickListener { showDialog() }
return binding.root
}
override fun onResume() {
super.onResume()
libraryHandler?.syncthingClient { it.addOnConnectionChangedListener(this::onConnectionChanged) }
libraryHandler?.syncthingClient { it.addOnConnectionChangedListener { updateDeviceList() } }
}
override fun onPause() {
super.onPause()
libraryHandler?.syncthingClient { it.removeOnConnectionChangedListener(this::onConnectionChanged) }
}
private fun onConnectionChanged(deviceId: DeviceId) {
updateDeviceList()
libraryHandler?.syncthingClient { it.removeOnConnectionChangedListener{ updateDeviceList() } }
}
override fun onLibraryLoaded() {
@@ -90,71 +82,42 @@ class DevicesFragment : SyncthingFragment() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
// Check if this was a QR code scan.
val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent)
if (scanResult != null) {
val deviceId = scanResult.contents
if (!isBlank(deviceId)) {
importDeviceId(deviceId)
}
if (scanResult?.contents != null && scanResult.contents.isNotBlank()) {
addDeviceDialogBinding?.deviceId?.setText(scanResult.contents)
}
}
private fun importDeviceId(deviceIdString: String) {
libraryHandler?.configuration { configuration ->
async(UI) {
val deviceId =
try {
DeviceId(deviceIdString)
} catch (e: IOException) {
Toast.makeText(this@DevicesFragment.context, R.string.invalid_device_id, Toast.LENGTH_SHORT).show()
return@async
private fun showDialog() {
addDeviceDialogBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.view_enter_device_id, null, false)
addDeviceDialogBinding?.let { binding ->
binding.scanQrCode.setOnClickListener {
FragmentIntentIntegrator(this@DevicesFragment).initiateScan()
}
binding.deviceId.post {
val imm = context!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(binding.deviceId, InputMethodManager.SHOW_IMPLICIT)
}
addDeviceDialog = AlertDialog.Builder(context)
.setTitle(R.string.device_id_dialog_title)
.setView(binding.root)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(android.R.string.cancel, null)
.show()
// Use different listener to keep dialog open after button click.
// https://stackoverflow.com/a/15619098
addDeviceDialog?.getButton(AlertDialog.BUTTON_POSITIVE)
?.setOnClickListener {
try {
val deviceId = binding.deviceId.text.toString()
Util.importDeviceId(libraryHandler, context, deviceId, { updateDeviceList() })
addDeviceDialog?.dismiss()
} catch (e: IOException) {
binding.deviceId.error = getString(R.string.invalid_device_id)
}
}
if (!configuration.peerIds.contains(deviceId)) {
configuration.peers = configuration.peers + DeviceInfo(deviceId, null)
configuration.persistLater()
Toast.makeText(this@DevicesFragment.context, getString(R.string.device_import_success, deviceId), Toast.LENGTH_SHORT).show()
updateDeviceList()//TODO remove this if event triggered (and handler trigger update)
} else {
Toast.makeText(this@DevicesFragment.context, getString(R.string.device_already_known, deviceId), Toast.LENGTH_SHORT).show()
}
}
}
}
private inner class FabMenuAdapter : SpeedDialMenuAdapter() {
override fun getCount(): Int {
return 2
}
override fun getMenuItem(context: Context, position: Int): SpeedDialMenuItem {
when (position) {
0 -> return SpeedDialMenuItem(context, R.drawable.ic_qr_code_white_24dp, R.string.scan_qr_code)
1 -> return SpeedDialMenuItem(context, R.drawable.ic_edit_white_24dp, R.string.enter_device_id)
}
throw InvalidParameterException()
}
override fun onMenuItemClick(position: Int): Boolean {
when (position) {
0 -> FragmentIntentIntegrator(this@DevicesFragment).initiateScan()
1 -> {
val editText = EditText(context)
val dialog = AlertDialog.Builder(context)
.setTitle(R.string.device_id_dialog_title)
.setView(editText)
.setPositiveButton(android.R.string.ok) { _, _ -> importDeviceId(editText.text.toString()) }
.setNegativeButton(android.R.string.cancel, null)
.create()
dialog.setOnShowListener {
val imm = context!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
}
dialog.show()
}
}
return true
}
}
}
@@ -0,0 +1,31 @@
package net.syncthing.lite.fragments
import android.os.Bundle
import android.support.v7.preference.EditTextPreference
import android.support.v7.preference.PreferenceFragmentCompat
import net.syncthing.lite.R
import net.syncthing.lite.activities.SyncthingActivity
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences)
val localDeviceName = findPreference("local_device_name") as EditTextPreference
val appVersion = findPreference("app_version")
(activity as SyncthingActivity?)?.let { activity ->
val versionName = activity.packageManager.getPackageInfo(activity.packageName, 0)?.versionName
appVersion.summary = versionName
activity.libraryHandler?.configuration { localDeviceName.text = it.localDeviceName }
localDeviceName.setOnPreferenceChangeListener { _, _ ->
activity.libraryHandler?.configuration { conf ->
conf.localDeviceName = localDeviceName.text
conf.persistLater()
}
true
}
}
}
}
@@ -11,8 +11,10 @@ import net.syncthing.java.core.beans.FileInfo
import net.syncthing.java.core.beans.FolderInfo
import net.syncthing.java.core.beans.IndexInfo
import net.syncthing.java.core.configuration.Configuration
import net.syncthing.lite.utils.Util
import org.jetbrains.anko.doAsync
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.SocketException
import java.util.*
class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit,
@@ -26,6 +28,7 @@ class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit
private var folderBrowser: FolderBrowser? = null
private val callbacks = ArrayList<(Configuration, SyncthingClient, FolderBrowser) -> Unit>()
private var isLoading = false
var isListeningPortTaken = false
}
private val TAG = "LibraryHandler"
@@ -35,6 +38,7 @@ class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit
if (configuration == null && !isLoading) {
isLoading = true
doAsync {
checkIsListeningPortTaken()
init(context)
async(UI) {
onLibraryLoaded(this@LibraryHandler)
@@ -68,11 +72,8 @@ class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit
}
private fun init(context: Context) {
val configuration = Configuration(configFolder = context.filesDir, cacheFolder = context.externalCacheDir)
configuration.localDeviceName = Util.getDeviceName()
configuration.persistLater()
val configuration = Configuration(configFolder = context.filesDir)
val syncthingClient = SyncthingClient(configuration)
//TODO listen for device events, update device list
val folderBrowser = syncthingClient.indexHandler.newFolderBrowser()
if (instanceCount == 0) {
@@ -89,7 +90,7 @@ class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit
LibraryHandler.folderBrowser = folderBrowser
}
private fun library(callback: (Configuration, SyncthingClient, FolderBrowser) -> Unit) {
fun library(callback: (Configuration, SyncthingClient, FolderBrowser) -> Unit) {
val nullCount = listOf(configuration, syncthingClient, folderBrowser).count { it == null }
assert(nullCount == 0 || nullCount == 3, { "Inconsistent library state" })
@@ -118,6 +119,19 @@ class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit
library { _, _, f -> callback(f) }
}
/**
* Check if listening port for local discovery is taken by another app. Do this check here to
* avoid adding another callback.
*/
private fun checkIsListeningPortTaken() {
try {
DatagramSocket(21027, InetAddress.getByName("0.0.0.0")).close()
} catch (e: SocketException) {
Log.w(TAG, e)
isListeningPortTaken = true
}
}
/**
* Unregisters index update listener and decreases instance count.
*
@@ -4,8 +4,17 @@ import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.OpenableColumns
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import net.syncthing.java.core.beans.DeviceId
import net.syncthing.java.core.beans.DeviceInfo
import net.syncthing.lite.R
import net.syncthing.lite.library.LibraryHandler
import org.apache.commons.lang3.StringUtils.capitalize
import org.jetbrains.anko.toast
import java.io.IOException
import java.security.InvalidParameterException
import java.util.*
object Util {
@@ -29,4 +38,24 @@ object Util {
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
}
@Throws(IOException::class)
fun importDeviceId(libraryHandler: LibraryHandler?, context: Context?, deviceId: String,
onComplete: () -> Unit) {
val deviceId2 = DeviceId(deviceId.toUpperCase(Locale.US))
libraryHandler?.configuration { configuration ->
if (!configuration.peerIds.contains(deviceId2)) {
configuration.peers = configuration.peers + DeviceInfo(deviceId2, null)
configuration.persistLater()
async(UI) {
context?.toast(context.getString(R.string.device_import_success, deviceId2.shortId))
onComplete()
}
} else {
async(UI) {
context?.toast(context.getString(R.string.device_already_known, deviceId2.shortId))
}
}
}
}
}
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,11H5V13H3V11M11,5H13V9H11V5M9,11H13V15H11V13H9V11M15,11H17V13H19V11H21V13H19V15H21V19H19V21H17V19H13V21H11V17H15V15H17V13H15V11M19,19V15H17V19H19M15,3H21V9H15V3M17,5V7H19V5H17M3,3H9V9H3V3M5,5V7H7V5H5M3,15H9V21H3V15M5,17V19H7V17H5Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#7D000000"
android:pathData="M3,11H5V13H3V11M11,5H13V9H11V5M9,11H13V15H11V13H9V11M15,11H17V13H19V11H21V13H19V15H21V19H19V21H17V19H13V21H11V17H15V15H17V13H15V11M19,19V15H17V19H19M15,3H21V9H15V3M17,5V7H19V5H17M3,3H9V9H3V3M5,5V7H7V5H5M3,15H9V21H3V15M5,17V19H7V17H5Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#7D000000"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/device_id"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:padding="8dp"
android:clickable="true"
android:drawableEnd="@drawable/ic_content_copy_black_24dp"
android:focusable="true"
android:fontFamily="monospace"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="ASD1ASD-ASD1ASD-ASD1ASD-ASD1ASD-ASD1ASD-ASD1ASD-ASD1ASD-ASD1ASD"/>
<TextView
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="8dp"
android:drawableEnd="@drawable/ic_share_black_24dp"
android:clickable="true"
android:focusable="true" />
</LinearLayout>
<ImageView
android:id="@+id/qr_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:visibility="gone"/>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>
</layout>
+9 -6
View File
@@ -13,6 +13,7 @@
android:divider="@color/divider"
android:dividerHeight="2dp">
</ListView>
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
@@ -22,12 +23,14 @@
android:textSize="20sp"
android:visibility="gone" />
<uk.co.markormesher.android_fab.FloatingActionButton
android:id="@+id/fab"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:buttonIcon="@drawable/ic_add_white_24dp"
app:buttonBackgroundColour="@color/accent"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/add_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
app:useCompatPadding="true"
android:src="@drawable/ic_add_white_24dp" />
</RelativeLayout>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/intro_primary"
android:padding="28dp"
android:gravity="center_horizontal">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:textColor="#eee"
android:textSize="24sp"
android:text="@string/intro_page_one_title"/>
<ImageView
android:layout_width="160dp"
android:layout_height="0dp"
android:layout_weight="1"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="@dimen/appIntroBottomBarHeight"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="#eee"
android:text="@string/intro_page_one_description" />
</LinearLayout>
</layout>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/intro_primary"
android:padding="28dp"
android:gravity="center_horizontal">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:textColor="#eee"
android:textSize="24sp"
android:text="@string/intro_page_three_title"/>
<ProgressBar
android:layout_width="72dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="@dimen/appIntroBottomBarHeight"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="#eee"
tools:text="@string/intro_page_three_description" />
</LinearLayout>
</layout>
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/intro_primary"
android:padding="28dp"
android:gravity="center_horizontal">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:textColor="#eee"
android:textSize="24sp"
android:text="@string/intro_page_two_title"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center">
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/enter_device_id"
layout="@layout/view_enter_device_id" />
</FrameLayout>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="@dimen/appIntroBottomBarHeight"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="#eee"
android:text="@string/intro_page_two_description" />
</LinearLayout>
</layout>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<android.support.design.widget.TextInputLayout
android:id="@+id/device_id_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/device_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minLines="3"
android:inputType="textNoSuggestions|textMultiLine|textCapCharacters"
tools:text="VPNPKMK-VL2SOQN-SS5I2AB-G4BV7ZK-RO5ODEE-Y2G3CZ4-C4FUW4P-ZEMJOAF"/>
</android.support.design.widget.TextInputLayout>
<ImageButton
android:id="@+id/scan_qr_code"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:padding="8dp"
android:layout_marginBottom="8dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_qr_code_black_24dp"
android:background="?android:selectableItemBackgroundBorderless"/>
</LinearLayout>
</layout>
+11
View File
@@ -13,6 +13,17 @@
android:icon="@drawable/ic_laptop_gray_24dp"
android:title="@string/devices_label" />
<item
android:id="@+id/settings"
android:icon="@drawable/ic_settings_gray_24dp"
android:title="@string/settings" />
<item
android:id="@+id/device_id"
android:icon="@drawable/ic_qr_code_black_24dp"
android:title="@string/show_device_id"
android:checkable="false" />
<item
android:id="@+id/clear_index"
android:icon="@drawable/ic_delete_gray_24dp"
+1 -4
View File
@@ -5,7 +5,6 @@
<string name="devices_list_view_empty_message">Keine Geräte verfügbar</string>
<string name="scan_qr_code">QR code scannen</string>
<string name="enter_device_id">Geräte ID eingeben</string>
<string name="invalid_device_id">Ungültige Geräte ID</string>
<string name="device_id_dialog_title">Geräte ID eingeben</string>
<string name="dialog_downloading_file">Datei %1$s wird heruntergeladen</string>
<string name="toast_file_download_failed">Datei konnte nicht heruntergeladen werden</string>
@@ -15,10 +14,8 @@
<string name="dialog_uploading_file">Datei %1$s wird hochgeladen</string>
<string name="clear_cache_and_index_title">Lokalen Cache und Index löschen?</string>
<string name="clear_cache_and_index_body">Gesamten lokalen Cache und Index löschen?</string>
<string name="loading_config_starting_syncthing_client">Konfiguartion wird geladen, Syncthing wird gestartet</string>
<string name="last_modified_time">Zuletzt modifiziert: %1$s</string>
<string name="remove_device_title">Gerät entfernen:</string>
<string name="remove_device_message">Gerät %1$s entfernen?</string>
<string name="device_import_success">Gerät %1$s erfolgreich importiert</string>
<string name="device_already_known">Gerät ist bereits bekannt $1%s</string>
<string name="folders_label">Ordner</string>
@@ -26,4 +23,4 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d Dateien, %3$d Ordner</string>
<string name="file_info">%1$s, zuletzt modifiziert %2$s</string>
</resources>
</resources>
+1 -4
View File
@@ -5,7 +5,6 @@
<string name="devices_list_view_empty_message">Aucun appareil disponible</string>
<string name="scan_qr_code">Scanner le QR Code</string>
<string name="enter_device_id">Entrer l\'ID de l\'appareil</string>
<string name="invalid_device_id">ID de l\'appareil invalide</string>
<string name="device_id_dialog_title">Entrer l\'ID de l\'appareil</string>
<string name="dialog_downloading_file">Téléchargement du fichier %1$s</string>
<string name="toast_file_download_failed">Le téléchargement du fichier a échoué</string>
@@ -15,10 +14,8 @@
<string name="dialog_uploading_file">Upload du fichier %1$s</string>
<string name="clear_cache_and_index_title">Effacer le cache local et l\'index?</string>
<string name="clear_cache_and_index_body">Effacer toutes les données du cache local et de l\'index ?</string>
<string name="loading_config_starting_syncthing_client">Chargement de la configuration, démarrage du client Syncthing</string>
<string name="last_modified_time">Dernière modification : %1$s</string>
<string name="remove_device_title">Supprimer l\'appareil %1$s\?</string>
<string name="remove_device_message">Supprimer l\'appareil %1$s de la liste des appareil connus ?</string>
<string name="device_import_success">Appareil %1$s importé avec succès</string>
<string name="device_already_known">Appareil déjà connu %1$s</string>
<string name="folders_label">Dossiers</string>
@@ -26,4 +23,4 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d fichiers, %3$d dossiers</string>
<string name="file_info">%1$s, dernière modification %2$s</string>
</resources>
</resources>
+2 -4
View File
@@ -5,7 +5,6 @@
<string name="devices_list_view_empty_message">Nessun dispositivo disponibile</string>
<string name="scan_qr_code">Scansiona codice QR</string>
<string name="enter_device_id">Inserisci ID dispositivo</string>
<string name="invalid_device_id">ID dispositivo non valido</string>
<string name="device_id_dialog_title">Inserisci ID Dispositivo</string>
<string name="dialog_downloading_file">Download del file %1$s</string>
<string name="toast_file_download_failed">Impossibile scaricare il file</string>
@@ -15,10 +14,9 @@
<string name="dialog_uploading_file">Caricamento del file %1$s</string>
<string name="clear_cache_and_index_title">Cancellare la cache locale e l\'indice?</string>
<string name="clear_cache_and_index_body">Cancellare tutti i dati della cache locale e i dati dell\'indice?</string>
<string name="loading_config_starting_syncthing_client">Caricamento configurazione, avvio del client syncthing</string>
<string name="index_update_progress_label">Aggiornamento dell\'indice per la cartella %1$s, %2$d%% sincronizzato</string>
<string name="last_modified_time">Ultima modifica: %1$s</string>
<string name="remove_device_title">Rimuovere il dispositivo %1$s\?</string>
<string name="remove_device_message">Rimuovere il dispositivo %1$s dalla lista dei dispositivi noti?</string>
<string name="device_import_success">Dispositivo %1$s importato con successo</string>
<string name="device_already_known">Dispositivo %1$s già presente</string>
<string name="folders_label">Cartelle</string>
@@ -26,4 +24,4 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d file, %3$d cartelle</string>
<string name="file_info">%1$s, ultima modifica %2$s</string>
</resources>
</resources>
+2 -4
View File
@@ -5,7 +5,6 @@
<string name="devices_list_view_empty_message">デバイスがありません</string>
<string name="scan_qr_code">QR コードをスキャン</string>
<string name="enter_device_id">デバイス ID を入力</string>
<string name="invalid_device_id">デバイス ID が無効です</string>
<string name="device_id_dialog_title">デバイス ID を入力</string>
<string name="dialog_downloading_file">ファイル %1$s のダウンロード中</string>
<string name="toast_file_download_failed">ファイルのダウンロードに失敗しました</string>
@@ -15,10 +14,9 @@
<string name="dialog_uploading_file">ファイル %1$s のアップロード中</string>
<string name="clear_cache_and_index_title">ローカルキャッシュと索引をクリアしますか?</string>
<string name="clear_cache_and_index_body">すべてのローカルキャッシュデータと索引データをクリアしますか?</string>
<string name="loading_config_starting_syncthing_client">設定の読み込み中、syncthing クライアントの開始中</string>
<string name="index_update_progress_label">フォルダー %1$s の索引を更新しました。 %2$d%% 同期しました</string>
<string name="last_modified_time">最終更新: %1$s</string>
<string name="remove_device_title">デバイス %1$sを削除しますか?</string>
<string name="remove_device_message">既存のデバイスリストからデバイス %1$s を削除しますか?</string>
<string name="device_import_success">デバイス %1$s のインポートに成功しました</string>
<string name="device_already_known">デバイスは既に存在します %1$s</string>
<string name="folders_label">フォルダー</string>
@@ -26,4 +24,4 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d ファイル, %3$d ディレクトリー</string>
<string name="file_info">%1$s, 最終更新 %2$s</string>
</resources>
</resources>
+1 -4
View File
@@ -5,7 +5,6 @@
<string name="devices_list_view_empty_message">Nici un dispozitiv disponibil</string>
<string name="scan_qr_code">Scanează cod QR</string>
<string name="enter_device_id">Introduceți ID dispozitiv</string>
<string name="invalid_device_id">ID dispozitiv invalid</string>
<string name="device_id_dialog_title">Introduceți ID dispozitiv</string>
<string name="dialog_downloading_file">Se descarcă fișierul %1$s</string>
<string name="toast_file_download_failed">Descărcarea fișierului a eșuat</string>
@@ -15,10 +14,8 @@
<string name="dialog_uploading_file">Se încarcă fișierul %1$s</string>
<string name="clear_cache_and_index_title">Se curăță memoria locală și indexul?</string>
<string name="clear_cache_and_index_body">Se curăță datele din memoria locală și datele indexului?</string>
<string name="loading_config_starting_syncthing_client">Încărcare setări, pornire client syncthing…</string>
<string name="last_modified_time">Modificat ultima dată pe: %1$s</string>
<string name="remove_device_title">Ștergere dispozitiv %1$s\?</string>
<string name="remove_device_message">Se va șterge %1$s din lista dispozitivelor cunoscute?</string>
<string name="device_import_success">Dispozitiv importat cu succes %1$s</string>
<string name="device_already_known">Dispozitiv deja prezent %1$s</string>
<string name="folders_label">Directoare</string>
@@ -26,4 +23,4 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d fișier(e), %3$d director(oare)</string>
<string name="file_info">%1$s, modificat ultima dată pe %2$s</string>
</resources>
</resources>
+4 -2
View File
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#f43703</color>
<color name="primary_dark">#d13602</color>
<color name="primary_light">#ff6f39</color>
<color name="primary_dark">#b90000</color>
<color name="accent">#FFC107</color>
<color name="divider">#1F000000</color>
<color name="intro_primary">#ff5252</color>
<color name="intro_primary_dark">#c50e29</color>
</resources>
+19 -3
View File
@@ -5,7 +5,7 @@
<string name="devices_list_view_empty_message">No devices available</string>
<string name="scan_qr_code">Scan QR code</string>
<string name="enter_device_id">Enter device ID</string>
<string name="invalid_device_id">Invalid device ID</string>
<string name="invalid_device_id">Error: Invalid device ID</string>
<string name="device_id_dialog_title">Enter Device ID</string>
<string name="dialog_downloading_file">Downloading file %1$s</string>
<string name="toast_file_download_failed">Failed to download file</string>
@@ -16,10 +16,10 @@
<string name="clear_cache_and_index_title">Clear local cache and index?</string>
<string name="clear_cache_and_index_body">Clear all local cache data and index data?</string>
<string name="index_update_progress_label">Index update for folder %1$s, %2$d%% synchronized</string>
<string name="loading_config_starting_syncthing_client">Loading config, starting syncthing client</string>
<string name="loading_config_starting_syncthing_client">Loading config, starting syncthing client</string>
<string name="last_modified_time">Last modified: %1$s</string>
<string name="remove_device_title">Remove device %1$s\?</string>
<string name="remove_device_message">Remove device %1$s from list of known devices?</string>
<string name="remove_device_message">Remove %1$s from the list of known devices?</string>
<string name="device_import_success">Successfully imported device %1$s</string>
<string name="device_already_known">Device already present %1$s</string>
<string name="folders_label">Folders</string>
@@ -27,4 +27,20 @@
<string name="folder_label_format">%1$s (%2$s)</string>
<string name="folder_content_info">%1$s, %2$d files, %3$d directories</string>
<string name="file_info">%1$s, last modified %2$s</string>
<string name="show_device_id">Show device ID</string>
<string name="device_id">Device ID</string>
<string name="device_id_copied">Device ID copied to clipboard</string>
<string name="share_device_id_chooser">Share device ID with</string>
<string name="other_syncthing_instance_title">Another Syncthing instance is running</string>
<string name="other_syncthing_instance_message">Local discovery will not work. Stop the other Syncthing instance to enable local discovery.</string>
<string name="intro_page_one_title">Welcome to Syncthing Lite</string>
<string name="intro_page_one_description">Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it\'s transmitted over the Internet.</string>
<string name="intro_page_two_title">Add a device</string>
<string name="intro_page_three_title">Share your folders</string>
<string name="intro_page_two_description">Enter a Syncthing device ID, or scan a device ID from a QR code</string>
<string name="intro_page_three_description">Now accept the device with ID %1$s, and share a folder with it. It may take a few minutes until the devices connect.</string>
<string name="settings">Settings</string>
<string name="settings_app_version_title">App version</string>
<string name="settings_local_device_name">Local device name</string>
<string name="settings_local_device_summary">The name that other devices will see for this device</string>
</resources>
+9
View File
@@ -4,6 +4,15 @@
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style>
<style name="Theme.Syncthing.NoActionBar" parent="@style/Theme.AppCompat">
<item name="colorPrimary">@color/intro_primary</item>
<item name="colorPrimaryDark">@color/intro_primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/settings">
<EditTextPreference
android:key="local_device_name"
android:title="@string/settings_local_device_name"
android:summary="@string/settings_local_device_summary"
android:persistent="false"/>
<Preference
android:key="app_version"
android:title="@string/settings_app_version_title"/>
</PreferenceCategory>
</PreferenceScreen>