Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.0.0 beta4 #40

Merged
merged 9 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## v1.0.0-beta4 (29.02.2024)

* Home screen menu arrangement
* Create folder feature added
* Minor UI fix

## v1.0.0-beta3 (16.02.2024)

* UI improvement
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ android {
applicationId "com.sn.snfilemanager"
minSdk 26
targetSdk 34
versionCode 3
versionName "1.0.0-beta3"
versionCode 4
versionName "1.0.0-beta4"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,10 @@ fun Context.startActivitySafely(intent: Intent) {
// Todo
}
}

fun Context.openUrl(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivitySafely(intent)
}

fun Context.getPackage(): String = "package:${this.packageName}"
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ object Constant {
"https://github.com/emreesen27/Android-Sn-File-Manager/blob/develop/PRIVACY.md"
const val OPEN_SOURCE_LICENSE =
"https://github.com/emreesen27/Android-Sn-File-Manager/blob/develop/LICENSE"
const val STORE_URL = "https://play.google.com/store/apps/details?id=com.sn.snfilemanager"
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.sn.snfilemanager.feature.about

import android.content.Intent
import android.net.Uri
import androidx.navigation.fragment.findNavController
import com.sn.snfilemanager.core.base.BaseFragment
import com.sn.snfilemanager.core.extensions.click
import com.sn.snfilemanager.core.extensions.startActivitySafely
import com.sn.snfilemanager.core.extensions.openUrl
import com.sn.snfilemanager.core.util.Constant.GITHUB_URL
import com.sn.snfilemanager.core.util.Constant.PRIVACY_URL
import com.sn.snfilemanager.databinding.FragmentAboutBinding
Expand All @@ -24,17 +22,12 @@ class AboutFragment : BaseFragment<FragmentAboutBinding, AboutViewModel>() {

private fun initClicks() {
binding.toolbar.setNavigationOnClickListener { findNavController().popBackStack() }
binding.btnGithub.click { openUrl(GITHUB_URL) }
binding.btnGithub.click { context?.openUrl(GITHUB_URL) }
binding.btnLicense.click { showLicensesDialog() }
binding.btnPrivacy.click { openUrl(PRIVACY_URL) }
binding.btnPrivacy.click { context?.openUrl(PRIVACY_URL) }
}

private fun showLicensesDialog() {
LicenseDialog().show(childFragmentManager, LicenseDialog.TAG)
}

private fun openUrl(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
context?.startActivitySafely(intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ class FileItemAdapter(
) : RecyclerView.Adapter<FileItemAdapter.FileViewHolder>() {
private val selectedItems: MutableList<FileModel> = mutableListOf()
private var isSelectionModeActive = false
private var fileItems: List<FileModel> = emptyList()
private var fileItems: MutableList<FileModel> = mutableListOf()

fun setItems(newItems: List<FileModel>) {
fun setItems(newItems: MutableList<FileModel>) {
val diffResult = DiffUtil.calculateDiff(FileDiffCallback(fileItems, newItems))
fileItems = newItems
diffResult.dispatchUpdatesTo(this)
}

fun addItem(newItems: FileModel) {
fileItems.add(newItems)
notifyItemInserted(fileItems.size)
}

fun removeItems(filesToRemove: List<FileModel>) {
for (fileToRemove in filesToRemove) {
val position = fileItems.indexOf(fileToRemove)
Expand All @@ -55,6 +60,8 @@ class FileItemAdapter(
selectionCallback?.onEndSelection()
}

fun getItems(): MutableList<FileModel> = fileItems

fun getSelectedItems(): MutableList<FileModel> = selectedItems

fun selectionIsActive(): Boolean = isSelectionModeActive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.sn.snfilemanager.core.util.RootPath
import com.sn.snfilemanager.databinding.FragmentFilesListBinding
import com.sn.snfilemanager.feature.files.adapter.FileItemAdapter
import com.sn.snfilemanager.feature.files.data.FileModel
import com.sn.snfilemanager.feature.files.data.toFileModel
import com.sn.snfilemanager.feature.pathpicker.presentation.PathPickerFragment
import com.sn.snfilemanager.job.JobCompletedCallback
import com.sn.snfilemanager.job.JobService
Expand All @@ -33,6 +34,7 @@ import com.sn.snfilemanager.view.component.breadcrumb.BreadCrumbItemClickListene
import com.sn.snfilemanager.view.component.breadcrumb.BreadItem
import com.sn.snfilemanager.view.dialog.ConfirmationDialog
import com.sn.snfilemanager.view.dialog.ConflictDialog
import com.sn.snfilemanager.view.dialog.CreateDirectoryDialog
import com.sn.snfilemanager.view.dialog.detail.DetailDialog
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
Expand All @@ -54,7 +56,7 @@ class FilesListFragment :

override fun getViewBinding() = FragmentFilesListBinding.inflate(layoutInflater)

override fun getMenuResId(): Int = R.menu.menu_base
override fun getMenuResId(): Int = R.menu.menu_files

override fun getToolbar(): Toolbar = binding.toolbar

Expand All @@ -65,6 +67,13 @@ class FilesListFragment :
true
}

R.id.create_folder -> {
viewModel.currentPath?.let { path ->
showCreateDirectoryDialog(path)
}
true
}

else -> super.onMenuItemSelected(menuItemId)
}

Expand Down Expand Up @@ -164,6 +173,16 @@ class FilesListFragment :
JobType.DELETE -> {
activity?.runOnUiThread {
data?.filterIsInstance<FileModel>()?.let { adapter?.removeItems(it) }
adapter?.getItems()?.let { viewModel.setUpdateList(it) }
}
}

JobType.CREATE -> {
activity?.runOnUiThread {
data?.filterIsInstance<Path>()?.firstOrNull()?.toFileModel()?.let { file ->
adapter?.addItem(file)
adapter?.getItems()?.let { viewModel.setUpdateList(it) }
}
}
}
}
Expand Down Expand Up @@ -195,6 +214,11 @@ class FilesListFragment :
startDeleteService(data)
}
}
observe(viewModel.startCreateFolderJob) { event ->
event.getContentIfNotHandled()?.let { path ->
startCreateDirectory(path)
}
}
observe(viewModel.updateListLiveData) { event ->
event.getContentIfNotHandled()?.let { list ->
adapter?.setItems(list)
Expand Down Expand Up @@ -330,6 +354,12 @@ class FilesListFragment :
)
}

private fun showCreateDirectoryDialog(path: String) {
CreateDirectoryDialog(path = path, onCreate = { folderName ->
viewModel.createFolder(folderName)
}).show(childFragmentManager, CreateDirectoryDialog.TAG)
}

private fun actionDetail() {
DetailDialog(requireContext(), viewModel.getSelectedItem()).show(
childFragmentManager,
Expand Down Expand Up @@ -389,6 +419,14 @@ class FilesListFragment :
)
}

private fun startCreateDirectory(destinationPath: Path) {
JobService.createDirectory(
destinationPath,
this@FilesListFragment,
requireContext(),
)
}

private fun initSearch() {
binding.toolbar.menu?.findItem(R.id.action_search)?.let { item ->
val searchView = item.actionView as? SearchView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,17 @@ class FilesListViewModel
MutableLiveData()
val startDeleteJobLiveData: LiveData<Event<List<FileModel>>> = _startDeleteJobLiveData

private val _updateListLiveData: MutableLiveData<Event<List<FileModel>>> = MutableLiveData()
val updateListLiveData: LiveData<Event<List<FileModel>>> = _updateListLiveData
private val _updateListLiveData: MutableLiveData<Event<MutableList<FileModel>>> =
MutableLiveData()
val updateListLiveData: LiveData<Event<MutableList<FileModel>>> = _updateListLiveData

private val _searchStateLiveData: MutableLiveData<Event<Pair<Boolean, Boolean>>> =
MutableLiveData()
val searchStateLiveData: LiveData<Event<Pair<Boolean, Boolean>>> = _searchStateLiveData

private val _startCreateFolderJob: MutableLiveData<Event<Path>> = MutableLiveData()
val startCreateFolderJob: LiveData<Event<Path>> = _startCreateFolderJob

var conflictDialogDeferred = CompletableDeferred<Pair<ConflictStrategy, Boolean>>()

companion object {
Expand Down Expand Up @@ -133,7 +137,7 @@ class FilesListViewModel
val fileList: MutableList<FileModel> = mutableListOf()

if (totalFiles == 0L) {
_updateListLiveData.postValue(Event(emptyList()))
_updateListLiveData.postValue(Event(mutableListOf()))
return@launch
}

Expand All @@ -145,25 +149,36 @@ class FilesListViewModel
.skip(processedFiles)
.limit(currentBatchSize)
.forEach { file ->
if (Files.isReadable(file) && (Config.hiddenFile || !Files.isHidden(file))) {
if (Files.isReadable(file) && (
Config.hiddenFile ||
!Files.isHidden(
file,
)
)
) {
fileList.add(file.toFileModel())
}
}
}
withContext(Dispatchers.Main) {
_updateListLiveData.postValue(Event(fileList.toList()))
_updateListLiveData.postValue(Event(fileList))
}
processedFiles += currentBatchSize
}
}
}

fun setUpdateList(files: MutableList<FileModel>) {
_updateListLiveData.postValue(Event(files))
}

fun cancelFileListJob() {
if (fileListJob != null && fileListJob?.isActive == true) {
fileListJob?.cancel()
}
}

// Todo check free space
fun moveFilesAndDirectories(destinationPath: Path) {
val operationItemList: MutableList<FileModel> = mutableListOf()
viewModelScope.launch {
Expand Down Expand Up @@ -208,6 +223,11 @@ class FilesListViewModel
}
}

// Todo check free space
fun createFolder(targetPath: Path) {
_startCreateFolderJob.value = Event(targetPath)
}

fun deleteFiles() {
val operationItemList: List<FileModel> = selectedItemList.toList()
_startDeleteJobLiveData.postValue(Event(operationItemList))
Expand Down Expand Up @@ -252,7 +272,7 @@ class FilesListViewModel
is BaseResult.Success -> {
val list = result.data.map { Paths.get(it).toFileModel() }
_searchStateLiveData.postValue(Event(Pair(false, false)))
_updateListLiveData.postValue(Event(list))
_updateListLiveData.postValue(Event(list.toMutableList()))
}

is BaseResult.Failure -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ import com.sn.mediastorepv.data.MediaType
import com.sn.snfilemanager.R
import com.sn.snfilemanager.core.base.BaseFragment
import com.sn.snfilemanager.core.extensions.click
import com.sn.snfilemanager.core.extensions.getPackage
import com.sn.snfilemanager.core.extensions.infoToast
import com.sn.snfilemanager.core.extensions.observe
import com.sn.snfilemanager.core.extensions.openUrl
import com.sn.snfilemanager.core.util.Constant
import com.sn.snfilemanager.core.util.DocumentType
import com.sn.snfilemanager.core.util.RootPath
import com.sn.snfilemanager.databinding.FragmentHomeBinding
Expand Down Expand Up @@ -53,12 +56,8 @@ class HomeFragment : BaseFragment<FragmentHomeBinding, HomeViewModel>() {

override fun onMenuItemSelected(menuItemId: Int) =
when (menuItemId) {
R.id.settings -> {
navigate(HomeFragmentDirections.actionSettings())
true
}

R.id.about -> {
R.id.store -> {
context?.openUrl(Constant.STORE_URL)
true
}

Expand Down Expand Up @@ -135,7 +134,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding, HomeViewModel>() {
val intent =
Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
Uri.parse("package:${requireActivity().packageName}"),
Uri.parse(context?.getPackage()),
)
startActivity(intent)
}
Expand All @@ -144,7 +143,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding, HomeViewModel>() {
val intent =
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:${requireActivity().packageName}"),
Uri.parse(context?.getPackage()),
)
startActivity(intent)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
mode: ActionMode?,
item: MenuItem?,
): Boolean {
checkActionMenuStatus()
when (item?.itemId) {
R.id.action_copy -> {
viewModel.isCopy = true
Expand Down Expand Up @@ -170,6 +171,7 @@
data?.filterIsInstance<Media>()?.let { adapter?.removeItems(it) }
}
}
JobType.CREATE -> {}

Check failure on line 174 in app/src/main/java/com/sn/snfilemanager/feature/media/presentation/MediaFragment.kt

View workflow job for this annotation

GitHub Actions / Check Code Quality

[ktlint] reported by reviewdog 🐶 Add a blank line between all when-condition in case at least one multiline when-condition is found in the statement Raw Output: app/src/main/java/com/sn/snfilemanager/feature/media/presentation/MediaFragment.kt:174:1: error: Add a blank line between all when-condition in case at least one multiline when-condition is found in the statement (standard:blank-line-between-when-conditions)
}
activity?.runOnUiThread { context?.infoToast(getString(R.string.completed)) }
}
Expand Down Expand Up @@ -372,8 +374,8 @@
MediaItemAdapter(
onClick = { model -> openFile(model) },
onSelected = { model, selected ->
checkActionMenuStatus()
viewModel.addSelectedItem(model, selected)
checkActionMenuStatus()
},
selectionCallback = this@MediaFragment,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.sn.snfilemanager.feature.start

import android.content.Intent
import android.net.Uri
import com.sn.snfilemanager.BuildConfig
import com.sn.snfilemanager.core.base.BaseFragment
import com.sn.snfilemanager.core.extensions.click
import com.sn.snfilemanager.core.extensions.startActivitySafely
import com.sn.snfilemanager.core.extensions.openUrl
import com.sn.snfilemanager.core.util.Constant.OPEN_SOURCE_LICENSE
import com.sn.snfilemanager.core.util.Constant.PRIVACY_URL
import com.sn.snfilemanager.databinding.FragmentStartBinding
Expand Down Expand Up @@ -35,12 +33,7 @@ class StartFragment : BaseFragment<FragmentStartBinding, StartViewModel>() {
}

private fun clicksPolicy() {
binding.mtvPrivacyPolicy.click { openUrl(PRIVACY_URL) }
binding.mtvOpenSourcePolicy.click { openUrl(OPEN_SOURCE_LICENSE) }
}

private fun openUrl(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
context?.startActivitySafely(intent)
binding.mtvPrivacyPolicy.click { context?.openUrl(PRIVACY_URL) }
binding.mtvOpenSourcePolicy.click { context?.openUrl(OPEN_SOURCE_LICENSE) }
}
}
Loading