FileBrowserFragment.kt 6.59 KB
Newer Older
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * *************************************************************************
 *  FileBrowserFragment.java
 * **************************************************************************
 *  Copyright © 2015 VLC authors and VideoLAN
 *  Author: Geoffrey Métais
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *  ***************************************************************************
 */

package org.videolan.vlc.gui.browser

import android.net.Uri
import android.os.Bundle
import android.text.TextUtils
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import androidx.fragment.app.Fragment
33
import androidx.lifecycle.lifecycleScope
34
import kotlinx.coroutines.*
35
import kotlinx.coroutines.flow.asFlow
Geoffrey Métais's avatar
Geoffrey Métais committed
36
37
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
38
import org.videolan.medialibrary.MLServiceLocator
39
import org.videolan.medialibrary.interfaces.media.MediaWrapper
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
40
import org.videolan.medialibrary.media.MediaLibraryItem
41
42
import org.videolan.resources.AndroidDevices
import org.videolan.resources.CTX_FAV_ADD
43
import org.videolan.tools.removeFileProtocole
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
44
45
import org.videolan.vlc.ExternalMonitor
import org.videolan.vlc.R
46
import org.videolan.vlc.gui.helpers.MedialibraryUtils
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
47
import org.videolan.vlc.gui.helpers.hf.OtgAccess
Geoffrey Métais's avatar
Geoffrey Métais committed
48
import org.videolan.vlc.gui.helpers.hf.requestOtgRoot
49
import org.videolan.vlc.util.FileUtils
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
50
import org.videolan.vlc.viewmodels.browser.TYPE_FILE
51
import org.videolan.vlc.viewmodels.browser.getBrowserModel
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
open class FileBrowserFragment : BaseBrowserFragment() {

    private var needsRefresh: Boolean = false

    override val categoryTitle: String
        get() = getString(R.string.directories)

    override fun createFragment(): Fragment {
        return FileBrowserFragment()
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupBrowser()
    }

    override fun onStart() {
        super.onStart()
73
        if (needsRefresh) viewModel.browseRoot()
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
74
75
76
77
78
79
80
81
82
83
84
    }

    override fun onStop() {
        super.onStop()
        if (isRootDirectory && adapter.isEmpty()) needsRefresh = true
    }

    override fun registerSwiperRefreshlayout() {
        if (!isRootDirectory)
            super.registerSwiperRefreshlayout()
        else
Geoffrey Métais's avatar
Geoffrey Métais committed
85
            swipeRefreshLayout.isEnabled = false
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
86
87
88
    }

    protected open fun setupBrowser() {
89
        viewModel = getBrowserModel(category = TYPE_FILE, url = if (!isRootDirectory) mrl else null, showHiddenFiles = showHiddenFiles)
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
    }

    override fun getTitle(): String = if (isRootDirectory)
        categoryTitle
    else {
        when {
            currentMedia != null -> when {
                TextUtils.equals(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY, mrl?.removeFileProtocole()) -> getString(R.string.internal_memory)
                this is FilePickerFragment -> currentMedia!!.uri.toString()
                else -> currentMedia!!.title
            }
            this is FilePickerFragment -> mrl ?: ""
            else -> FileUtils.getFileNameFromPath(mrl)
        }
    }


    public override fun browseRoot() {
108
        viewModel.browseRoot()
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
109
110
111
112
    }

    override fun onClick(v: View, position: Int, item: MediaLibraryItem) {
        if (item.itemType == MediaLibraryItem.TYPE_MEDIA) {
113
            val mw = item as MediaWrapper
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
114
115
            if ("otg://" == mw.location) {
                val title = getString(R.string.otg_device_title)
Geoffrey Métais's avatar
Geoffrey Métais committed
116
                val rootUri = OtgAccess.otgRoot.value
117
                if (rootUri != null && ExternalMonitor.devices.size == 1) {
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
118
119
                    browseOtgDevice(rootUri, title)
                } else {
Geoffrey Métais's avatar
Geoffrey Métais committed
120
                    lifecycleScope.launchWhenStarted {
121
                        val uri = OtgAccess.otgRoot.filterNotNull().first()
Geoffrey Métais's avatar
Geoffrey Métais committed
122
123
124
                        browseOtgDevice(uri, title)
                    }
                    requireActivity().requestOtgRoot()
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
125
126
127
128
129
130
131
                }
                return
            }
        }
        super.onClick(v, position, item)
    }

132
    override fun onCtxAction(position: Int, option: Long) {
133
        val mw = this.adapter.getItem(position) as MediaWrapper?
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
134
        when (option) {
135
            CTX_FAV_ADD -> lifecycleScope.launch { browserFavRepository.addLocalFavItem(mw!!.uri, mw.title, mw.artworkURL) }
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
136
137
138
139
140
141
            else -> super.onCtxAction(position, option)
        }
    }

    override fun onMainActionClick(v: View, position: Int, item: MediaLibraryItem) {}

142
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
143
        if (!(this is FilePickerFragment || this is StorageBrowserFragment))
144
            inflater.inflate(R.menu.fragment_option_network, menu)
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
145
146
147
        super.onCreateOptionsMenu(menu, inflater)
    }

Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
148
149
150
151
152
    override fun containerActivity() = requireActivity()

    override val isNetwork = false
    override val isFile = true

Geoffrey Métais's avatar
Geoffrey Métais committed
153
    override fun onPrepareOptionsMenu(menu: Menu) {
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
154
        super.onPrepareOptionsMenu(menu)
Geoffrey Métais's avatar
Geoffrey Métais committed
155
        val item = menu.findItem(R.id.ml_menu_save) ?: return
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
156
        item.isVisible = !isRootDirectory && mrl!!.startsWith("file")
157
158
        lifecycleScope.launchWhenStarted {
            mrl?.let {
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
159
                val isScanned = withContext(Dispatchers.IO) { MedialibraryUtils.isScanned(it) }
160
                menu.findItem(R.id.ml_menu_scan)?.isVisible = !isRootDirectory && it.startsWith("file") && !isScanned
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
161
            }
162
            val isFavorite = mrl != null && browserFavRepository.browserFavExists(Uri.parse(mrl))
163
164
165
166
167
            item.setIcon(if (isFavorite)
                R.drawable.ic_menu_bookmark_w
            else
                R.drawable.ic_menu_bookmark_outline_w)
            item.setTitle(if (isFavorite) R.string.favorites_remove else R.string.favorites_add)
168
        }
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
169
170
171
    }

    private fun browseOtgDevice(uri: Uri, title: String) {
172
        val mw = MLServiceLocator.getAbstractMediaWrapper(uri)
173
        mw.type = MediaWrapper.TYPE_DIR
Nicolas Pomepuy's avatar
Nicolas Pomepuy committed
174
175
176
177
        mw.title = title
        handler.post { browse(mw, true) }
    }
}