forked from zhanghai/MaterialFiles
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Add checksums tab for file properties
Fixes: zhanghai#48
- Loading branch information
Showing
11 changed files
with
328 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/ChecksumInfo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
* All Rights Reserved. | ||
*/ | ||
|
||
package me.zhanghai.android.files.fileproperties.checksum | ||
|
||
import androidx.annotation.StringRes | ||
import me.zhanghai.android.files.R | ||
import java.security.MessageDigest | ||
|
||
class ChecksumInfo(val checksums: Map<Algorithm, String>) { | ||
enum class Algorithm(@StringRes val nameRes: Int) { | ||
CRC32(R.string.file_properties_checksum_crc32), | ||
MD5(R.string.file_properties_checksum_md5), | ||
SHA1(R.string.file_properties_checksum_sha_1), | ||
SHA256(R.string.file_properties_checksum_sha_256), | ||
SHA512(R.string.file_properties_checksum_sha_512); | ||
|
||
fun createMessageDigest(): MessageDigest = | ||
when (this) { | ||
CRC32 -> Crc32MessageDigest() | ||
MD5 -> MessageDigest.getInstance("MD5") | ||
SHA1 -> MessageDigest.getInstance("SHA-1") | ||
SHA256 -> MessageDigest.getInstance("SHA-256") | ||
SHA512 -> MessageDigest.getInstance("SHA-512") | ||
} | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/ChecksumInfoLiveData.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
* All Rights Reserved. | ||
*/ | ||
|
||
package me.zhanghai.android.files.fileproperties.checksum | ||
|
||
import android.os.AsyncTask | ||
import java8.nio.file.Path | ||
import me.zhanghai.android.files.fileproperties.PathObserverLiveData | ||
import me.zhanghai.android.files.provider.common.newInputStream | ||
import me.zhanghai.android.files.util.Failure | ||
import me.zhanghai.android.files.util.Loading | ||
import me.zhanghai.android.files.util.Stateful | ||
import me.zhanghai.android.files.util.Success | ||
import me.zhanghai.android.files.util.toHexString | ||
import me.zhanghai.android.files.util.valueCompat | ||
|
||
class ChecksumInfoLiveData(path: Path) : PathObserverLiveData<Stateful<ChecksumInfo>>(path) { | ||
init { | ||
loadValue() | ||
observe() | ||
} | ||
|
||
override fun loadValue() { | ||
value = Loading(value?.value) | ||
AsyncTask.THREAD_POOL_EXECUTOR.execute { | ||
val value = try { | ||
val messageDigests = | ||
ChecksumInfo.Algorithm.entries.associateWith { it.createMessageDigest() } | ||
path.newInputStream().use { inputStream -> | ||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE) | ||
while (true) { | ||
val readSize = inputStream.read(buffer) | ||
if (readSize == -1) { | ||
break | ||
} | ||
messageDigests.values.forEach { it.update(buffer, 0, readSize) } | ||
} | ||
} | ||
val checksumInfo = ChecksumInfo( | ||
messageDigests.mapValues { it.value.digest().toHexString() } | ||
) | ||
Success(checksumInfo) | ||
} catch (e: Exception) { | ||
Failure(valueCompat.value, e) | ||
} | ||
postValue(value) | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/Crc32MessageDigest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
* All Rights Reserved. | ||
*/ | ||
|
||
package me.zhanghai.android.files.fileproperties.checksum | ||
|
||
import java.security.MessageDigest | ||
import java.util.zip.CRC32 | ||
|
||
class Crc32MessageDigest : MessageDigest("CRC32") { | ||
private val crc32 = CRC32() | ||
|
||
override fun engineUpdate(input: Byte) { | ||
crc32.update(input.toInt()) | ||
} | ||
|
||
override fun engineUpdate(input: ByteArray, offset: Int, length: Int) { | ||
crc32.update(input, offset, length) | ||
} | ||
|
||
override fun engineDigest(): ByteArray { | ||
val value = crc32.value | ||
crc32.reset() | ||
return ByteArray(4).apply { | ||
this[0] = (value ushr 24).toByte() | ||
this[1] = (value ushr 16).toByte() | ||
this[2] = (value ushr 8).toByte() | ||
this[3] = value.toByte() | ||
} | ||
} | ||
|
||
override fun engineReset() { | ||
crc32.reset() | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
...va/me/zhanghai/android/files/fileproperties/checksum/FilePropertiesChecksumTabFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
* All Rights Reserved. | ||
*/ | ||
|
||
package me.zhanghai.android.files.fileproperties.checksum | ||
|
||
import androidx.core.widget.doAfterTextChanged | ||
import java8.nio.file.Path | ||
import kotlinx.parcelize.Parcelize | ||
import kotlinx.parcelize.WriteWith | ||
import me.zhanghai.android.files.R | ||
import me.zhanghai.android.files.databinding.FilePropertiesChecksumCompareItemBinding | ||
import me.zhanghai.android.files.file.FileItem | ||
import me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment | ||
import me.zhanghai.android.files.util.ParcelableArgs | ||
import me.zhanghai.android.files.util.ParcelableParceler | ||
import me.zhanghai.android.files.util.Stateful | ||
import me.zhanghai.android.files.util.args | ||
import me.zhanghai.android.files.util.layoutInflater | ||
import me.zhanghai.android.files.util.viewModels | ||
|
||
class FilePropertiesChecksumTabFragment : FilePropertiesTabFragment() { | ||
private val args by args<Args>() | ||
|
||
private val viewModel by viewModels { { FilePropertiesChecksumTabViewModel(args.path) } } | ||
|
||
override fun onResume() { | ||
super.onResume() | ||
|
||
viewModel.checksumInfoLiveData.observe(viewLifecycleOwner) { onChecksumInfoChanged(it) } | ||
} | ||
|
||
override fun refresh() { | ||
viewModel.reload() | ||
} | ||
|
||
private fun onChecksumInfoChanged(stateful: Stateful<ChecksumInfo>) { | ||
bindView(stateful) { checksumInfo -> | ||
checksumInfo.checksums.forEach { addItemView(it.key.nameRes, it.value) } | ||
addCompareEdit(checksumInfo) | ||
} | ||
} | ||
|
||
private fun ViewBuilder.addCompareEdit(checksumInfo: ChecksumInfo) { | ||
val binding = getScrapItemBinding(FilePropertiesChecksumCompareItemBinding::class.java) | ||
?.also { addView(it) } | ||
?: FilePropertiesChecksumCompareItemBinding.inflate( | ||
linearLayout.context.layoutInflater, linearLayout, true | ||
) | ||
.also { it.root.tag = it } | ||
binding.compareEdit.doAfterTextChanged { editable -> | ||
val text = editable!!.toString().trim() | ||
if (text.isEmpty()) { | ||
binding.compareLayout.helperText = null | ||
binding.compareLayout.error = null | ||
return@doAfterTextChanged | ||
} | ||
val matchingAlgorithm = checksumInfo.checksums.firstNotNullOfOrNull { | ||
if (it.value.equals(text, true)) it.key else null | ||
} | ||
if (matchingAlgorithm != null) { | ||
binding.compareLayout.helperText = | ||
getString( | ||
R.string.file_properties_checksum_compare_match_format, | ||
getString(matchingAlgorithm.nameRes) | ||
) | ||
return@doAfterTextChanged | ||
} | ||
val prefixMatchingAlgorithm = checksumInfo.checksums.firstNotNullOfOrNull { | ||
if (it.value.startsWith(text, true)) it.key else null | ||
} | ||
if (prefixMatchingAlgorithm != null) { | ||
binding.compareLayout.helperText = | ||
getString( | ||
R.string.file_properties_checksum_compare_prefix_match_format, | ||
getString(prefixMatchingAlgorithm.nameRes) | ||
) | ||
return@doAfterTextChanged | ||
} | ||
binding.compareLayout.error = | ||
getString(R.string.file_properties_checksum_compare_no_match) | ||
} | ||
} | ||
|
||
companion object { | ||
fun isAvailable(file: FileItem): Boolean = file.attributes.isRegularFile | ||
} | ||
|
||
@Parcelize | ||
class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs | ||
} |
25 changes: 25 additions & 0 deletions
25
...a/me/zhanghai/android/files/fileproperties/checksum/FilePropertiesChecksumTabViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
* All Rights Reserved. | ||
*/ | ||
|
||
package me.zhanghai.android.files.fileproperties.checksum | ||
|
||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.ViewModel | ||
import java8.nio.file.Path | ||
import me.zhanghai.android.files.util.Stateful | ||
|
||
class FilePropertiesChecksumTabViewModel(path: Path) : ViewModel() { | ||
private val _checksumInfoLiveData = ChecksumInfoLiveData(path) | ||
val checksumInfoLiveData: LiveData<Stateful<ChecksumInfo>> | ||
get() = _checksumInfoLiveData | ||
|
||
fun reload() { | ||
_checksumInfoLiveData.loadValue() | ||
} | ||
|
||
override fun onCleared() { | ||
_checksumInfoLiveData.close() | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
app/src/main/res/layout/file_properties_checksum_compare_item.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
|
||
<!-- | ||
~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com> | ||
~ All Rights Reserved. | ||
--> | ||
|
||
<com.google.android.material.textfield.TextInputLayout | ||
xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||
android:id="@+id/compareLayout" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:hint="@string/file_properties_checksum_compare" | ||
app:errorEnabled="true" | ||
app:expandedHintEnabled="false" | ||
app:placeholderText="@string/file_properties_checksum_compare_placeholder"> | ||
|
||
<com.google.android.material.textfield.TextInputEditText | ||
android:id="@+id/compareEdit" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:importantForAutofill="no" | ||
android:inputType="textMultiLine" /> | ||
</com.google.android.material.textfield.TextInputLayout> |
Oops, something went wrong.