четверг, 6 февраля 2014 г.

Пишем свой файловый менеджер для Android, #2


В предыдущей статье (#1) я обещал, что продолжу разработку файлового менеджера и добавлю новый функционал:
1) Создание файлов и папок
2) Копирование/перемещение файлов и папок
3) Более дружелюбный интерфейс приложения


Видео в конце поста.
Upd (20.04.14). Проверил код на Delphi XE6 и добавил исходники для новой версии IDE.







Начал я с последнего пункта.
Решил сделать пару иконок, добавить их к пунктам в листбоксе, в зависимости от типа (папка или файл). Так визуально становилось легче определять папки, для дальнейшего перемещения.
Сразу надо оговориться, что я никогда не рисовал иконок и совсем не разбирался с иконками в Android’е. Время пришло! Начал читать в интернете статьи и рекомендации от гугла (раз Metrics and Grids, два: Iconography, три: Devices and Displays), в итоге запутался…
Суть вопроса в том, что для разных экранов (с разным dpi) нужно несколько вариантов одной картинки. Но мне нужно было загружать подходящую картинку в «ListBoxItem», а загрузить туда сразу все варианты нельзя, только одну картинку. Поэтому я не мог решить какой вариант картинки использовать.
Пришлось обратиться за консультацией к Профессионалу (Ярославу Бровину). Ярослав всё подробно объяснил и привёл оригинальное решение для данного вопроса. Для всех кому интересно описание и решение, посетите форум: Как использовать иконки разного качества для экранов с разным DPI?
Ярослав, если вы читаете это сообщение, ещё раз, Спасибо вам! 

В проекте я использовал иконки, базовый размер 48x48:
Для кнопок:
  • MDPI - 1X - 32x32
  • HDPI - 1,5X - 48x48
  • XHDPI - 2X - 64x64
  • XXHDPI – 3X - 96x96
Для листбокса:
  • MDPI - 1X - 48x48
  • HDPI - 1,5X - 64x64
  • XHDPI - 2X - 96x96
Все используемые иконки, в том числе сделанные мной (строго не судите, я вообще рисовать не умею :) залил на гугл драйв (в конце статьи), вдруг кому-то пригодится…

Также теперь подключается стиль, специально сделанный только для «ListBox»(listboxitemnodetail), уменьшающий расстояние между картинкой и текстом в «ListBoxItem».
Вообще для удобства пользователя сделал много чего, теперь вроде стало удобнее пользоваться приложением.

Создание файлов и папок
Принцип: жмём кнопку «Создать файл» или «Создать папку», появляется окно для ввода имени, вводим имя, жмём кнопку «Создать» и файл/папка появляются в списке.

Проблема возникла с созданием окна для ввода имени. Я хотел сделать отдельную форму и вызывать её как модальное окно, но когда всё сделал, обнаружил, что на Android’е вместо прозрачной формы появляется чёрная. Начал экспериментировать, если выставить «FormStyle := fsPopup», то форма появляется прозрачная, но неправильно позиционируется (эта задача решаема) и если на такой форме есть «TEdit», то вы не сможете ни чего ввести, т.к. клавиатура просто не появится. Ярослав был оповещён о подобных проблемах и сказал, что баг уже известен и будет исправлен, также он посоветовал использовать «TCustomPopupForm». В то время я уже делал другой вариант (как временный), который описал в предыдущей статье. Внимание! В данной версии файлового менеджера не используется «TCustomPopupForm» (Почитать можно тут Всплывающие формы в XE5).

Далее техническая часть вопроса. Введённое имя нужно проверять на запрещённые символы. Написал функцию проверки, в коде это «function CheckName». Описывать проверки на существование файла/папки, обработку нажатия кнопок «Enter» и «HardwareBack» я не буду.
Для создания файлов и папок используем методы «TFile.Create» и «TDirectory.CreateDirectory»

Копирование/перемещение файлов и папок
Нам понадобится 3 кнопки «Копировать», «Вставить», «Вырезать». В коде главной кнопкой будет, кнопка «Вставить». 

Принцип: Выбираем пункт (отмечаем галочкой) в «ListBox», жмём кнопку «Копировать/Вырезать», перемещаемся в другую папку, жмём кнопку «Вставить», готово.
Для копирования используем методы «TDirectory.Copy» и «TFile.Copy». Для перемещения «TDirectory.Move» и «TFile.Move». Ловим исключения – выдаём сообщения :).

