diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1a015ab..830df70 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { minSdk = 26 // targetSdk = 28 targetSdk = 34 - versionCode = 54 - versionName = "1.3.9" + versionCode = 55 + versionName = "1.4.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -67,6 +67,7 @@ dependencies { implementation("androidx.compose.material:material") implementation("androidx.compose.material3:material3") implementation("androidx.annotation:annotation:1.7.1") + implementation("androidx.documentfile:documentfile:1.0.1") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 7cb752e..ca0eec1 100644 Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/hwinzniej/musichelper/MainActivity.kt b/app/src/main/java/com/hwinzniej/musichelper/MainActivity.kt index d38c788..03b93f9 100644 --- a/app/src/main/java/com/hwinzniej/musichelper/MainActivity.kt +++ b/app/src/main/java/com/hwinzniej/musichelper/MainActivity.kt @@ -104,6 +104,7 @@ import java.util.Locale class MainActivity : ComponentActivity() { private lateinit var openDirectoryLauncher: ActivityResultLauncher + private lateinit var openLunaJSONDirLauncher: ActivityResultLauncher private lateinit var openEncryptDirectoryLauncher: ActivityResultLauncher private lateinit var openDecryptDirectoryLauncher: ActivityResultLauncher private lateinit var openMusicPlatformSqlFileLauncher: ActivityResultLauncher> @@ -156,6 +157,10 @@ class MainActivity : ComponentActivity() { registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> scanPage.handleUri(uri) } + openLunaJSONDirLauncher = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + convertPage.handelLunaDirUri(uri) + } openEncryptDirectoryLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> unlockPage.handleSelectedInputPath(uri) @@ -214,6 +219,7 @@ class MainActivity : ComponentActivity() { openMusicPlatformSqlFileLauncher = openMusicPlatformSqlFileLauncher, openResultSqlFileLauncher = openResultSqlFileLauncher, openPlaylistFileLauncher = openPlaylistFileLauncher, + openLunaJSONDirLauncher = openLunaJSONDirLauncher, db = db, componentActivity = this, encryptServer = settingsPage.encryptServer, diff --git a/app/src/main/java/com/hwinzniej/musichelper/activity/ConvertPage.kt b/app/src/main/java/com/hwinzniej/musichelper/activity/ConvertPage.kt index 1fd9378..0f98f42 100644 --- a/app/src/main/java/com/hwinzniej/musichelper/activity/ConvertPage.kt +++ b/app/src/main/java/com/hwinzniej/musichelper/activity/ConvertPage.kt @@ -68,6 +68,7 @@ class ConvertPage( private val openMusicPlatformSqlFileLauncher: ActivityResultLauncher>, private val openResultSqlFileLauncher: ActivityResultLauncher>, private val openPlaylistFileLauncher: ActivityResultLauncher>, + private val openLunaJSONDirLauncher: ActivityResultLauncher, val db: MusicDatabase, componentActivity: ComponentActivity, val encryptServer: MutableState, @@ -95,7 +96,6 @@ class ConvertPage( var enableAlbumNameMatch = mutableStateOf(true) var similarity = mutableFloatStateOf(85f) var useRootAccess = mutableStateOf(false) - var sourceAppText = mutableStateOf("") private var playlistId = mutableStateListOf() var playlistName = mutableStateListOf() var playlistEnabled = mutableStateListOf() @@ -245,6 +245,34 @@ class ConvertPage( } } + fun selectJsonFileDir() { + try { + openLunaJSONDirLauncher.launch(null) + } catch (_: Exception) { + Toast.makeText( + context, + context.getString(R.string.unable_start_documentsui), + Toast.LENGTH_SHORT + ).show() + } + } + + fun handelLunaDirUri(uri: Uri?) { + if (uri == null) { + return + } + try { + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + val temp = Tools().copyFilesToExternalFilesDir(uri, context, "lunaJsonDir", true) + delay(200L) //播放动画 + databaseFileName.value = temp + } + } catch (e: Exception) { + Toast.makeText(context, R.string.failed_to_get_file_from_dir, Toast.LENGTH_SHORT).show() + return + } + } + fun handleUri(uri: Uri?, code: Int) { if (uri == null) { return @@ -304,16 +332,15 @@ class ConvertPage( loadingProgressSema.acquire() loadingProgressSema.acquire() if (selectedMethod.intValue == 0) { - MyVibrationEffect( - context, - (context as MainActivity).enableHaptic.value, - context.hapticStrength.intValue - ).done() +// MyVibrationEffect( +// context, +// (context as MainActivity).enableHaptic.value, +// context.hapticStrength.intValue +// ).done() if (haveError) { showLoadingProgressBar.value = false } else { showLoadingProgressBar.value = true - currentPage.intValue = 1 delay(500L) databaseSummary() } @@ -345,6 +372,7 @@ class ConvertPage( }\n" errorDialogCustomAction.value = {} showErrorDialog.value = true + currentPage.intValue = 0 loadingProgressSema.release() haveError = true return@launch @@ -372,6 +400,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 } finally { db?.close() loadingProgressSema.release() @@ -392,6 +421,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 } } catch (e: Exception) { errorDialogTitle.value = @@ -405,6 +435,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 } finally { loadingProgressSema.release() } @@ -442,6 +473,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 loadingProgressSema.release() return@launch } @@ -459,14 +491,31 @@ class ConvertPage( if (!dir.exists()) dir.mkdirs() - val copyResult = Tools().execShellCmd( - "cp -f '/data/data/${appExists.split(":")[1]}/databases/${ - sourceApp.databaseName - }' '${dir.absolutePath}/${sourceApp.sourceEng}_temp.db'" - ) + val copyResult = if (selectedSourceApp.intValue == 5) { + val tempDir = + File(context.getExternalFilesDir(null)?.absolutePath + "/lunaJsonDir") + if (!tempDir.exists()) { + tempDir.mkdirs() + } + val tempPath = context.getExternalFilesDir(null)?.absolutePath + Tools().execShellCmd( + "find '/data/data/${appExists.split(":")[1]}/cache/NetCacheLoader' -type f -exec cp {} '${ + tempPath + }/lunaJsonDir' \\; && chmod -R 777 '${tempPath}/lunaJsonDir'" + ) + } else { + Tools().execShellCmd( + "cp -f '/data/data/${appExists.split(":")[1]}/databases/${ + sourceApp.databaseName + }' '${dir.absolutePath}/${sourceApp.sourceEng}_temp.db' && chmod 777 '${dir.absolutePath}/${sourceApp.sourceEng}_temp.db'" + ) + } if (copyResult == "") { databaseFilePath.value = "${dir.absolutePath}/${sourceApp.sourceEng}_temp.db" + if (selectedSourceApp.intValue == 5) { + lunaJsonToDatabase() + } loadingProgressSema.release() } else { errorDialogTitle.value = @@ -481,6 +530,7 @@ class ConvertPage( showErrorDialog.value = true haveError = true loadingProgressSema.release() + currentPage.intValue = 0 return } } else { @@ -509,11 +559,21 @@ class ConvertPage( R.string.read_failed ) }:\n - ${ - context.getString(R.string.app_not_installed).replace("#", sourceAppText.value) + context.getString(R.string.app_not_installed).replace( + "#", when (selectedSourceApp.intValue) { + 1 -> context.getString(R.string.source_netease_cloud_music) + 2 -> context.getString(R.string.source_qq_music) + 3 -> context.getString(R.string.source_kugou_music) + 4 -> context.getString(R.string.source_kuwo_music) + 5 -> context.getString(R.string.source_luna_music) + else -> "" + } + ) }\n" errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 loadingProgressSema.release() } } @@ -538,6 +598,7 @@ class ConvertPage( showErrorDialog.value = true loadingProgressSema.release() haveError = true + currentPage.intValue = 0 return@launch } @@ -549,30 +610,34 @@ class ConvertPage( } if (selectedMethod.intValue == 0) { if (sourceApp.sourceEng != "") { + currentPage.intValue = 1 if (useRootAccess.value) checkAppStatusWithRoot() else { - if (databaseFilePath.value.isEmpty()) { - errorDialogTitle.value = - context.getString(R.string.error_while_getting_data_dialog_title) - errorDialogContent.value += - "- ${context.getString(R.string.database_file)} ${ - context.getString( - R.string.read_failed - ) - }:\n - ${ - context.getString( - R.string.please_select_database_file - ) - }\n" - errorDialogCustomAction.value = {} - showErrorDialog.value = true - loadingProgressSema.release() - haveError = true - return@launch - } var db: SQLiteDatabase? = null try { + if (selectedSourceApp.intValue == 5) { + lunaJsonToDatabase() + } + if (databaseFilePath.value.isEmpty()) { + errorDialogTitle.value = + context.getString(R.string.error_while_getting_data_dialog_title) + errorDialogContent.value += + "- ${context.getString(R.string.database_file)} ${ + context.getString( + R.string.read_failed + ) + }:\n - ${ + context.getString( + R.string.please_select_database_file + ) + }\n" + errorDialogCustomAction.value = {} + showErrorDialog.value = true + currentPage.intValue = 0 + haveError = true + return@launch + } db = SQLiteDatabase.openDatabase( databaseFilePath.value, null, @@ -615,6 +680,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 } finally { db?.close() loadingProgressSema.release() @@ -637,6 +703,7 @@ class ConvertPage( errorDialogCustomAction.value = {} showErrorDialog.value = true haveError = true + currentPage.intValue = 0 loadingProgressSema.release() } } else { @@ -1872,7 +1939,7 @@ class ConvertPage( if (songArtists.isBlank() || songArtists == "null") { songArtists = context.getString(R.string.unknown) } - if (songAlbum == null || songAlbum.isBlank() || songAlbum == "null") { + if (songAlbum.isNullOrBlank() || songAlbum == "null") { songAlbum = context.getString(R.string.unknown) } db.execSQL( @@ -1940,7 +2007,7 @@ class ConvertPage( songArtists = context.getString(R.string.unknown) } - if (songAlbum == null || songAlbum.isBlank() || songAlbum == "null") { + if (songAlbum.isNullOrBlank() || songAlbum == "null") { songAlbum = context.getString(R.string.unknown) } db.execSQL( @@ -2019,7 +2086,7 @@ class ConvertPage( songArtists = context.getString(R.string.unknown) } - if (songAlbum == null || songAlbum.isBlank() || songAlbum == "null") { + if (songAlbum.isNullOrBlank() || songAlbum == "null") { songAlbum = context.getString(R.string.unknown) } db.execSQL( @@ -2126,7 +2193,7 @@ class ConvertPage( songArtists = context.getString(R.string.unknown) } - if (songAlbum == null || songAlbum.isBlank() || songAlbum == "null") { + if (songAlbum.isNullOrBlank() || songAlbum == "null") { songAlbum = context.getString(R.string.unknown) } db.execSQL( @@ -2229,7 +2296,7 @@ class ConvertPage( songArtists = context.getString(R.string.unknown) } - if (songAlbum == null || songAlbum.isBlank() || songAlbum == "null") { + if (songAlbum.isNullOrBlank() || songAlbum == "null") { songAlbum = context.getString(R.string.unknown) } db.execSQL( @@ -3405,5 +3472,107 @@ class ConvertPage( it[DataStoreConstants.LUNA_COOKIE] = "" } } + + private fun lunaJsonToDatabase() { + val dirPath = context.getExternalFilesDir(null)?.absolutePath + "/lunaJsonDir" + val dir = File(dirPath) + if (!dir.exists()) + return + val files = dir.listFiles() + if (files == null || files.isEmpty()) + return + Tools().copyAssetFileToExternalFilesDir(context, "QQMusic") + val databaseFile = File(context.getExternalFilesDir(null), "QQMusic") + val db = SQLiteDatabase.openDatabase( + databaseFile.absolutePath, + null, + SQLiteDatabase.OPEN_READWRITE + ) + databaseFilePath.value = databaseFile.absolutePath + files.forEach { file -> + val content = file.readText() + + val json: JSONObject + try { + json = JSON.parseObject(content) + } catch (_: Exception) { + return@forEach + } + + if (json.containsKey("media_resources")) { + // 歌单详情 + json.getJSONArray("media_resources").forEachIndexed { index, it -> + val song = (it as JSONObject).getJSONObject("entity") + .getJSONObject("track_wrapper") + .getJSONObject("track") + val playlistId = json.getJSONObject("playlist").getString("id") + val songId = song.getString("id") + val songName = song.getString("name") + + val songArtistsBuilder = StringBuilder() + song.getJSONArray("artists").forEach { it1 -> + val artistsInfo = it1 as JSONObject + songArtistsBuilder.append(artistsInfo.getString("name")) + songArtistsBuilder.append("/") + } + songArtistsBuilder.deleteCharAt(songArtistsBuilder.length - 1) + var songArtists = songArtistsBuilder.toString() + + var songAlbum = song.getJSONObject("album").getString("name") + if (songArtists.isBlank() || songArtists == "null") { + songArtists = context.getString(R.string.unknown) + } + if (songAlbum.isNullOrBlank() || songAlbum == "null") { + songAlbum = context.getString(R.string.unknown) + } + db.execSQL( + "INSERT INTO ${sourceApp.songListSongInfoTableName} (${sourceApp.songListSongInfoPlaylistId}, ${sourceApp.songListSongInfoSongId}, ${sourceApp.sortField}) VALUES (?, ?, ?)", + arrayOf( + playlistId, + songId, + index + ) + ) + + db.rawQuery( + "SELECT COUNT(*) FROM ${sourceApp.songInfoTableName} WHERE ${sourceApp.songInfoSongId} = ?", + arrayOf(songId) + ).use { cursor -> + cursor.moveToFirst() + if (cursor.getInt(0) == 0) { + db.execSQL( + "INSERT INTO ${sourceApp.songInfoTableName} (${sourceApp.songInfoSongId}, ${sourceApp.songInfoSongName}, ${sourceApp.songInfoSongArtist}, ${sourceApp.songInfoSongAlbum}) VALUES (?, ?, ?, ?)", + arrayOf( + songId, + songName, + songArtists, + songAlbum + ) + ) + } + } + } + } else if (json.containsKey("playlists")) { + // 歌单列表 + val playlistInfo = + json.getJSONArray("playlists") + playlistInfo.forEach pForEach@{ + val playlist = it as JSONObject + if (!playlist.containsKey("count_tracks") || playlist.getInteger("count_tracks") == 0) { + return@pForEach + } + db.execSQL( + "INSERT INTO ${sourceApp.songListTableName} (${sourceApp.songListId}, ${sourceApp.songListName}, ${sourceApp.musicNum}) VALUES (?, ?, ?)", + arrayOf( + playlist.getString("id"), + playlist.getString("title"), + playlist.getInteger("count_tracks") + ) + ) + } + } else return@forEach + } + db.close() + } } diff --git a/app/src/main/java/com/hwinzniej/musichelper/ui/ConvertPageUi.kt b/app/src/main/java/com/hwinzniej/musichelper/ui/ConvertPageUi.kt index 12a7e93..3273156 100644 --- a/app/src/main/java/com/hwinzniej/musichelper/ui/ConvertPageUi.kt +++ b/app/src/main/java/com/hwinzniej/musichelper/ui/ConvertPageUi.kt @@ -210,6 +210,14 @@ fun ConvertPageUi( } BackHandler(enabled = (currentPage.intValue != 0 && mainActivityPageState.currentPage == 1)) { + if (showLoadingProgressBar.value || showNumberProgressBar.value || showDialogProgressBar.value) { + Toast.makeText( + context, + context.getString(R.string.wait_operate_end), + Toast.LENGTH_SHORT + ).show() + return@BackHandler + } if (convertResult.isEmpty()) { if (currentPage.intValue == 3) { init(500L) @@ -984,6 +992,8 @@ fun ConvertPageUi( qrCodeDeviceRelated["token"]?.let { userLoggedIn = convertPage.lunaGetLoginStatus(token = it) + if (userLoggedIn) + convertPage.lunaVerifyRelated.clear() } showDialogProgressBar.value = false } @@ -1676,6 +1686,14 @@ fun ConvertPageUi( ) { TitleBar( onBack = { + if (showLoadingProgressBar.value || showNumberProgressBar.value || showDialogProgressBar.value) { + Toast.makeText( + context, + context.getString(R.string.wait_operate_end), + Toast.LENGTH_SHORT + ).show() + return@TitleBar + } if (convertResult.isEmpty()) { MyVibrationEffect(context, enableHaptic.value, hapticStrength.intValue).click() if (currentPage.intValue == 3) { @@ -1914,13 +1932,6 @@ fun ConvertPageUi( } sourceAppPopupMenuState.dismiss() databaseFileName.value = "" - selectedMethod.intValue = 1 - coroutine.launch { - dataStore.edit { settings -> - settings[DataStoreConstants.GET_PLAYLIST_METHOD] = - 1 - } - } }, selected = selectedSourceApp.intValue == 5, text = stringResource(R.string.source_luna_music), @@ -1934,34 +1945,41 @@ fun ConvertPageUi( state = methodPopupMenuState, text = stringResource(R.string.way_to_get_song_list), selectedItem = when (selectedMethod.intValue) { - 0 -> stringResource(R.string.database) + 0 -> if (selectedSourceApp.intValue == 5) + stringResource(R.string.json_file) + else + stringResource(R.string.database) + 1 -> stringResource(R.string.online) else -> "" }, popupWidth = 165, ) { - if (selectedSourceApp.intValue != 5) - PopupMenuItem( - onClick = { - MyVibrationEffect( - context, - enableHaptic.value, - hapticStrength.intValue - ).click() - selectedMethod.intValue = 0 - coroutine.launch { - dataStore.edit { settings -> - settings[DataStoreConstants.GET_PLAYLIST_METHOD] = - 0 - } + PopupMenuItem( + onClick = { + MyVibrationEffect( + context, + enableHaptic.value, + hapticStrength.intValue + ).click() + selectedMethod.intValue = 0 + coroutine.launch { + dataStore.edit { settings -> + settings[DataStoreConstants.GET_PLAYLIST_METHOD] = + 0 } - methodPopupMenuState.dismiss() - }, - selected = selectedMethod.intValue == 0, - text = stringResource(R.string.database), - iconPainter = painterResource(id = R.drawable.database), - iconColor = SaltTheme.colors.text - ) + } + methodPopupMenuState.dismiss() + }, + selected = selectedMethod.intValue == 0, + text = if (selectedSourceApp.intValue == 5) + stringResource(R.string.json_file) + else stringResource(R.string.database), + iconPainter = if (selectedSourceApp.intValue == 5) + painterResource(id = R.drawable.json_file) + else painterResource(id = R.drawable.database), + iconColor = SaltTheme.colors.text + ) PopupMenuItem( onClick = { MyVibrationEffect( @@ -1987,28 +2005,41 @@ fun ConvertPageUi( AnimatedVisibility( visible = (selectedSourceApp.intValue != 0) && !useRootAccess.value && (selectedMethod.intValue == 0) ) { - Item( - onClick = { convertPage.selectDatabaseFile() }, - text = stringResource(R.string.select_database_file_match_to_source).replace( - "#", - when (selectedSourceApp.intValue) { - 1 -> stringResource(R.string.source_netease_cloud_music) - 2 -> stringResource(R.string.source_qq_music) - 3 -> stringResource(R.string.source_kugou_music) - 4 -> stringResource(R.string.source_kuwo_music) - 5 -> stringResource(R.string.source_luna_music) - else -> "" - } + Column { + Item( + onClick = { + if (selectedSourceApp.intValue == 5) + convertPage.selectJsonFileDir() + else + convertPage.selectDatabaseFile() + }, + text = + if (selectedSourceApp.intValue == 5) + stringResource(R.string.select_json_file_dir_match_to_source).replace( + "#", + stringResource(R.string.source_luna_music) + ) + else + stringResource(R.string.select_database_file_match_to_source).replace( + "#", + when (selectedSourceApp.intValue) { + 1 -> stringResource(R.string.source_netease_cloud_music) + 2 -> stringResource(R.string.source_qq_music) + 3 -> stringResource(R.string.source_kugou_music) + 4 -> stringResource(R.string.source_kuwo_music) + else -> "" + } + ) ) - ) - } - AnimatedVisibility( - visible = (databaseFileName.value != "") && !useRootAccess.value - ) { - ItemValue( - text = stringResource(R.string.you_have_selected), - rightSub = databaseFileName.value - ) + AnimatedVisibility( + visible = (databaseFileName.value != "") && !useRootAccess.value + ) { + ItemValue( + text = stringResource(R.string.you_have_selected), + rightSub = databaseFileName.value + ) + } + } } } @@ -2030,12 +2061,7 @@ fun ConvertPageUi( AnimatedVisibility( visible = useCustomResultFile.value ) { - Column( - modifier = Modifier - .weight(1f) - .fillMaxSize() - .background(color = SaltTheme.colors.subBackground) - ) { + Column { Item( onClick = { convertPage.selectResultFile() }, text = stringResource(R.string.select_result_file_item_title).replace( @@ -2140,7 +2166,7 @@ fun ConvertPageUi( ) ) AnimatedVisibility( - visible = (convertPage.sourcePlaylistFileName.value.isNotBlank()) + visible = convertPage.sourcePlaylistFileName.value.isNotBlank() ) { ItemValue( text = stringResource(R.string.you_have_selected), @@ -2301,6 +2327,32 @@ fun ConvertPageUi( ) { RoundedColumn { ItemTitle(text = stringResource(R.string.select_needed_musiclist)) + AnimatedVisibility( + visible = playlistEnabled.isEmpty() + ) { + ItemContainer { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AnimatedContent( + targetState = showLoadingProgressBar.value, + label = "", + transitionSpec = { + fadeIn() togetherWith fadeOut() + }) { + Text( + text = if (it) + stringResource(id = R.string.loading) + else + stringResource(id = R.string.no_playlist_found), + fontSize = 16.sp, + color = SaltTheme.colors.subText + ) + } + } + } + } AnimatedVisibility( visible = playlistEnabled.isNotEmpty() ) { diff --git a/app/src/main/java/com/hwinzniej/musichelper/utils/Tools.kt b/app/src/main/java/com/hwinzniej/musichelper/utils/Tools.kt index d62cab8..42f8757 100644 --- a/app/src/main/java/com/hwinzniej/musichelper/utils/Tools.kt +++ b/app/src/main/java/com/hwinzniej/musichelper/utils/Tools.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.provider.OpenableColumns +import androidx.documentfile.provider.DocumentFile import com.alibaba.fastjson2.JSON import com.alibaba.fastjson2.JSONObject import com.google.zxing.BarcodeFormat @@ -77,6 +78,45 @@ class Tools { return outputFile.absolutePath } + fun copyFilesToExternalFilesDir( + uri: Uri, + context: Context, + dirName: String, + clean: Boolean = false + ): String { + val directory = DocumentFile.fromTreeUri(context, uri) + val files = directory?.listFiles() + if (clean) { + val externalFilesDir = + File("${context.getExternalFilesDir(null)?.absolutePath}/${dirName}") + if (externalFilesDir.isDirectory) + externalFilesDir.listFiles()?.forEach { file -> + file.delete() + } + } + + files?.forEach { file -> + if (file.isFile) { + val inputStream: InputStream? = context.contentResolver.openInputStream(file.uri) + val outputFile = File( + "${context.getExternalFilesDir(null)?.absolutePath}/${dirName}", + file.name ?: "" + ) + if (outputFile.parentFile?.exists() == false) { + outputFile.parentFile?.mkdirs() + } + val outputStream: OutputStream = FileOutputStream(outputFile) + + inputStream?.use { input -> + outputStream.use { output -> + input.copyTo(output) + } + } + } + } + return directory?.name ?: "" + } + fun deleteOldFiles(context: Context) { val directory = context.getExternalFilesDir(null) val currentTime = System.currentTimeMillis() @@ -86,7 +126,7 @@ class Tools { val fileAgeInDays = TimeUnit.MILLISECONDS.toDays(fileAge) if (fileAgeInDays > 3) { - file.delete() + file.deleteRecursively() } } } @@ -381,10 +421,9 @@ class Tools { ) .get() .build() - - return JSON.parseObject( - client.newCall(request).execute().body?.string() - ) + client.newCall(request).execute().use { + return JSON.parseObject(it.body?.string()) + } } fun copyAssetFileToExternalFilesDir(context: Context, filename: String) { @@ -399,7 +438,7 @@ class Tools { if (outFile.exists()) { outFile.delete() } - outputStream = FileOutputStream(outFile) + outputStream = FileOutputStream(outFile, false) val buffer = ByteArray(1024) var read: Int @@ -507,18 +546,25 @@ class Tools { } fun getCurrentIp(): String { - val client = OkHttpClient() - var request = Request.Builder() - request = request.url("https://myip.ipip.net/s").get() - var currentIp = - client.newCall(request.build()).execute().body?.string()?.replace("\n", "") - if (currentIp == null) { - request = Request.Builder() - request = request.url("https://ip.3322.net").get() - currentIp = - client.newCall(request.build()).execute().body?.string()?.replace("\n", "") - if (currentIp == null) - currentIp = "1.180.115.20" + var currentIp: String + try { + val client = OkHttpClient() + var request = Request.Builder() + request = request.url("https://myip.ipip.net/s").get() + client.newCall(request.build()).execute().use { + currentIp = it.body?.string()?.replace("\n", "") ?: "" + } + if (currentIp.isBlank()) { + request = Request.Builder() + request = request.url("https://ip.3322.net").get() + client.newCall(request.build()).execute().use { + currentIp = it.body?.string()?.replace("\n", "") ?: "" + } + if (currentIp.isBlank()) + currentIp = "1.180.115.20" + } + } catch (e: Exception) { + currentIp = "1.180.115.20" } return currentIp } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_border_foreground.xml b/app/src/main/res/drawable/ic_launcher_border_foreground.xml new file mode 100644 index 0000000..4a6d350 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_border_foreground.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..26a34b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/json_file.xml b/app/src/main/res/drawable/json_file.xml new file mode 100644 index 0000000..70fdde6 --- /dev/null +++ b/app/src/main/res/drawable/json_file.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09b..ba6ecb5 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,6 @@ - - + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09b..ba6ecb5 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,6 @@ - - + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index b861f02..6a45dc0 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index 3e6155c..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index 926c314..455da6e 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 03b550d..ff87aae 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index edbdd85..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 7abbef6..f51815d 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index b0ee671..35965ad 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 7fbebd6..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 0df1a3e..e87303a 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index ec01ce5..65a83ca 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 45f4114..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index b829d83..b7cb6dd 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index 94f5e4f..6aebe91 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index c3d9bbb..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index b71d162..682b2f3 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c33cd76..48dae6e 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -145,7 +145,7 @@ 소스 선택 태그 정보의 소스 선택 플레이리스트 소스 플랫폼 - "선택한 디렉토리: " + "선택한 디렉토리" 유사성 임계값 설정 유사성 임계값 설정 @@ -187,7 +187,7 @@ 아래 링크를 방문하시겠습니까? 브라우저에서 열립니다. 루트 액세스는 필요한 파일을 가져오는 데 사용됩니다 - 선택한 파일 + 선택됨 (일시적으로 사용할 수 없습니다) Gitee에서 이 프로젝트 보기 ⚠️ 선택한 디렉토리에 지원되는 음악 파일이 없습니다 @@ -366,4 +366,11 @@ Luna 음악은 이 로그인을 위험하다고 판단하여 보안 인증이 필요합니다.#n`#phone`를 사용하여 문자 메시지를 보내주세요:#n- 내용: `#content`#n- 받는 사람: `#target`#n#n전송이 성공하면 `확인`을 클릭합니다.#n지역 번호인 경우 `SMS 보내기` 버튼을 클릭하여 SMS 전송 인터페이스로 이동합니다. SMS 보내기 인증에 실패했습니다. SMS가 성공적으로 전송되었는지 확인하세요. + JSON 파일 디렉토리 + JSON 파일 + #의 JSON 파일이 있는 디렉터리를 선택 + 디렉토리에서 파일을 가져오지 못했습니다 + 노래 목록 데이터가 없습니다 + 로딩 중… + 작업이 완료될 때까지 기다려주세요 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4ebc6ec..cfaaa63 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -4,7 +4,7 @@ 扫描过程将会显示在这里。 正在扫描中,请稍后再试。 需要权限才能继续操作 - 选择的目录: + 选择的目录 扫描完成 存在上次扫描的结果!#n请选择您的操作: 检测到冲突 @@ -366,4 +366,11 @@ 汽水音乐认为本次登录存在风险,需要进行安全验证。#n请使用`#phone`,发送短信:#n- 内容:`#content`#n- 收信人:`#target`#n#n发送成功后,请点击`确定 `按钮。#n如果是本机号码,点击`发送短信 `按钮,即可跳转到短信发送界面。 发送短信 认证失败,请检查短信是否发送成功 + JSON文件目录 + JSON文件 + 选择#的JSON文件所在目录 + 从目录中获取文件失败 + 无歌单数据 + 加载中… + 请等待操作完成 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e3b831..e6bdf70 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,7 @@ Scanning, please try again later. #_Export Permissions are required to continue. - "Selected Directory: " + "Selected Directory" Done Conflict detected "Existence of results from last scan! #nPlease select your action: " @@ -49,7 +49,7 @@ Import database Select the database file of # the corresponding app - File selected + Selected Next Please select source App Use custom music output file @@ -372,4 +372,11 @@ Luna Music considers this login to be risky and requires security verification.#nPlease use `#phone` and send a text message:#n- Content: `#content`#n- To: `#target`.#n#nAfter sending successfully, click `OK`.#nIf it is a local number, click `Send SMS` button to jump to the SMS sending interface. Send SMS Authentication failed, please check if the SMS was sent successfully. + Directory of JSON files + JSON files + Select the directory where the JSON file for # + Failed to get file from directory + No playlist data + Loading… + Please wait for the operation to complete \ No newline at end of file