Directory Picker 구현해보기

|

팝업 형태의 다이얼로그(Dialog)로 실행하는 Directory Picker 입니다.

dialog_directory_picker.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:orientation="vertical">

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimaryDark">

    <TextView
      android:textStyle="bold"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="8dp"
      android:layout_marginBottom="8dp"
      android:layout_marginLeft="12dp"
      android:text="@string/directory_picker_title"
      android:textColor="@color/colorDialogTitle"
      android:textSize="24sp"/>

  </FrameLayout>

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorCurrentPathBackground">

    <TextView
      android:id="@+id/current_path"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="4dp"
      android:layout_marginBottom="4dp"
      android:layout_marginLeft="12dp"
      android:text="current directory path"
      android:textColor="@color/colorCurrentPathText"
      android:textSize="16dp"/>

  </FrameLayout>

  <android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="16dp"
    android:minHeight="320dp"/>

</LinearLayout>


DirectoryListAdapter.kt

import android.content.Context
import android.os.Environment
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.snowdeer.ftpserver.R
import kotlinx.android.synthetic.main.item_directory.view.*
import java.io.File

interface OnDirectoryListEventListener {
    fun onDirectoryChanged(path: String)
}

data class DirectoryItem(val name: String, val path: String)

class DirectoryListAdapter(private val ctx: Context) : RecyclerView.Adapter<ViewHolder>() {

    private val list = ArrayList<DirectoryItem>()
    private val root = Environment.getExternalStorageDirectory().absolutePath
    var currentPath: String = ""

    var onDirectoryListEventListener: OnDirectoryListEventListener? = null

    fun refresh(path: String) {
        list.clear()

        val file = File(path)
        currentPath = file.path
        onDirectoryListEventListener?.onDirectoryChanged(path)

        addParentDirectory(file)

        if (!file.exists()) {
            notifyDataSetChanged()
            return
        }

        val files = file.listFiles()
        files?.let {
            for (f in it) {
                if (f.isHidden) {
                    continue
                }

                if (f.isDirectory) {
                    list.add(DirectoryItem(f.name, f.absolutePath))
                }
            }
        }

        notifyDataSetChanged()
    }

    private fun addParentDirectory(file: File) {
        val path = file.path
        if ((path == "/") || (path == root)) {
            return
        }

        list.add(DirectoryItem("..", file.parentFile.path))
    }

    override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
        val view: View = LayoutInflater.from(ctx).inflate(R.layout.item_directory, parent, false)
        return ViewHolder(view)
    }

    fun getItem(position: Int): DirectoryItem {
        return list[position]
    }

    override fun getItemCount(): Int {
        return list.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(holder.adapterPosition)

        holder.name.text = item.name

        holder.layout_item.setOnClickListener {
            refresh(item.path)
        }
    }
}

class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val layout_item = view.layout_item!!
    val name = view.name!!
}


DirectoryPickerDialog.kt

import android.app.Dialog
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.app.FragmentActivity
import android.support.v7.app.AlertDialog
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.widget.Toast
import com.snowdeer.ftpserver.R
import com.snowdeer.ftpserver.SnowPreference
import kotlinx.android.synthetic.main.dialog_directory_picker.view.*
import kotlinx.android.synthetic.main.item_directory.view.*
import java.io.File
import java.util.*

interface OnDirectoryPickerEventListener {
    fun onDirectorySelected(path: String)
}

class DirectoryPickerDialog : DialogFragment() {

    private lateinit var adapter: DirectoryListAdapter

    var onDirectoryPickerEventListener: OnDirectoryPickerEventListener? = null

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(Objects.requireNonNull<FragmentActivity>(activity))
        val inflater = activity!!.layoutInflater
        val view = inflater.inflate(R.layout.dialog_directory_picker, null)

        adapter = DirectoryListAdapter(activity!!)
        adapter.onDirectoryListEventListener = object : OnDirectoryListEventListener {
            override fun onDirectoryChanged(path: String) {
                view.current_path.text = path
            }
        }

        view.recycler_view.layoutManager = LinearLayoutManager(activity)
        view.recycler_view.adapter = adapter
        adapter.refresh(SnowPreference.getDirectory(activity!!))

        builder.setView(view)
                .setPositiveButton(getString(R.string.directory_picker_ok), null)
                .setNeutralButton(getString(R.string.directory_picker_new_directory), null)

        val dialog = builder.create()

        dialog.setOnShowListener {
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
                onDirectoryPickerEventListener?.onDirectorySelected(adapter.currentPath)
                dialog.dismiss()
            }

            dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener {
                showNewDirectoryDialog(adapter.currentPath)
            }
        }

        return dialog
    }

    private fun showNewDirectoryDialog(path: String) {
        val view = LayoutInflater.from(activity).inflate(R.layout.dialog_new_directory, null)

        AlertDialog.Builder(activity!!)
                .setTitle(getString(R.string.directory_picker_new_directory))
                .setView(view)
                .setPositiveButton("Ok") { _, _ ->
                    val newPath = path + "/" + view.name.text.toString()
                    if (createDirectory(newPath)) {
                        adapter.refresh(path)
                    } else {
                        Toast.makeText(activity!!, getString(R.string.new_directory_failed), Toast.LENGTH_SHORT).show()
                    }

                }
                .show()
    }

    private fun createDirectory(path: String): Boolean {
        val file = File(path)

        if (file.exists()) {
            return false
        }

        return file.mkdir()
    }
}