blob: 2778f96055f88822d7730f657bffded8100b1f0a [file] [log] [blame]
Dees_Troya13d74f2013-03-24 08:54:55 -05001/*
2 Copyright 2012 bigbiff/Dees_Troy TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
Dees_Troy51a0e822012-09-05 15:24:24 -040018
Dees_Troy51a0e822012-09-05 15:24:24 -040019#include <string.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040020#include <sys/stat.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040021#include <dirent.h>
Dees_Troy51a0e822012-09-05 15:24:24 -040022#include <algorithm>
mauronofrio38ed6a72020-01-10 16:44:28 +010023#ifdef __ANDROID_API_M__
24#include <vector>
25#ifdef __ANDROID_API_N__
26#include <android-base/strings.h>
27#else
28#include <base/strings.h>
29#endif
30#else
31#endif
Dees_Troy51a0e822012-09-05 15:24:24 -040032extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000033#include "../twcommon.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040034}
Ethan Yonkerfbb43532015-12-28 21:54:50 +010035#include "../minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040036
37#include "rapidxml.hpp"
38#include "objects.hpp"
39#include "../data.hpp"
Dees_Troy3ee47bc2013-01-25 21:47:37 +000040#include "../twrp-functions.hpp"
bigbiff bigbiff19fb79c2016-09-05 21:04:51 -040041#include "../adbbu/libtwadbbu.hpp"
Dees_Troy51a0e822012-09-05 15:24:24 -040042
Dees_Troy51a0e822012-09-05 15:24:24 -040043int GUIFileSelector::mSortOrder = 0;
44
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010045GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIScrollList(node)
Dees_Troy51a0e822012-09-05 15:24:24 -040046{
47 xml_attribute<>* attr;
48 xml_node<>* child;
Dees_Troy51a0e822012-09-05 15:24:24 -040049
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010050 mFolderIcon = mFileIcon = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -040051 mShowFolders = mShowFiles = mShowNavFolders = 1;
52 mUpdate = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -040053 mPathVar = "cwd";
Dees_Troyc0583f52013-02-28 11:19:57 -060054 updateFileList = false;
Dees_Troy51a0e822012-09-05 15:24:24 -040055
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010056 // Load filter for filtering files (e.g. *.zip for only zips)
that72b90e32015-02-23 22:57:14 +010057 child = FindNode(node, "filter");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010058 if (child) {
Dees_Troy51a0e822012-09-05 15:24:24 -040059 attr = child->first_attribute("extn");
60 if (attr)
61 mExtn = attr->value();
62 attr = child->first_attribute("folders");
63 if (attr)
64 mShowFolders = atoi(attr->value());
65 attr = child->first_attribute("files");
66 if (attr)
67 mShowFiles = atoi(attr->value());
68 attr = child->first_attribute("nav");
69 if (attr)
70 mShowNavFolders = atoi(attr->value());
71 }
72
73 // Handle the path variable
that72b90e32015-02-23 22:57:14 +010074 child = FindNode(node, "path");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010075 if (child) {
Dees_Troy51a0e822012-09-05 15:24:24 -040076 attr = child->first_attribute("name");
77 if (attr)
78 mPathVar = attr->value();
79 attr = child->first_attribute("default");
Ethan Yonker21ff02a2015-02-18 14:35:00 -060080 if (attr) {
81 mPathDefault = attr->value();
Dees_Troy51a0e822012-09-05 15:24:24 -040082 DataManager::SetValue(mPathVar, attr->value());
Ethan Yonker21ff02a2015-02-18 14:35:00 -060083 }
Dees_Troy51a0e822012-09-05 15:24:24 -040084 }
85
86 // Handle the result variable
that72b90e32015-02-23 22:57:14 +010087 child = FindNode(node, "data");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010088 if (child) {
Dees_Troy51a0e822012-09-05 15:24:24 -040089 attr = child->first_attribute("name");
90 if (attr)
91 mVariable = attr->value();
92 attr = child->first_attribute("default");
93 if (attr)
94 DataManager::SetValue(mVariable, attr->value());
95 }
96
97 // Handle the sort variable
that72b90e32015-02-23 22:57:14 +010098 child = FindNode(node, "sort");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +010099 if (child) {
Dees_Troy51a0e822012-09-05 15:24:24 -0400100 attr = child->first_attribute("name");
101 if (attr)
102 mSortVariable = attr->value();
103 attr = child->first_attribute("default");
104 if (attr)
105 DataManager::SetValue(mSortVariable, attr->value());
106
107 DataManager::GetValue(mSortVariable, mSortOrder);
108 }
109
110 // Handle the selection variable
that72b90e32015-02-23 22:57:14 +0100111 child = FindNode(node, "selection");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100112 if (child && (attr = child->first_attribute("name")))
113 mSelection = attr->value();
114 else
Dees_Troy51a0e822012-09-05 15:24:24 -0400115 mSelection = "0";
116
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100117 // Get folder and file icons if present
that72b90e32015-02-23 22:57:14 +0100118 child = FindNode(node, "icon");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100119 if (child) {
thatf6ed8fc2015-02-14 20:23:16 +0100120 mFolderIcon = LoadAttrImage(child, "folder");
121 mFileIcon = LoadAttrImage(child, "file");
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100122 }
Ethan Yonker58f21322018-08-24 11:17:36 -0500123 int iconWidth = 0, iconHeight = 0;
124 if (mFolderIcon && mFolderIcon->GetResource() && mFileIcon && mFileIcon->GetResource()) {
125 iconWidth = std::max(mFolderIcon->GetWidth(), mFileIcon->GetWidth());
126 iconHeight = std::max(mFolderIcon->GetHeight(), mFileIcon->GetHeight());
127 } else if (mFolderIcon && mFolderIcon->GetResource()) {
128 iconWidth = mFolderIcon->GetWidth();
129 iconHeight = mFolderIcon->GetHeight();
130 } else if (mFileIcon && mFileIcon->GetResource()) {
131 iconWidth = mFileIcon->GetWidth();
132 iconHeight = mFileIcon->GetHeight();
133 }
thatf6ed8fc2015-02-14 20:23:16 +0100134 SetMaxIconSize(iconWidth, iconHeight);
Dees_Troy51a0e822012-09-05 15:24:24 -0400135
136 // Fetch the file/folder list
137 std::string value;
138 DataManager::GetValue(mPathVar, value);
Dees_Troy80a11d92013-01-25 16:36:07 +0000139 GetFileList(value);
Dees_Troy51a0e822012-09-05 15:24:24 -0400140}
141
142GUIFileSelector::~GUIFileSelector()
143{
Dees_Troy51a0e822012-09-05 15:24:24 -0400144}
145
146int GUIFileSelector::Update(void)
147{
Matt Mowera8a89d12016-12-30 18:10:37 -0600148 if (!isConditionTrue())
Vojtech Bocekede51c52014-02-07 23:58:09 +0100149 return 0;
150
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100151 GUIScrollList::Update();
152
153 // Update the file list if needed
154 if (updateFileList) {
155 string value;
156 DataManager::GetValue(mPathVar, value);
157 if (GetFileList(value) == 0) {
158 updateFileList = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400159 mUpdate = 1;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100160 } else
161 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400162 }
163
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100164 if (mUpdate) {
Dees_Troy51a0e822012-09-05 15:24:24 -0400165 mUpdate = 0;
166 if (Render() == 0)
167 return 2;
168 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400169 return 0;
170}
171
Vojtech Bocek07220562014-02-08 02:05:33 +0100172int GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value)
Dees_Troy51a0e822012-09-05 15:24:24 -0400173{
thatfa30aca2015-02-13 01:22:22 +0100174 GUIScrollList::NotifyVarChange(varName, value);
175
Matt Mowera8a89d12016-12-30 18:10:37 -0600176 if (!isConditionTrue())
Vojtech Bocekede51c52014-02-07 23:58:09 +0100177 return 0;
178
Dees_Troy146d72a2013-03-11 17:46:19 +0000179 if (varName.empty()) {
180 // Always clear the data variable so we know to use it
181 DataManager::SetValue(mVariable, "");
182 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100183 if (varName == mPathVar || varName == mSortVariable) {
Dees_Troy4622cf92013-03-01 15:29:36 -0600184 if (varName == mSortVariable) {
185 DataManager::GetValue(mSortVariable, mSortOrder);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100186 } else {
187 // Reset the list to the top
188 SetVisibleListLocation(0);
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600189 if (value.empty())
190 DataManager::SetValue(mPathVar, mPathDefault);
Dees_Troyc0583f52013-02-28 11:19:57 -0600191 }
192 updateFileList = true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400193 mUpdate = 1;
194 return 0;
195 }
196 return 0;
197}
198
Dees_Troy51a0e822012-09-05 15:24:24 -0400199bool GUIFileSelector::fileSort(FileData d1, FileData d2)
200{
201 if (d1.fileName == ".")
202 return -1;
203 if (d2.fileName == ".")
204 return 0;
thatc12a7f02016-01-28 20:52:16 +0100205 if (d1.fileName == "..")
Dees_Troy51a0e822012-09-05 15:24:24 -0400206 return -1;
thatc12a7f02016-01-28 20:52:16 +0100207 if (d2.fileName == "..")
Dees_Troy51a0e822012-09-05 15:24:24 -0400208 return 0;
Matt Mowerfb1c4ff2014-04-16 13:43:36 -0500209
Dees_Troy51a0e822012-09-05 15:24:24 -0400210 switch (mSortOrder) {
211 case 3: // by size largest first
212 if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
213 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600214 return d1.fileSize < d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400215 case -3: // by size smallest first
216 if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
217 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600218 return d1.fileSize > d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400219 case 2: // by last modified date newest first
220 if (d1.lastModified == d2.lastModified)
221 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600222 return d1.lastModified < d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400223 case -2: // by date oldest first
224 if (d1.lastModified == d2.lastModified)
225 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600226 return d1.lastModified > d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400227 case -1: // by name descending
228 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
229 default: // should be a 1 - sort by name ascending
230 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
231 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100232 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400233}
234
235int GUIFileSelector::GetFileList(const std::string folder)
236{
237 DIR* d;
238 struct dirent* de;
239 struct stat st;
240
241 // Clear all data
242 mFolderList.clear();
243 mFileList.clear();
244
245 d = opendir(folder.c_str());
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100246 if (d == NULL) {
Dees_Troy2673cec2013-04-02 20:22:16 +0000247 LOGINFO("Unable to open '%s'\n", folder.c_str());
Dees_Troy80a11d92013-01-25 16:36:07 +0000248 if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
249 size_t found;
250 found = folder.find_last_of('/');
251 if (found != string::npos) {
252 string new_folder = folder.substr(0, found);
253
254 if (new_folder.length() < 2)
255 new_folder = "/";
256 DataManager::SetValue(mPathVar, new_folder);
257 }
258 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400259 return -1;
260 }
261
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100262 while ((de = readdir(d)) != NULL) {
Dees_Troy51a0e822012-09-05 15:24:24 -0400263 FileData data;
264
265 data.fileName = de->d_name;
266 if (data.fileName == ".")
267 continue;
268 if (data.fileName == ".." && folder == "/")
269 continue;
thatc12a7f02016-01-28 20:52:16 +0100270
271 data.fileType = de->d_type;
Dees_Troy51a0e822012-09-05 15:24:24 -0400272
273 std::string path = folder + "/" + data.fileName;
274 stat(path.c_str(), &st);
275 data.protection = st.st_mode;
276 data.userId = st.st_uid;
277 data.groupId = st.st_gid;
278 data.fileSize = st.st_size;
279 data.lastAccess = st.st_atime;
280 data.lastModified = st.st_mtime;
281 data.lastStatChange = st.st_ctime;
282
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000283 if (data.fileType == DT_UNKNOWN) {
284 data.fileType = TWFunc::Get_D_Type_From_Stat(path);
285 }
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100286 if (data.fileType == DT_DIR) {
thatc12a7f02016-01-28 20:52:16 +0100287 if (mShowNavFolders || (data.fileName != "." && data.fileName != ".."))
Dees_Troy51a0e822012-09-05 15:24:24 -0400288 mFolderList.push_back(data);
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100289 } else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK) {
mauronofrio38ed6a72020-01-10 16:44:28 +0100290#ifdef __ANDROID_API_M__
291 std::vector<std::string> mExtnResults = android::base::Split(mExtn, ";");
292 for (const std::string& mExtnElement : mExtnResults)
293 {
294 std::string mExtnName = android::base::Trim(mExtnElement);
295 if (mExtnName.empty() || (data.fileName.length() > mExtnName.length() && data.fileName.substr(data.fileName.length() - mExtnName.length()) == mExtnName)) {
296 if (mExtnName == ".ab" && twadbbu::Check_ADB_Backup_File(path))
297 mFolderList.push_back(data);
298 else
299 mFileList.push_back(data);
300 }
301#else //On android 5.1 we can't use android::base::Trim and Split so just use the first extension written in the list
302 std::size_t seppos = mExtn.find_first_of(";");
303 std::string mExtnf;
304 if (seppos!=std::string::npos){
305 mExtnf = mExtn.substr(0, seppos);
306 } else {
307 mExtnf = mExtn;
308 }
309 if (mExtnf.empty() || (data.fileName.length() > mExtnf.length() && data.fileName.substr(data.fileName.length() - mExtnf.length()) == mExtnf)) {
310 if (mExtnf == ".ab" && twadbbu::Check_ADB_Backup_File(path))
bigbiff bigbiff19fb79c2016-09-05 21:04:51 -0400311 mFolderList.push_back(data);
312 else
313 mFileList.push_back(data);
mauronofrio38ed6a72020-01-10 16:44:28 +0100314#endif
Dees_Troy51a0e822012-09-05 15:24:24 -0400315 }
316 }
317 }
318 closedir(d);
319
320 std::sort(mFolderList.begin(), mFolderList.end(), fileSort);
321 std::sort(mFileList.begin(), mFileList.end(), fileSort);
Dees_Troyc0583f52013-02-28 11:19:57 -0600322
Dees_Troy51a0e822012-09-05 15:24:24 -0400323 return 0;
324}
325
326void GUIFileSelector::SetPageFocus(int inFocus)
327{
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100328 GUIScrollList::SetPageFocus(inFocus);
329 if (inFocus) {
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600330 std::string value;
331 DataManager::GetValue(mPathVar, value);
332 if (value.empty())
333 DataManager::SetValue(mPathVar, mPathDefault);
Dees_Troyc0583f52013-02-28 11:19:57 -0600334 updateFileList = true;
Dees_Troyc0583f52013-02-28 11:19:57 -0600335 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400336 }
Dees_Troya13d74f2013-03-24 08:54:55 -0500337}
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100338
339size_t GUIFileSelector::GetItemCount()
340{
341 size_t folderSize = mShowFolders ? mFolderList.size() : 0;
342 size_t fileSize = mShowFiles ? mFileList.size() : 0;
343 return folderSize + fileSize;
344}
345
that0af77952015-02-25 08:52:19 +0100346void GUIFileSelector::RenderItem(size_t itemindex, int yPos, bool selected)
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100347{
348 size_t folderSize = mShowFolders ? mFolderList.size() : 0;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100349
that0af77952015-02-25 08:52:19 +0100350 ImageResource* icon;
351 std::string text;
352
353 if (itemindex < folderSize) {
354 text = mFolderList.at(itemindex).fileName;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100355 icon = mFolderIcon;
thatc12a7f02016-01-28 20:52:16 +0100356 if (text == "..")
357 text = gui_lookup("up_a_level", "(Up A Level)");
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100358 } else {
that0af77952015-02-25 08:52:19 +0100359 text = mFileList.at(itemindex - folderSize).fileName;
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100360 icon = mFileIcon;
361 }
that0af77952015-02-25 08:52:19 +0100362
363 RenderStdItem(yPos, selected, icon, text.c_str());
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100364}
365
366void GUIFileSelector::NotifySelect(size_t item_selected)
367{
368 size_t folderSize = mShowFolders ? mFolderList.size() : 0;
369 size_t fileSize = mShowFiles ? mFileList.size() : 0;
370
371 if (item_selected < folderSize + fileSize) {
372 // We've selected an item!
373 std::string str;
374 if (item_selected < folderSize) {
375 std::string cwd;
376
377 str = mFolderList.at(item_selected).fileName;
378 if (mSelection != "0")
379 DataManager::SetValue(mSelection, str);
380 DataManager::GetValue(mPathVar, cwd);
381
382 // Ignore requests to do nothing
383 if (str == ".") return;
thatc12a7f02016-01-28 20:52:16 +0100384 if (str == "..") {
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100385 if (cwd != "/") {
386 size_t found;
387 found = cwd.find_last_of('/');
388 cwd = cwd.substr(0,found);
389
390 if (cwd.length() < 2) cwd = "/";
391 }
392 } else {
393 // Add a slash if we're not the root folder
394 if (cwd != "/") cwd += "/";
395 cwd += str;
396 }
397
bigbiff bigbiff19fb79c2016-09-05 21:04:51 -0400398 if (mShowNavFolders == 0 && (mShowFiles == 0 || mExtn == ".ab")) {
399 // this is probably the restore list and we need to save chosen location to mVariable instead of mPathVar
Ethan Yonker0a3a98f2015-02-05 00:48:28 +0100400 DataManager::SetValue(mVariable, cwd);
401 } else {
402 // We are changing paths, so we need to set mPathVar
403 DataManager::SetValue(mPathVar, cwd);
404 }
405 } else if (!mVariable.empty()) {
406 str = mFileList.at(item_selected - folderSize).fileName;
407 if (mSelection != "0")
408 DataManager::SetValue(mSelection, str);
409
410 std::string cwd;
411 DataManager::GetValue(mPathVar, cwd);
412 if (cwd != "/")
413 cwd += "/";
414 DataManager::SetValue(mVariable, cwd + str);
415 }
416 }
417 mUpdate = 1;
418}