Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Candidate #7

Merged
merged 17 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
stability, continue from last dir, delete functionality with confirma…
…tion, upload functionality, show filename before sending bin or file upload
  • Loading branch information
Mr-Smithy-x committed Jan 19, 2022
commit a3b94da717cde7d731ce319e0131a111d51b798d
6 changes: 3 additions & 3 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/src/main/java/nyc/vonley/mi/di/network/MiFTPClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface MiFTPClient : CoroutineScope, ProtocolCommandListener {
suspend fun upload(file: String, byteArray: InputStream): Boolean
suspend fun upload(file: String, byteArray: ByteArray): Boolean = upload(file, ByteArrayInputStream(byteArray))
suspend fun upload(file: File): Boolean = upload(file.name, FileInputStream(file))
suspend fun delete(file: FTPFile): Boolean
fun disconnect()
fun up()

}
1 change: 1 addition & 0 deletions app/src/main/java/nyc/vonley/mi/di/network/SyncService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ interface SyncService: CoroutineScope {
fun setTarget(client: Client)
fun addConsoleListener(console: OnConsoleListener)
fun stop()
fun removeConsoleListener(console: OnConsoleListener)
}
146 changes: 100 additions & 46 deletions app/src/main/java/nyc/vonley/mi/di/network/impl/MiFTPClientImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ import nyc.vonley.mi.BuildConfig
import nyc.vonley.mi.di.annotations.SharedPreferenceStorage
import nyc.vonley.mi.di.network.MiFTPClient
import nyc.vonley.mi.utils.SharedPreferenceManager
import nyc.vonley.mi.utils.get
import okhttp3.internal.notify
import nyc.vonley.mi.utils.set
import okhttp3.internal.notifyAll
import org.apache.commons.net.ProtocolCommandEvent
import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPFile
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import javax.inject.Inject