Переписал процедуру удаления файлов и папок, теперь работает быстрее.


Структура проекта:


Код приложения (Delphi XE5 Update 2):

{
  ******************************************************************************
  FileManager2 for Android
  Autor: Andrey Yefimov (Contact: http://delphifmandroid.blogspot.ru/)
  Use only non-commercial purposes. Necessarily indicating the authorship.
  Используйте только в некоммерческих целях. Обязательно с указанием авторства.
  ******************************************************************************
}

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.ListBox, FMX.Layouts, FMX.StdCtrls, FMX.Objects, FMX.Edit, FMX.Effects;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Label1: TLabel;
    ToolBar1: TToolBar;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    ImageBook: TStyleBook;
    SpeedButton4: TSpeedButton;
    Image1: TImage;
    StyleBook1: TStyleBook;
    Image2: TImage;
    Dialog: TRectangle;
    ToolBar2: TToolBar;
    Layout1: TLayout;
    DialogEdit: TEdit;
    DialogTitle: TLabel;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    ShadowEffect1: TShadowEffect;
    DialogFon: TRectangle;
    DialogError: TLabel;
    SpeedButton7: TSpeedButton;
    Image3: TImage;
    SpeedButton8: TSpeedButton;
    ToolBar3: TToolBar;
    Image4: TImage;
    SpeedButton9: TSpeedButton;
    Image5: TImage;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1ItemClick(const Sender: TCustomListBox;
      const Item: TListBoxItem);
    procedure SpeedButton1Click(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char;
      Shift: TShiftState);
    procedure ListBox1ChangeCheck(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure SpeedButton5Click(Sender: TObject);
    procedure SpeedButton6Click(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
    procedure DialogEditChangeTracking(Sender: TObject);
    procedure SpeedButton8Click(Sender: TObject);
    procedure SpeedButton7Click(Sender: TObject);
    procedure SpeedButton9Click(Sender: TObject);
  private
    procedure AddListItem(list: array of string; itype: string);
    procedure TotalWork(path_tr: string; clear: boolean);
    procedure OnOffButton(del, add, copy, cut: boolean);
    { Private declarations }
  public
    function GetImage(const AImageName: string): TBitmap;
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  System.IOUtils, System.Generics.Collections, Generics.Defaults,
  FMX.Helpers.Android, Androidapi.JNI.JavaTypes,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Webkit;

var
  path: string; // Здесь будем хранить путь
  ItemsCheck: array of array of string; // Массив с помеченными пунктами

function CompareLowerStr(const Left, Right: string): Integer;
begin
  Result := CompareStr(AnsiLowerCase(Left), AnsiLowerCase(Right));
end;

{Функция получения картинки из ImageBook'а(Автор: Ярослав Бровин)}
function TForm1.GetImage(const AImageName: string): TBitmap;
var
  StyleObject: TFmxObject;
  Image: TImage;
begin
  StyleObject := ImageBook.Style.FindStyleResource(AImageName);
  if (StyleObject <> nil) and (StyleObject is TImage) then
  begin
    Image := StyleObject as TImage;
    // Здесь мы получим картинку нужного dpi (Scale)
    Result := Image.Bitmap;
  end
  else
    Result := nil;
end;

{Процедура для вставки массивов в ListBox}
procedure TForm1.AddListItem(list: array of string; itype: string);
var
  c: integer;
  LItem: TListBoxItem;
  BitmapFolder, BitmapFile: TBitmap;
begin

  BitmapFolder := GetImage('folder');
  BitmapFile := GetImage('file');

  ListBox1.BeginUpdate;

  for c := 0 to Length(list) - 1 do
  begin

    LItem := TListBoxItem.Create(ListBox1);

    if itype = 'folder' then
    begin
      if BitmapFolder <> nil then
      begin
        LItem.ItemData.Bitmap.Assign(BitmapFolder);
      end;
    end
    else
    begin
      if BitmapFile <> nil then begin
        LItem.ItemData.Bitmap.Assign(BitmapFile);
      end;
    end;

    LItem.ItemData.Text := ExtractFileName(list[c]);
    LItem.ItemData.Detail := list[c]; // Помещаем полный путь в Detail
    LItem.TagString := itype;
    ListBox1.AddObject(LItem);

  end;

  ListBox1.EndUpdate;

end;

{Функция для проверки введённого имени}
function CheckName(NewName: string): Boolean;
const
  InvalidChars: Array[0..9] of Char = ('\', '/', ':', '*', '?', '"', '<', '>', '|', '~');
var
  LengthName, i, j: integer;
begin

  Result := False;

  LengthName := Length(NewName);

  if LengthName <> 0 then
  begin
    for i := 0 to LengthName - 1 do
    begin
      for j := 0 to 9 do
      begin
        if NewName[i] = InvalidChars[j] then
        begin
          Result := True;
        end;
      end;
    end;
  end;

end;

{Отслеживаем ввод символов и проверяем их}
procedure TForm1.DialogEditChangeTracking(Sender: TObject);
begin
  if CheckName(DialogEdit.Text) then
    DialogError.Text := 'Недопускаются \ / : * ? " < > | ~'
  else
    DialogError.Text := '';
end;

{Загружаем список файлов в корне устройства}
procedure TForm1.FormCreate(Sender: TObject);
begin
  // Корень устройства
  path := '/';

  TotalWork(path, False);
end;

{Обрабатываем кнопки Hardware Back, Enter}
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char;
  Shift: TShiftState);
begin
  if Dialog.Visible = False then
  begin

    if (Key = vkHardwareBack) AND (path <> '/') then
    begin
      SpeedButton1Click(Self); // Вызываем кнопку Назад
      Key := 0;
    end;

  end
  else
  begin
    if Key = vkHardwareBack then
    begin
      SpeedButton5Click(Self); // Вызываем кнопку Отмена
      Key := 0;
    end;

    if Key = 13 then
    begin
      SpeedButton6Click(Self); // Вызываем кнопку Создать
    end;
  end;
end;

{Пункт пометили галкой}
procedure TForm1.ListBox1ChangeCheck(Sender: TObject);
var
  i: integer;
begin
  SetLength(ItemsCheck, 0, 0);

  for i := 0 to ListBox1.Count-1 do
  begin

    if ListBox1.ListItems[i].IsChecked then
    begin
      SetLength(ItemsCheck, i + 1, 3);
      ItemsCheck[i][0] := ListBox1.ListItems[i].Text; // Имя
      ItemsCheck[i][1] := ListBox1.ListItems[i].TagString; // Тип
      ItemsCheck[i][2] := ListBox1.ListItems[i].ItemData.Detail; // Путь
    end;

  end;

  if Length(ItemsCheck) <> 0 then begin
    OnOffButton(True, False, True, True);
  end
  else begin
    OnOffButton(False, False, False, False);
  end;

end;

{Клик по Item'у, вперёд}
procedure TForm1.ListBox1ItemClick(const Sender: TCustomListBox;
  const Item: TListBoxItem);
var
  FileName, ExtFile: string;
  mime: JMimeTypeMap;
  ExtToMime: JString;
  Intent: JIntent;
begin

  if Length(ItemsCheck) <> 0 then begin
    OnOffButton(False, True, False, False);
  end
  else begin
    OnOffButton(False, False, False, False);
  end;

  if Item.TagString = 'folder' then
  begin

    // Сохраняем выбранный путь
    path := Item.ItemData.Detail;

    if TDirectory.Exists(path) then
    begin
      TotalWork(path, True);
    end
    else
    begin
      ListBox1.Items.Delete(Item.Index);
      ShowMessage('Папка не найдена');
    end;
  end
  else if Item.TagString = 'file' then
  begin

    // Получаем путь до файла
    FileName := Item.ItemData.Detail;

    try
      //Определяем расширение файла и его mime тип
      ExtFile := AnsiLowerCase(StringReplace(TPath.GetExtension(FileName), '.', '',[]));
      mime := TJMimeTypeMap.JavaClass.getSingleton();
      ExtToMime := mime.getMimeTypeFromExtension(StringToJString(ExtFile));

      //Запрашиваем открытие файла
      Intent := TJIntent.Create;
      Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
      Intent.setDataAndType(StrToJURI('file:' + FileName), ExtToMime);
      SharedActivity.startActivity(Intent);
    except
      ShowMessage('Невозможно открыть файл!');
    end;
  end;
end;

{Управляем кнопками}
procedure TForm1.OnOffButton(del, add, copy, cut: boolean);
begin
    SpeedButton2.Enabled := del; // кнопка Удаления
    SpeedButton7.Enabled := add; // кнопка Вставить
    SpeedButton8.Enabled := copy; // кнопка Копировать
    SpeedButton9.Enabled := cut; // кнопка Вырезать
end;

{Кнопка назад}
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  // Определяем предыдущую папку
  path := ExtractFileDir(path);

  if path = '/' then path := '/'
  else path := path;

  TotalWork(path, True);

  if Length(ItemsCheck) <> 0 then begin
    OnOffButton(False, True, False, False);
  end
  else begin
    OnOffButton(False, False, False, False);
  end;
end;

{Кнопка удалить}
procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  i: integer; // Устанавливаем счётчик
  LItemPath: string;
  msg: integer;
begin
  msg := MessageDlg('Удалить выбранные файлы?', System.UITypes.TMsgDlgType.mtConfirmation,
  [
    System.UITypes.TMsgDlgBtn.mbYes,
    System.UITypes.TMsgDlgBtn.mbNo
  ], 0);

  if msg = mrYes then
  begin

    for i := 0 to Length(ItemsCheck) - 1 do
    begin

      if ItemsCheck[i][0] <> '' then
      begin
        // Получаем путь из массива
        LItemPath := ItemsCheck[i][2];

        if ItemsCheck[i][1] = 'folder' then begin

          if TDirectory.Exists(LItemPath) then
          begin
            TDirectory.Delete(LItemPath, True); // Удаляем папку и подпапки
          end;

        end
        else if ItemsCheck[i][1] = 'file' then begin

          if TFile.Exists(LItemPath) then
          begin
            TFile.Delete(LItemPath); // Удаляем файл
          end;

        end;
      end;
    end;
    TotalWork(path, True); // Обновляем список
    ListBox1ChangeCheck(Self); // Чистим массив и т.д.
  end;
end;

{Кнопка для создания файла - выводим окно для ввода имени файла}
procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
  DialogTitle.Text := 'Введите имя файла';
  DialogEdit.TagString := 'CreateFile';
  DialogFon.Visible := True;
  Dialog.Visible := True;
end;

{Кнопка для создания папки - выводим окно для ввода имени папки}
procedure TForm1.SpeedButton4Click(Sender: TObject);
begin
  DialogTitle.Text := 'Введите имя папки';
  DialogEdit.TagString := 'CreateFolder';
  DialogFon.Visible := True;
  Dialog.Visible := True;
end;

{Кастомное окно - кнопка Отмена - "закрываем" окно}
procedure TForm1.SpeedButton5Click(Sender: TObject);
begin
  DialogEdit.Text := '';
  DialogError.Text := '';
  Dialog.Visible := False;
  DialogFon.Visible := False;
end;

{Кастомное окно - кнопка Создать - создаём папку или файл}
procedure TForm1.SpeedButton6Click(Sender: TObject);
var
  newpath: string;
  newfile: TFileStream;
begin

  if (Length(DialogEdit.Text) = 0) OR (DialogEdit.Text = ' ') then begin
    DialogError.Text := 'Введите имя!';
    Exit;
  end;

  newpath := path + PathDelim + DialogEdit.Text;

  if DialogEdit.TagString = 'CreateFile' then
  begin
    if TFile.Exists(newpath) then begin
      ShowMessage('Файл существует!');
    end
    else begin
      newfile := TFile.Create(newpath); // Создаём файл(поток)
      newfile.Free; // Очищаем поток
      SpeedButton5Click(Self); // Закрываем окно
      TotalWork(path, True); // Обновляем список
    end;
  end
  else if DialogEdit.TagString = 'CreateFolder' then
  begin
    if TDirectory.Exists(newpath) then begin
      ShowMessage('Папка существует!');
    end
    else begin
      TDirectory.CreateDirectory(newpath); // Создаём папку
      SpeedButton5Click(Self); // Закрываем окно
      TotalWork(path, True); // Обновляем список
    end;
  end;
end;

{кнопка Вставить}
procedure TForm1.SpeedButton7Click(Sender: TObject);
var
  i: integer; // Устанавливаем счётчик
  LItemPath: string; // Старый путь до файла или папки
begin

  for i := 0 to Length(ItemsCheck) - 1 do
  begin

    if ItemsCheck[i][0]<>'' then
    begin
      // Получаем путь из массива
      LItemPath := ItemsCheck[i][2];

      if ItemsCheck[i][1] = 'folder' then begin
        try
          if SpeedButton9.Tag <> 1 then begin
            TDirectory.Copy(LItemPath, path + PathDelim + ItemsCheck[i][0]); // Копируем папку
          end
          else begin
            TDirectory.Move(LItemPath, path + PathDelim + ItemsCheck[i][0]); // Перемещаем папку
          end;
        except
          ShowMessage('Копирование папки ' + ItemsCheck[i][0] + ' не возможно!');
        end;
      end
      else if ItemsCheck[i][1] = 'file' then begin
        try
          if SpeedButton9.Tag <> 1 then begin
            TFile.Copy(LItemPath, path + PathDelim + ItemsCheck[i][0], True); // Копируем файл
          end
          else begin
            TFile.Move(LItemPath, path + PathDelim + ItemsCheck[i][0]); // Перемещаем файл
          end;
        except
          ShowMessage('Копирование файла ' + ItemsCheck[i][0] + ' не возможно!');
        end;
      end;
    end;
  end;
  SpeedButton9.Tag := 0;
  TotalWork(path, True); // Обновляем список
  ListBox1ChangeCheck(Self); // Чистим массив и т.д.
end;

{кнопка Копировать}
procedure TForm1.SpeedButton8Click(Sender: TObject);
begin
  if Length(ItemsCheck) <> 0 then
  begin
    ShowMessage('Скопировали в буфер, перейдите в другую папку для вставки объектов');
  end;
end;

{кнопка Вырезать/перместить}
procedure TForm1.SpeedButton9Click(Sender: TObject);
begin
  if Length(ItemsCheck) <> 0 then
  begin
    SpeedButton9.Tag := 1;
    ShowMessage('Вырезали в буфер, перейдите в другую папку для вставки объектов');
  end;
end;

{Основа для обработки перемещений по папкам}
procedure TForm1.TotalWork(path_tr: string; clear: boolean);
var
  folders, files: TStringDynArray;
begin

  // Выводим текущий путь
  Label1.Text := path_tr;

//*****Папки*****
  // Ищем папки
  folders := TDirectory.GetDirectories(path_tr);

  // Сортируем папки
  TArray.Sort(folders, TComparer.Construct(CompareLowerStr));

  if clear then begin
    // Очищаем Листбокс
    ListBox1.Clear;
  end;

  // Заполняем Листбокс списком отсортированных папок
  AddListItem(folders, 'folder');
//***************

//*****Файлы*****
  // Ищем файлы
  files := TDirectory.GetFiles(path_tr);

  // Сортируем файлы
  TArray.Sort(files, TComparer.Construct(CompareLowerStr));

  // Дополняем Листбокс списком отсортированных файлов
  AddListItem(files, 'file');
//***************

end;

end.

Скриншоты:



Видео:
Новая версия приложения

Старая версия приложения


Когда посвящаешь себя полностью делу, которое нравится, начинаешь параллельно думать, как и что можно улучшить/сделать лучше.
Вот у меня так и произошло, пока дописывал основной функционал (Копирование/перемещение) приложения, в голове параллельно появлялись новые мысли по улучшению приложения, поэтому пришлось создать текстовый документ, в который записывалась каждая пришедшая мысль. В итоге, когда дописал основной функционал, в текстовом документе было уже 12 строчек, в этой версии я реализовал 4 мысли, некоторые идеи пока даже не знаю как реализовать, но с ними приложение стало бы ещё немного дружелюбнее/удобнее. Возможно, будет ещё одна часть, зависит от вашего мнения/лайков.



Исходный код (Delphi XE5 Update 2): Скачать с Google Drive
Исходный код (Delphi XE6): Скачать с Google Drive
APK файл для установки на ваш девайс: Скачать с Google Drive
Иконки: Скачать с Google Drive

На этом всё. Спасибо за внимание.
Надеюсь, вам понравилась/пригодится данная статья, приложение, иконки и т.д.

p.s.1 Пока добавлял пост заметил, что количество строк выросло с 258 до 551,  можно уменьшить и сохранить при этом читабельность кода, но в моих целях этого пока не было.


17 комментариев:

  1. Хорошее получилось приложение. Прогресс на лицо. Все основные функции файлового менеджера сделали. Судя по видео работает достойно и бесперебойно.

    Есть парочка советов, если я все правильно понял в вашем коде, но это вам решать:
    1. Когда отображается всплывающее окно ввода названия папки и вводите текст, то клавиатура закрывает кнопки. Решить можно, перекрыв событие у формы OnShowVirtualKeyboard, в событие приходит размеры и положение виртуальной клавиатуры, на основании этих данных можно сдвигать выпадающее окно. Для совсем красоты, с анимацией...
    По коду:
    1. Не стесняйтесь называть кнопки своими именами, это сделает ваш код намного читабельнее.
    2. Посмотрите в сторону использования TAсtionList, там есть свои уже готовые механизмы для обновления состояний кнопок (Enable). И можно будет вынести весь код Так же с ними можно будет легко адаптировать свое приложение под планшет. И с ними можно избежать таких вызовов "SpeedButton5Click(Self)"
    Ну и прочие мелочи.

    P.S. Большой молодец. Хороший пост, подробно расписано. Даже видео снято, особенно удобно. Все исходники есть. В общем есть все, что нужно для обучения :)

    ОтветитьУдалить
  2. За отзыв спасибо!
    По поводу советов, всё очень кстати, клавиатуру и имена кнопок задумывал сделать в следующей версии, а с TAсtionList обязательно ознакомлюсь. У меня в списке ещё много подобных изменений, с которыми приложение стало бы лучше. Возможности FireMonkey настолько широки, что сразу даже и не получается во всём разобраться :)

    p.s. Спасибо разработчикам, что продолжаете развивать этот продукт!

    ОтветитьУдалить
  3. Можете писать про БД клиент серверный прием заказ иттд через андроид

    ОтветитьУдалить
  4. Этот комментарий был удален автором.

    ОтветитьУдалить
  5. А без этого никак нельзя?
    ItemsCheck: array of array of string;

    ОтветитьУдалить
    Ответы
    1. Знаете способ лучше? Поделитесь.
      Чем вообще не устраивает массив?
      Сейчас я занят, чтобы придумать способ лучше…

      Удалить
    2. Если все что надо уже хранится в Листбоксе. Какой смысл перегонять вагоны.

      Удалить
    3. При переходах из папки в папку, галочки сбрасываются, соответственно мы уже ни как не узнаем какие итемы нужно перенести в другую папку. Также массив позволяет более точно управлять активностью кнопок, если массив пуст, значит и часть кнопок не работает.
      Повторюсь, если у вас есть предложения лучше, пишите. Т.к. я собираюсь всё-таки выпустить 3 статью, но используя ListView, скорее всего новый файловый менеджер будет многим отличаться от этой версии.

      Удалить
  6. Привет. Спасибо за проектом. Было бы вопрос?

    - data/app папка без ROOT по тому, как вы знаете?
    - Процесс ROOT или как мы это делаем?

    ОтветитьУдалить
  7. Жду третью часть про файловый менеджер

    ОтветитьУдалить
  8. Помогите пожалуйста!!!! компилятор ругается на StringToJString, на StrToJURI и на SharedActivity.startActivity....

    ОтветитьУдалить
    Ответы
    1. Androidapi.JNI.Util в USES прописать нужно, если не ошибаюсь...

      Удалить
  9. Добрый день, подскажите пожалуйста где можно прочитать про файловый менеджер для сохранения или открытия файла определенного формата .csv, нужно для программы сделать, чтобы можно было открывать или сохранять таблицу

    ОтветитьУдалить
    Ответы
    1. Добрый.
      Готового компонента нет, всё придётся писать самостоятельно.

      Удалить
  10. А как реализовать иконки для типов файлов? Если какой встроенный механизм в андроиде или надо все с нуля писать и таскать с приложением базу соответствующих картинок? Заранее благодарю за ответ.

    ОтветитьУдалить
    Ответы
    1. Думаю, придётся таскать с собой свои иконки и подставлять их после определения Mime-типа файла.
      В принципе неплохой вариант, иконок не так много понадобится, сделать для всех основных типов. Музыка(все форматы) - 1 иконка, картинки - 1 иконка или вообще предпросмотр и т.д.
      Если глянуть внутренности у ES Проводника, то иконки носят с собой, по той же схеме, что я описал выше.

      Удалить