diff --git a/app/build.gradle b/app/build.gradle index 9cd39b1..5867471 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,6 +44,9 @@ android { targetCompatibility 1.8 sourceCompatibility 1.8 } + kotlinOptions { + freeCompilerArgs = ["-Xallow-result-return-type"] + } } dependencies { diff --git a/app/src/main/java/io/neoterm/component/colorscheme/ColorSchemeComponent.kt b/app/src/main/java/io/neoterm/component/colorscheme/ColorSchemeComponent.kt index 4e2875f..f855c5f 100644 --- a/app/src/main/java/io/neoterm/component/colorscheme/ColorSchemeComponent.kt +++ b/app/src/main/java/io/neoterm/component/colorscheme/ColorSchemeComponent.kt @@ -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(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) diff --git a/app/src/main/java/io/neoterm/component/extrakey/ExtraKeyComponent.kt b/app/src/main/java/io/neoterm/component/extrakey/ExtraKeyComponent.kt index 9a7fc41..36ed3a7 100644 --- a/app/src/main/java/io/neoterm/component/extrakey/ExtraKeyComponent.kt +++ b/app/src/main/java/io/neoterm/component/extrakey/ExtraKeyComponent.kt @@ -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(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}") } diff --git a/app/src/main/java/io/neoterm/component/font/FontComponent.kt b/app/src/main/java/io/neoterm/component/font/FontComponent.kt index 02a4a33..3c9d337 100644 --- a/app/src/main/java/io/neoterm/component/font/FontComponent.kt +++ b/app/src/main/java/io/neoterm/component/font/FontComponent.kt @@ -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 diff --git a/app/src/main/java/io/neoterm/component/userscript/UserScriptComponent.kt b/app/src/main/java/io/neoterm/component/userscript/UserScriptComponent.kt index 9cee684..a15eaa0 100644 --- a/app/src/main/java/io/neoterm/component/userscript/UserScriptComponent.kt +++ b/app/src/main/java/io/neoterm/component/userscript/UserScriptComponent.kt @@ -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*/) diff --git a/app/src/main/java/io/neoterm/frontend/floating/TerminalDialog.kt b/app/src/main/java/io/neoterm/frontend/floating/TerminalDialog.kt index 9833bc5..918f569 100644 --- a/app/src/main/java/io/neoterm/frontend/floating/TerminalDialog.kt +++ b/app/src/main/java/io/neoterm/frontend/floating/TerminalDialog.kt @@ -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) } diff --git a/app/src/main/java/io/neoterm/frontend/floating/WindowTermView.kt b/app/src/main/java/io/neoterm/frontend/floating/WindowTermView.kt index c07b22e..b05b835 100644 --- a/app/src/main/java/io/neoterm/frontend/floating/WindowTermView.kt +++ b/app/src/main/java/io/neoterm/frontend/floating/WindowTermView.kt @@ -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?) { diff --git a/app/src/main/java/io/neoterm/services/NeoTermService.kt b/app/src/main/java/io/neoterm/services/NeoTermService.kt index c9d0472..c25b492 100644 --- a/app/src/main/java/io/neoterm/services/NeoTermService.kt +++ b/app/src/main/java/io/neoterm/services/NeoTermService.kt @@ -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 } diff --git a/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt b/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt index afb1010..0e21a34 100644 --- a/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt +++ b/app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt @@ -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) } diff --git a/app/src/main/java/io/neoterm/ui/customize/ColorSchemeActivity.kt b/app/src/main/java/io/neoterm/ui/customize/ColorSchemeActivity.kt index 714215e..d824943 100644 --- a/app/src/main/java/io/neoterm/ui/customize/ColorSchemeActivity.kt +++ b/app/src/main/java/io/neoterm/ui/customize/ColorSchemeActivity.kt @@ -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(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) { diff --git a/app/src/main/java/io/neoterm/ui/customize/CustomizeActivity.kt b/app/src/main/java/io/neoterm/ui/customize/CustomizeActivity.kt index 45d1c2d..1d45e2e 100644 --- a/app/src/main/java/io/neoterm/ui/customize/CustomizeActivity.kt +++ b/app/src/main/java/io/neoterm/ui/customize/CustomizeActivity.kt @@ -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) diff --git a/app/src/main/java/io/neoterm/ui/pm/PackageManagerActivity.kt b/app/src/main/java/io/neoterm/ui/pm/PackageManagerActivity.kt index 7016102..725e64b 100644 --- a/app/src/main/java/io/neoterm/ui/pm/PackageManagerActivity.kt +++ b/app/src/main/java/io/neoterm/ui/pm/PackageManagerActivity.kt @@ -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() - val sourceFiles = SourceHelper.detectSourceFiles() + val pm = ComponentManager.getComponent() + 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, query: String, mapper: (NeoPackageInfo) -> String ): List> { 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 } diff --git a/app/src/main/java/io/neoterm/ui/pm/model/PackageModel.kt b/app/src/main/java/io/neoterm/ui/pm/model/PackageModel.kt index 5fb6909..a52704e 100644 --- a/app/src/main/java/io/neoterm/ui/pm/model/PackageModel.kt +++ b/app/src/main/java/io/neoterm/ui/pm/model/PackageModel.kt @@ -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 ) } diff --git a/app/src/main/java/io/neoterm/ui/settings/GeneralSettingsActivity.kt b/app/src/main/java/io/neoterm/ui/settings/GeneralSettingsActivity.kt index 889d27d..361f167 100644 --- a/app/src/main/java/io/neoterm/ui/settings/GeneralSettingsActivity.kt +++ b/app/src/main/java/io/neoterm/ui/settings/GeneralSettingsActivity.kt @@ -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) diff --git a/app/src/main/java/io/neoterm/ui/setup/SetupActivity.kt b/app/src/main/java/io/neoterm/ui/setup/SetupActivity.kt index 4134d14..982bf01 100644 --- a/app/src/main/java/io/neoterm/ui/setup/SetupActivity.kt +++ b/app/src/main/java/io/neoterm/ui/setup/SetupActivity.kt @@ -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(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)) - } - } - } -} \ No newline at end of file + private fun executeAptUpgrade() = runApt("upgrade", "-y") + .onSuccess { finish() } + .onFailure { Toast.makeText(this, R.string.error, Toast.LENGTH_SHORT).show() } +} diff --git a/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt b/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt index c733cc2..9962804 100644 --- a/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt +++ b/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt @@ -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() } diff --git a/app/src/main/java/io/neoterm/ui/term/NeoTermRemoteInterface.kt b/app/src/main/java/io/neoterm/ui/term/NeoTermRemoteInterface.kt index 205693d..4a96dbe 100644 --- a/app/src/main/java/io/neoterm/ui/term/NeoTermRemoteInterface.kt +++ b/app/src/main/java/io/neoterm/ui/term/NeoTermRemoteInterface.kt @@ -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()) { diff --git a/app/src/main/java/io/neoterm/ui/term/tab/NeoTabDecorator.kt b/app/src/main/java/io/neoterm/ui/term/tab/NeoTabDecorator.kt index 3cfee1d..3644998 100644 --- a/app/src/main/java/io/neoterm/ui/term/tab/NeoTabDecorator.kt +++ b/app/src/main/java/io/neoterm/ui/term/tab/NeoTabDecorator.kt @@ -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(R.id.terminal_view) val extraKeysView = view.findViewById(R.id.extra_keys) - TerminalUtils.setupTerminalView(terminalView) - TerminalUtils.setupExtraKeysView(extraKeysView) + Terminals.setupTerminalView(terminalView) + Terminals.setupExtraKeysView(extraKeysView) val colorSchemeManager = ComponentManager.getComponent() colorSchemeManager.applyColorScheme( diff --git a/app/src/main/java/io/neoterm/utils/AssetsUtils.kt b/app/src/main/java/io/neoterm/utils/AssetsUtils.kt deleted file mode 100644 index c6aa699..0000000 --- a/app/src/main/java/io/neoterm/utils/AssetsUtils.kt +++ /dev/null @@ -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()) } - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/utils/FileUtils.kt b/app/src/main/java/io/neoterm/utils/FileUtils.kt deleted file mode 100644 index 94ea8eb..0000000 --- a/app/src/main/java/io/neoterm/utils/FileUtils.kt +++ /dev/null @@ -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" - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/utils/MediaUtils.kt b/app/src/main/java/io/neoterm/utils/MediaUtils.kt deleted file mode 100644 index 1c0cf32..0000000 --- a/app/src/main/java/io/neoterm/utils/MediaUtils.kt +++ /dev/null @@ -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? { - - 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 - } -} diff --git a/app/src/main/java/io/neoterm/utils/PackageUtils.kt b/app/src/main/java/io/neoterm/utils/PackageUtils.kt deleted file mode 100644 index 00e97cd..0000000 --- a/app/src/main/java/io/neoterm/utils/PackageUtils.kt +++ /dev/null @@ -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?, 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") - } -} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/utils/RangedInt.kt b/app/src/main/java/io/neoterm/utils/RangedInt.kt deleted file mode 100644 index 611bfe2..0000000 --- a/app/src/main/java/io/neoterm/utils/RangedInt.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/utils/TerminalUtils.kt b/app/src/main/java/io/neoterm/utils/Terminals.kt similarity index 98% rename from app/src/main/java/io/neoterm/utils/TerminalUtils.kt rename to app/src/main/java/io/neoterm/utils/Terminals.kt index bc8c690..2a44c87 100644 --- a/app/src/main/java/io/neoterm/utils/TerminalUtils.kt +++ b/app/src/main/java/io/neoterm/utils/Terminals.kt @@ -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(); diff --git a/app/src/main/java/io/neoterm/utils/utils.kt b/app/src/main/java/io/neoterm/utils/utils.kt new file mode 100644 index 0000000..73d8015 --- /dev/null +++ b/app/src/main/java/io/neoterm/utils/utils.kt @@ -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 = 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?) = + 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 runAsyncCatching(block: () -> Unit): Result = 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) +}