Expand All @@ -29,6 +25,8 @@ class MiFTPClientImpl @Inject constructor(@SharedPreferenceStorage override val
private val ftpPath get() = manager.ftpPath
private val ftpUser get() = manager.ftpUser
private val ftpPass get() = manager.ftpPass
private var _ip: String? = null
private var _port: Int? = null

private val _cwd: MutableLiveData<Array<out FTPFile>> = MutableLiveData<Array<out FTPFile>>()

Expand All @@ -39,16 +37,34 @@ class MiFTPClientImpl @Inject constructor(@SharedPreferenceStorage override val

private val callback = object : MiFTPEventListener {

override fun onLoggedIn() {
override fun onFailedToConnect() {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Unable to connect")
}
}

override fun onLoggedIn() {
if (BuildConfig.DEBUG) {
Log.e(TAG, "User is logged in")
}
}

override fun onInvalidCredentials() {

if (BuildConfig.DEBUG) {
Log.e(TAG, "User entered invalid credentials")
}
}

override fun onDirChanged() {
if (BuildConfig.DEBUG) {
Log.e(TAG, "Directory Changed")
}
}

override fun isLoggedInAlready() {
if (BuildConfig.DEBUG) {
Log.e(TAG, "User is already logged in")
}
}

}
Expand All @@ -57,28 +73,53 @@ class MiFTPClientImpl @Inject constructor(@SharedPreferenceStorage override val
fun onLoggedIn()
fun onInvalidCredentials()
fun onDirChanged()
fun isLoggedInAlready()
fun onFailedToConnect()
}

private suspend fun _connect(ip: String, port: Int) {
try {
client.connect(ip, port)
client.addProtocolCommandListener(this@MiFTPClientImpl)
val login = client.login(ftpUser, ftpPass)
if (login) {
setWorkingDir(ftpPath)
client.setFileType(FTP.BINARY_FILE_TYPE)
callback.onLoggedIn()
} else {
callback.onInvalidCredentials()
}
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
Log.e(TAG, e.message ?: "Something went wrong")
}
callback.onFailedToConnect()
}
}

override fun connect(ip: String, port: Int) {
if (!this::client.isInitialized) client = FTPClient()
if (!this::client.isInitialized) {
client = FTPClient()
_ip = ip
_port = port
}
if (this::client.isInitialized) {
if (client.isConnected) {
client.disconnect()
}
client.addProtocolCommandListener(this)
val block: suspend CoroutineScope.() -> Unit = {
client.connect(ip, port)
val login = client.login(ftpUser, ftpPass)
if (login) {
setWorkingDir(ftpPath)
client.setFileType(FTP.BINARY_FILE_TYPE)
withContext(Dispatchers.Main) {
callback.onLoggedIn()
if (client.isConnected) {
if (_ip != ip || _port != port) {
_ip = ip
_port = port
client.logout()
client.disconnect()
_connect(ip, port)
} else {
callback.isLoggedInAlready()
}
} else {
withContext(Dispatchers.Main) {
callback.onInvalidCredentials()
}
_connect(ip, port)
}
}
launch(block = block)
Expand All @@ -94,28 +135,28 @@ class MiFTPClientImpl @Inject constructor(@SharedPreferenceStorage override val
}

override fun setWorkingDir(dir: String?) {
if(BuildConfig.DEBUG){
if (BuildConfig.DEBUG) {
Log.e(TAG, "dir: $dir")
}
launch {
val changed = client.changeWorkingDirectory(dir)
if(changed){
manager[SharedPreferenceManager.FTPPATH] = client.printWorkingDirectory()
if (changed) {
getGWD()
callback.onDirChanged()
}
}
}

private suspend fun getGWD() {

if (BuildConfig.DEBUG) {
val cwm = client.printWorkingDirectory()
Log.e("CWD", "CWD: $cwm")
}
val dir: Array<out FTPFile> = if (client.isConnected) {

if(BuildConfig.DEBUG) {
val cwm = client.printWorkingDirectory()
Log.e("CWD", "CWD: $cwm")
}

client.listFiles()
val listFiles = client.listFiles()
listFiles.sortByDescending { it.isDirectory }
listFiles
} else arrayOf()
withContext(Dispatchers.Main) {
synchronized(_cwd) {
Expand All @@ -125,32 +166,45 @@ class MiFTPClientImpl @Inject constructor(@SharedPreferenceStorage override val
}
}

override fun up() {
launch {
client.cdup()
getGWD()
}
}

override suspend fun upload(file: String, byteArray: ByteArray): Boolean {
val stream = ByteArrayInputStream(byteArray)
return upload(file, stream)
}

override suspend fun upload(file: String, stream: InputStream): Boolean {
client.enterLocalPassiveMode()
val file = client.storeFile(file, stream)
stream.close()
return file
if (client.isConnected) {
client.enterLocalPassiveMode()
val file = client.storeFile(file, stream)
stream.close()
if (file) {
getGWD()
}
return file
}
return false
}

override fun disconnect() {
launch {
client.logout()
client.disconnect()
try {
client.removeProtocolCommandListener(this@MiFTPClientImpl)
client.logout()
client.disconnect()
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
Log.e(TAG, e.message ?: "Something went wrong")
}
}
}
}

override suspend fun delete(file: FTPFile): Boolean {
if (client.isConnected) {
if (file.isFile) {
//TODO: WTF is this a bug?
val deleteFile = client.deleteFile("${ftpPath}/${file.name}")
getGWD()
return deleteFile
}
}
return false
}

override fun protocolCommandSent(event: ProtocolCommandEvent?) {
if (BuildConfig.DEBUG) {
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/java/nyc/vonley/mi/di/network/impl/SyncServiceImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.coroutines.*
import nyc.vonley.mi.BuildConfig
import nyc.vonley.mi.di.network.SyncService
import nyc.vonley.mi.di.network.handlers.ClientHandler
import nyc.vonley.mi.di.network.handlers.base.BaseClientHandler
import nyc.vonley.mi.di.network.handlers.impl.ConsoleClientHandler
import nyc.vonley.mi.di.network.listeners.OnConsoleListener
import nyc.vonley.mi.extensions.client
Expand Down Expand Up @@ -166,11 +167,22 @@ class SyncServiceImpl constructor(
this[ConsoleClientHandler::class.java].listeners[console.javaClass] = console
}

override fun removeConsoleListener(console: OnConsoleListener) {
this[ConsoleClientHandler::class.java].listeners.remove(console.javaClass)
}

/**
* TODO: Clean up all listeners so there no
* memory leak1
* memory leak
*/
override fun cleanup() = Unit
override fun cleanup() {
this.handlers.onEach {
val handler = it.value
if (handler is BaseClientHandler<*, *>) {
handler.listeners.clear()
}
}
}

/**
* Fetch PS4 & PS3 Consoles on the current
Expand Down
8 changes: 5 additions & 3 deletions app/src/main/java/nyc/vonley/mi/ui/main/ftp/FTPContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ interface FTPContract {
interface Presenter : BaseContract.Presenter, Observer<Array<out FTPFile>> {
fun navigateTo(ftpFile: FTPFile)
fun navigateTo(path: String)
fun delete(ftpFile: FTPFile): Boolean
fun download(ftpFile: FTPFile, location: String): Boolean
fun replace(ftpFile: FTPFile, file: File): Boolean
fun delete(ftpFile: FTPFile)
fun download(ftpFile: FTPFile, location: String)
fun replace(ftpFile: FTPFile, file: File)
fun upload(filename: String, stream: InputStream)
fun upload(filename: String, bytes: ByteArray) = upload(filename, ByteArrayInputStream(bytes))
fun upload(file: File) = upload(file.name, FileInputStream(file))
Expand All @@ -29,6 +29,8 @@ interface FTPContract {
fun onFTPLongClickFile(view: android.view.View, ftpFile: FTPFile)
fun onFileUpload(filename: String)
fun onFileFailed(filename: String)
fun onFTPFileDeleted(ftpFile: FTPFile)
fun onFTPFailedToDelete(ftpFile: FTPFile)
}

}
24 changes: 21 additions & 3 deletions app/src/main/java/nyc/vonley/mi/ui/main/ftp/FTPFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import androidx.core.database.getStringOrNull
import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_f_t_p.*
import nyc.vonley.mi.BuildConfig
import nyc.vonley.mi.R
import nyc.vonley.mi.databinding.FragmentFTPBinding
import nyc.vonley.mi.ui.main.ftp.adapters.FTPChildAttachChangeListener
import nyc.vonley.mi.ui.main.ftp.adapters.FTPFileTouchListener
import nyc.vonley.mi.ui.main.ftp.adapters.FTPRecyclerAdapter
import nyc.vonley.mi.ui.main.ftp.adapters.FTPScrollListener
import nyc.vonley.mi.ui.main.home.dialog
import org.apache.commons.net.ftp.FTPFile
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -91,6 +93,7 @@ class FTPFragment : Fragment(), FTPContract.View, ActivityResultCallback<Activit

override fun onFTPDirOpened(files: Array<out FTPFile>) {
adapter.set(files)
recycler.scrollToPosition(0)
}

override fun onFTPDirClicked(ftpFile: FTPFile) {
Expand All @@ -109,14 +112,20 @@ class FTPFragment : Fragment(), FTPContract.View, ActivityResultCallback<Activit
Toast.makeText(requireContext(), "Feature not implemented yet", Toast.LENGTH_SHORT).show()
}


override fun onFTPLongClickFile(view: View, ftpFile: FTPFile) {
val popup = PopupMenu(view.context, view)
val inflater = popup.menuInflater
inflater.inflate(R.menu.ftp_long_click, popup.menu)
popup.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.ftp_delete -> {
presenter.delete(ftpFile)
dialog("Are you sure you want to delete ${ftpFile.name}", "OK") { d, i ->
presenter.delete(ftpFile)
d.dismiss()
}.setNegativeButton("Cancel") { d, i ->
d.dismiss()
}.create().show()
true
}
R.id.ftp_download -> {
Expand Down Expand Up @@ -145,11 +154,20 @@ class FTPFragment : Fragment(), FTPContract.View, ActivityResultCallback<Activit
}

override fun onFileUpload(filename: String) {
Snackbar.make(requireView(), "$filename upload successfully!", Snackbar.LENGTH_LONG).show()
Snackbar.make(requireView(), "$filename upload successful!", Snackbar.LENGTH_LONG).show()
}

override fun onFileFailed(filename: String) {
Snackbar.make(requireView(), "Failed to upload $filename", Snackbar.LENGTH_LONG).show()
Snackbar.make(requireView(), "Failed to upload $filename!", Snackbar.LENGTH_LONG).show()
}

override fun onFTPFileDeleted(ftpFile: FTPFile) {
Snackbar.make(requireView(), "${ftpFile.name} deleted!", Snackbar.LENGTH_LONG)
.show()
}

override fun onFTPFailedToDelete(ftpFile: FTPFile) {
Snackbar.make(requireView(), "Failed to delete $ftpFile!", Snackbar.LENGTH_LONG).show()
}

override fun onFTPFileClicked(ftpFile: FTPFile) {
Expand Down
Loading