MISC: enhance code step 1

This commit is contained in:
imkiva 2021-05-08 13:42:17 +08:00 committed by Kiva Oyama
parent 2dbc16cc68
commit cc1fe9de00
25 changed files with 242 additions and 385 deletions

View File

@ -44,6 +44,9 @@ android {
targetCompatibility 1.8
sourceCompatibility 1.8
}
kotlinOptions {
freeCompilerArgs = ["-Xallow-result-return-type"]
}
}
dependencies {

View File

@ -9,10 +9,9 @@ import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.component.helper.ConfigFileBasedComponent
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.utils.AssetsUtils
import io.neoterm.utils.extractAssetsDir
import java.io.File
import java.nio.file.Files
@ -103,15 +102,8 @@ class ColorSchemeComponent : ConfigFileBasedComponent<NeoColorScheme>(NeoTermPat
setCurrentColorScheme(color.colorName)
}
private fun extractDefaultColor(context: Context): Boolean {
try {
AssetsUtils.extractAssetsDir(context, "colors", baseDir)
return true
} catch (e: Exception) {
NLog.e("ColorScheme", "Failed to extract default colors: ${e.localizedMessage}")
return false
}
}
private fun extractDefaultColor(context: Context) =
kotlin.runCatching { context.extractAssetsDir("colors", baseDir) }.isSuccess
fun saveColorScheme(colorScheme: NeoColorScheme) {
val colorFile = colorFile(colorScheme.colorName)

View File

@ -7,7 +7,7 @@ import io.neoterm.frontend.component.helper.ConfigFileBasedComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.utils.AssetsUtils
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
@ -52,7 +52,7 @@ class ExtraKeyComponent : ConfigFileBasedComponent<NeoExtraKey>(NeoTermPath.EKS_
private fun extractDefaultConfig(context: Context) {
try {
AssetsUtils.extractAssetsDir(context, "eks", baseDir)
context.extractAssetsDir("eks", baseDir)
} catch (e: Exception) {
NLog.e("ExtraKey", "Failed to extract configure: ${e.localizedMessage}")
}

View File

@ -10,7 +10,7 @@ import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.utils.AssetsUtils
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
@ -90,7 +90,7 @@ class FontComponent : NeoComponent {
private fun extractDefaultFont(context: Context): Boolean {
try {
AssetsUtils.extractAssetsDir(context, "fonts", NeoTermPath.FONT_PATH)
context.extractAssetsDir( "fonts", NeoTermPath.FONT_PATH)
return true
} catch (e: Exception) {
return false

View File

@ -6,7 +6,7 @@ import io.neoterm.App
import io.neoterm.frontend.component.NeoComponent
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.logging.NLog
import io.neoterm.utils.AssetsUtils
import io.neoterm.utils.extractAssetsDir
import java.io.File
/**
@ -28,7 +28,7 @@ class UserScriptComponent : NeoComponent {
private fun extractDefaultScript(context: Context): Boolean {
try {
AssetsUtils.extractAssetsDir(context, "scripts", NeoTermPath.USER_SCRIPT_PATH)
context.extractAssetsDir( "scripts", NeoTermPath.USER_SCRIPT_PATH)
File(NeoTermPath.USER_SCRIPT_PATH)
.listFiles().forEach {
Os.chmod(it.absolutePath, 448 /*Dec of 0700*/)

View File

@ -9,7 +9,7 @@ import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.shell.ShellTermSession
import io.neoterm.frontend.session.shell.client.BasicSessionCallback
import io.neoterm.frontend.session.shell.client.BasicViewClient
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
* @author kiva
@ -56,7 +56,7 @@ class TerminalDialog(val context: Context) {
.arguments(arguments)
.callback(terminalSessionCallback)
.systemShell(false)
terminalSession = TerminalUtils.createSession(context, parameter)
terminalSession = Terminals.createSession(context, parameter)
if (terminalSession is ShellTermSession) {
(terminalSession as ShellTermSession).exitPrompt = context.getString(R.string.process_exit_prompt_press_back)
}

View File

@ -8,7 +8,7 @@ import io.neoterm.R
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.TerminalViewClient
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
* @author kiva
@ -21,7 +21,7 @@ class WindowTermView(val context: Context) {
private set
init {
TerminalUtils.setupTerminalView(terminalView)
Terminals.setupTerminalView(terminalView)
}
fun setTerminalViewClient(terminalViewClient: TerminalViewClient?) {

View File

@ -19,7 +19,7 @@ import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.xorg.XParameter
import io.neoterm.frontend.session.xorg.XSession
import io.neoterm.ui.term.NeoTermActivity
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
@ -94,7 +94,7 @@ class NeoTermService : Service() {
}
fun createXSession(activity: AppCompatActivity, parameter: XParameter): XSession {
val session = TerminalUtils.createSession(activity, parameter)
val session = Terminals.createSession(activity, parameter)
mXSessions.add(session)
updateNotification()
return session
@ -112,7 +112,7 @@ class NeoTermService : Service() {
private fun createOrFindSession(parameter: ShellParameter): TerminalSession {
if (parameter.willCreateNewSession()) {
NLog.d("createOrFindSession: creating new session")
val session = TerminalUtils.createSession(this, parameter)
val session = Terminals.createSession(this, parameter)
mTerminalSessions.add(session)
return session
}

View File

@ -12,7 +12,7 @@ import io.neoterm.frontend.session.shell.client.BasicSessionCallback
import io.neoterm.frontend.session.shell.client.BasicViewClient
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
* @author kiva
@ -36,8 +36,8 @@ open class BaseCustomizeActivity : AppCompatActivity() {
extraKeysView = findViewById(R.id.custom_extra_keys)
viewClient = BasicViewClient(terminalView)
sessionCallback = BasicSessionCallback(terminalView)
TerminalUtils.setupTerminalView(terminalView, viewClient)
TerminalUtils.setupExtraKeysView(extraKeysView)
Terminals.setupTerminalView(terminalView, viewClient)
Terminals.setupExtraKeysView(extraKeysView)
val script = resources.getStringArray(R.array.custom_preview_script_colors)
val parameter = ShellParameter()
@ -46,7 +46,7 @@ open class BaseCustomizeActivity : AppCompatActivity() {
.callback(sessionCallback)
.systemShell(false)
session = TerminalUtils.createSession(this, parameter)
session = Terminals.createSession(this, parameter)
terminalView.attachSession(session)
}

View File

@ -24,7 +24,7 @@ import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.ui.customize.adapter.ColorItemAdapter
import io.neoterm.ui.customize.model.ColorItem
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
@ -51,7 +51,7 @@ class ColorSchemeActivity : BaseCustomizeActivity() {
editingColorScheme.colorName = ""
val terminalView = findViewById<TerminalView>(R.id.terminal_view)
TerminalUtils.setupTerminalView(terminalView, null)
Terminals.setupTerminalView(terminalView, null)
adapter = ColorItemAdapter(this, editingColorScheme, COMPARATOR, object : ColorItemAdapter.Listener {
override fun onModelClicked(model: ColorItem) {

View File

@ -13,7 +13,7 @@ import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.component.font.FontComponent
import io.neoterm.frontend.component.ComponentManager
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.utils.MediaUtils
import io.neoterm.utils.getPathOfMediaUri
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
@ -114,7 +114,7 @@ class CustomizeActivity : BaseCustomizeActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == RESULT_OK && data != null) {
val selected = MediaUtils.getPath(this, data.data)
val selected = this.getPathOfMediaUri( data.data)
if (selected != null && selected.isNotEmpty()) {
when (requestCode) {
REQUEST_SELECT_FONT -> installFont(selected)

View File

@ -25,7 +25,8 @@ import io.neoterm.frontend.floating.TerminalDialog
import io.neoterm.ui.pm.adapter.PackageAdapter
import io.neoterm.ui.pm.model.PackageModel
import io.neoterm.ui.pm.utils.StringDistance
import io.neoterm.utils.PackageUtils
import io.neoterm.utils.runApt
import java.util.*
/**
* @author kiva
@ -178,67 +179,64 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
executeAptUpdate()
}
private fun executeAptUpdate() {
PackageUtils.apt(this, "update", null, { exitStatus, dialog ->
if (exitStatus != 0) {
dialog.setTitle(getString(R.string.error))
return@apt
}
Toast.makeText(this, R.string.apt_update_ok, Toast.LENGTH_SHORT).show()
dialog.dismiss()
refreshPackageList()
})
private fun executeAptUpdate() = runApt("update", null) { exitStatus, dialog ->
if (exitStatus != 0) {
dialog.setTitle(getString(R.string.error))
return@runApt
}
Toast.makeText(this, R.string.apt_update_ok, Toast.LENGTH_SHORT).show()
dialog.dismiss()
refreshPackageList()
}
private fun executeAptUpgrade() {
PackageUtils.apt(this, "update", null, { exitStatus, dialog ->
private fun executeAptUpgrade() = runApt("update", null) { exitStatus, dialog ->
if (exitStatus != 0) {
dialog.setTitle(getString(R.string.error))
return@runApt
}
dialog.dismiss()
runApt("upgrade", arrayOf("-y")) aptUpgrade@{ exitStatus, dialog ->
if (exitStatus != 0) {
dialog.setTitle(getString(R.string.error))
return@apt
return@aptUpgrade
}
Toast.makeText(this, R.string.apt_upgrade_ok, Toast.LENGTH_SHORT).show()
dialog.dismiss()
PackageUtils.apt(this, "upgrade", arrayOf("-y"), out@{ exitStatus, dialog ->
if (exitStatus != 0) {
dialog.setTitle(getString(R.string.error))
return@out
}
Toast.makeText(this, R.string.apt_upgrade_ok, Toast.LENGTH_SHORT).show()
dialog.dismiss()
})
})
}
}
private fun refreshPackageList() {
private fun refreshPackageList() = Thread {
models.clear()
Thread {
val pm = ComponentManager.getComponent<PackageComponent>()
val sourceFiles = SourceHelper.detectSourceFiles()
val pm = ComponentManager.getComponent<PackageComponent>()
val sourceFiles = SourceHelper.detectSourceFiles()
pm.clearPackages()
sourceFiles.forEach { pm.reloadPackages(it, false) }
pm.packages.values.mapTo(models, { PackageModel(it) })
pm.clearPackages()
sourceFiles.forEach { pm.reloadPackages(it, false) }
pm.packages.values.mapTo(models, { PackageModel(it) })
this@PackageManagerActivity.runOnUiThread {
adapter.edit()
.replaceAll(models)
.commit()
if (models.isEmpty()) {
Toast.makeText(this@PackageManagerActivity, R.string.package_list_empty, Toast.LENGTH_SHORT).show()
changeSource()
}
this@PackageManagerActivity.runOnUiThread {
adapter.edit()
.replaceAll(models)
.commit()
if (models.isEmpty()) {
Toast.makeText(this@PackageManagerActivity, R.string.package_list_empty, Toast.LENGTH_SHORT).show()
changeSource()
}
}.start()
}
}
}.start()
private fun sortDistance(
models: List<PackageModel>, query: String,
mapper: (NeoPackageInfo) -> String
): List<Pair<PackageModel, Int>> {
return models
.map({
Pair(it, StringDistance.distance(mapper(it.packageInfo).toLowerCase(), query.toLowerCase()))
})
.map {
Pair(
it,
StringDistance.distance(mapper(it.packageInfo).toLowerCase(Locale.ROOT), query.toLowerCase(Locale.ROOT))
)
}
.sortedWith(Comparator { l, r -> r.second.compareTo(l.second) })
.toList()
}
@ -255,17 +253,10 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
return filteredModelList
}
override fun onQueryTextSubmit(text: String?): Boolean {
return false
}
override fun onQueryTextSubmit(text: String?) = false
override fun onQueryTextChange(text: String?): Boolean {
if (text != null) {
val filteredModelList = filter(models, text)
adapter.edit()
.replaceAll(filteredModelList)
.commit()
}
text?.let { adapter.edit().replaceAll(filter(models, it)).commit() }
return true
}

View File

@ -5,7 +5,7 @@ import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter
import io.neoterm.R
import io.neoterm.component.pm.NeoPackageInfo
import io.neoterm.utils.FileUtils
import io.neoterm.utils.formatSizeInKB
/**
* @author kiva
@ -28,7 +28,7 @@ class PackageModel(val packageInfo: NeoPackageInfo) : SortedListAdapter.ViewMode
R.string.package_details,
packageInfo.packageName, packageInfo.version,
packageInfo.dependenciesString,
FileUtils.formatSizeInKB(packageInfo.installedSizeInBytes),
packageInfo.installedSizeInBytes.formatSizeInKB(),
packageInfo.description, packageInfo.homePage
)
}

View File

@ -5,7 +5,7 @@ import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import io.neoterm.R
import io.neoterm.frontend.config.NeoPreference
import io.neoterm.utils.PackageUtils
import io.neoterm.utils.runApt
/**
* @author kiva
@ -39,16 +39,14 @@ class GeneralSettingsActivity : BasePreferenceActivity() {
AlertDialog.Builder(this)
.setTitle(getString(R.string.shell_not_found, shellName))
.setMessage(R.string.shell_not_found_message)
.setPositiveButton(R.string.install, { _, _ ->
PackageUtils.apt(this, "install", arrayOf("-y", shellName), { exitStatus, dialog ->
.setPositiveButton(R.string.install) { _, _ ->
runApt("install", arrayOf("-y", shellName)) { exitStatus, dialog ->
if (exitStatus == 0) {
dialog.dismiss()
postChangeShell(shellName)
} else {
dialog.setTitle(getString(R.string.error))
}
})
})
} else dialog.setTitle(getString(R.string.error))
}
}
.setNegativeButton(android.R.string.no, null)
.setOnDismissListener {
postChangeShell(currentShell)

View File

@ -19,8 +19,8 @@ import io.neoterm.setup.SourceConnection
import io.neoterm.setup.connections.BackupFileConnection
import io.neoterm.setup.connections.LocalFileConnection
import io.neoterm.setup.connections.NetworkConnection
import io.neoterm.utils.MediaUtils
import io.neoterm.utils.PackageUtils
import io.neoterm.utils.getPathOfMediaUri
import io.neoterm.utils.runApt
import java.io.File
@ -32,7 +32,6 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener
private const val REQUEST_SELECT_PARAMETER = 520;
}
private var aptUpdated = false
private var setupParameter = ""
private var setupParameterUri: Uri? = null
@ -83,8 +82,7 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == REQUEST_SELECT_PARAMETER && resultCode == RESULT_OK) {
if (resultData != null) {
setupParameterUri = resultData.data
val path = MediaUtils.getPath(this, setupParameterUri!!)
val path = this.getPathOfMediaUri(resultData.data)
findViewById<EditText>(R.id.setup_source_parameter).setText(path)
return
}
@ -236,26 +234,11 @@ class SetupActivity : AppCompatActivity(), View.OnClickListener, ResultListener
}
}
private fun executeAptUpdate() {
PackageUtils.apt(this, "update", null) { exitStatus, dialog ->
if (exitStatus == 0) {
dialog.dismiss()
aptUpdated = true
executeAptUpgrade()
} else {
dialog.setTitle(getString(R.string.error))
}
}
}
private fun executeAptUpdate() = runApt("update")
.onSuccess { executeAptUpgrade() }
.onFailure { Toast.makeText(this, R.string.error, Toast.LENGTH_SHORT).show() }
private fun executeAptUpgrade() {
PackageUtils.apt(this, "upgrade", arrayOf("-y")) { exitStatus, dialog ->
if (exitStatus == 0) {
dialog.dismiss()
finish()
} else {
dialog.setTitle(getString(R.string.error))
}
}
}
}
private fun executeAptUpgrade() = runApt("upgrade", "-y")
.onSuccess { finish() }
.onFailure { Toast.makeText(this, R.string.error, Toast.LENGTH_SHORT).show() }
}

View File

@ -777,9 +777,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
}
val rangedInt = RangedInt(tabSwitcher.selectedTabIndex, (0 until tabSwitcher.count))
val nextIndex = if (switchSessionEvent.toNext)
rangedInt.increaseOne()
else rangedInt.decreaseOne()
val nextIndex = if (switchSessionEvent.toNext) rangedInt.inc() else rangedInt.dec()
if (!tabSwitcher.isSwitcherShown) {
tabSwitcher.showSwitcher()
}

View File

@ -21,8 +21,8 @@ import io.neoterm.frontend.config.NeoPreference
import io.neoterm.frontend.session.shell.ShellParameter
import io.neoterm.frontend.session.shell.client.TermSessionCallback
import io.neoterm.services.NeoTermService
import io.neoterm.utils.MediaUtils
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
import io.neoterm.utils.getPathOfMediaUri
import java.io.File
/**
@ -67,13 +67,10 @@ class NeoTermRemoteInterface : AppCompatActivity(), ServiceConnection {
handleIntent()
}
private fun handleIntent() {
val className = intent.component.className.substringAfterLast('.')
when (className) {
"TermHere" -> handleTermHere()
"UserScript" -> handleUserScript()
else -> handleNormal()
}
private fun handleIntent() = when (intent.component?.className?.substringAfterLast('.')) {
"TermHere" -> handleTermHere()
"UserScript" -> handleUserScript()
else -> handleNormal()
}
private fun handleNormal() {
@ -98,12 +95,12 @@ class NeoTermRemoteInterface : AppCompatActivity(), ServiceConnection {
private fun handleTermHere() {
if (intent.hasExtra(Intent.EXTRA_STREAM)) {
val extra = intent.extras.get(Intent.EXTRA_STREAM)
val extra = intent.extras?.get(Intent.EXTRA_STREAM)
if (extra is Uri) {
val path = MediaUtils.getPath(this, extra)
val path = this.getPathOfMediaUri(extra)
val file = File(path)
val dirPath = if (file.isDirectory) path else file.parent
val command = "cd " + TerminalUtils.escapeString(dirPath)
val command = "cd " + Terminals.escapeString(dirPath)
openTerm(command, null)
}
finish()
@ -128,23 +125,23 @@ class NeoTermRemoteInterface : AppCompatActivity(), ServiceConnection {
if (intent.hasExtra(Intent.EXTRA_STREAM)) {
// action send
val extra = intent.extras.get(Intent.EXTRA_STREAM)
val extra = intent.extras?.get(Intent.EXTRA_STREAM)
when (extra) {
is ArrayList<*> -> {
extra.takeWhile { it is Uri }
.mapTo(filesToHandle) {
val uri = it as Uri
File(MediaUtils.getPath(this, uri)).absolutePath
File(this.getPathOfMediaUri(uri)).absolutePath
}
}
is Uri -> {
filesToHandle.add(File(MediaUtils.getPath(this, extra)).absolutePath)
filesToHandle.add(File(this.getPathOfMediaUri(extra)).absolutePath)
}
}
} else if (intent.data != null) {
// action view
filesToHandle.add(File(intent.data.path).absolutePath)
filesToHandle.add(File(intent.data?.path).absolutePath)
}
if (filesToHandle.isNotEmpty()) {

View File

@ -23,7 +23,7 @@ import io.neoterm.frontend.session.shell.client.TermCompleteListener
import io.neoterm.frontend.terminal.TerminalView
import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
import io.neoterm.ui.term.NeoTermActivity
import io.neoterm.utils.TerminalUtils
import io.neoterm.utils.Terminals
/**
* @author kiva
@ -43,8 +43,8 @@ class NeoTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
val view = inflater.inflate(R.layout.ui_term, parent, false)
val terminalView = view.findViewById<TerminalView>(R.id.terminal_view)
val extraKeysView = view.findViewById<ExtraKeysView>(R.id.extra_keys)
TerminalUtils.setupTerminalView(terminalView)
TerminalUtils.setupExtraKeysView(extraKeysView)
Terminals.setupTerminalView(terminalView)
Terminals.setupExtraKeysView(extraKeysView)
val colorSchemeManager = ComponentManager.getComponent<ColorSchemeComponent>()
colorSchemeManager.applyColorScheme(

View File

@ -1,23 +0,0 @@
package io.neoterm.utils
import android.content.Context
import java.io.File
import java.nio.file.Files
/**
* @author kiva
*/
object AssetsUtils {
fun extractAssetsDir(context: Context, dirName: String, extractDir: String) {
val assets = context.assets
assets.list(dirName)?.let {
it.map { File(extractDir, it) }
.takeWhile { !it.exists() }
.forEach { file ->
assets.open("$dirName/${file.name}").use {
kotlin.runCatching { Files.copy(it, file.toPath()) }
}
}
}
}
}

View File

@ -1,26 +0,0 @@
package io.neoterm.utils
import java.text.DecimalFormat
/**
* @author kiva
*/
object FileUtils {
fun formatSizeInKB(size: Long): String {
val decimalFormat = DecimalFormat("####.00");
if (size < 1024) {
return "$size KB"
} else if (size < 1024 * 1024) {
val parsedSize: Float = size / 1024f
return decimalFormat.format(parsedSize) + " MB"
} else if (size < 1024 * 1024 * 1024) {
val parsedSize: Float = size / 1024f / 1024f
return decimalFormat.format(parsedSize) + " GB"
} else if (size < 1024L * 1024 * 1024 * 1024) {
val parsedSize: Float = size / 1024f / 1024f / 1024f
return decimalFormat.format(parsedSize) + " TB"
} else {
return "$size KB"
}
}
}

View File

@ -1,141 +0,0 @@
package io.neoterm.utils
import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
/**
* @author kiva
*/
object MediaUtils {
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
* @param context The context.
* *
* @param uri The Uri to query.
*/
@SuppressLint("ObsoleteSdkInt")
fun getPath(context: Context, uri: Uri?): String? {
uri ?: return null
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
}
// Temporary workaround for non-primary volumes
return "/storage/$type/${split[1]}"
} else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)!!
)
return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val contentUri = when (split[0]) {
"image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> null
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri!!, selection, selectionArgs)
}
} else if ("content".equals(uri.scheme, ignoreCase = true)) {
return getDataColumn(context, uri, null, null)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
return uri.path
}
return null
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
* @param context The context.
* *
* @param uri The Uri to query.
* *
* @param selection (Optional) Filter used in the query.
* *
* @param selectionArgs (Optional) Selection arguments used in the query.
* *
* @return The value of the _data column, which is typically a file path.
*/
private fun getDataColumn(
context: Context, uri: Uri, selection: String?,
selectionArgs: Array<String>?
): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val column_index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index)
}
} finally {
if (cursor != null)
cursor.close()
}
return null
}
/**
* @param uri The Uri to check.
* *
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* *
* @return Whether the Uri authority is DownloadsProvider.
*/
private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* *
* @return Whether the Uri authority is MediaProvider.
*/
private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
}

View File

@ -1,28 +0,0 @@
package io.neoterm.utils
import android.content.Context
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.floating.TerminalDialog
/**
* @author kiva
*/
object PackageUtils {
fun apt(context: Context, subCommand: String, extraArgs: Array<String>?, callback: (Int, TerminalDialog) -> Unit) {
val argArray =
if (extraArgs != null) arrayOf(NeoTermPath.APT_BIN_PATH, subCommand, *extraArgs)
else arrayOf(NeoTermPath.APT_BIN_PATH, subCommand)
TerminalDialog(context)
.onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
val exit = finishedSession?.exitStatus ?: 1
callback(exit, dialog)
}
})
.imeEnabled(true)
.execute(NeoTermPath.APT_BIN_PATH, argArray)
.show("apt $subCommand")
}
}

View File

@ -1,22 +0,0 @@
package io.neoterm.utils
/**
* @author kiva
*/
class RangedInt(val number: Int, val range: IntRange) {
fun increaseOne(): Int {
var result = number + 1
if (result > range.last) {
result = 0
}
return result
}
fun decreaseOne(): Int {
var result = number - 1
if (result < 0) {
result = range.last
}
return result
}
}

View File

@ -17,7 +17,7 @@ import io.neoterm.frontend.terminal.extrakey.ExtraKeysView
/**
* @author kiva
*/
object TerminalUtils {
object Terminals {
fun setupTerminalView(terminalView: TerminalView?, terminalViewClient: TerminalViewClient? = null) {
terminalView?.textSize = NeoPreference.getFontSize();

View File

@ -0,0 +1,135 @@
package io.neoterm.utils
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import io.neoterm.backend.TerminalSession
import io.neoterm.frontend.config.NeoTermPath
import io.neoterm.frontend.floating.TerminalDialog
import java.io.File
import java.nio.file.Files
import java.text.DecimalFormat
class RangedInt(private val number: Int, private val range: IntRange) {
fun inc() = (number + 1).takeIf { range.contains(it) } ?: 0
fun dec() = (number - 1).takeIf { range.contains(it) } ?: range.last
}
fun Long.formatSizeInKB(): String {
val decimalFormat = DecimalFormat("####.00");
if (this < 1024) {
return "$this KB"
} else if (this < 1024 * 1024) {
val parsedSize: Float = this / 1024f
return decimalFormat.format(parsedSize) + " MB"
} else if (this < 1024 * 1024 * 1024) {
val parsedSize: Float = this / 1024f / 1024f
return decimalFormat.format(parsedSize) + " GB"
} else if (this < 1024L * 1024 * 1024 * 1024) {
val parsedSize: Float = this / 1024f / 1024f / 1024f
return decimalFormat.format(parsedSize) + " TB"
} else {
return "$this KB"
}
}
fun Context.extractAssetsDir(dirName: String, extractDir: String) {
val assets = this.assets
assets.list(dirName)?.let {
it.map { File(extractDir, it) }
.takeWhile { !it.exists() }
.forEach { file ->
assets.open("$dirName/${file.name}").use {
kotlin.runCatching { Files.copy(it, file.toPath()) }
}
}
}
}
fun Context.runApt(subCommand: String, vararg extraArgs: String): Result<TerminalDialog> = runAsyncCatching {
val args = arrayOf(NeoTermPath.APT_BIN_PATH, subCommand, *extraArgs)
TerminalDialog(this)
.onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
val exit = finishedSession?.exitStatus ?: 1
if (exit == 0) {
dialog.dismiss()
throw SuccessResult(dialog)
}
else throw RuntimeException()
}
})
.imeEnabled(true)
.execute(NeoTermPath.APT_BIN_PATH, args)
.show("apt $subCommand")
}
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
* @param context The context.
* @param inUri The Uri to query.
*/
fun Context.getPathOfMediaUri(inUri: Uri?) = inUri?.let {
when {
"content".equals(it.scheme, ignoreCase = true) -> getDataColumn(this, it, null, null)
"file".equals(it.scheme, ignoreCase = true) -> it.path
DocumentsContract.isDocumentUri(this, it) -> this.getPathOfDocumentUri(it)
else -> null
}
}
private fun Context.getPathOfDocumentUri(uri: Uri) = if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) Environment.getExternalStorageDirectory().toString() + "/" + split[1]
else "/storage/$type/${split[1]}" // Temporary workaround for non-primary volumes
} else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
)
getDataColumn(this, contentUri, null, null)
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val contentUri = when (split[0]) {
"image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> null
}
getDataColumn(this, contentUri!!, "_id=?", arrayOf(split[1]))
} else null
/**
* Get the value of the data column for this Uri
*/
private fun getDataColumn(context: Context, uri: Uri, selection: String?, selectionArgs: Array<String>?) =
context.contentResolver.query(uri, arrayOf("_data"), selection, selectionArgs, null)?.use {
if (it.moveToFirst()) {
val columnIndex = it.getColumnIndex("_data").takeIf { it != -1 } ?: return@use null
it.getString(columnIndex)
} else null
}
private fun isExternalStorageDocument(uri: Uri) = "com.android.externalstorage.documents" == uri.authority
private fun isDownloadsDocument(uri: Uri) = "com.android.providers.downloads.documents" == uri.authority
private fun isMediaDocument(uri: Uri) = "com.android.providers.media.documents" == uri.authority
data class SuccessResult(val data: Any) : RuntimeException()
inline fun <reified R> runAsyncCatching(block: () -> Unit): Result<R> = try {
block()
Result.failure(IllegalStateException())
} catch (s: SuccessResult) {
if (s.data is R) Result.success(s.data)
else Result.failure(ClassCastException())
} catch (e: Throwable) {
Result.failure(e)
}