Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a3495784f7 | |||
| 82c92c9031 | |||
| bd5d89c158 | |||
| 9cf96d86dd | |||
| f4c1e6a0f0 | |||
| 036d3846bc | |||
| 6696f0ff88 |
+21
-4
@@ -10,8 +10,8 @@ android {
|
||||
applicationId "net.syncthing.lite"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 25
|
||||
versionCode 3
|
||||
versionName "0.1.2"
|
||||
versionCode 5
|
||||
versionName "0.1.3"
|
||||
multiDexEnabled true
|
||||
}
|
||||
sourceSets {
|
||||
@@ -21,17 +21,34 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile = {
|
||||
def path = System.getenv("SYNCTHING_LITE_RELEASE_STORE_FILE")
|
||||
return (path) ? file(path) : null
|
||||
}()
|
||||
storePassword System.getenv("SIGNING_PASSWORD") ?: ""
|
||||
keyAlias System.getenv("SYNCTHING_LITE_RELEASE_KEY_ALIAS") ?: ""
|
||||
keyPassword System.getenv("SIGNING_PASSWORD") ?: ""
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "org.jetbrains.anko:anko-commons:0.10.4"
|
||||
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:support-v4:$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.2") {
|
||||
implementation ("com.github.Nutomic:syncthing-java:0.1.3") {
|
||||
exclude group: 'commons-logging', module:'commons-logging'
|
||||
exclude group: 'commons-codec'
|
||||
exclude group: 'org.apache.httpcomponents', module:'httpclient'
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
android:parentActivityName=".activities.MainActivity"/>
|
||||
<activity android:name=".activities.FilePickerActivity"
|
||||
android:parentActivityName=".activities.FolderBrowserActivity" />
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="net.syncthing.lite.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.databinding.DataBindingUtil
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
@@ -15,8 +14,6 @@ import android.widget.Toast
|
||||
import com.google.common.base.Preconditions.checkArgument
|
||||
import net.syncthing.java.bep.IndexBrowser
|
||||
import net.syncthing.java.core.beans.FileInfo
|
||||
import net.syncthing.java.core.beans.FolderInfo
|
||||
import net.syncthing.java.core.utils.FileInfoOrdering
|
||||
import net.syncthing.java.core.utils.PathUtils
|
||||
import net.syncthing.lite.R
|
||||
import net.syncthing.lite.adapters.FolderContentsAdapter
|
||||
@@ -52,14 +49,10 @@ class FolderBrowserActivity : SyncthingActivity() {
|
||||
navigateToFolder(fileInfo)
|
||||
}
|
||||
val folder = intent.getStringExtra(EXTRA_FOLDER_NAME)
|
||||
indexBrowser = syncthingClient().indexHandler
|
||||
.newIndexBrowserBuilder()
|
||||
.setOrdering(FileInfoOrdering.ALPHA_ASC_DIR_FIRST)
|
||||
.includeParentInList(true)
|
||||
.allowParentInRoot(true)
|
||||
.setFolder(folder)
|
||||
.build()
|
||||
indexBrowser.setOnFolderChangedListener(this::onFolderChanged)
|
||||
libraryHandler?.syncthingClient {
|
||||
indexBrowser = it.indexHandler.newIndexBrowser(folder, true, true)
|
||||
indexBrowser.setOnFolderChangedListener(this::onFolderChanged)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -78,19 +71,22 @@ class FolderBrowserActivity : SyncthingActivity() {
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
if (requestCode == REQUEST_SELECT_UPLOAD_FILE && resultCode == Activity.RESULT_OK) {
|
||||
UploadFileTask(this, syncthingClient(), intent!!.data, indexBrowser.folder,
|
||||
indexBrowser.currentPath, { this.updateFolderListView() }).uploadFile()
|
||||
libraryHandler?.syncthingClient { syncthingClient ->
|
||||
UploadFileTask(this@FolderBrowserActivity, syncthingClient, intent!!.data,
|
||||
indexBrowser.folder, indexBrowser.currentPath,
|
||||
this@FolderBrowserActivity::showUploadHereDialog).uploadFile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFolderListView(path: String) {
|
||||
indexBrowser.navigateToNearestPath(path)
|
||||
navigateToFolder(indexBrowser.currentPathInfo)
|
||||
navigateToFolder(indexBrowser.currentPathInfo())
|
||||
}
|
||||
|
||||
private fun navigateToFolder(fileInfo: FileInfo) {
|
||||
Log.d(TAG, "navigate to path = '" + fileInfo.path + "' from path = '" + indexBrowser.currentPath + "'")
|
||||
if (indexBrowser.isRoot && PathUtils.isParent(fileInfo.path)) {
|
||||
if (indexBrowser.isRoot() && PathUtils.isParent(fileInfo.path)) {
|
||||
finish()
|
||||
} else {
|
||||
if (fileInfo.isDirectory) {
|
||||
@@ -101,7 +97,7 @@ class FolderBrowserActivity : SyncthingActivity() {
|
||||
} else {
|
||||
Log.i(TAG, "pulling file = " + fileInfo)
|
||||
executeWithPermissions(
|
||||
Runnable { DownloadFileTask(this, syncthingClient(), fileInfo).downloadFile() })
|
||||
Runnable { libraryHandler?.syncthingClient { DownloadFileTask(this, it, fileInfo).downloadFile() } })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,12 +114,12 @@ class FolderBrowserActivity : SyncthingActivity() {
|
||||
adapter.addAll(list)
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.listView.setSelection(0)
|
||||
val title =
|
||||
if (indexBrowser.isRoot)
|
||||
folderBrowser()?.getFolderInfo(indexBrowser.folder)?.label
|
||||
else
|
||||
indexBrowser.currentPathInfo.fileName
|
||||
supportActionBar!!.setTitle(title)
|
||||
if (indexBrowser.isRoot())
|
||||
libraryHandler?.folderBrowser {
|
||||
supportActionBar?.title = it.getFolderInfo(indexBrowser.folder)?.label
|
||||
}
|
||||
else
|
||||
supportActionBar?.title = indexBrowser.currentPathInfo().fileName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +133,10 @@ class FolderBrowserActivity : SyncthingActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
override fun onIndexUpdateProgress(folder: FolderInfo, percentage: Int) {
|
||||
override fun onIndexUpdateProgress(folder: String, percentage: Int) {
|
||||
binding.indexUpdate.visibility = View.VISIBLE
|
||||
binding.indexUpdateLabel.text = (getString(R.string.index_update_folder)
|
||||
+ folder.label + " " + percentage + getString(R.string.index_update_percent_synchronized))
|
||||
+ folder + " " + percentage + getString(R.string.index_update_percent_synchronized))
|
||||
updateFolderListView()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import android.support.v7.app.ActionBarDrawerToggle
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import net.syncthing.java.core.beans.FolderInfo
|
||||
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.fragments.DevicesFragment
|
||||
@@ -50,10 +51,6 @@ class MainActivity : SyncthingActivity() {
|
||||
onNavigationItemSelectedListener(selection)
|
||||
}
|
||||
|
||||
override fun onLibraryLoaded() {
|
||||
currentFragment?.onLibraryLoaded()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
drawerToggle!!.onConfigurationChanged(newConfig)
|
||||
@@ -72,7 +69,7 @@ class MainActivity : SyncthingActivity() {
|
||||
when (menuItem.itemId) {
|
||||
R.id.folders -> setContentFragment(FoldersFragment())
|
||||
R.id.devices -> setContentFragment(DevicesFragment())
|
||||
R.id.update_index -> UpdateIndexTask(this, syncthingClient()).updateIndex()
|
||||
R.id.update_index -> libraryHandler?.syncthingClient { UpdateIndexTask(this@MainActivity, it).updateIndex() }
|
||||
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))
|
||||
@@ -94,14 +91,16 @@ class MainActivity : SyncthingActivity() {
|
||||
}
|
||||
|
||||
private fun cleanCacheAndIndex() {
|
||||
syncthingClient().clearCacheAndIndex()
|
||||
recreate()
|
||||
async(UI) {
|
||||
libraryHandler?.syncthingClient { it.clearCacheAndIndex() }
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIndexUpdateProgress(folder: FolderInfo, percentage: Int) {
|
||||
override fun onIndexUpdateProgress(folder: String, percentage: Int) {
|
||||
binding.indexUpdate.visibility = View.VISIBLE
|
||||
binding.indexUpdateLabel.text = (getString(R.string.index_update_folder) + " "
|
||||
+ folder.label + " " + percentage + getString(R.string.index_update_percent_synchronized))
|
||||
+ folder + " " + percentage + getString(R.string.index_update_percent_synchronized))
|
||||
}
|
||||
|
||||
override fun onIndexUpdateComplete() {
|
||||
|
||||
@@ -5,81 +5,48 @@ import android.databinding.DataBindingUtil
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.LayoutInflater
|
||||
import net.syncthing.java.bep.FolderBrowser
|
||||
import net.syncthing.java.client.SyncthingClient
|
||||
import net.syncthing.java.core.beans.FolderInfo
|
||||
import net.syncthing.java.core.configuration.ConfigurationService
|
||||
import net.syncthing.lite.BuildConfig
|
||||
import net.syncthing.lite.R
|
||||
import net.syncthing.lite.databinding.DialogLoadingBinding
|
||||
import net.syncthing.lite.library.InitLibraryTask
|
||||
import net.syncthing.lite.library.LibraryHandler
|
||||
import org.slf4j.impl.HandroidLoggerAdapter
|
||||
|
||||
abstract class SyncthingActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private var activityCount = 0
|
||||
private var libraryHandler: LibraryHandler? = null
|
||||
}
|
||||
|
||||
var libraryHandler: LibraryHandler? = null
|
||||
private set
|
||||
private var loadingDialog: AlertDialog? = null
|
||||
|
||||
fun syncthingClient(): SyncthingClient {
|
||||
if (isDestroyed)
|
||||
throw IllegalStateException("activity is already destroyed")
|
||||
return libraryHandler!!.syncthingClient!!
|
||||
}
|
||||
|
||||
fun configuration(): ConfigurationService {
|
||||
if (isDestroyed)
|
||||
throw IllegalStateException("activity is already destroyed")
|
||||
return libraryHandler!!.configuration!!
|
||||
}
|
||||
|
||||
fun folderBrowser(): FolderBrowser? {
|
||||
if (isDestroyed)
|
||||
throw IllegalStateException("activity is already destroyed")
|
||||
return libraryHandler?.folderBrowser
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
HandroidLoggerAdapter.DEBUG = BuildConfig.DEBUG
|
||||
activityCount++
|
||||
if (libraryHandler == null) {
|
||||
val binding = DataBindingUtil.inflate<DialogLoadingBinding>(
|
||||
LayoutInflater.from(this), R.layout.dialog_loading, null, false)
|
||||
binding.loadingText.text = getString(R.string.loading_config_starting_syncthing_client)
|
||||
loadingDialog = AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setView(binding.root)
|
||||
.show()
|
||||
InitLibraryTask(this, this::onLibraryLoaded, this::onIndexUpdateProgress, this::onIndexUpdateComplete)
|
||||
}
|
||||
|
||||
val binding = DataBindingUtil.inflate<DialogLoadingBinding>(
|
||||
LayoutInflater.from(this), R.layout.dialog_loading, null, false)
|
||||
binding.loadingText.text = getString(R.string.loading_config_starting_syncthing_client)
|
||||
loadingDialog = AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setView(binding.root)
|
||||
.show()
|
||||
LibraryHandler(this, this::onLibraryLoadedInternal,
|
||||
this::onIndexUpdateProgress, this::onIndexUpdateComplete)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
activityCount--
|
||||
Thread {
|
||||
if (activityCount == 0) {
|
||||
libraryHandler?.destroy()
|
||||
libraryHandler = null
|
||||
}
|
||||
}.start()
|
||||
libraryHandler?.close()
|
||||
loadingDialog?.dismiss()
|
||||
}
|
||||
|
||||
private fun onLibraryLoaded(libraryHandler: LibraryHandler) {
|
||||
if (activityCount == 0)
|
||||
return
|
||||
|
||||
SyncthingActivity.libraryHandler = libraryHandler
|
||||
loadingDialog!!.cancel()
|
||||
private fun onLibraryLoadedInternal(libraryHandler: LibraryHandler) {
|
||||
this.libraryHandler = libraryHandler
|
||||
if (!isDestroyed) {
|
||||
loadingDialog?.dismiss()
|
||||
}
|
||||
onLibraryLoaded()
|
||||
}
|
||||
|
||||
open fun onIndexUpdateProgress(folder: FolderInfo, percentage: Int) {}
|
||||
open fun onIndexUpdateProgress(folder: String, percentage: Int) {}
|
||||
|
||||
open fun onIndexUpdateComplete() {}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ 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.DeviceInfo
|
||||
import net.syncthing.java.core.beans.DeviceStats
|
||||
import net.syncthing.java.core.security.KeystoreHandler
|
||||
@@ -43,7 +45,7 @@ class DevicesFragment : SyncthingFragment() {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onLibraryLoadedAndActivityCreated() {
|
||||
override fun onLibraryLoaded() {
|
||||
initDeviceList()
|
||||
updateDeviceList()
|
||||
}
|
||||
@@ -57,7 +59,8 @@ class DevicesFragment : SyncthingFragment() {
|
||||
.setTitle(getString(R.string.remove_device_title) + " " + deviceId.substring(0, 7) + "?")
|
||||
.setMessage(getString(R.string.remove_device_body_1) + " " + deviceId.substring(0, 7) + " " + getString(R.string.remove_device_body_2))
|
||||
.setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
getSyncthingActivity().configuration().edit().removePeer(deviceId).persistLater() }
|
||||
libraryHandler?.configuration { it.edit().removePeer(deviceId).persistLater() }
|
||||
}
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show()
|
||||
Log.d(TAG, "showFolderListView delete device = '$deviceId'")
|
||||
@@ -66,9 +69,11 @@ class DevicesFragment : SyncthingFragment() {
|
||||
}
|
||||
|
||||
private fun updateDeviceList() {
|
||||
adapter.clear()
|
||||
adapter.addAll(getSyncthingActivity().syncthingClient().devicesHandler.deviceStatsList)
|
||||
adapter.notifyDataSetChanged()
|
||||
libraryHandler?.syncthingClient { syncthingClient ->
|
||||
adapter.clear()
|
||||
adapter.addAll(syncthingClient.devicesHandler.deviceStatsList)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
@@ -83,21 +88,25 @@ class DevicesFragment : SyncthingFragment() {
|
||||
}
|
||||
|
||||
private fun importDeviceId(deviceId: String) {
|
||||
try {
|
||||
KeystoreHandler.validateDeviceId(deviceId)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.invalid_device_id, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
libraryHandler?.library { configuration, syncthingClient, _ ->
|
||||
async(UI) {
|
||||
try {
|
||||
KeystoreHandler.validateDeviceId(deviceId)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(this@DevicesFragment.context, R.string.invalid_device_id, Toast.LENGTH_SHORT).show()
|
||||
return@async
|
||||
}
|
||||
|
||||
val modified = getSyncthingActivity().configuration().edit().addPeers(DeviceInfo(deviceId, null))
|
||||
if (modified) {
|
||||
getSyncthingActivity().configuration().edit().persistLater()
|
||||
Toast.makeText(context, getString(R.string.device_import_success) + " " + deviceId, Toast.LENGTH_SHORT).show()
|
||||
updateDeviceList()//TODO remove this if event triggered (and handler trigger update)
|
||||
UpdateIndexTask(context!!, getSyncthingActivity().syncthingClient()).updateIndex()
|
||||
} else {
|
||||
Toast.makeText(context, getString(R.string.device_already_known) + " " + deviceId, Toast.LENGTH_SHORT).show()
|
||||
val modified = configuration.edit().addPeers(DeviceInfo(deviceId, null))
|
||||
if (modified) {
|
||||
configuration.edit().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)
|
||||
UpdateIndexTask(this@DevicesFragment.context!!, syncthingClient).updateIndex()
|
||||
} else {
|
||||
Toast.makeText(this@DevicesFragment.context, getString(R.string.device_already_known) + " " + deviceId, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ import java.util.*
|
||||
|
||||
class FoldersFragment : SyncthingFragment() {
|
||||
|
||||
companion object {
|
||||
private val TAG = "FoldersFragment"
|
||||
}
|
||||
private val TAG = "FoldersFragment"
|
||||
|
||||
private lateinit var binding: FragmentFoldersBinding
|
||||
|
||||
@@ -33,21 +31,23 @@ class FoldersFragment : SyncthingFragment() {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onLibraryLoadedAndActivityCreated() {
|
||||
override fun onLibraryLoaded() {
|
||||
showAllFoldersListView()
|
||||
}
|
||||
|
||||
private fun showAllFoldersListView() {
|
||||
val list = Lists.newArrayList(getSyncthingActivity().folderBrowser()!!.folderInfoAndStatsList)
|
||||
Collections.sort(list, Ordering.natural<Comparable<String>>()
|
||||
.onResultOf<Pair<FolderInfo, FolderStats>> { input -> input?.left?.label })
|
||||
Log.i(TAG, "list folders = " + list + " (" + list.size + " records)")
|
||||
val adapter = FoldersListAdapter(context, list)
|
||||
binding.list.adapter = adapter
|
||||
binding.list.setOnItemClickListener { _, _, position, _ ->
|
||||
val folder = adapter.getItem(position)!!.left.folder
|
||||
val intent = context?.intentFor<FolderBrowserActivity>(FolderBrowserActivity.EXTRA_FOLDER_NAME to folder)
|
||||
startActivity(intent)
|
||||
libraryHandler?.folderBrowser { folderBrowser ->
|
||||
val list = Lists.newArrayList(folderBrowser.folderInfoAndStatsList())
|
||||
Collections.sort(list, Ordering.natural<Comparable<String>>()
|
||||
.onResultOf<Pair<FolderInfo, FolderStats>> { input -> input?.left?.label })
|
||||
Log.i(TAG, "list folders = " + list + " (" + list.size + " records)")
|
||||
val adapter = FoldersListAdapter(context, list)
|
||||
binding.list.adapter = adapter
|
||||
binding.list.setOnItemClickListener { _, _, position, _ ->
|
||||
val folder = adapter.getItem(position)!!.left.folder
|
||||
val intent = context?.intentFor<FolderBrowserActivity>(FolderBrowserActivity.EXTRA_FOLDER_NAME to folder)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,30 +2,32 @@ package net.syncthing.lite.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import net.syncthing.lite.activities.SyncthingActivity
|
||||
import net.syncthing.lite.library.LibraryHandler
|
||||
|
||||
/**
|
||||
* Handle connection to [[SyncthingActivity]], and make sure device rotation are handled correctly.
|
||||
*/
|
||||
abstract class SyncthingFragment : Fragment() {
|
||||
|
||||
protected fun getSyncthingActivity() = activity as SyncthingActivity
|
||||
var libraryHandler: LibraryHandler? = null
|
||||
private set
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
checkConditions()
|
||||
LibraryHandler(context!!, this::onLibraryLoadedInternal, this::onIndexUpdateProgress,
|
||||
this::onIndexUpdateComplete)
|
||||
}
|
||||
|
||||
fun onLibraryLoaded() {
|
||||
checkConditions()
|
||||
private fun onLibraryLoadedInternal(libraryHandler: LibraryHandler) {
|
||||
this.libraryHandler = libraryHandler
|
||||
onLibraryLoaded()
|
||||
}
|
||||
|
||||
private fun checkConditions() {
|
||||
if (activity != null && getSyncthingActivity().folderBrowser() != null ) {
|
||||
onLibraryLoadedAndActivityCreated()
|
||||
}
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
libraryHandler?.close()
|
||||
}
|
||||
|
||||
open fun onLibraryLoadedAndActivityCreated() {
|
||||
}
|
||||
open fun onLibraryLoaded() {}
|
||||
|
||||
open fun onIndexUpdateProgress(folder: String, percentage: Int) {}
|
||||
|
||||
open fun onIndexUpdateComplete() {}
|
||||
}
|
||||
@@ -4,9 +4,8 @@ import android.app.ProgressDialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v4.content.FileProvider
|
||||
import android.util.Log
|
||||
import android.webkit.MimeTypeMap
|
||||
import net.syncthing.java.bep.BlockPuller
|
||||
@@ -34,18 +33,17 @@ class DownloadFileTask(private val mContext: Context, private val mSyncthingClie
|
||||
mSyncthingClient.pullFile(mFileInfo, { observer ->
|
||||
onProgress(observer)
|
||||
try {
|
||||
while (!observer.isCompleted) {
|
||||
while (!observer.isCompleted()) {
|
||||
if (cancelled)
|
||||
return@pullFile
|
||||
|
||||
observer.waitForProgressUpdate()
|
||||
Log.i("pullFile", "download progress = " + observer.progressMessage)
|
||||
Log.i("pullFile", "download progress = " + observer.progressMessage())
|
||||
onProgress(observer)
|
||||
}
|
||||
|
||||
val outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
val outputFile = File(outputDir, mFileInfo.fileName)
|
||||
FileUtils.copyInputStreamToFile(observer.inputStream, outputFile)
|
||||
val outputFile = File("${mContext.externalCacheDir}/${mFileInfo.folder}/${mFileInfo.path}")
|
||||
FileUtils.copyInputStreamToFile(observer.inputStream(), outputFile)
|
||||
Log.i(TAG, "downloaded file = " + mFileInfo.path)
|
||||
onComplete(outputFile)
|
||||
} catch (e: IOException) {
|
||||
@@ -73,7 +71,7 @@ class DownloadFileTask(private val mContext: Context, private val mSyncthingClie
|
||||
uiThread {
|
||||
progressDialog.isIndeterminate = false
|
||||
progressDialog.max = (mFileInfo.size as Long).toInt()
|
||||
progressDialog.progress = (fileDownloadObserver.progress * mFileInfo.size!!).toInt()
|
||||
progressDialog.progress = (fileDownloadObserver.progress() * mFileInfo.size!!).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,15 +83,16 @@ class DownloadFileTask(private val mContext: Context, private val mSyncthingClie
|
||||
|
||||
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FilenameUtils.getExtension(file.name))
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(Uri.fromFile(file), mimeType)
|
||||
val uri = FileProvider.getUriForFile(mContext, "net.syncthing.lite.fileprovider", file)
|
||||
intent.setDataAndType(uri, mimeType)
|
||||
intent.newTask()
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
try {
|
||||
mContext.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
onError(R.string.toast_open_file_failed)
|
||||
Log.w(TAG, "No handler found for file " + file.name, e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onError(@StringRes error: Int) {
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package net.syncthing.lite.library
|
||||
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import net.syncthing.java.core.beans.FolderInfo
|
||||
import org.jetbrains.anko.doAsync
|
||||
import org.jetbrains.anko.uiThread
|
||||
import java.util.*
|
||||
|
||||
class InitLibraryTask(private val context: Context, private val onLibraryLoaded: (LibraryHandler) -> Unit,
|
||||
private val onIndexUpdateProgressListener: (FolderInfo, Int) -> Unit,
|
||||
private val onIndexUpdateCompleteListener: () -> Unit) {
|
||||
|
||||
private val TAG = "InitLibraryTask"
|
||||
|
||||
init {
|
||||
doAsync {
|
||||
val libraryHandler = LibraryHandler()
|
||||
libraryHandler.init(context)
|
||||
libraryHandler.setOnIndexUpdatedListener(object : LibraryHandler.OnIndexUpdatedListener {
|
||||
override fun onIndexUpdateProgress(folder: FolderInfo, percentage: Int) {
|
||||
onIndexUpdateProgressListener(folder, percentage)
|
||||
}
|
||||
|
||||
override fun onIndexUpdateComplete() {
|
||||
onIndexUpdateCompleteListener()
|
||||
}
|
||||
})
|
||||
//trigger update if last was more than 10mins ago
|
||||
val lastUpdateMillis = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getLong(UpdateIndexTask.LAST_INDEX_UPDATE_TS_PREF, -1)
|
||||
val lastUpdateTimeAgo = Date().time - lastUpdateMillis
|
||||
if (lastUpdateMillis == -1L || lastUpdateTimeAgo > 10 * 60 * 1000) {
|
||||
Log.d(TAG, "trigger index update, last was " + Date(lastUpdateMillis))
|
||||
UpdateIndexTask(context, libraryHandler.syncthingClient!!).updateIndex()
|
||||
}
|
||||
uiThread {
|
||||
onLibraryLoaded(libraryHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,178 @@
|
||||
package net.syncthing.lite.library
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import com.google.common.eventbus.Subscribe
|
||||
import kotlinx.coroutines.experimental.android.UI
|
||||
import kotlinx.coroutines.experimental.async
|
||||
import net.syncthing.java.bep.FolderBrowser
|
||||
import net.syncthing.java.bep.IndexHandler
|
||||
import net.syncthing.java.client.SyncthingClient
|
||||
import net.syncthing.java.core.beans.FolderInfo
|
||||
import net.syncthing.java.core.configuration.ConfigurationService
|
||||
import net.syncthing.java.core.security.KeystoreHandler
|
||||
import net.syncthing.lite.utils.Util
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.jetbrains.anko.doAsync
|
||||
import org.jetbrains.anko.uiThread
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class LibraryHandler {
|
||||
class LibraryHandler(context: Context, onLibraryLoaded: (LibraryHandler) -> Unit,
|
||||
onIndexUpdateProgressListener: (String, Int) -> Unit,
|
||||
onIndexUpdateCompleteListener: () -> Unit) {
|
||||
|
||||
companion object {
|
||||
private var instanceCount = 0
|
||||
private var configuration: ConfigurationService? = null
|
||||
private var syncthingClient: SyncthingClient? = null
|
||||
private var folderBrowser: FolderBrowser? = null
|
||||
private val callbacks = ArrayList<(ConfigurationService, SyncthingClient, FolderBrowser) -> Unit>()
|
||||
private var isLoading = false
|
||||
}
|
||||
|
||||
private val TAG = "LibConnectionHandler"
|
||||
|
||||
private var mOnIndexUpdatedListener: OnIndexUpdatedListener? = null
|
||||
var configuration: ConfigurationService? = null
|
||||
private set
|
||||
var syncthingClient: SyncthingClient? = null
|
||||
private set
|
||||
var folderBrowser: FolderBrowser? = null
|
||||
private set
|
||||
private val onIndexUpdateListener: Any
|
||||
|
||||
interface OnIndexUpdatedListener {
|
||||
fun onIndexUpdateProgress(folder: FolderInfo, percentage: Int)
|
||||
fun onIndexUpdateComplete()
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
configuration = ConfigurationService.newLoader()
|
||||
.setCache(File(context.externalCacheDir, "cache"))
|
||||
.setDatabase(File(context.getExternalFilesDir(null), "database"))
|
||||
.loadFrom(File(context.getExternalFilesDir(null), "config.properties"))
|
||||
configuration!!.edit().setDeviceName(Util.getDeviceName())
|
||||
try {
|
||||
FileUtils.cleanDirectory(configuration!!.temp)
|
||||
} catch (ex: IOException) {
|
||||
Log.e(TAG, "error", ex)
|
||||
destroy()
|
||||
init {
|
||||
instanceCount++
|
||||
if (configuration == null && !isLoading) {
|
||||
doAsync {
|
||||
init(context)
|
||||
//trigger update if last was more than 10mins ago
|
||||
val lastUpdateMillis = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getLong(UpdateIndexTask.LAST_INDEX_UPDATE_TS_PREF, -1)
|
||||
val lastUpdateTimeAgo = Date().time - lastUpdateMillis
|
||||
if (lastUpdateMillis == -1L || lastUpdateTimeAgo > 10 * 60 * 1000) {
|
||||
Log.d(TAG, "trigger index update, last was " + Date(lastUpdateMillis))
|
||||
syncthingClient { UpdateIndexTask(context, it).updateIndex() }
|
||||
}
|
||||
uiThread {
|
||||
onLibraryLoaded(this@LibraryHandler)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onLibraryLoaded(this)
|
||||
}
|
||||
|
||||
KeystoreHandler.newLoader().loadAndStore(configuration!!)
|
||||
configuration!!.edit().persistLater()
|
||||
Log.i(TAG, "loaded mConfiguration = " + configuration!!.newWriter().dumpToString())
|
||||
Log.i(TAG, "storage space = " + configuration!!.storageInfo.dumpAvailableSpace())
|
||||
syncthingClient = net.syncthing.java.client.SyncthingClient(configuration!!)
|
||||
//TODO listen for device events, update device list
|
||||
folderBrowser = syncthingClient!!.indexHandler.newFolderBrowser()
|
||||
}
|
||||
|
||||
fun setOnIndexUpdatedListener(onIndexUpdatedListener: OnIndexUpdatedListener) {
|
||||
mOnIndexUpdatedListener = onIndexUpdatedListener
|
||||
syncthingClient!!.indexHandler.eventBus.register(object : Any() {
|
||||
|
||||
onIndexUpdateListener = object : Any() {
|
||||
@Subscribe
|
||||
fun handleIndexRecordAquiredEvent(event: IndexHandler.IndexRecordAquiredEvent) {
|
||||
val folder = syncthingClient!!.indexHandler.getFolderInfo(event.folder)
|
||||
val indexInfo = event.indexInfo
|
||||
event.newRecords.size
|
||||
val indexInfo = event.indexInfo()
|
||||
event.newRecords().size
|
||||
Log.i(TAG, "handleIndexRecordEvent trigger folder list update from index record acquired")
|
||||
mOnIndexUpdatedListener!!.onIndexUpdateProgress(folder, (indexInfo.completed * 100).toInt())
|
||||
onIndexUpdateProgressListener(event.folder(), (indexInfo.completed * 100).toInt())
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun handleRemoteIndexAquiredEvent(event: IndexHandler.FullIndexAquiredEvent) {
|
||||
Log.i(TAG, "handleIndexAquiredEvent trigger folder list update from index acquired")
|
||||
mOnIndexUpdatedListener!!.onIndexUpdateComplete()
|
||||
Log.i(TAG, "handleIndexAcquiredEvent trigger folder list update from index acquired")
|
||||
onIndexUpdateCompleteListener()
|
||||
}
|
||||
})
|
||||
}
|
||||
syncthingClient {
|
||||
it.indexHandler.eventBus.register(onIndexUpdateListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
folderBrowser!!.close()
|
||||
syncthingClient!!.close()
|
||||
configuration!!.close()
|
||||
private fun init(context: Context) {
|
||||
isLoading = true
|
||||
val configuration = ConfigurationService.newLoader()
|
||||
.setCache(File(context.externalCacheDir, ".cache"))
|
||||
.setDatabase(File(context.getExternalFilesDir(null), "database"))
|
||||
.loadFrom(File(context.getExternalFilesDir(null), "config.properties"))
|
||||
configuration.edit().setDeviceName(Util.getDeviceName())
|
||||
try {
|
||||
FileUtils.cleanDirectory(configuration.temp)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Failed to delete temporary files", e)
|
||||
close()
|
||||
}
|
||||
|
||||
KeystoreHandler.newLoader().loadAndStore(configuration)
|
||||
configuration.edit().persistLater()
|
||||
Log.i(TAG, "loaded mConfiguration = " + configuration.newWriter().dumpToString())
|
||||
Log.i(TAG, "storage space = " + configuration.storageInfo.dumpAvailableSpace())
|
||||
val syncthingClient = SyncthingClient(configuration)
|
||||
//TODO listen for device events, update device list
|
||||
val folderBrowser = syncthingClient.indexHandler.newFolderBrowser()
|
||||
|
||||
if (instanceCount == 0) {
|
||||
Log.d(TAG, "All LibraryHandler instances were closed during init")
|
||||
configuration.close()
|
||||
syncthingClient.close()
|
||||
folderBrowser.close()
|
||||
}
|
||||
|
||||
async(UI) {
|
||||
callbacks.forEach { it(configuration, syncthingClient, folderBrowser) }
|
||||
}
|
||||
LibraryHandler.configuration = configuration
|
||||
LibraryHandler.syncthingClient = syncthingClient
|
||||
LibraryHandler.folderBrowser = folderBrowser
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
fun library(callback: (ConfigurationService, SyncthingClient, FolderBrowser) -> Unit) {
|
||||
val nullCount = listOf(configuration, syncthingClient, folderBrowser).count { it == null }
|
||||
assert(nullCount == 0 || nullCount == 3, { "Inconsistent library state" })
|
||||
|
||||
// https://stackoverflow.com/a/35522422/1837158
|
||||
fun <T1: Any, T2: Any, T3: Any, R: Any> safeLet(p1: T1?, p2: T2?, p3: T3?, block: (T1, T2, T3)->R?): R? {
|
||||
return if (p1 != null && p2 != null && p3 != null) block(p1, p2, p3) else null
|
||||
}
|
||||
safeLet(configuration, syncthingClient, folderBrowser) { c, s, f ->
|
||||
callback(c, s, f)
|
||||
} ?: run {
|
||||
if (isLoading) {
|
||||
callbacks.add(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun syncthingClient(callback: (SyncthingClient) -> Unit) {
|
||||
library { _, s, _ -> callback(s) }
|
||||
}
|
||||
|
||||
fun configuration(callback: (ConfigurationService) -> Unit) {
|
||||
library { c, _, _ -> callback(c) }
|
||||
}
|
||||
|
||||
fun folderBrowser(callback: (FolderBrowser) -> Unit) {
|
||||
library { _, _, f -> callback(f) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters index update listener and decreases instance count.
|
||||
*
|
||||
* We wait a bit before closing [[syncthingClient]] etc, in case LibraryHandler is opened again
|
||||
* soon (eg in case of device rotation).
|
||||
*/
|
||||
fun close() {
|
||||
syncthingClient {
|
||||
try {
|
||||
it.indexHandler.eventBus.unregister(onIndexUpdateListener)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// ignored, no idea why this is thrown
|
||||
}
|
||||
}
|
||||
|
||||
instanceCount--
|
||||
Handler().postDelayed({
|
||||
Thread {
|
||||
if (instanceCount == 0) {
|
||||
folderBrowser?.close()
|
||||
folderBrowser = null
|
||||
syncthingClient?.close()
|
||||
syncthingClient = null
|
||||
configuration?.close()
|
||||
configuration = null
|
||||
}
|
||||
}.start()
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ class UploadFileTask(private val context: Context, private val syncthingClient:
|
||||
syncthingClient.pushFile(uploadStream, syncthingFolder, syncthingPath, { observer ->
|
||||
onProgress(observer)
|
||||
try {
|
||||
while (!observer.isCompleted) {
|
||||
while (!observer.isCompleted()) {
|
||||
if (mCancelled)
|
||||
return@pushFile
|
||||
|
||||
observer.waitForProgressUpdate()
|
||||
Log.i(TAG, "upload progress = " + observer.progressMessage)
|
||||
Log.i(TAG, "upload progress = " + observer.progressMessage())
|
||||
onProgress(observer)
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
@@ -72,8 +72,8 @@ class UploadFileTask(private val context: Context, private val syncthingClient:
|
||||
doAsync {
|
||||
uiThread {
|
||||
mProgressDialog.isIndeterminate = false
|
||||
mProgressDialog.max = observer.dataSource.size.toInt()
|
||||
mProgressDialog.progress = (observer.progress * observer.dataSource.size).toInt()
|
||||
mProgressDialog.max = observer.dataSource().getSize().toInt()
|
||||
mProgressDialog.progress = (observer.progress() * observer.dataSource().getSize()).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-cache-path name="files" path="/" />
|
||||
</paths>
|
||||
@@ -4,6 +4,7 @@ buildscript {
|
||||
ext.kotlin_version = '1.2.0'
|
||||
ext.support_version = '27.0.2'
|
||||
ext.build_tools_version = '3.0.1'
|
||||
ext.anko_version = '0.10.4'
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
NEW_VERSION_NAME=$1
|
||||
OLD_VERSION_NAME=$(grep "versionName" "app/build.gradle" | awk '{print $2}')
|
||||
if [[ -z ${NEW_VERSION_NAME} ]]
|
||||
then
|
||||
echo "New version name is empty. Please set a new version. Current version: $OLD_VERSION_NAME"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
echo "
|
||||
|
||||
Running Lint
|
||||
-----------------------------
|
||||
"
|
||||
./gradlew clean lintVitalRelease
|
||||
|
||||
|
||||
echo "
|
||||
|
||||
Updating Version
|
||||
-----------------------------
|
||||
"
|
||||
OLD_VERSION_CODE=$(grep "versionCode" "app/build.gradle" -m 1 | awk '{print $2}')
|
||||
NEW_VERSION_CODE=$(($OLD_VERSION_CODE + 1))
|
||||
sed -i "s/versionCode $OLD_VERSION_CODE/versionCode $NEW_VERSION_CODE/" "app/build.gradle"
|
||||
|
||||
OLD_VERSION_NAME=$(grep "versionName" "app/build.gradle" | awk '{print $2}')
|
||||
sed -i "s/$OLD_VERSION_NAME/\"$NEW_VERSION_NAME\"/" "app/build.gradle"
|
||||
git add "app/build.gradle"
|
||||
git commit -m "Version $NEW_VERSION_NAME"
|
||||
git tag ${NEW_VERSION_NAME}
|
||||
|
||||
echo "
|
||||
Update ready.
|
||||
"
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
version=$(git describe --tags)
|
||||
regex='^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
if [[ ! ${version} =~ $regex ]]
|
||||
then
|
||||
echo "Current commit is not a release"
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "
|
||||
|
||||
Pushing to Github
|
||||
-----------------------------
|
||||
"
|
||||
git push
|
||||
git push --tags
|
||||
|
||||
echo "
|
||||
|
||||
Push to Google Play
|
||||
-----------------------------
|
||||
"
|
||||
|
||||
read -s -p "Enter signing password: " password
|
||||
|
||||
SIGNING_PASSWORD=${password} ./gradlew assembleRelease
|
||||
|
||||
# Upload apk and listing to Google Play
|
||||
SIGNING_PASSWORD=${password} ./gradlew publishRelease
|
||||
|
||||
echo "
|
||||
|
||||
Release published!
|
||||
"
|
||||
Reference in New Issue
Block a user