Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 087b0f4ec1 | |||
| a6cc38f4a1 | |||
| 96a4ec1738 | |||
| 42b77cab12 | |||
| a28e5d704d | |||
| b924b50ddc | |||
| 96503dd9c1 | |||
| c83504c155 | |||
| acea170854 |
+7
-8
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "net.syncthing.lite"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 7
|
||||
versionName "0.1.5"
|
||||
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.1.5") {
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -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,
|
||||
+4
-3
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user