diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f5bfba9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*.kt] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL +ij_kotlin_line_comment_at_first_column = false +ij_kotlin_line_comment_add_space = true +ij_kotlin_name_count_to_use_star_import = 2147483647 +ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 +ij_kotlin_keep_blank_lines_in_declarations = 1 +ij_kotlin_keep_blank_lines_in_code = 1 +ij_kotlin_keep_blank_lines_before_right_brace = 0 +ij_kotlin_align_multiline_parameters = false +ij_continuation_indent_size = 4 +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *,^ diff --git a/app/build.gradle b/app/build.gradle index b4ab4bc..28240c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'org.jmailen.kotlinter' } android { @@ -38,4 +39,8 @@ dependencies { implementation 'androidx.fragment:fragment-ktx:1.4.1' implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'com.google.android.material:material:1.6.1' -} \ No newline at end of file +} + +// remove this line to disable ktlint +preBuild.dependsOn(lintKotlin) +lintKotlin.dependsOn(formatKotlin) diff --git a/app/src/main/java/vegabobo/dsusideloader/AboutActivity.kt b/app/src/main/java/vegabobo/dsusideloader/AboutActivity.kt index b0c0e4b..50e96d3 100644 --- a/app/src/main/java/vegabobo/dsusideloader/AboutActivity.kt +++ b/app/src/main/java/vegabobo/dsusideloader/AboutActivity.kt @@ -40,5 +40,4 @@ class AboutActivity : AppCompatActivity() { fun btnGoogle(view: View) { launchUrlIntent("https://developer.android.com/") } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/HomeFragment.kt b/app/src/main/java/vegabobo/dsusideloader/HomeFragment.kt index 8cd6e89..a4c4eab 100644 --- a/app/src/main/java/vegabobo/dsusideloader/HomeFragment.kt +++ b/app/src/main/java/vegabobo/dsusideloader/HomeFragment.kt @@ -20,6 +20,7 @@ import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textview.MaterialTextView +import kotlin.math.roundToInt import vegabobo.dsusideloader.checks.CompatibilityCheck import vegabobo.dsusideloader.checks.OperationMode import vegabobo.dsusideloader.dsuhelper.GsiDsuObject @@ -28,7 +29,6 @@ import vegabobo.dsusideloader.util.FilenameUtils import vegabobo.dsusideloader.util.SPUtils import vegabobo.dsusideloader.util.SetupStorageAccess import vegabobo.dsusideloader.util.WorkspaceFilesUtils -import kotlin.math.roundToInt class HomeFragment : Fragment() { @@ -38,13 +38,15 @@ class HomeFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - if (isEnvCompatible(true)) + if (isEnvCompatible(true)) { SetupStorageAccess(requireContext()) + } // gsid refuses to start installation when < 40% free storage // prevent user from using app on this circumstances - if (!hasAvailableStorage()) + if (!hasAvailableStorage()) { showNoAvaiableStorageDialog() + } gsiDsuObject.userdataSize = SPUtils.getUserdataSize(requireActivity()) @@ -59,7 +61,7 @@ class HomeFragment : Fragment() { val tc = requireView().findViewById(R.id.tv_defaultuserdata) val txDebugBuildInfo = requireView().findViewById(R.id.text_debugbuild) - if(BuildConfig.DEBUG) { + if (BuildConfig.DEBUG) { txDebugBuildInfo.visibility = View.VISIBLE txDebugBuildInfo.text = getString(R.string.debug_build_info, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE) } @@ -103,12 +105,12 @@ class HomeFragment : Fragment() { btnIncrease.setOnClickListener { gsiDsuObject.userdataSize++ edDSsize.setText(getString(R.string.gigabyte_holder, gsiDsuObject.userdataSize)) - } btnDecrease.setOnClickListener { - if (gsiDsuObject.userdataSize >= 2) + if (gsiDsuObject.userdataSize >= 2) { gsiDsuObject.userdataSize-- + } edDSsize.setText(getString(R.string.gigabyte_holder, gsiDsuObject.userdataSize)) } @@ -150,14 +152,16 @@ class HomeFragment : Fragment() { } btnInstall.setOnClickListener { - if (!cbGSIsize.isChecked) { gsiDsuObject.fileSize = if (edGSIsize.toString().isNotEmpty()) { edGSIsize.text.toString().toLong() } else { Toast.makeText( activity, - getString(R.string.invalid_gsi_size, getString(R.string.auto)), + getString( + R.string.invalid_gsi_size, + getString(R.string.auto) + ), Toast.LENGTH_SHORT ).show() -1 @@ -165,7 +169,11 @@ class HomeFragment : Fragment() { } if (!cbDSsize.isChecked) { - gsiDsuObject.userdataSize = edDSsize.text.toString().split("GB")[0].toInt() + gsiDsuObject.userdataSize = edDSsize.text + .toString() + .split("GB") + .first() + .toInt() } beginInstall(selectedGsi, gsiDsuObject) @@ -181,24 +189,27 @@ class HomeFragment : Fragment() { ) } - if (selectedGsi != Uri.EMPTY) + if (selectedGsi != Uri.EMPTY) { btnInstall.isEnabled = true - + } } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_home, container, false) } - private fun checkDialog(title: String, text: String, finish: Boolean) { MaterialAlertDialogBuilder(requireActivity()) .setTitle(title) .setMessage(text) - .setPositiveButton(if (finish) getString(R.string.close_app) else getString(R.string.got_it)) { _, _ -> if (finish) requireActivity().finish() } + .setPositiveButton( + if (finish) getString(R.string.close_app) + else getString(R.string.got_it) + ) { _, _ -> if (finish) requireActivity().finish() } .setCancelable(false) .show() } @@ -208,7 +219,9 @@ class HomeFragment : Fragment() { val blockSize = statFs.blockSizeLong val totalSize = statFs.blockCountLong * blockSize val availableSize = statFs.availableBlocksLong * blockSize - return ((availableSize.toFloat() / totalSize.toFloat()) * 100).roundToInt() > 40 + // free storage in percentage + val freeStorage = (availableSize.toFloat() / totalSize.toFloat()) * 100 + return freeStorage.roundToInt() > 40 } private fun isEnvCompatible(showDialogs: Boolean): Boolean { @@ -277,60 +290,67 @@ class HomeFragment : Fragment() { } private fun beginInstall(selectedGsi: Uri, gsiDsuObject: GsiDsuObject) { - - val selectedFile = FilenameUtils.queryName(requireActivity().contentResolver, selectedGsi) + val selectedFile = FilenameUtils.queryName( + requireActivity().contentResolver, + selectedGsi + ) // file need to have a extension, if not, show error dialog. if (selectedFile.contains(".")) { - - when (selectedFile.substring(selectedFile.lastIndexOf("."))) { - ".xz", ".gz", ".img", ".zip" -> { - - MaterialAlertDialogBuilder(requireActivity()) - .setTitle(R.string.info) - .setMessage(getString(R.string.warning)) - .setPositiveButton(getString(R.string.proceed)) { _, _ -> - - MaterialAlertDialogBuilder(requireActivity()) - .setTitle(getString(R.string.installation)) - .setMessage( - getString( - R.string.installation_details, - selectedFile, - gsiDsuObject.userdataSize.toString(), - if (gsiDsuObject.fileSize == -1L) getString(R.string.auto) else gsiDsuObject.fileSize - ) - ) - .setPositiveButton(getString(R.string.proceed)) { _, _ -> - WorkspaceFilesUtils.cleanWorkspaceFolder( - requireActivity(), - true - ) - Thread( - PrepareDsu( - requireActivity(), - selectedGsi, - gsiDsuObject - ) - ).start() - } - .setNegativeButton(getString(R.string.cancel), null) - .setCancelable(true) - .show() - - } - .setNegativeButton(getString(R.string.cancel), null) - .show() - } - else -> { - showUnsupportedDialog() + when (selectedFile.substringAfterLast(".")) { + "xz", "gz", "img", "zip" -> { + beginInstallDialog(selectedFile, selectedGsi, gsiDsuObject) } + else -> showUnsupportedDialog() } } else { showUnsupportedDialog() } } + private fun beginInstallDialog( + selectedFile: String, + selectedGsi: Uri, + gsiDsuObject: GsiDsuObject + ) { + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(R.string.info) + .setMessage(getString(R.string.warning)) + .setPositiveButton(getString(R.string.proceed)) { _, _ -> + + MaterialAlertDialogBuilder(requireActivity()) + .setTitle(getString(R.string.installation)) + .setMessage( + getString( + R.string.installation_details, + selectedFile, + gsiDsuObject.userdataSize.toString(), + if (gsiDsuObject.fileSize == -1L) { + getString(R.string.auto) + } else gsiDsuObject.fileSize + ) + ) + .setPositiveButton(getString(R.string.proceed)) { _, _ -> + WorkspaceFilesUtils.cleanWorkspaceFolder( + requireActivity(), + true + ) + Thread( + PrepareDsu( + requireActivity(), + selectedGsi, + gsiDsuObject + ) + ).start() + } + .setNegativeButton(getString(R.string.cancel), null) + .setCancelable(true) + .show() + } + .setNegativeButton(getString(R.string.cancel), null) + .show() + } + private fun showUnsupportedDialog() { MaterialAlertDialogBuilder(requireActivity()) .setTitle(R.string.unsupported) @@ -344,9 +364,10 @@ class HomeFragment : Fragment() { MaterialAlertDialogBuilder(requireActivity()) .setTitle(R.string.error) .setMessage(getString(R.string.storage_warning)) - .setPositiveButton(getString(R.string.close_app)) { _, _ -> requireActivity().finish() } + .setPositiveButton(getString(R.string.close_app)) { _, _ -> + requireActivity().finish() + } .setCancelable(false) .show() } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/LogsActivity.kt b/app/src/main/java/vegabobo/dsusideloader/LogsActivity.kt index a098b34..d48d98c 100644 --- a/app/src/main/java/vegabobo/dsusideloader/LogsActivity.kt +++ b/app/src/main/java/vegabobo/dsusideloader/LogsActivity.kt @@ -39,11 +39,12 @@ class LogsActivity : AppCompatActivity() { override fun onAddElement(s: String?) { entireLogcat = entireLogcat + s + "\n" if (logLines != -1 && logLines < 200) { - tvLog.append("\n"+s) + tvLog.append("\n" + s) logLines++ } else { - if(logLines!=-1) + if (logLines != -1) { tvLog.text = "Logs are too big, export to see everything.\n\n${tvLog.text}" + } logLines = -1 } } @@ -68,7 +69,6 @@ class LogsActivity : AppCompatActivity() { intent.putExtra(Intent.EXTRA_TITLE, "logs") resultLauncher.launch(intent) } - } override fun onBackPressed() { @@ -93,5 +93,4 @@ class LogsActivity : AppCompatActivity() { Toast.makeText(this@LogsActivity, getString(R.string.error), Toast.LENGTH_SHORT).show() } } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/MainActivity.kt b/app/src/main/java/vegabobo/dsusideloader/MainActivity.kt index f195e78..e2de2d4 100644 --- a/app/src/main/java/vegabobo/dsusideloader/MainActivity.kt +++ b/app/src/main/java/vegabobo/dsusideloader/MainActivity.kt @@ -5,7 +5,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import com.google.android.material.bottomnavigation.BottomNavigationView - class MainActivity : AppCompatActivity() { private var fragment1: Fragment = HomeFragment() @@ -35,7 +34,6 @@ class MainActivity : AppCompatActivity() { } } - bottomNav.setOnItemSelectedListener { item -> when (item.itemId) { R.id.tab_home -> { @@ -51,8 +49,5 @@ class MainActivity : AppCompatActivity() { } true } - } - - } diff --git a/app/src/main/java/vegabobo/dsusideloader/PreferencesFragment.kt b/app/src/main/java/vegabobo/dsusideloader/PreferencesFragment.kt index 4c737c7..42a3ab2 100644 --- a/app/src/main/java/vegabobo/dsusideloader/PreferencesFragment.kt +++ b/app/src/main/java/vegabobo/dsusideloader/PreferencesFragment.kt @@ -7,7 +7,6 @@ import androidx.preference.PreferenceFragmentCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder import vegabobo.dsusideloader.checks.OperationMode - class PreferencesFragment : PreferenceFragmentCompat() { private var OP_MODE = "" @@ -33,7 +32,6 @@ class PreferencesFragment : PreferenceFragmentCompat() { true } } - } private fun dialogInfo(message: String) { @@ -73,5 +71,4 @@ class PreferencesFragment : PreferenceFragmentCompat() { } } } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/RunOnAdbActivity.kt b/app/src/main/java/vegabobo/dsusideloader/RunOnAdbActivity.kt index c907489..2e415b4 100644 --- a/app/src/main/java/vegabobo/dsusideloader/RunOnAdbActivity.kt +++ b/app/src/main/java/vegabobo/dsusideloader/RunOnAdbActivity.kt @@ -17,7 +17,6 @@ class RunOnAdbActivity : AppCompatActivity() { tvAdbCommandFull.text = value tvAdbCommandQuick.text = value!!.replace("adb shell ", "") - } override fun onBackPressed() { @@ -28,5 +27,4 @@ class RunOnAdbActivity : AppCompatActivity() { .setNegativeButton(R.string.cancel, null) .show() } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/SplashActivity.kt b/app/src/main/java/vegabobo/dsusideloader/SplashActivity.kt index c3a2c45..29c6a27 100644 --- a/app/src/main/java/vegabobo/dsusideloader/SplashActivity.kt +++ b/app/src/main/java/vegabobo/dsusideloader/SplashActivity.kt @@ -26,5 +26,4 @@ class SplashActivity : Activity() { finish() } } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/checks/CompatibilityCheck.kt b/app/src/main/java/vegabobo/dsusideloader/checks/CompatibilityCheck.kt index 1ddb9ee..a5dc366 100644 --- a/app/src/main/java/vegabobo/dsusideloader/checks/CompatibilityCheck.kt +++ b/app/src/main/java/vegabobo/dsusideloader/checks/CompatibilityCheck.kt @@ -32,8 +32,5 @@ class CompatibilityCheck { } return value } - } - - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/checks/OperationMode.kt b/app/src/main/java/vegabobo/dsusideloader/checks/OperationMode.kt index deae324..5b39bb8 100644 --- a/app/src/main/java/vegabobo/dsusideloader/checks/OperationMode.kt +++ b/app/src/main/java/vegabobo/dsusideloader/checks/OperationMode.kt @@ -22,17 +22,17 @@ class OperationMode { // untested on other root solution, warn not tested const val OTHER_ROOT_SOLUTION = 100 - } companion object { fun getOperationMode(): Int { return if (Shell.cmd("whoami").exec().isSuccess) { - if (Shell.cmd("su --version").exec().out.toString().contains("MAGISK")) { - if (obtainMagiskVersion() < 23018) + if ("MAGISK" in Shell.cmd("su --version").exec().out.toString()) { + if (obtainMagiskVersion() < 23018) { Constants.MAGISK_UNSUPPORTED - else + } else { Constants.ROOT_MAGISK + } } else { Constants.OTHER_ROOT_SOLUTION } @@ -42,10 +42,11 @@ class OperationMode { } fun obtainMagiskVersion(): Int { - return Shell.cmd("su -V").exec().out.toString().replace("[", "").replace("]", "") + return Shell.cmd("su -V").exec().out + .toString() + .replace("[", "") + .replace("]", "") .toInt() } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/DSUCommand.kt b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/DSUCommand.kt index 2637b92..f762093 100644 --- a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/DSUCommand.kt +++ b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/DSUCommand.kt @@ -6,7 +6,9 @@ import vegabobo.dsusideloader.util.SPUtils import vegabobo.dsusideloader.util.WorkspaceFilesUtils class DSUCommand( - private val gsiDsuObject: GsiDsuObject, val context: Context, private val skipDebugMode: Boolean + private val gsiDsuObject: GsiDsuObject, + val context: Context, + private val skipDebugMode: Boolean ) { object Constants { @@ -22,7 +24,10 @@ class DSUCommand( fun getInstallScript(): String { return String.format( - getShellScriptFromAssets(), getDebugMode(), genArguments(), installationInfoAsString() + getShellScriptFromAssets(), + getDebugMode(), + genArguments(), + installationInfoAsString() ) } @@ -46,10 +51,13 @@ class DSUCommand( var arguments = "" arguments += addArgument("-d", "${gsiDsuObject.absolutePath}") arguments += addArgument("--el", "KEY_USERDATA_SIZE", gsiDsuObject.getUserdataInBytes()) - if (gsiDsuObject.fileSize != -1L) + if (gsiDsuObject.fileSize != -1L) { arguments += addArgument( - "--el", "KEY_SYSTEM_SIZE", gsiDsuObject.fileSize + "--el", + "KEY_SYSTEM_SIZE", + gsiDsuObject.fileSize ) + } return arguments.trim() } @@ -62,18 +70,18 @@ class DSUCommand( } private fun getDebugMode(): Boolean { - return if (skipDebugMode) + return if (skipDebugMode) { false - else + } else { SPUtils.isDebugModeEnabled(context) + } } - fun installationInfoAsString():String{ + fun installationInfoAsString(): String { return "Installation info: " + - "\nAbsolute path: " + this.gsiDsuObject.absolutePath + - "\nFile size: " + this.gsiDsuObject.fileSize + - "\nUserdata size: " + this.gsiDsuObject.userdataSize + - "\n\nLogcat:\n" + "\nAbsolute path: " + this.gsiDsuObject.absolutePath + + "\nFile size: " + this.gsiDsuObject.fileSize + + "\nUserdata size: " + this.gsiDsuObject.userdataSize + + "\n\nLogcat:\n" } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/GsiDsuObject.kt b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/GsiDsuObject.kt index 126a126..ebe2b54 100644 --- a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/GsiDsuObject.kt +++ b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/GsiDsuObject.kt @@ -43,5 +43,4 @@ class GsiDsuObject( return arrayOfNulls(size) } } - } diff --git a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/PrepareDsu.kt b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/PrepareDsu.kt index 0178cf1..126d454 100644 --- a/app/src/main/java/vegabobo/dsusideloader/dsuhelper/PrepareDsu.kt +++ b/app/src/main/java/vegabobo/dsusideloader/dsuhelper/PrepareDsu.kt @@ -9,6 +9,7 @@ import androidx.appcompat.app.AlertDialog import androidx.documentfile.provider.DocumentFile import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.topjohnwu.superuser.Shell +import java.math.BigInteger import vegabobo.dsusideloader.LogsActivity import vegabobo.dsusideloader.R import vegabobo.dsusideloader.RunOnAdbActivity @@ -19,7 +20,7 @@ import vegabobo.dsusideloader.util.SPUtils import vegabobo.dsusideloader.util.WorkspaceFilesUtils class PrepareDsu( - private val c: Context, + private val ctx: Context, private val uri: Uri, private val gsiDsuObject: GsiDsuObject? ) : Runnable { @@ -28,53 +29,61 @@ class PrepareDsu( var cleanWorkspace = true override fun run() { - - val builder = MaterialAlertDialogBuilder(c) - (c as Activity).runOnUiThread { + val builder = MaterialAlertDialogBuilder(ctx) + (ctx as Activity).runOnUiThread { builder.setCancelable(false) builder.setView(R.layout.progress) dialog = builder.create() dialog.show() } - val file = FilenameUtils.queryName(c.contentResolver, uri) + val file = FilenameUtils.queryName(ctx.contentResolver, uri) - val dsu: GsiDsuObject? = when (file.substring(file.lastIndexOf("."))) { - ".xz" -> { - transformFile2Gzip(uri, DeCompressionUtils.Constants.XZ_TO_IMG, gsiDsuObject) + val dsu: GsiDsuObject? = when (file.substringAfterLast(".")) { + "xz" -> { + transformFile2Gzip( + uri, + DeCompressionUtils.Constants.XZ_TO_IMG, + gsiDsuObject + ) } - ".img" -> { - transformFile2Gzip(uri, DeCompressionUtils.Constants.IMG_TO_GZ, gsiDsuObject) + "img" -> { + transformFile2Gzip( + uri, + DeCompressionUtils.Constants.IMG_TO_GZ, + gsiDsuObject + ) } - ".gz" -> { + "gz" -> { var gzUri = uri if (uri.path.toString().contains("msf:")) { - updateText(c.getString(R.string.gz_copy)) + updateText(ctx.getString(R.string.gz_copy)) gzUri = WorkspaceFilesUtils.copyFileToSafFolder( - c, + ctx, uri, file, - WorkspaceFilesUtils.getWorkspaceFolder(c) + WorkspaceFilesUtils.getWorkspaceFolder(ctx) ) } - if (gsiDsuObject!!.fileSize != -1L) + if (gsiDsuObject!!.fileSize != -1L) { gsiDsuObject.absolutePath = FilenameUtils.getFilePath(gzUri, true) + } + transformFile2Gzip( gzUri, DeCompressionUtils.Constants.GZ_TO_GSI_OBJECT, gsiDsuObject ) } - ".zip" -> { - val dsuPackageUri = if ( - uri.path.toString().contains("msf:")) { - updateText(c.getString(R.string.copying_file)) + "zip" -> { + val dsuPackageUri = if ("msf:" in uri.path.toString()) { + updateText(ctx.getString(R.string.copying_file)) WorkspaceFilesUtils.copyFileToSafFolder( - c, + ctx, uri, "dsu.zip", - WorkspaceFilesUtils.getWorkspaceFolder(c) + WorkspaceFilesUtils.getWorkspaceFolder(ctx) ) } else { uri @@ -84,32 +93,32 @@ class PrepareDsu( // workaround for java.net.URISyntaxException: Illegal character in path at index // com.android.dynsystem.InstallationAsyncTask.verifyAndPrepare(InstallationAsyncTask.java:273) - if (filePath.contains(" ")) + if (filePath.contains(" ")) { gsiDsuObject!!.absolutePath = filePath.replace(" ", "%20") - else + } else { gsiDsuObject!!.absolutePath = filePath + } gsiDsuObject } - else -> - gsiDsuObject + else -> gsiDsuObject } - c.runOnUiThread { + ctx.runOnUiThread { dialog.dismiss() } - if (cleanWorkspace) - WorkspaceFilesUtils.cleanWorkspaceFolder(c, false) + if (cleanWorkspace) { + WorkspaceFilesUtils.cleanWorkspaceFolder(ctx, false) + } when (OperationMode.getOperationMode()) { OperationMode.Constants.ROOT_MAGISK, OperationMode.Constants.OTHER_ROOT_SOLUTION -> { + val dsuCommand = DSUCommand(dsu!!, ctx, true) - val dsuCommand = DSUCommand(dsu!!, c, true) - - if (SPUtils.isDebugModeEnabled(c)) { - c.startActivity( - Intent(c, LogsActivity::class.java).putExtra( + if (SPUtils.isDebugModeEnabled(ctx)) { + ctx.startActivity( + Intent(ctx, LogsActivity::class.java).putExtra( "script", dsuCommand.getInstallScript() ).putExtra( @@ -121,55 +130,62 @@ class PrepareDsu( showFinishedDialog() Shell.cmd(dsuCommand.getInstallScript()).exec() } - } OperationMode.Constants.UNROOTED -> { - val dsuCommand = DSUCommand(dsu!!, c, false) + val dsuCommand = DSUCommand(dsu!!, ctx, false) showAdbCommandToDeployGSI(dsuCommand.writeInstallScript()) - } } } + private fun transformFile2Gzip(uri: Uri, mode: Int, gsiDsuObject: GsiDsuObject?): GsiDsuObject? { + val workspaceFolder = WorkspaceFilesUtils.getWorkspaceFolder(ctx) + val outputFilename = FilenameUtils.queryName(ctx.contentResolver, uri) - private fun transformFile2Gzip(uri: Uri, op: Int, gsiDsuObject: GsiDsuObject?): GsiDsuObject? { - - val workspaceFolder = WorkspaceFilesUtils.getWorkspaceFolder(c) - val outputFilename = FilenameUtils.queryName(c.contentResolver, uri) - - return when (op) { + return when (mode) { DeCompressionUtils.Constants.GZ_TO_GSI_OBJECT -> { - updateText(c.getString(R.string.almost_ready)) - if (gsiDsuObject!!.fileSize == -1L && gsiDsuObject.absolutePath == "") { - transformFile2Gzip(uri, DeCompressionUtils.Constants.GZ_TO_IMG, gsiDsuObject) + updateText(ctx.getString(R.string.almost_ready)) + val gsiSize = gsiDsuObject!!.fileSize + val gsiAbsPath = gsiDsuObject.absolutePath + if (gsiSize == -1L && gsiAbsPath == "") { + transformFile2Gzip( + uri, + DeCompressionUtils.Constants.GZ_TO_IMG, + gsiDsuObject + ) } else { - return gsiDsuObject + gsiDsuObject } } DeCompressionUtils.Constants.XZ_TO_IMG -> { - updateText(c.getString(R.string.extracting_xz)) + updateText(ctx.getString(R.string.extracting_xz)) val filenameWithoutExtension = - outputFilename.substring(0, outputFilename.length - 3) + outputFilename.substringBeforeLast(".") + + val uriFromIMGFile = DeCompressionUtils( + ctx, + uri, + filenameWithoutExtension, + workspaceFolder + ).extractXZFile()!! + transformFile2Gzip( - DeCompressionUtils( - c, - uri, - filenameWithoutExtension, - workspaceFolder - ).extractXZFile()!!, DeCompressionUtils.Constants.IMG_TO_GZ, gsiDsuObject + uriFromIMGFile, + DeCompressionUtils.Constants.IMG_TO_GZ, + gsiDsuObject ) } DeCompressionUtils.Constants.IMG_TO_GZ -> { - updateText(c.getString(R.string.compressing_img_to_gzip)) + updateText(ctx.getString(R.string.compressing_img_to_gzip)) val uriFromGZFile = DeCompressionUtils( - c, + ctx, uri, - "${outputFilename}.gz", + "$outputFilename.gz", workspaceFolder ).compressGzip() val absolutePath = FilenameUtils.getFilePath(uriFromGZFile!!, true) val bytesFromImageFile = - DocumentFile.fromSingleUri(c, uri)!!.length() + DocumentFile.fromSingleUri(ctx, uri)!!.length() gsiDsuObject!!.fileSize = bytesFromImageFile gsiDsuObject.absolutePath = absolutePath transformFile2Gzip( @@ -179,54 +195,81 @@ class PrepareDsu( ) } DeCompressionUtils.Constants.GZ_TO_IMG -> { - updateText(c.getString(R.string.extracting_gzip)) - val filenameWithoutExtension = - outputFilename.substring(0, outputFilename.length - 3) - val p = DeCompressionUtils( - c, - uri, - filenameWithoutExtension, - workspaceFolder - ).decompressGzip() val absolutePath = FilenameUtils.getFilePath(uri, true) gsiDsuObject!!.absolutePath = absolutePath - val bytesFromImageFile = - DocumentFile.fromSingleUri(c, p!!)!!.length() + val bytesFromImageFile = getImageSize( + uri, + workspaceFolder, + outputFilename + ) gsiDsuObject.fileSize = bytesFromImageFile transformFile2Gzip(uri, DeCompressionUtils.Constants.GZ_TO_GSI_OBJECT, gsiDsuObject) } else -> null } + } + + private fun getImageSize(uri: Uri, workspace: DocumentFile, output: String): Long { + val fileSize = DocumentFile.fromSingleUri(ctx, uri)!!.length() + val three_gb = Int.MAX_VALUE.toLong() * 1.5 // 2^32 * 0.75 + // If the .gz is smaller than 3gb, then try returning the image size + // by reading the lasts four bytes. + if (fileSize < three_gb) { + val inputStream = ctx.contentResolver.openInputStream(uri)!! + inputStream.skip(fileSize - 4) + val bytes = ByteArray(4) + inputStream.read(bytes) + bytes.reverse() // Little endian -> Big endian + val imageSize = BigInteger(1, bytes).toLong() + // If the image size is LOWER than the compressed file, then + // the image size must be wrong. + if (imageSize > fileSize) { + return imageSize + } + } + // If the .gz is bigger than 3gb or the fast-way returns a + // wrong value, we need to decompress the file and calculate + // the size. SLOWWWWWWWWWWWWWW + updateText(ctx.getString(R.string.extracting_gzip)) + val filenameWithoutExtension = output.substringBeforeLast(".") + + val decompressedImage = DeCompressionUtils( + ctx, + uri, + filenameWithoutExtension, + workspace + ).decompressGzip()!! + + val image = DocumentFile.fromSingleUri(ctx, decompressedImage)!! + return image.length() } private fun updateText(text: String) { - (c as Activity).runOnUiThread { + (ctx as Activity).runOnUiThread { val loadingMsg = dialog.findViewById(R.id.tv_loadingtext) loadingMsg!!.text = text } } private fun showFinishedDialog() { - (c as Activity).runOnUiThread { - MaterialAlertDialogBuilder(c) - .setTitle(c.getString(R.string.done)) - .setMessage(c.getString(R.string.process_finished)) - .setPositiveButton(c.getString(R.string.close_app)) { _, _ -> c.finish() } + (ctx as Activity).runOnUiThread { + MaterialAlertDialogBuilder(ctx) + .setTitle(ctx.getString(R.string.done)) + .setMessage(ctx.getString(R.string.process_finished)) + .setPositiveButton(ctx.getString(R.string.close_app)) { _, _ -> ctx.finish() } .setCancelable(false) .show() } } private fun showAdbCommandToDeployGSI(cmdline: String) { - (c as Activity).runOnUiThread { - val myIntent = Intent(c, RunOnAdbActivity::class.java) + (ctx as Activity).runOnUiThread { + val myIntent = Intent(ctx, RunOnAdbActivity::class.java) myIntent.putExtra("cmdline", "adb shell sh $cmdline") - c.startActivity(myIntent) + ctx.startActivity(myIntent) } } - - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/util/DeCompressionUtils.kt b/app/src/main/java/vegabobo/dsusideloader/util/DeCompressionUtils.kt index c8d8c99..1f3f609 100644 --- a/app/src/main/java/vegabobo/dsusideloader/util/DeCompressionUtils.kt +++ b/app/src/main/java/vegabobo/dsusideloader/util/DeCompressionUtils.kt @@ -4,10 +4,10 @@ import android.content.Context import android.net.Uri import android.util.Log import androidx.documentfile.provider.DocumentFile -import org.tukaani.xz.XZInputStream import java.io.BufferedInputStream import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream +import org.tukaani.xz.XZInputStream class DeCompressionUtils( private val context: Context, @@ -24,7 +24,6 @@ class DeCompressionUtils( const val WORKSPACE_FOLDER = "workspace_dsuhelper" } - fun extractXZFile(): Uri? { return try { val extractedFile: DocumentFile? = @@ -89,7 +88,5 @@ class DeCompressionUtils( Log.e("decompressGzip", e.stackTraceToString()) null } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/util/FilenameUtils.kt b/app/src/main/java/vegabobo/dsusideloader/util/FilenameUtils.kt index 4377ee9..c421a79 100644 --- a/app/src/main/java/vegabobo/dsusideloader/util/FilenameUtils.kt +++ b/app/src/main/java/vegabobo/dsusideloader/util/FilenameUtils.kt @@ -13,8 +13,9 @@ class FilenameUtils { val input = uri.path.toString() val safStorage = input.split("/document/")[1].replace("/tree/", "") val path = safStorage.split(":")[1] - if (path.contains("/storage/emulated")) + if (path.contains("/storage/emulated")) { return if (addQuotes) "'file://'$path" else "file://$path" + } return if (safStorage.contains("primary")) { val storagePath = "file:///storage/emulated/0/" val finalPath = "$storagePath$path" @@ -34,7 +35,5 @@ class FilenameUtils { returnCursor.close() return name } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/util/SPUtils.kt b/app/src/main/java/vegabobo/dsusideloader/util/SPUtils.kt index 3cb0dfa..95537a6 100644 --- a/app/src/main/java/vegabobo/dsusideloader/util/SPUtils.kt +++ b/app/src/main/java/vegabobo/dsusideloader/util/SPUtils.kt @@ -1,6 +1,7 @@ package vegabobo.dsusideloader.util import android.content.Context +import android.content.SharedPreferences import androidx.appcompat.app.AppCompatActivity import vegabobo.dsusideloader.BuildConfig @@ -40,48 +41,44 @@ class SPUtils { writeStringToSP(c, Constants.PREF_RW_PATH, path) } - private fun getStringFromSP(c: Context, key: String): String { - val prefs = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) + private fun getSharedPrefs(ctx: Context): SharedPreferences { + return ctx.getSharedPreferences( + Constants.PREFERENCE_FILE, + AppCompatActivity.MODE_PRIVATE + ) + } + + private fun getStringFromSP(ctx: Context, key: String): String { + val prefs = getSharedPrefs(ctx) return prefs.getString(key, "")!! } - private fun writeStringToSP(c: Context, key: String, value: String) { - val editor = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) - .edit() + private fun writeStringToSP(ctx: Context, key: String, value: String) { + val editor = getSharedPrefs(ctx).edit() editor.putString(key, value) editor.apply() } - private fun getBooleanFromSP(c: Context, key: String): Boolean { - val prefs = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) + private fun getBooleanFromSP(ctx: Context, key: String): Boolean { + val prefs = getSharedPrefs(ctx) return prefs.getBoolean(key, false) } - private fun writeBooleanToSP(c: Context, key: String, value: Boolean) { - val editor = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) - .edit() + private fun writeBooleanToSP(ctx: Context, key: String, value: Boolean) { + val editor = getSharedPrefs(ctx).edit() editor.putBoolean(key, value) editor.apply() } - private fun getIntegerFromSP(c: Context, key: String, fallback: Int): Int { - val prefs = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) + private fun getIntegerFromSP(ctx: Context, key: String, fallback: Int): Int { + val prefs = getSharedPrefs(ctx) return prefs.getInt(key, fallback) } - private fun writeIntegerToSP(c: Context, key: String, value: Int) { - val editor = - c.getSharedPreferences(Constants.PREFERENCE_FILE, AppCompatActivity.MODE_PRIVATE) - .edit() + private fun writeIntegerToSP(ctx: Context, key: String, value: Int) { + val editor = getSharedPrefs(ctx).edit() editor.putInt(key, value) editor.apply() } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/util/SetupStorageAccess.kt b/app/src/main/java/vegabobo/dsusideloader/util/SetupStorageAccess.kt index 08e7409..138cf8b 100644 --- a/app/src/main/java/vegabobo/dsusideloader/util/SetupStorageAccess.kt +++ b/app/src/main/java/vegabobo/dsusideloader/util/SetupStorageAccess.kt @@ -11,13 +11,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import vegabobo.dsusideloader.R class SetupStorageAccess( - private val c: Context + private val ctx: Context ) { private lateinit var v: ActivityResultLauncher init { - if (!arePermissionsGranted(SPUtils.getSafRwPath(c))) { + if (!arePermissionsGranted(SPUtils.getSafRwPath(ctx))) { setupSAFActivityResult() askSafStorageAccess() } @@ -25,18 +25,18 @@ class SetupStorageAccess( private fun setupSAFActivityResult() { v = - (c as AppCompatActivity).registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + (ctx as AppCompatActivity).registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { val data: Intent? = result.data val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION - c.contentResolver.takePersistableUriPermission( + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ctx.contentResolver.takePersistableUriPermission( data?.data!!, takeFlags ) - SPUtils.writeSafRwPath(c, data.data.toString()) - }else{ - (c as Activity).finish() + SPUtils.writeSafRwPath(ctx, data.data.toString()) + } else { + (ctx as Activity).finish() } } } @@ -44,24 +44,24 @@ class SetupStorageAccess( private fun arePermissionsGranted( folderUri: String ): Boolean { - val foldersUriPermissions = c.contentResolver.persistedUriPermissions + val foldersUriPermissions = ctx.contentResolver.persistedUriPermissions for (folder in foldersUriPermissions) { val persistedUriString = folder.uri.toString() if (folderUri == persistedUriString) { - // If folder with granted permissions doesn't exists // (eg. user deleted folder, or apk data restored externally from a backup) // then, we should ask user to grant permissions to a folder again - if (!DocumentFile.fromTreeUri(c, folder.uri)!!.exists()) + if (!DocumentFile.fromTreeUri(ctx, folder.uri)!!.exists()) { return false + } // check if uri has r/w permission - if (folder.isWritePermission && folder.isReadPermission) + if (folder.isWritePermission && folder.isReadPermission) { return true - + } } else { // if we have permission in some folder we don't need to, permission to it will be revoked - c.revokeUriPermission( + ctx.revokeUriPermission( folder.uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) @@ -76,15 +76,14 @@ class SetupStorageAccess( } private fun askSafStorageAccess() { - if (!arePermissionsGranted(SPUtils.getSafRwPath(c))) { - MaterialAlertDialogBuilder(c) - .setTitle(c.getString(R.string.storage)) - .setMessage(c.getString(R.string.storage_info)) + if (!arePermissionsGranted(SPUtils.getSafRwPath(ctx))) { + MaterialAlertDialogBuilder(ctx) + .setTitle(ctx.getString(R.string.storage)) + .setMessage(ctx.getString(R.string.storage_info)) .setPositiveButton(R.string.got_it) { _, _ -> setupSafStorage() } - .setNegativeButton(R.string.close_app) { _, _ -> (c as Activity).finish() } + .setNegativeButton(R.string.close_app) { _, _ -> (ctx as Activity).finish() } .setCancelable(false) .show() } } - -} \ No newline at end of file +} diff --git a/app/src/main/java/vegabobo/dsusideloader/util/WorkspaceFilesUtils.kt b/app/src/main/java/vegabobo/dsusideloader/util/WorkspaceFilesUtils.kt index eb78719..ab4c4a5 100644 --- a/app/src/main/java/vegabobo/dsusideloader/util/WorkspaceFilesUtils.kt +++ b/app/src/main/java/vegabobo/dsusideloader/util/WorkspaceFilesUtils.kt @@ -14,16 +14,18 @@ class WorkspaceFilesUtils { fun getWorkspaceFolder(context: Context): DocumentFile { val workspaceFolder = DocumentFile.fromTreeUri(context, (SPUtils.getSafRwPath(context)).toUri())!! - if (workspaceFolder.findFile(DeCompressionUtils.Constants.WORKSPACE_FOLDER) == null) + if (workspaceFolder.findFile(DeCompressionUtils.Constants.WORKSPACE_FOLDER) == null) { workspaceFolder.createDirectory(DeCompressionUtils.Constants.WORKSPACE_FOLDER)!! + } return workspaceFolder.findFile(DeCompressionUtils.Constants.WORKSPACE_FOLDER)!! } fun cleanWorkspaceFolder(context: Context, deleteAlsoGzFile: Boolean) { for (i in getWorkspaceFolder(context).listFiles()) { - if (deleteAlsoGzFile || !i.name.toString().endsWith("gz")) + if (deleteAlsoGzFile || !i.name.toString().endsWith("gz")) { i.delete() + } } } @@ -54,5 +56,4 @@ class WorkspaceFilesUtils { return finalFile!!.uri } } - -} \ No newline at end of file +} diff --git a/build.gradle b/build.gradle index 7b9b67d..986790e 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,9 @@ plugins { id 'com.android.application' version '7.2.1' apply false id 'com.android.library' version '7.2.1' apply false id 'org.jetbrains.kotlin.android' version '1.6.10' apply false + id 'org.jmailen.kotlinter' version "3.9.0" apply false } task clean(type: Delete) { delete rootProject.buildDir -} \ No newline at end of file +}