mirror of
https://github.com/koreader/koreader.git
synced 2026-10-03 13:57:36 +02:00
1146 lines
45 KiB
Lua
1146 lines
45 KiB
Lua
local BD = require("ui/bidi")
|
|
local BookList = require("ui/widget/booklist")
|
|
local ButtonDialog = require("ui/widget/buttondialog")
|
|
local ButtonSelector = require("ui/widget/buttonselector")
|
|
local CheckButton = require("ui/widget/checkbutton")
|
|
local ConfirmBox = require("ui/widget/confirmbox")
|
|
local DocumentRegistry = require("document/documentregistry")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
local InputDialog = require("ui/widget/inputdialog")
|
|
local MultiConfirmBox = require("ui/widget/multiconfirmbox")
|
|
local PathChooser = require("ui/widget/pathchooser")
|
|
local ProgressbarDialog = require("ui/widget/progressbardialog")
|
|
local SortWidget = require("ui/widget/sortwidget")
|
|
local UIManager = require("ui/uimanager")
|
|
local ffiUtil = require("ffi/util")
|
|
local filemanagerutil = require("apps/filemanager/filemanagerutil")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local util = require("util")
|
|
local _ = require("gettext")
|
|
local N_ = _.ngettext
|
|
local T = ffiUtil.template
|
|
|
|
local CloudStorage = BookList:extend{
|
|
collates = {
|
|
strcoll = {
|
|
text = _("name"),
|
|
sort_func = BookList.getCollateSortFunc(),
|
|
},
|
|
natural = {
|
|
text = _("name (natural sorting)"),
|
|
sort_func = BookList.getCollateSortFunc("natural"),
|
|
},
|
|
type = {
|
|
text = _("type"),
|
|
sort_func = BookList.getCollateSortFunc("type"),
|
|
},
|
|
size = {
|
|
text = _("size"),
|
|
sort_func = function(a, b)
|
|
if a.filesize and b.filesize then
|
|
return a.filesize < b.filesize
|
|
end
|
|
return ffiUtil.strcoll(a.text, b.text)
|
|
end,
|
|
},
|
|
date = {
|
|
text = _("date modified"),
|
|
sort_func = function(a, b)
|
|
if a.modification and b.modification then
|
|
return a.modification > b.modification
|
|
end
|
|
return ffiUtil.strcoll(a.text, b.text)
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
|
|
function CloudStorage:init(re_init)
|
|
self.choose_folder_callback = nil
|
|
self.item_table = {}
|
|
for i, server in ipairs(self.servers) do
|
|
if self.providers[server.type] then
|
|
table.insert(self.item_table, self:genItemFromServer(i))
|
|
end
|
|
if #self.item_table > 1 then
|
|
table.sort(self.item_table, function(a, b) return a.order < b.order end)
|
|
end
|
|
end
|
|
if re_init then
|
|
self.paths = {} -- list of visited urls, including the current one; used as flag that we are in cloud
|
|
self:switchItemTable(self.title, self.item_table, self.item_idx, nil, "")
|
|
self.item_idx = nil -- set item_idx before opening a server to keep the page when reopening the root list
|
|
self.remote_selected_files = nil -- select mode off
|
|
self:setTitleBarLeftIcon("plus")
|
|
else
|
|
self.title_bar_left_icon = "plus"
|
|
self.onLeftButtonTap = function()
|
|
if next(self.paths) then -- cloud
|
|
if self.remote_selected_files then
|
|
self:showSelectModeDialog()
|
|
else
|
|
self:showPlusCloudDialog()
|
|
end
|
|
else -- root
|
|
self:showPlusRootDialog()
|
|
end
|
|
end
|
|
self.onLeftButtonHold = function()
|
|
if next(self.paths) and not self.choose_folder_callback then
|
|
self:toggleSelectMode()
|
|
end
|
|
end
|
|
BookList.init(self)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:genItemFromServer(idx)
|
|
local server = self.servers[idx]
|
|
local mandatory = self.providers[server.type].name
|
|
if idx == self.settings:readSetting("default_server") then
|
|
mandatory = "★ " .. mandatory
|
|
end
|
|
return {
|
|
text = server.name,
|
|
mandatory = mandatory,
|
|
server_idx = idx,
|
|
type = server.type,
|
|
url = server.url,
|
|
order = server.order or idx,
|
|
}
|
|
end
|
|
|
|
function CloudStorage:initServer(server_idx)
|
|
local server = self.servers[server_idx]
|
|
self.provider = self.providers[server.type]
|
|
self.address = server.address
|
|
self.username = server.username
|
|
self.password = server.password
|
|
self.collate = server.collate or "strcoll"
|
|
return server
|
|
end
|
|
|
|
function CloudStorage:sortItemTable(tbl, url)
|
|
tbl = tbl or self.item_table
|
|
|
|
local folder_mode_item
|
|
if self.choose_folder_callback and #tbl > 0 and tbl[1].is_folder_long_press then
|
|
folder_mode_item = table.remove(tbl, 1)
|
|
end
|
|
|
|
if #tbl > 1 then
|
|
local sort_func = self.collates[self.collate].sort_func
|
|
table.sort(tbl, function(a, b)
|
|
if a.is_file and b.is_file then
|
|
return sort_func(a, b)
|
|
elseif a.is_folder and b.is_folder then
|
|
return ffiUtil.strcoll(a.text, b.text)
|
|
else -- folders first
|
|
return a.is_folder
|
|
end
|
|
end)
|
|
end
|
|
|
|
if self.choose_folder_callback then
|
|
table.insert(tbl, 1, folder_mode_item or {
|
|
is_folder_long_press = true,
|
|
text = _("Long-press here to choose current folder"),
|
|
bold = true,
|
|
url = url,
|
|
})
|
|
end
|
|
end
|
|
|
|
function CloudStorage:show()
|
|
local default_server_idx = self.settings:readSetting("default_server")
|
|
if default_server_idx then -- open default server
|
|
self.server_idx = default_server_idx
|
|
self:openCloudServer(self.servers[default_server_idx].url, nil, true)
|
|
else -- show root list of servers
|
|
UIManager:show(self)
|
|
end
|
|
end
|
|
|
|
function CloudStorage:openCloudServer(url, choose_folder_callback, do_show)
|
|
local server = self:initServer(self.server_idx)
|
|
self.provider.run(function()
|
|
local tbl = self.provider.listFolder(url, true) -- including folders
|
|
if tbl then
|
|
self.choose_folder_callback = self.caller_choose_folder_callback or choose_folder_callback
|
|
if self.paths[#self.paths] ~= url then
|
|
table.insert(self.paths, url)
|
|
end
|
|
if self.remote_selected_files then
|
|
for _, item in ipairs(tbl) do
|
|
if self.remote_selected_files[item.url] then
|
|
item.dim = true
|
|
end
|
|
end
|
|
else
|
|
self:setTitleBarLeftIcon("appbar.menu")
|
|
end
|
|
self:sortItemTable(tbl, url)
|
|
self:switchItemTable(server.name, tbl, nil, nil, url == "" and "/" or url)
|
|
if do_show then
|
|
UIManager:show(self)
|
|
end
|
|
else
|
|
if do_show then
|
|
-- could not show the server content; show the root list of servers
|
|
-- "flashui" is needed when called with wi-fi off (NetworkMgr:willRerunWhenConnected())
|
|
UIManager:show(self, "flashui")
|
|
end
|
|
UIManager:show(InfoMessage:new{
|
|
text = T(_("Server: %1"), server.name) .. "\n" ..
|
|
_("Could not fetch server's content.\nPlease check your configuration or network connection."),
|
|
})
|
|
end
|
|
end)
|
|
end
|
|
|
|
function CloudStorage:onReturn()
|
|
if next(self.paths) then -- up one level
|
|
table.remove(self.paths)
|
|
if next(self.paths) then
|
|
self:openCloudServer(self.paths[#self.paths])
|
|
else -- return to root list
|
|
self:init(true)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:onHoldReturn()
|
|
if #self.paths > 1 then -- return to the server start folder
|
|
local path = self.paths[1]
|
|
self.paths = { path }
|
|
self:openCloudServer(path)
|
|
end
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:onMenuSelect(item)
|
|
if item.server_idx then -- root list
|
|
self.item_idx = item.idx
|
|
self.server_idx = item.server_idx
|
|
self:openCloudServer(item.url)
|
|
elseif item.is_folder then
|
|
self:openCloudServer(item.url)
|
|
elseif item.is_file and not self.choose_folder_callback then
|
|
if self.remote_selected_files then
|
|
item.dim = not item.dim and true or nil
|
|
self.remote_selected_files[item.url] = item.dim
|
|
self:updateItems(1, true)
|
|
else
|
|
self:showFileDownloadDialog(item)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:onMenuHold(item)
|
|
if self.choose_folder_callback then
|
|
if item.is_folder or item.is_folder_long_press then
|
|
self:showFolderChooseDialog(item)
|
|
end
|
|
else
|
|
if item.server_idx then -- root list
|
|
self:showServerDialog(item)
|
|
elseif item.is_file then
|
|
if self.remote_selected_files then
|
|
self:showSelectModeDialog()
|
|
else
|
|
self:showFileDialog(item)
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function CloudStorage:showFolderChooseDialog(item)
|
|
local url = item.url == "" and "/" or item.url
|
|
local folder_dialog
|
|
folder_dialog = ButtonDialog:new{
|
|
title = _("Choose this folder?") .. "\n\n" .. BD.dirpath(url) .. "\n",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
callback = function()
|
|
UIManager:close(folder_dialog)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Choose"),
|
|
callback = function()
|
|
UIManager:close(folder_dialog)
|
|
if self.caller_choose_folder_callback then
|
|
self:onClose()
|
|
local server = self.servers[self.server_idx]
|
|
self.caller_choose_folder_callback({
|
|
name = server.name,
|
|
type = server.type,
|
|
address = server.address,
|
|
username = server.username,
|
|
password = server.password,
|
|
url = item.url,
|
|
})
|
|
else
|
|
self.choose_folder_callback(item.url)
|
|
self:init(true)
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(folder_dialog)
|
|
end
|
|
|
|
function CloudStorage:showFileDialog(item)
|
|
local file_dialog
|
|
file_dialog = ButtonDialog:new{
|
|
title = item.text,
|
|
title_align = "center",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Delete"),
|
|
enabled = self.provider.deleteFile and true or false,
|
|
callback = function()
|
|
UIManager:close(file_dialog)
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("Delete this file?") .. "\n\n" .. item.text,
|
|
ok_text = _("Delete"),
|
|
ok_callback = function()
|
|
local ok = self.provider.deleteFile(item.url)
|
|
if ok then
|
|
table.remove(self.item_table, item.idx)
|
|
self:switchItemTable()
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = T(_("Could not delete file:\n%1"), item.text) })
|
|
end
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
{
|
|
text = _("Select"),
|
|
callback = function()
|
|
UIManager:close(file_dialog)
|
|
self:toggleSelectMode() -- turn on
|
|
self.remote_selected_files[item.url] = true
|
|
self.item_table[item.idx].dim = true
|
|
self:updateItems(1, true)
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(file_dialog)
|
|
end
|
|
|
|
function CloudStorage:showSelectModeDialog()
|
|
local select_count = util.tableSize(self.remote_selected_files)
|
|
local actions_enabled = select_count > 0
|
|
local select_dialog
|
|
select_dialog = ButtonDialog:new{
|
|
title = actions_enabled and T(N_("1 file selected", "%1 files selected", select_count), select_count)
|
|
or _("No files selected"),
|
|
title_align = "center",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Delete"),
|
|
enabled = actions_enabled and self.provider.deleteFile and true or false,
|
|
callback = function()
|
|
UIManager:close(select_dialog)
|
|
self:showSelectedFilesDeleteDialog()
|
|
end,
|
|
},
|
|
{
|
|
text = _("Download"),
|
|
enabled = actions_enabled,
|
|
callback = function()
|
|
UIManager:close(select_dialog)
|
|
self:showSelectedFilesDownloadDialog()
|
|
end,
|
|
},
|
|
},
|
|
{}, -- separator
|
|
{
|
|
{
|
|
text = _("Deselect all"),
|
|
enabled = actions_enabled,
|
|
callback = function()
|
|
UIManager:close(select_dialog)
|
|
for url in pairs (self.remote_selected_files) do
|
|
self.remote_selected_files[url] = nil
|
|
end
|
|
for _, item in ipairs(self.item_table) do
|
|
item.dim = nil
|
|
end
|
|
self:updateItems(1, true)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Select all files in folder"),
|
|
callback = function()
|
|
UIManager:close(select_dialog)
|
|
for _, item in ipairs(self.item_table) do
|
|
if item.is_file then
|
|
item.dim = true
|
|
self.remote_selected_files[item.url] = true
|
|
end
|
|
end
|
|
self:updateItems(1, true)
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Exit select mode"),
|
|
callback = function()
|
|
UIManager:close(select_dialog)
|
|
self:toggleSelectMode()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(select_dialog)
|
|
end
|
|
|
|
function CloudStorage:toggleSelectMode()
|
|
if self.remote_selected_files then
|
|
for _, item in ipairs(self.item_table) do
|
|
item.dim = nil
|
|
end
|
|
self:updateItems(1, true)
|
|
self:setTitleBarLeftIcon("appbar.menu")
|
|
self.remote_selected_files = nil
|
|
else
|
|
self:setTitleBarLeftIcon("check")
|
|
self.remote_selected_files = {}
|
|
end
|
|
end
|
|
|
|
function CloudStorage:showServerDialog(item)
|
|
local is_not_default_server = item.server_idx ~= self.settings:readSetting("default_server")
|
|
local provider = self.providers[item.type]
|
|
local server_dialog
|
|
local buttons = {
|
|
{
|
|
{
|
|
text = is_not_default_server and _("Set default") or _("Reset default"),
|
|
callback = function()
|
|
UIManager:close(server_dialog)
|
|
local idx = is_not_default_server and item.server_idx or nil
|
|
self.settings:saveSetting("default_server", idx)
|
|
self._manager.updated = true
|
|
self:init(true)
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Remove server"),
|
|
callback = function()
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("Remove this server?") .. "\n\n" .. item.text,
|
|
ok_text = _("Remove"),
|
|
ok_callback = function()
|
|
UIManager:close(server_dialog)
|
|
table.remove(self.servers, item.server_idx)
|
|
local default_server_idx = self.settings:readSetting("default_server")
|
|
if default_server_idx then
|
|
if default_server_idx == item.server_idx then
|
|
self.settings:delSetting("default_server")
|
|
elseif default_server_idx > item.server_idx then
|
|
self.settings:saveSetting("default_server", default_server_idx - 1)
|
|
end
|
|
end
|
|
self._manager.updated = true
|
|
self:init(true)
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
{
|
|
text = _("Server settings"),
|
|
callback = function()
|
|
UIManager:close(server_dialog)
|
|
local update_callback = function()
|
|
self._manager.updated = true
|
|
self.item_table[item.idx] = self:genItemFromServer(item.server_idx)
|
|
self:updateItems(1, true)
|
|
end
|
|
provider.config(item.server_idx, update_callback)
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
if provider.downloadFile and not self.caller_choose_folder_callback then
|
|
local server = self.servers[item.server_idx]
|
|
table.insert(buttons, {}) -- separator
|
|
table.insert(buttons, {
|
|
{
|
|
text = _("Sync now"),
|
|
enabled = server.sync_source_folder ~= nil and server.sync_dest_folder ~= nil,
|
|
callback = function()
|
|
UIManager:close(server_dialog)
|
|
self:syncCloud(item)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Sync settings"),
|
|
callback = function()
|
|
UIManager:close(server_dialog)
|
|
self:showSyncSettingsDialog(item)
|
|
end,
|
|
},
|
|
})
|
|
end
|
|
server_dialog = ButtonDialog:new{
|
|
title = item.text,
|
|
title_align = "center",
|
|
buttons = buttons,
|
|
}
|
|
UIManager:show(server_dialog)
|
|
end
|
|
|
|
function CloudStorage:showPlusRootDialog()
|
|
local plus_root_dialog
|
|
local buttons = {}
|
|
for _, provider in pairs(self.providers) do
|
|
table.insert(buttons, {
|
|
{
|
|
text = provider.name, -- add new storage
|
|
callback = function()
|
|
UIManager:close(plus_root_dialog)
|
|
local update_callback = function(new_server)
|
|
self._manager.updated = true
|
|
local max_order = #self.servers
|
|
for _, item in ipairs(self.servers) do
|
|
if item.order and max_order < item.order then
|
|
max_order = item.order
|
|
end
|
|
end
|
|
new_server.order = max_order + 1
|
|
local next_idx = #self.servers + 1
|
|
self.servers[next_idx] = new_server
|
|
self.item_table[next_idx] = self:genItemFromServer(next_idx)
|
|
self:switchItemTable(nil, self.item_table, next_idx)
|
|
end
|
|
provider.config(nil, update_callback)
|
|
end,
|
|
},
|
|
})
|
|
end
|
|
if #buttons > 1 then
|
|
table.sort(buttons, function(a, b) return ffiUtil.strcoll(a[1].text, b[1].text) end)
|
|
end
|
|
table.insert(buttons, {}) -- separator
|
|
table.insert(buttons, {
|
|
{
|
|
text = _("Arrange servers"),
|
|
enabled = #self.item_table > 1,
|
|
callback = function()
|
|
UIManager:close(plus_root_dialog)
|
|
local sort_widget
|
|
sort_widget = SortWidget:new{
|
|
title = _("Arrange servers"),
|
|
item_table = self.item_table,
|
|
callback = function()
|
|
self._manager.updated = true
|
|
for i, item in ipairs(sort_widget.item_table) do
|
|
self.servers[item.server_idx].order = i
|
|
end
|
|
self:init(true)
|
|
end,
|
|
}
|
|
UIManager:show(sort_widget)
|
|
end,
|
|
},
|
|
})
|
|
plus_root_dialog = ButtonDialog:new{
|
|
title = _("Add new server"),
|
|
title_align = "center",
|
|
buttons = buttons,
|
|
}
|
|
UIManager:show(plus_root_dialog)
|
|
end
|
|
|
|
function CloudStorage:showPlusCloudDialog()
|
|
local url = self.paths[#self.paths]
|
|
local plus_cloud_dialog
|
|
plus_cloud_dialog = ButtonDialog:new{
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("New folder"),
|
|
enabled = self.provider.createFolder and true or false,
|
|
callback = function()
|
|
UIManager:close(plus_cloud_dialog)
|
|
self:showFolderCreateDialog(url)
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Upload selected files"),
|
|
enabled = self.provider.uploadFile and not self.choose_folder_callback
|
|
and self._manager.ui.selected_files ~= nil,
|
|
callback = function()
|
|
UIManager:close(plus_cloud_dialog)
|
|
self:showSelectedFilesUploadDialog(url)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Upload file"),
|
|
enabled = self.provider.uploadFile and not self.choose_folder_callback,
|
|
callback = function()
|
|
UIManager:close(plus_cloud_dialog)
|
|
self:showFileUploadDialog(url)
|
|
end,
|
|
},
|
|
},
|
|
{}, -- separator
|
|
{
|
|
{
|
|
text = _("Info"),
|
|
enabled = self.provider.info and true or false,
|
|
callback = function()
|
|
UIManager:close(plus_cloud_dialog)
|
|
self.provider.info()
|
|
end,
|
|
},
|
|
{
|
|
text = T(_("Sort by: %1"), self.collates[self.collate].text),
|
|
callback = function()
|
|
UIManager:show(ButtonSelector:new{
|
|
width_factor = 0.5,
|
|
current_value = self.collate,
|
|
values = {
|
|
{ self.collates["strcoll"].text, "strcoll" },
|
|
{ self.collates["natural"].text, "natural" },
|
|
{ self.collates["type"].text, "type" },
|
|
{ self.collates["size"].text, "size" },
|
|
{ self.collates["date"].text, "date" },
|
|
},
|
|
callback = function(value)
|
|
UIManager:close(plus_cloud_dialog)
|
|
if self.collate ~= value then
|
|
self.collate = value
|
|
self.servers[self.server_idx].collate = value ~= "strcoll" and value or nil
|
|
self._manager.updated = true
|
|
self:sortItemTable()
|
|
self:updateItems(1, true)
|
|
end
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Return to server list"),
|
|
callback = function()
|
|
UIManager:close(plus_cloud_dialog)
|
|
self:init(true)
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(plus_cloud_dialog)
|
|
end
|
|
|
|
function CloudStorage:showFileDownloadDialog(item)
|
|
if self.provider.downloadFile == nil then return end
|
|
local function startDownloadFile(unit_item, local_path)
|
|
local progressbar_dialog = ProgressbarDialog:new{
|
|
title = _("Downloading…"),
|
|
subtitle = unit_item.text,
|
|
progress_max = unit_item.filesize,
|
|
}
|
|
UIManager:scheduleIn(1, function()
|
|
local progress_callback = function(progress)
|
|
progressbar_dialog:reportProgress(progress)
|
|
end
|
|
local code = self.provider.downloadFile(unit_item.url, local_path, progress_callback)
|
|
progressbar_dialog:close()
|
|
if code == 200 then
|
|
local text = T(_("File saved to:\n%1"), BD.filepath(local_path))
|
|
if DocumentRegistry:hasProvider(local_path) then
|
|
UIManager:show(ConfirmBox:new{
|
|
text = text .. "\n\n" .. _("Would you like to read the downloaded book now?"),
|
|
ok_callback = function()
|
|
self:onClose()
|
|
filemanagerutil.openFile(self._manager.ui, local_path, nil, true)
|
|
end,
|
|
})
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = text })
|
|
end
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = T(_("Could not save file to:\n%1"), BD.filepath(local_path)) })
|
|
end
|
|
end)
|
|
progressbar_dialog:show()
|
|
end
|
|
|
|
local function createTitle(filename_orig, filesize, filename, path) -- title for ButtonDialog
|
|
local filesize_str = filesize and util.getFriendlySize(filesize) or _("N/A")
|
|
return T(_("Filename:\n%1\n\nFile size:\n%2\n\nDownload filename:\n%3\n\nDownload folder:\n%4"),
|
|
filename_orig, filesize_str, filename, BD.dirpath(path))
|
|
end
|
|
|
|
local download_dir = self.settings:readSetting("download_dir") or G_reader_settings:readSetting("lastdir")
|
|
local filename_orig = item.text
|
|
local filename = filename_orig
|
|
local filesize = item.filesize
|
|
|
|
local download_dialog
|
|
local buttons = {
|
|
{
|
|
{
|
|
text = _("Choose folder"),
|
|
callback = function()
|
|
UIManager:show(PathChooser:new{
|
|
select_file = false,
|
|
path = download_dir,
|
|
onConfirm = function(path)
|
|
self._manager.ui.folder_shortcuts:updateShortcut("cloudstorage", path)
|
|
self.settings:saveSetting("download_dir", path)
|
|
self._manager.updated = true
|
|
download_dir = path
|
|
download_dialog:setTitle(createTitle(filename_orig, filesize, filename, download_dir))
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
{
|
|
text = _("Change filename"),
|
|
callback = function()
|
|
local input_dialog
|
|
input_dialog = InputDialog:new{
|
|
title = _("Enter filename"),
|
|
input = filename,
|
|
input_hint = filename_orig,
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
id = "close",
|
|
callback = function()
|
|
UIManager:close(input_dialog)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Set filename"),
|
|
is_enter_default = true,
|
|
callback = function()
|
|
filename = input_dialog:getInputValue()
|
|
if filename == "" then
|
|
filename = filename_orig
|
|
end
|
|
UIManager:close(input_dialog)
|
|
download_dialog:setTitle(createTitle(filename_orig, filesize, filename, download_dir))
|
|
end,
|
|
},
|
|
}
|
|
},
|
|
}
|
|
UIManager:show(input_dialog)
|
|
input_dialog:onShowKeyboard()
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
callback = function()
|
|
UIManager:close(download_dialog)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Download"),
|
|
callback = function()
|
|
UIManager:close(download_dialog)
|
|
local local_path = (download_dir ~= "/" and download_dir or "") .. "/" .. filename
|
|
local_path = util.fixUtf8(local_path, "_")
|
|
if lfs.attributes(local_path) then
|
|
UIManager:show(ConfirmBox:new{
|
|
text = _("File already exists. Would you like to overwrite it?"),
|
|
ok_callback = function()
|
|
startDownloadFile(item, local_path)
|
|
end,
|
|
})
|
|
else
|
|
startDownloadFile(item, local_path)
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
|
|
download_dialog = ButtonDialog:new{
|
|
title = createTitle(filename_orig, filesize, filename, download_dir),
|
|
buttons = buttons,
|
|
}
|
|
UIManager:show(download_dialog)
|
|
end
|
|
|
|
function CloudStorage:showFileUploadDialog(url)
|
|
local old_path = self.settings:readSetting("upload_dir") or filemanagerutil.getHomeFolder()
|
|
UIManager:show(PathChooser:new{
|
|
select_directory = false,
|
|
path = old_path,
|
|
onConfirm = function(file_path)
|
|
local new_path = file_path:match("(.*)/")
|
|
if new_path == "" then new_path = "/" end
|
|
if new_path ~= old_path then
|
|
self._manager.ui.folder_shortcuts:updateShortcut("cloudstorage_upload", new_path)
|
|
self.settings:saveSetting("upload_dir", new_path)
|
|
self._manager.updated = true
|
|
end
|
|
if lfs.attributes(file_path, "size") > 157286400 then
|
|
UIManager:show(InfoMessage:new{ text = _("File size must be less than 150 MB.") })
|
|
else
|
|
UIManager:nextTick(function()
|
|
UIManager:show(InfoMessage:new{
|
|
text = _("Uploading…"),
|
|
timeout = 1,
|
|
})
|
|
end)
|
|
UIManager:tickAfterNext(function()
|
|
local url_base = url ~= "/" and url or ""
|
|
local code = self.provider.uploadFile(url_base, file_path)
|
|
if code == 200 then
|
|
self:openCloudServer(url)
|
|
UIManager:show(InfoMessage:new{ text = T(_("File uploaded:\n%1"), BD.filepath(file_path)) })
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = T(_("Could not upload file:\n%1"), BD.filepath(file_path)) })
|
|
end
|
|
end)
|
|
end
|
|
end,
|
|
})
|
|
end
|
|
|
|
function CloudStorage:showSelectedFilesUploadDialog(url)
|
|
local files = self._manager.ui.selected_files
|
|
local files_nb = util.tableSize(files)
|
|
UIManager:show(ConfirmBox:new{
|
|
text = T(N_("Upload 1 file?", "Upload %1 files?", files_nb), files_nb),
|
|
ok_text = _("Upload"),
|
|
ok_callback = function()
|
|
local url_base = url ~= "/" and url or ""
|
|
local Trapper = require("ui/trapper")
|
|
Trapper:wrap(function()
|
|
Trapper:setPausedText("Upload paused.\nDo you want to continue or abort uploading files?")
|
|
local proccessed_files, success_files, unsuccess_files = 0, 0, 0
|
|
for file in pairs(files) do
|
|
proccessed_files = proccessed_files + 1
|
|
local text = string.format("Uploading file (%d/%d):\n%s", proccessed_files, files_nb, file:gsub(".*/", ""))
|
|
if not Trapper:info(text) then
|
|
break
|
|
end
|
|
local code = self.provider.uploadFile(url_base, file)
|
|
if code == 200 then
|
|
files[file] = nil
|
|
success_files = success_files + 1
|
|
else
|
|
unsuccess_files = unsuccess_files + 1
|
|
end
|
|
end
|
|
Trapper:clear()
|
|
if success_files > 0 then
|
|
self:openCloudServer(url)
|
|
end
|
|
local text = T(N_("Uploaded 1 file.", "Uploaded %1 files.", success_files), success_files)
|
|
if unsuccess_files > 0 then
|
|
text = text .. "\n" ..
|
|
T(N_("Could not upload 1 file.", "Could not upload %1 files.", unsuccess_files), unsuccess_files)
|
|
end
|
|
UIManager:show(InfoMessage:new{ text = text })
|
|
if not next(files) then
|
|
self._manager.ui:onToggleSelectMode()
|
|
end
|
|
end)
|
|
end,
|
|
})
|
|
end
|
|
|
|
function CloudStorage:showFolderCreateDialog(url)
|
|
local input_dialog, check_button_enter_folder
|
|
input_dialog = InputDialog:new{
|
|
title = _("New folder"),
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
id = "close",
|
|
callback = function()
|
|
UIManager:close(input_dialog)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Create"),
|
|
is_enter_default = true,
|
|
callback = function()
|
|
local folder_name = input_dialog:getInputText()
|
|
if folder_name == "" then return end
|
|
UIManager:close(input_dialog)
|
|
local url_base = url ~= "/" and url or ""
|
|
local ok = self.provider.createFolder(url_base, folder_name)
|
|
if ok then
|
|
if check_button_enter_folder.checked then
|
|
url = url_base .. "/" .. folder_name
|
|
end
|
|
self:openCloudServer(url)
|
|
else
|
|
UIManager:show(InfoMessage:new{ text = T(_("Could not create folder:\n%1"), folder_name) })
|
|
end
|
|
end,
|
|
},
|
|
}
|
|
},
|
|
}
|
|
check_button_enter_folder = CheckButton:new{
|
|
text = _("Enter folder after creation"),
|
|
checked = false,
|
|
parent = input_dialog,
|
|
}
|
|
input_dialog:addWidget(check_button_enter_folder)
|
|
UIManager:show(input_dialog)
|
|
input_dialog:onShowKeyboard()
|
|
end
|
|
|
|
function CloudStorage:showSelectedFilesDeleteDialog()
|
|
local files = self.remote_selected_files
|
|
local files_nb = util.tableSize(files)
|
|
UIManager:show(ConfirmBox:new{
|
|
text = T(N_("Delete 1 file?", "Delete %1 files?", files_nb), files_nb),
|
|
ok_text = _("Delete"),
|
|
ok_callback = function()
|
|
local Trapper = require("ui/trapper")
|
|
Trapper:wrap(function()
|
|
Trapper:setPausedText("Deleting paused.\nDo you want to continue or abort deleting files?")
|
|
local proccessed_files, success_files, unsuccess_files = 0, 0, 0
|
|
for file in pairs(files) do
|
|
proccessed_files = proccessed_files + 1
|
|
local text = string.format("Deleting file (%d/%d):\n%s", proccessed_files, files_nb, file:gsub(".*/", ""))
|
|
if not Trapper:info(text) then
|
|
break
|
|
end
|
|
local ok = self.provider.deleteFile(file)
|
|
if ok then
|
|
files[file] = nil
|
|
success_files = success_files + 1
|
|
else
|
|
unsuccess_files = unsuccess_files + 1
|
|
end
|
|
end
|
|
Trapper:clear()
|
|
if success_files > 0 then
|
|
if not next(files) then
|
|
self:toggleSelectMode() -- turn off
|
|
end
|
|
self:openCloudServer(self.paths[#self.paths]) -- refresh
|
|
end
|
|
local text = T(N_("Deleted 1 file.", "Deleted %1 files.", success_files), success_files)
|
|
if unsuccess_files > 0 then
|
|
text = text .. "\n" ..
|
|
T(N_("Could not delete 1 file.", "Could not delete %1 files.", unsuccess_files), unsuccess_files)
|
|
end
|
|
UIManager:show(InfoMessage:new{ text = text })
|
|
end)
|
|
end,
|
|
})
|
|
end
|
|
|
|
function CloudStorage:showSelectedFilesDownloadDialog()
|
|
local files = self.remote_selected_files
|
|
local files_nb = util.tableSize(files)
|
|
local download_dir = self.settings:readSetting("download_dir") or G_reader_settings:readSetting("lastdir")
|
|
local local_path = (download_dir ~= "/" and download_dir or "") .. "/"
|
|
UIManager:show(MultiConfirmBox:new{
|
|
text = T(N_("Download 1 file?", "Download %1 files?", files_nb), files_nb) .. "\n" ..
|
|
"\n" .. _("Download folder:") .. "\n" .. download_dir ..
|
|
"\n" .. _("Existing files will be overwritten."),
|
|
choice1_text = _("Choose folder"),
|
|
choice1_callback = function()
|
|
UIManager:show(PathChooser:new{
|
|
select_file = false,
|
|
path = download_dir,
|
|
onConfirm = function(path)
|
|
self._manager.ui.folder_shortcuts:updateShortcut("cloudstorage", path)
|
|
self.settings:saveSetting("download_dir", path)
|
|
self._manager.updated = true
|
|
download_dir = path
|
|
self:showSelectedFilesDownloadDialog()
|
|
end,
|
|
})
|
|
end,
|
|
choice2_text = _("Download"),
|
|
choice2_callback = function()
|
|
local Trapper = require("ui/trapper")
|
|
Trapper:wrap(function()
|
|
Trapper:setPausedText("Downloading paused.\nDo you want to continue or abort downloading files?")
|
|
local proccessed_files, success_files, unsuccess_files = 0, 0, 0
|
|
for file in pairs(files) do
|
|
proccessed_files = proccessed_files + 1
|
|
local file_name = file:gsub(".*/", "")
|
|
local text = string.format("Downloading file (%d/%d):\n%s", proccessed_files, files_nb, file_name)
|
|
if not Trapper:info(text) then
|
|
break
|
|
end
|
|
local code = self.provider.downloadFile(file, local_path .. file_name)
|
|
if code == 200 then
|
|
files[file] = nil
|
|
success_files = success_files + 1
|
|
else
|
|
unsuccess_files = unsuccess_files + 1
|
|
end
|
|
end
|
|
Trapper:clear()
|
|
if success_files > 0 then
|
|
if not next(files) then
|
|
self:toggleSelectMode() -- turn off
|
|
end
|
|
self:openCloudServer(self.paths[#self.paths]) -- refresh
|
|
end
|
|
local text = T(N_("Downloaded 1 file.", "Downloaded %1 files.", success_files), success_files)
|
|
if unsuccess_files > 0 then
|
|
text = text .. "\n" ..
|
|
T(N_("Could not download 1 file.", "Could not download %1 files.", unsuccess_files), unsuccess_files)
|
|
end
|
|
UIManager:show(InfoMessage:new{ text = text })
|
|
end)
|
|
end,
|
|
})
|
|
end
|
|
|
|
function CloudStorage:showSyncSettingsDialog(item)
|
|
local server = self.servers[item.server_idx]
|
|
local sync_source_folder = server.sync_source_folder == "" and "/" or server.sync_source_folder
|
|
local sync_dialog
|
|
sync_dialog = ButtonDialog:new{
|
|
title = server.name .. "\n\n" .. T(_("Remote (source) folder:\n%1\nLocal (destination) folder:\n%2"),
|
|
sync_source_folder and BD.dirpath(sync_source_folder) or _("not set"),
|
|
server.sync_dest_folder and BD.dirpath(server.sync_dest_folder) or _("not set")),
|
|
title_align = "center",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Choose remote folder"),
|
|
callback = function()
|
|
UIManager:close(sync_dialog)
|
|
local choose_folder_callback = function(path)
|
|
server.sync_source_folder = path
|
|
self._manager.updated = true
|
|
self:showSyncSettingsDialog(item)
|
|
end
|
|
self.item_idx = item.idx
|
|
self.server_idx = item.server_idx
|
|
self:openCloudServer(item.url, choose_folder_callback)
|
|
end,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
text = _("Choose local folder"),
|
|
callback = function()
|
|
UIManager:close(sync_dialog)
|
|
UIManager:show(PathChooser:new{
|
|
select_file = false,
|
|
path = server.sync_dest_folder,
|
|
onConfirm = function(path)
|
|
server.sync_dest_folder = path
|
|
self._manager.updated = true
|
|
self:showSyncSettingsDialog(item)
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(sync_dialog)
|
|
end
|
|
|
|
function CloudStorage:syncCloud(item)
|
|
local server = self:initServer(item.server_idx)
|
|
self.provider.run(function()
|
|
local Trapper = require("ui/trapper")
|
|
Trapper:wrap(function()
|
|
Trapper:setPausedText("Download paused.\nDo you want to continue or abort downloading files?")
|
|
Trapper:info(_("Retrieving files…"))
|
|
local url = server.sync_source_folder == "/" and "" or server.sync_source_folder
|
|
local remote_files = self.provider.listFolder(url) -- excluding folders
|
|
if not remote_files then
|
|
Trapper:clear()
|
|
UIManager:show(InfoMessage:new{
|
|
text = T(_("Server: %1"), server.name) .. "\n" ..
|
|
_("Could not fetch server's content.\nPlease check your configuration or network connection."),
|
|
})
|
|
return
|
|
end
|
|
|
|
local local_files = {}
|
|
local path = server.sync_dest_folder
|
|
local ok, iter, dir_obj = pcall(lfs.dir, path)
|
|
if ok then
|
|
for f in iter, dir_obj do
|
|
local filename = path .."/" .. f
|
|
local attributes = lfs.attributes(filename)
|
|
if attributes.mode == "file" then
|
|
local_files[f] = attributes.size
|
|
end
|
|
end
|
|
end
|
|
|
|
local files_to_download = 0
|
|
for i, file in ipairs(remote_files) do
|
|
if not local_files[file.text] or local_files[file.text] ~= file.filesize then
|
|
files_to_download = files_to_download + 1
|
|
remote_files[i].download = true
|
|
end
|
|
end
|
|
if files_to_download == 0 then
|
|
Trapper:clear()
|
|
UIManager:show(InfoMessage:new{ text = _("No files to download.") })
|
|
return
|
|
end
|
|
|
|
local proccessed_files, success_files, unsuccess_files = 0, 0, 0
|
|
for _, file in ipairs(remote_files) do
|
|
if file.download then
|
|
proccessed_files = proccessed_files + 1
|
|
local text = string.format("Downloading file (%d/%d):\n%s", proccessed_files, files_to_download, file.text)
|
|
if not Trapper:info(text) then
|
|
break
|
|
end
|
|
local code = self.provider.downloadFile(file.url, server.sync_dest_folder .. "/" .. file.text)
|
|
if code == 200 then
|
|
success_files = success_files + 1
|
|
else
|
|
unsuccess_files = unsuccess_files + 1
|
|
end
|
|
end
|
|
end
|
|
Trapper:clear()
|
|
local text = T(N_("Downloaded 1 file.", "Downloaded %1 files.", success_files), success_files)
|
|
if unsuccess_files > 0 then
|
|
text = text .. "\n" ..
|
|
T(N_("Could not download 1 file.", "Could not download %1 files.", unsuccess_files), unsuccess_files)
|
|
end
|
|
UIManager:show(InfoMessage:new{ text = text })
|
|
end)
|
|
end)
|
|
end
|
|
|
|
return CloudStorage
|