среда, 30 апреля 2014 г.

WarTest - компаратор для WarThunder


После недавнего размышления про странности события Compare мой калькулятор для самолётиков назвали компаратором и попросили скриншотов. Мне очень понравилось слово "компаратор" и я решил и скриншотов накидать, и даже снять ролик.


Скачать текущую версию тут: https://www.dropbox.com/s/hzabel4nb6gsu0j/WarTest.apk



А вот и кино:




ЗЫ: В следующем сообщении я описал, как пробовал продать этот компаратор через Яндекс.

вторник, 29 апреля 2014 г.

Как я обустроил TNumberBox

Я уже как-то жаловался на тяжёлую жизнь с TNumberBox. Три основные проблемы это:
1.Значение Value и OnChange живут отдельно от происходящего в поле ввода.
2.При тыкании пальцем в контрол число может внезапно поменяться.
3.При вводе цифр в нулевое поле ноль так и остаётся в начале числа.

Но что делать? Отказаться от имеющегося, написать и регистрировать свой компонент? Я настолько глуп и ленив, что мне это всё трудно, и нужно что-то совершенно простое. Например:

unit MyNumberBox;
 
interface
 
uses System.Classes, FMX.Edit;
 
type
  TNumberBox = class( FMX.Edit.TNumberBox )
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure DoChangeTracking; override;
  end;
 
implementation
uses
  System.SysUtils, System.Character;
 
{ TNumberBox }
 
procedure TNumberBox.DoChangeTracking;
var
  S : String;
  I : Integer;
  V : Single;
begin
  S := Text;
  if not S.IsEmpty then begin
    I := Low( S );
    while ( S[ I ] = '0' ) and S[ I + 1 ].IsDigit do
      Inc( I );
    if I <> Low( S ) then
      Text := S.Substring( I - Low( S ) );
  end;
  if TryTextToValue( Text, V, Value ) then
    if not SameStr( FloatToStr( V ), FloatToStr( Value ) ) then
      Value := V;
  inherited;
end;
 
procedure TNumberBox.MouseMove(Shift: TShiftState; X, Y: Single);
type
  PClass = ^TClass;
var
  ClassOld: TClass;
begin
  ClassOld := PClass(Self)^;
  PClass(Self)^ := TCustomEditBox;
  try
    MouseMove( Shift, X, Y );
  finally
    PClass(Self)^ := ClassOld;
  end;
end;
 
end.

Это такая подмена-заглушка, которая не требует никаких настроек среды разработки и никак не мешает в дизайн-тайме, а действует автоматически во время выполнения. DoChangeTracking выполняет обрезку лидирующего нуля (достаточно хорошо для меня) и проверяет изменения в Value. MouseMove перекрывает обработку скрола ( всего-то навсего можно было сохранять значение ноль в вертикальной прокрутке, но я глуп, и не знаю как это делать ) и на всякий случай (на всякий случай, да) вызывает "дедушкин" метод-обработчик. Вот и всё.

Теперь вставляем в интерфейсный uses наш перехватчик, чтобы он сидел в конце и переопределял тип TNumberBox.
unit uMain;
 
interface
 
uses
...
  FMX.ListBox, FMX.StdCtrls, FMX.Layouts, FMX.Edit, System.Actions, FMX.ActnList,
  MyNumberBox;
 
type
  TForm2 = class(TForm)
...
    ItemDenominator: TNumberBox;
...
 
Я использую XE5 и у меня всё работает замечательно. А как обстоят дела у вас? 

понедельник, 28 апреля 2014 г.

Как выбрать наихудший самолёт? Или про странности события Compare.


Когда есть дома свободных минут 10-20 я люблю запустить игру про самолётики и поучаствовать в сражении или двух или даже на всю ночь. Есть такой грех. Ну так вот, (извините за длинное предложение) в каждом сражении я выбираю одну из пяти стран, у каждой из которых несколько экипажей, каждый из которых использует один самолёт из кучи уникальных самолётов, полученных за накопление очков опыта и прочего вознаграждения.  И когда я получаю очередной новый самолёт, то возникает вопрос - какой из старых самолётов заменить на новую модель? Т.е. какой из действующих самолётов наименее эффективен?

Очевидно, что нужно сделать мобильное приложение, чтобы когда на экране компьютера видна игровая статистика можно было бы использовать в качестве калькулятора телефон. Но просто калькулятора недостаточно. Нужно сделать список машин, который можно сортировать по отношению добытых конкретным самолётом богатств к числу катастроф этого самолёта. Для вычисления такого показателя достаточно двух чисел, которые не нужно(!) хранить. Нужно хранить только вычисленное для сортировки значение. Самое сложное - отказаться от создания многофункциональной системы анализа, и оставить только простой список с сортировкой либо по имени, либо по одному показателю. Но всё-таки я не устоял перед соблазном, и добавил для фильтрации списка страну и признак архивности, чтобы выбывших из полётов не убивать совсем (я противник смертной казни), но убрать с глаз долой. Общий вид приложения такой:


Я установил TListBox.Sorted = True, которое вызовет событие OnCompare в момент  TListBox.EndUpdate. Причём при сортировке перед каждым вызовом OnCompare уже успевает отработать стандартное сравнение по TListBoxItem.Text, и результат этого сравнения даётся нам в Result. Если нам это и нужно, то делать уже ничего не надо. Если потребуется сравнение по отношению зарплата/гибель, то нужно изменить Result, воспользовавшись Item.TagFloat, где хранится вычисленное ранее значение. Ничего сложного? Вроде бы да, и я написал так:

  if tacSort.TabIndex > 0 then
    Result := 1 - 2*Byte( Item1.TagFloat > Item2.TagFloat );
 
И вот! Вот из-за этого всё и стало падать! Причём падает не здесь, а в FMX.ListBox.CompareListItem на Item1 is TListBoxItem. Странно, да? Т.е. когда у нас TabIndex = 0, то Result остаётся без изменений и всё хорошо, а когда переключаемся на "Рейтинг", то начинаются неприятности.



Я поясню, пожалуй, свою математику. Берём данные нам алгоритмом сортировки два предмета, сравниваем кто больше, получаем при приведении к байту 0 (False) или 1 (True), что даёт нам либо 1 = 1 - 2*0, либо -1 = 1 - 2*1.

Разумеется, догадаться, что основная причина сбоя программы - это "сам дурак", было не трудно. Дело в том, что алгоритм сортировки подсовывает между делом нам для сравнения в качестве Item1 и Item2 - один и тот же объект! И получается, что каждый элемент никогда не равен сам себе, и этого алгоритм выдержать не может. Он сам не знает и знать не желает, что сравнивается что-то с самим собой. И достаточно дополнить процедуру сравнения условием равенства Item1= Item2, как всё становится хорошо.

  if tacSort.TabIndex > 0 then
  if Item1 <> Item2 then
    Result := 1 - 2*Byte( Item1.TagFloat > Item2.TagFloat )
 else
   Result := 0;
 
Боже мой! Что это за глупость такая? Ясно же как день, что каждый предмет равен сам себе! Для чего же тогда вызывать лишний раз событие? Бред какой-то! Я негодовал. Но проверив VCL мне пришлось смириться с судьбой - там всё то же самое. Ужас.

Я несколько расстроен своим детским  открытием. И как это мне удавалось много лет счастливо жить, не зная таких элементарных вещей? Мне кажется, Quality Central будет надо мной громко смеяться, если я подниму этот вопрос. Мне грустно. Солнце перестало светить в моё окно. Надеюсь, читающие мимо друзья найдут пару слов мне в утешение.

ЗЫ: апк тут: https://www.dropbox.com/s/hzabel4nb6gsu0j/WarTest.apk
Cкриншоты и даже ролик тут: http://alhymov.blogspot.ru/2014/04/wartest-warthunder.html.

понедельник, 7 апреля 2014 г.

Как заставить TPath рисовать график

Задача простая - отобразить простой график. TPath - вот идеальный кандидат! Стоит ему только дать точек, и он сам всё прекрасно отмасштабирует! Ура, ура, ура!

Но оказалось, что ...

Вот простой контрол, который вы можете бросить себе на форму:

object Path1: TPath
  Align = alClient
  Data.Path = {
    0700000000000000000000009A99193E010000000000803FC3F5A83E01000000
    00000040AE47E13E0100000000004040000000000100000000008040CDCC4C3F
    010000000000A040000000C001000000000040410000A040}
  Fill.Kind = bkNone
  HitTest = False
  RotationAngle = 180.000000000000000000
  Stroke.Color = claRed
  Stroke.Thickness = 5.000000000000000000
  Stroke.Cap = scRound
  Stroke.Join = sjRound
end
 
Я задал в Инспекторе несколько точек, изобразив некий, как мне кажется, красивый график. Здорово? Великолепно! Но, не забудьте, что ордината ( ось Y ) направлена вниз. Поэтому координаты по Y следует задавать в отрицательном виде. Мне в моей программе  показалось удобнее задавать в минусах абсциссу. Поэтому я развернул контрол на 180 градусов и минусую координаты X. Вот как это происходит:

var
  PathMax   : Single;
  PathMin   : Single;
  I         : Integer;
  P         : TPointF;
begin
 ...
    Path1.Data.Clear;
    P :=  LI2PointF( 0 );
 ...
PathMax := P.Y; PathMin := PathMax;   with Path1.Data do begin MoveTo( P ); for I := 1 to List1.Count - 1 do begin P := LI2PointF( I ); if P.Y > PathMax then PathMax := P.Y else if P.Y < PathMin then PathMin := P.Y; LineTo( P ); end; end;
 ...
Здесь LI2PointF - ф-я, возвращающая очередную точку. Причём, поделюсь страшным секретом: ордината у меня - дата TDateTime, приведённая к -Double. Да, всё так просто. Не просто только оказалось увидеть график в "бегущем" приложении. Но почему?

А что нам советуют профессионалы? Если посмотреть WebDelphi или Всеволода Леонова, то похоже, что следует использовать TPath.Data(:TPathData).Data - строку SVG. Надо сформировать скрипт типа 'M 227 239 L 328 90 L 346 250' и присвоить. И знаете что? Оно работает!

А если посмотреть в недавно вышедшую книгу Дмитрия Осипова, то кажется, что надо использовать методы MoveTo и LineTo. А я так и делал. Но стоит посмотреть на представленный в книге код по отрисовке уже заданной траектории (так можно перевести "Path"), как желание пользоваться методами начинает пропадать.

А чем плох SVG? Да собственно, нельзя сказать, что он плох. Но не кажется ли вам, что использование SVG - это вырезание гланд через анальное отверстие? Собирать скрипт - это, как ни крути - отдельная мелодия, на которую нужны таки свои инструменты - костанты команд, форматирование чисел, хранение и конкатенация строк. А потом этот свежеиспечённый скрипт придётся нашему компоненту парсить, и применять все те методы отрисовки, которые и так нам даны напрямую!

Да что же не так с этими методами? Почему они не рисуют, как полагается? Я искал какой-нибудь Begin-End или Update и нашёл ClosePath. С ним вдруг всё стало рисоваться. И даже больше, чем всё - стало рисоваться ещё линия, замыкающая последнюю и первую точки. Мне не нужна эта линия, но, ведь, остальное всё рисуется же! Что же там такого, чего нет, скажем, в AddEllipse? А там, как и в том же SetPathString, который вызывается при Data := SVG, в конце метода скромно стоит   if Assigned(FOnChanged) then    FOnChanged(Self);

Вот оно что! Ну, осталось только посмотреть подробности и вытащить метод DoChanged из protected. Всё, можно рисовать:

type
  TMyPath = class ( FMX.Objects.TPath )
//  public
//    procedure DoChanged(Sender: TObject);
  end;
...
          LineTo( P );
        end;
      end;
      TMyPath( Path1 ).DoChanged( nil );
 
      laPathMax.Text := Format( '%.3f', [ PathMax ] );
      laPathMin.Text := Format( '%.3f', [ PathMin ] );
  end;
...



понедельник, 31 марта 2014 г.

Как жить с таким TNumberBox?

Очень мне хотелось бы поговорить о TNumberBox. С одной стороны - чудо-контрол, который нам должен давать в установленных пределах готовое число, а с другой стороны - что-то он работает не слишком предсказуемо. 


Сначала обсудим, что в нём вкусного. Во-первых, он нацелен на цифровую виртуальную клавиатуру. Это хорошо. А то ужас как неудобно устанавливать KeyboardType в vktNumberPad. Шучу, конечно. Но ведь в самом деле, вряд ли только из-за этого нужен дополнительный контрол. Поэтому смотрим, что там во-вторых.

Во вторых, он обрабатывает вертикальный и горизонтальный жесты, позволяя менять численное значение без ввода цифр. Да и мышь в Windows (и, наверно, на Mac ) тоже прекрасно работает. Причём мы можем отдельно регулировать скорость приращения по вертикали и по горизонтали! Допустим, вдоль длинной горизонтали меняется целая часть числа, а вдоль короткой вертикали - десятые доли. Такого поведения в VCL не было!

В третьих, ЧисловойКоробок способен форматировать ввод и обеспечивать валидный (от франц. valide - законный, действующий) результат ввода в виде численного значения Value. Для форматирования и валидации у нас есть количество точек после запятой и диапазон значений Min и Max. Вот это уже кажется настоящим делом для нового контрола.


А теперь рассмотрим некоторые сложности. Обратите внимание - Min и Max = 0..100. Вас устроит такое ограничение? А если вообще ограничения не нужны, если нужен диапазон "по-максимуму"? Установка 0..0 по моей логике должна бы отменить всякие ограничения, но - нет. Стоит отвернуться, отвлечься и нажать даже не другой контрол, а всего лишь какую-нибудь "не такую" клавишу, как значение в поле редактирования сбрасывается в 0. Вот беда! Да ладно, не беда! - скажем мы, посмотрим в справкепосмотрим в справке ещё и найдём диапазон Single = 1.5e-45..3.4e+38 и ... Увы, Object Inspector не понимает таких значений. Я лично оставляю -1E38..1E38. Если переборщить, то мы даже можем увидеть нехорошее слово из трёх букв - INF (я думаю, что это Infinity - бесконечность). Нехороши эти буквы потому, что вместо обещания Infinity они не только не дадут нам диапазон -∞↔+∞, но и не дадут запустить программу, и, что особенно неприятно, не дадут загрузить  в следующий раз саму форму, если нечаянно записать её с этими буквами вместо цифр. Придётся открывать <unit>.FMX в текстовом редакторе и менять буквы на на 0. При этом не помешает соблюдать осторожность, чтобы не споткнуться о какой-нибудь [Inf]o или Ma[inF]orm.

Теперь жесты. Казалось бы - вот здорово! А оказалось - а вот и не очень. Если поле ввода перекрывается всплывающей клавиатурой, то что надо сделать с полем? Да, мы его двигаем вверх, да ещё и, как real programmers, с анимацией. А пока мы двигаем поле, NumberBox отслеживает точку нажатия относительно своих границ. Т.е. он думает, что это не он перемещается, а что это мы двигаем пальцем! Он меняет значение, хотя палец просто ткнулся и стоит там, где только что было поле ввода. Мне кажется, что когда программа вдруг меняет значение, которое должен менять лично сам пользователь, то это доверия пользователя к программе совсем не добавляет. Это какая-то ерунда. Но тогда, может, ну их - жесты эти - к лешему? Мы как-то и без них раньше обходились. Верно? К чему тут виски напрягать да копья ломать? Возьмём просто и отключим их! Это же кажется легче лёгкого - достаточно установить HorizontalIncrement и/или VerticalIncrement в 0. Но увы и снова увы - после запуска (!) приложения мы обнаруживаем, в VerticalIncrement что 0 заменяется дефолтным 5! Когда я столкнулся с этим мне почему-то вспомнилось классическое "Братья и сёстры! Это ли не чудо?!" Я пробовал организовать чудоборство запоминанием в TagString текста контрола перед каждым FloatAnimation1.Start и, соответственно, восстановлением на OnFinish анимации. Но - увы, ничего хорошего не получилось. Кстати, что мешало сделать TAnimation.OnStart? Если кто знает как уведомить EMBT о целесообразности этого события, напишите, пожалуйста.

И форматирование ввода - тоже не слишком похоже на ожидаемое. Когда при значении 0 вводится какая-то цифра, то вводится следующая цифра! Что это за число такое "008,9" или "+00"? Что это за левые (во всех смыслах) нолики? Может, это мода такая новая? Мне  лично эта мода кажется ругательной, и я прошу её ко мне не применять! Поэтому я вешаюсь на ChangeTracking и режу первый символ, если он 0 и при этом второй - цифра (минус бы тоже учесть можно, но это уже для гурманов). Такую ерунду подвешивать к каждому полю - это разве дело? Но иначе я на поле ввода числа смотреть не могу. Чес-сло, я уже с теплом начинаю вспоминать TMaskEdit. И ещё вопрос у меня - а кому там нужен плюсик? Ведь любое ненулевое число без минуса - уже плюс! Просветите меня, если можете. Мне кажется, я какой-то малопрозрачный.

А как апофеоз моих исследований - свойство Value. Я-то думал, что стоит ValueType установить в vtFloat, так сразу буду получать введённое пользователем значение типа Single. Ну да, кто-то кое-где у нас порой этого не хочет, ему подавай Int64, а Value - только Single. Но это уже не так важно, как то, что не получится получить значение "сразу". Я имею в виду, что если по какой-то причине вы захотите получить значение Value сразу, как только пользователь нажмёт Enter (Ну и что же тут криминального, если в OnKeyDown установить if Key = vkReturn then AddValue?), то скорее всего в Value будет что-то такое, что напоминает число в свойстве Text очень и очень отдалённо. Вешаться же на OnChange для того, чтобы отследить нажатие Enter на виртуальной клавиатуре я вам не советую - оно срабатывает совсем не только на Enter и совсем не всегда на Enter. Слава Богу, что он вдохновил программиста (или его начальника) вынести в интерфейс модуля FMX.Edit процедуру TryTextToValue, которую я использую сам, и рекомендую вам. Она запускает старый (как время летит!) добрый TryStrToFloat, которому входная строка причёсывается по пробелам и десятичному сепаратору. В случае неудачи возвращается дефолтное значение. Вот спасибо!

Эпилог

TNumberBox, не смотря на далеко не первую версию платформы FM,  выглядит довольно сыро. Возможно, это связано с небольшим опытом его применения программистами. Возможно,  это связано с небольшим количеством обращений программистов в qc.embarcadero.com. Я лично (это моя стандартная отговорка) плохо формулирую свои мысли даже на русском. А то, что получилось сложить - слишком многословно, чтобы внятно перевести на английский. Если вы чувствуете в себе силы написать о проблемах этого контрола в EMBT, то прошу вас, напишите. Если вы можете что-то подсказать мне по применению этого контрола - тоже пишите.

Спасибо за внимание!
Всем всего доброго!

ЗЫ: Спустя некоторое время я стал использовать специальную заглушку-перехватчик, чтобы обойти основные проблемы контрола. http://alhymov.blogspot.ru/2014/04/tnumberbox.html

вторник, 11 февраля 2014 г.

Помоги зоопарку. Samsung GALAXY Note 10.1,Fly IQ440 Energie,МТС 970H,Samsung Galaxy S III GT-I9300

Широко известный в наших кругах Всеводод Леонов обратился к общественности с манифестом Спаси Delphi, помоги зоопарку! Я уже писал ранее про Nexus 4. В ответ на призыв я отправил ему несколько заметок, некоторые из которых он опубликовал. Также Всеволод мне написал, что ничего плохого не случится, если я помещу свои заметки в своём блоге. Так красиво оформлять заметки, как он, я ленюсь, но думаю, что больших неудобств Читателю это не доставит.  Итак:

Samsung GALAXY Note 10.1 (http://www.samsung.com/ru/consumer/mobile-devices/tablet-pc-slate-pc/samsung-galaxy-note/GT-N8000EAASER-gallery)


Android 4.1.2. Купил в позапрошлом году  планшет супруге на ДР. Обошёлся с картой памяти примерно в 25 килорубля + чехол. Колебался - iPad или Android? Выбрал по цифрам самый мощный Андроид. Яблоки мне казались снобизмом, пугали я-мелодии, отсутствие поддержки Flash, отсутствие SD-карточки, платность всего-привсего, странность поведения поклонников торговой марки. 

После приобретения был неприятно поражён стоимостью чехлов и непродуманностью их конструкций. Оказалось, что планшет не поддерживает Flash! К тому же ещё и кабель имеет проприетарный разъём. Кнопки по периметру расположены неудачно - легко случайно выключить, взяв устройство за угол. По сравнению с яблоком - тяжёл, экран проигрывает в качестве. Гориллное стекло в женской сумке без чехла быстро получило заметную царапину от ключей. Рекламируемый стилус терялся пару раз и в настоящее время сидит в своём гнезде и практически не используется. Детям купил уже яблочные дощечки.


Аппарат был подключен к Win7 и сразу оснащён ПО Kies от производителя ещё до установки Делфи. Когда настала пора - сразу стал в работу. Но! При переходе на другой комп(Win8) работать с отладчиком отказался. Кто виноват? - Делфи? Win8? USB3? Оказалось, что если поставить Kies, то и тут всё работает - кто бы мог подумать?!


Fly IQ440 Energie (http://www.fly-phone.com/devices/smartphones/iq440_energie/?sphrase_id=56721)


Андроид 4.0.4. Это мой первый смартфон. Я посмотрел, как люди постоянно держат на верёвке свои аппараты, и решил взять повышенной ёмкости. К тому же, для меня было важно иметь 2 симки. Удалось уложиться в 10 килорублей вместе с картой на 32 Гига.


Аппарат действительно может целый день активно использоваться. Неактивно может легко провести выходные. Весьма тяжёл. Скоростью не балует. Фронтальная камера так плоха, что в помещении трудно считать штрих- или QR-код. Разъём под провод сбоку мешает разместить аппарат в моём автомобильном держателе. Плюс (точнее - минус) разъём довольно скоро хрустнул, и теперь провод слегка люфтит, что несколько тревожит, но пока не привело к каким-то проблемам. Большая проблема - подобрать гарнитуру: многие "пробки" шумят, тикают и стучат при прослушивании, а прилагаемые в комплекте свои - без резинок, в ушах не держаться.

Сначала аппарат был не виден, и я не знал почему - это был мой первый опыт с телефоном. У меня тогда оказался ещё другой аппарат (планшет жены), для которого я стал компилить программу, а потом просто заливал приложение на Fly и запускал. С Делфи телефон заработал через пару недель ВНЕЗАПНО! По ощущениям :) дело в том, что приходили люди с HTC-аппаратом и проставили свой софт. Увы, ничего подробнее сказать не могу - теперь Муха прекрасно себя чувствует и на Win7, и на Win8.


МТС 970H (http://www.shop.mts.ru/smartfony/mts/smartfon-970-black-dlya-raboty-v-seti-mts.html)


Андроид 4.1.1. Куплен ребёнку задёшево - меньше 3 килорублей. Вполне себя оправдывает. Народ интересуется - чё так дёшево? Глючный? Тормозной? Ни разу глюков и тормозов. Очень лёгкая машинка как по весу, так и по отзывчивости. По-шустрее моей энергичной мухи iq440, да и легче раза в два.

Дешевизна сказывается на экране - инерционность и узкий угол обзора. Не слишком приятно листать списки и смотреть фотки, особенно после iPad Air. Но, скажем, в Cut the Rope играть вполне можно. А небольшой размер - даже плюс для людей с пальцами менее 5":)


Сначала был не виден, и я уже знал почему - нужен драйвер. Почти первая ссылка в поисковике отправила меня на известный форум, а оттуда я попал на сайт настоящих производителей аппарата - alcatel(http://www.alcatelonetouch.com/global-en/support/faq/usbdriver.html), где, как рекомендовали на форуме, скачал именно ВТОРОЙ драйвер, и все заработало. В общем - всё оказалось довольно легко и быстро. :)

Samsung Galaxy S III GT-I9300 (http://www.samsung.com/ru/consumer/mobile-devices/smart-phones/samsung-galaxy/GT-I9300RWDSER-gallery)


Аппарат не мой, в руках почти не держал, подробностей не скажу. Но запускает мою программу давно и исправно. Мне дали воткнуть шнурок чисто из любопытства - посмотреть, как выглядит отладка в Делфи на Андроиде.

При первом подключении в Делфи был не виден, хотя в диспетчере задач висел как вполне хорошее и нормальное "переносное устройство" GT-I9300. После включения на аппарате режима отладки (да, это тоже камлание - настройки, о телефоне, 10 раз ткнуть в номерок версии, вернуться, зайти в таинственно появившееся программисткое меню, включить отладку) появилось неопределённое Composite Device( Вин8 выдала банер "Составное устройство"). 


Яндекс массово советовал поставить ПО от изготовителя - Kies. Но я уже знаком с этим обеспечением и не хотел его излишней заботы. Поэтому выбрал на (уж не знаю, реклама ли это) сайте http://www.sidenxab.ru USB Android Драйвер. Как только диспетчер устройств снова увидел GT-I9300, Делфи также смогла с ним работать.

Вывод.


Я хочу, чтобы начинающие Андроид-программисты поняли, что "проблема подключения Андроид к Делфи" или "Делфи к Андроид" - не проблема Делфи, а проблема драйверов Windows. Тот, кто впервые сталкивается с этим в Делфи, винит в первую очередь Делфи, также как, например, в своё время винил я. Но на самом деле, если посмотреть вокруг, видно, что эта проблема встречается У ВСЕХ систем разработки, а не конкретно у Делфи. Так что некоторые трудности с андроидными устройствами - это, как говорит Елена Малышева, НОРМА. И не надо расстраиваться, просто надо понимать ситуацию, и знать, куда копать. А копать-то на самом деле особо глубоко - и не нужно. Просто Диспетчер Устройств, просто Драйвер.

Всем удачи!!!

воскресенье, 9 февраля 2014 г.

TestFlightApp - рекомендую

Как раздавать свои мелкие утилитарные йОс-ные(а между прочим, об Андроиде здесь тоже речь будет) приложения? Есть (я буду по-колхозному писать) АпСторе, есть Дебагинг со шнурком, и есть ещё один странный штук - АдХук. 

Первый вариант для нас - не вариант. Это только "продукт" можно в магазин отдать и ждать полмесяца пока его на полку выложат. А могут и не выложить хотя бы потому, что у Яблончан ВНЕЗАПНО изменились какие-то взгляды на жизнь. Програмульку, которую дать попробовать и быстро отзыв получить для дальнейшей модификации, с таким выходом на исполняющие устройства - не сделать никак. 

Отладкой по шнурку тоже не сильно далеко разбежишься. Это надо брать устройство, отвлекать человека, смотреть его фотки, которые в айТюнз при подключении очень хотят выскочить и резервно скопироваться - жутко невежливо. Никто не любит, когда его почти(да можно и без "почти") интимную вещь подключают к чужому компу, когда какой-то электрический провод втыкают в  это милое родное существо, и какой-то хаккер-багописатель пытается туда внедрить свою колхозную самоделку, да ещё и неизвестно, сколько времени это займёт. А надо учесть, что люди с надкусанными яблоками ещё и очень заняты всегда на разных очень важных совещаниях в самых разных местах нашей необъятной Родины(да что там, Планеты!). Короче - всё плохо.

АдХук - вот спасение! Надо взять с устройства идентификатор, сформировать провижинг(кто не знает перевод - я не виноват), выслать программу на устройство, там перенести в айТюнз, и установить. Хм.. - тоже как-то, уже начиная с " взять идентификатор", не очень чтобы просто...

Что же делать? А вот - прошу обратить внимание на чудо-сайт "TestFlightApp.com" - превосходное решение для мелкоштучных поделий! Вы регистрируетесь сами, просите зарегистрироваться своих тестеров, шлёте им приглашения, и когда они принимают эти приглашения, то вам - о, чудо! - безо всяких подключений к посторонним компам сразу приходят идентификаторы тестовых устройств. Осталось только заполнить провижинг на Developer.Apple.Com, чтобы скомпилить-подписать новым провижингом приложение и загрузить свежеиспечённый билд на этот замечательный сайт TestFlightApp. Нет, ещё не совсем всё. Ещё вы на сайте должны отметить, кому дать разрешение на публикуемое приложение. При этом можно попросить сайт отправить тестерам уведомление. Очень удобно.

А как это выглядит со стороны пользователя? Ему приходит от нас письмо с просьбой подтвердить участие в нашем проекте. Он регистрируется, и на столе его устройства появляется значок TestFlight. Это ссылка на сайт,  где можно посмотреть список доступных приложений с описанием. Нетерпеливый пользователь может сам следить за новостями, периодически заглядывая в TestFlight, а нормальные люди просто получают письма счастья от разработчика. И - чудо из чудес! - совсем как в АпСторе - на столе,  стоит только немного подождать пока пройдёт загрузка,  появляется иконка нашего(вашего) приложения. И всё - вперёд и с песней!

Ура! Никаких проводов и ограничений по времени или местности! Это ли не чудо?!

Разумеется, TestFlightApp предлагает дополнительные функции, связанные с тестированием приложений. Но это уже мелочи по сравнению с тем, что мы уже получили.

Теперь пару слов о том, каковы особенности переустановки йОс-приложения через TestFlight. Я слышал жалобы, что при переустановке все накопленные данные, изменения - всё пропадает. Если с Андроидом проблем больших нет - надо просто все изменения класть во внешнее место, то Яблочная песочница нас файловой системой не сильно балует. А как установить приложение через айТюнз, если оно уже стоит? Надо старое удалить. И если что-то совсем специальное не придумать, то всякие данные (настройки, например) - тю-тю. Так вот - TestFlight пишет новую версию программы поверх старой, не затрагивая те файлы, которые получились в результате прикладной деятельности! Т.е. все изменения и дополнения пользователя сохраняются! Здорово?

Будьте осторожны. Делфи-приложение при запуске начинает выделять из себя в песочницу файлы, которые нужны ему в работе. При этом если файл уже существует, то он не будет перезаписан. И вот представьте, что вы положили в деплоймент не просто новую картинку, а  изменённую картинку. Что будет? Да, изменений на устройстве вы не увидите - новая картинка не будет выгружена из пакета приложения. Имейте это в виду. Praemonitus praemunitus!

Итак, желаю всем, кто ещё не изведал прелестей TestFlight,  приятного знакомства!

И, кстати, Андроид-приложения также могут участвовать в процессе тестирования под опекой TestFlightApp.

пятница, 7 февраля 2014 г.

DropBox против файлов

Есть у меня утилитка DeployFolder (http://alhymov.blogspot.ru/2013/12/deployfolder.html), которая текстовый файл меняет. Файл - проект Делфи. Т.е. XML, но без заголовка. И как этот заголовок отрезать, я не знаю (может, кто надоумит?). Поэтому тупо взял, записанный на диск XML всосал в TStringList, ну и, после удаления верхней строки, записал обратно.

Сначала всё было хорошо, а потом - как-то не очень: ругань пошла о занятом файле. Я, поскольку утилитка-то из Дельфи-студии запускается, решил, что Дельфи его как-то блокирует. Причём маленькие проекты - ОК, а большие - плохо. Тогда я стал проект в Дельфи ручками закрывать перед запуском утилиты, и как-бы стало работать. А потом прошло время, и - опять 25!

Ну, понял я - дело не в Делфи. Наверно, в Винде дело - она не успевает от предыдущей записи отблокироваться. Бред, конечно, но мозгный натиск не сразу Измаил берёт.

И вот я так думал-думал, и - придумал! Т.е. догадался! Проект, который "блокируется" - лежит в DropBox-е, и я его там и пользую. А если посмотреть в проводник, то в окошке этом очень хорошо видно, что на файликах значки меняются - DropBox апдэйтит файлы. А когда DropBox апдэйтит файлы, то что? - правильно! - файл блокируется от внешних посягательств.

Э! - сказали мы с Петром Ивановичем (Это я так раздваиваю свою личность иногда) - так надо же самому первому блокировку положить! Но как вспомнишь про Win32 API, так что-то хочется ещё немного подумать.

Решил я мысль свою в блоге отразить, и по ходу изложения (слава блогу!) догадался, что глупый я, однако, совсем! Зачем же я на диск пишу, и потом тут же считываю, если можно в памятный поток всё сложить, и к диску обращаться только однажды!

Вот сколько буковок из себя выдавил. А смысл - избаловали нас ПК, а не всё, что прокатывает в нормальных условиях, прокатывает в распределённой системе, в DropBox-е. Т.е. польза от буковок должна быть. А мыслеметания бредовые свои я здесь изложил, чтобы читатель понял, что он-то гораздо умнее, и было бы ему от этого очень приятно мои буковки читать. Т.е. полезное с приятным. Так, мне кажется, в мире будет больше счастья и радости.

Итого. Было:

    XMLDocument1.SaveToFile( XMLDocument1.FileName );
    XMLDocument1.Active := False;
 
    ShowStatus( 'Set standard header' ) ;
    with TStringList.Create do
    try
      LoadFromFile( XMLDocument1.FileName );
      Delete( 0 );
      SaveToFile( XMLDocument1.FileName );
    finally Free;
    end;
 
Стало:

    Memory := TMemoryStream.Create;
    try
  //    XMLDocument1.SaveToFile( XMLDocument1.FileName );
      XMLDocument1.SaveToStream( Memory );
      XMLDocument1.Active := False;
 
      ShowStatus( 'Set standard header' ) ;
      with TStringList.Create do
      try
//        LoadFromFile( XMLDocument1.FileName );
        Memory.Position := 0;
        LoadFromStream( Memory );
        Delete( 0 );
        SaveToFile( XMLDocument1.FileName );
      finally Free;
      end;
    finally FreeAndNil( Memory );
    end;
 
Жизнь покажет, будет ли теперешнее отсутствие ошибок постоянным. А читателя благодарю за внимание и надеюсь, что грабли сии минуют его, а сам я буду впредь рачительнее использовать вычислительные ресурсы.

суббота, 18 января 2014 г.

Как DelphiFeeds.RU ловит статьи с блогов?

Это вторая статья по одной теме. Предыдущая здесь: http://alhymov.blogspot.ru/2014/01/delphifeeds.html

Итак, делаем вторую статью. На этот раз - с заголовком. Если лента формируется действительно " в режиме реального времени", то я тут же увижу заголовок "Как DelphiFeeds.RU ловит статьи с блогов?" на DelphiFeeds.

Смотрим?
У меня закралось подозрение, что DelphiFeeds.RU не вполне корректно ловит статьи с блогов. А именно - если в моём блоге я забуду указать заголовок, то статья не будет опубликована на сайте DelphiFeeds.RU.

Проверим. Сейчас я сделал первую публикацию - без заголовка. А потом сделаю вторую - с заголовком. Ожидается, что будет опубликована только вторая.

Объясню, почему две статьи, а не 12. Я писал в форму обратной связи на сайте, что одна моя статья не публикуется. Вот текст:

> Это сообщение было отправлено через раздел Контакты сайта http://delphifeeds.ru/:
> Павел <alhymov@mail.ru>
> Всех благ!
http://alhymov.blogspot.ru/2013/12/deployfolder.html
> пятница, 27 декабря 2013 г.
> DeployFolder - Развёртывание мобильного подкаталога
> Предыдущие и последующие статьи в моей категории у вас есть, этой
> нет. Причины технического или политического порядка?

Мне почти сразу ответили: "В ленту попадают последние 2 статьи в режиме реального времени."

Ответ мне не ясен - чьи "последние 2" и за какой период? Кажется, что просто 2 от каждого зарегистрированного блога. Но моих статей в ленте больше. Там даже самый первый пробник. А вот про DeployFolder почему-то нет. Причина, мне кажется, кроется в отсутствии заголовка.

Не буду терзать перепиской администрацию сайта, а просто проверю. Итак - Публикация!

пятница, 17 января 2014 г.

Nexus 4 - хорошо



УРА!! EMBT (при личном участии Всеволода Леонова) прислала мне на работу пакет с призом за конкурс "Осенняя мобилизация". Нет, с ПРИЗАМИ! Я не знаю как вам, а мне - просто СУПЕРСКАЯ футболка!

А теперь о главном: я получил 100% совместимый дивайс! Хорошо, что у него не 2 симки. Иначе мне пришлось бы думать - куда потерять свой свой старый аппарат, чтобы не отдавать жене новый.

Устройство при включении моментально обнаружило, что нужно поднять уровень программного обеспечения до следующей версии желейных бобов, потом перейти к формату Кристофера Кэтлинга, а потом ещё и к формуле 4.4.2. Однако, не смотря на обновления, и якобы 100% совместимости, работать с Delphi аппарат не хотел. Как я расстроился! А надо было найти сайт http://all4nexus.ru/ После всех установок я едва не потерял меню настроек и обнаружил новую заставку при включении. Всё это так интересно!

TLocation действительно работает на этом телефоне. А вот пример Gyroscope так и не хочет крутить кубик - показатели датчика не меняются. Значит, дело уже точно не в машинке.

Здорово, что я не поленился поучаствовать в конкурсе!

среда, 15 января 2014 г.

Nexus 4 - правильное устройство

Ла-ла, ла-ла-ла! Ла-ла, ла-ла-ла!
http://delphimobile.ru/
Ну и кто сомневался в успехе?

Я стал призёром конкурса Emarcadero

ОСЕННЯЯ МОБИЛИЗАЦИЯ

Теперь мне пришлют телефон Nexus 4, официально включённый в список совместимости Delphi XE5 (http://docwiki.embarcadero.com/RADStudio/XE5/en/Android_Devices_Supported_for_Application_Development#Results_of_Our_Android_Device_Testing) как 100% совместимое устройство. Это важно.

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

Я подписан на Андроид-форум (https://forums.embarcadero.com//forum.jspa?forumID=522), где некоторые люди XE5 высоко поднимают вопрос об отсутствии совместимости с "не NEON"-аппаратами. Вероятно, в этой же плоскости лежит вопрос и несовместимости с Андроид-устройствами на процессорах INTEL. Мне уже пришлось столкнуться с этим. Это, естественно, следует принимать во внимание.

Но у корпоративного софта есть такая штука как Технические Условия. И если эти условия пишет Исполнитель (а по большей части так и бывает), то - просто добавь в ТУ нужную строчку, и проблема решена. Если для iOS-программ применять устройства от одной фирмы нормально, то почему не подойти таким же образом к распределённому программно-аппаратному комплексу с мобильными компонентами на основе Android?


Кое-кто говорит (http://www.tdelphiblog.com/2013/10/sovmestimostjandroididelphi.html) о 89%-й совместимости XE5 с Андроид-устройствами. Примерно такой же процент был у платформы Windows 3.x в начале её победного шествия (см. наверху диаграмму, которую мы видели неоднократно на встречах с Embarcadero). И этого было настолько достаточно, что и думать об остальных процентах нужды не было. Я думаю, рынок покажет, насколько сейчас Embarcadero нужно думать об этом. Сейчас они и так уже сделали достаточно и для дум, и для дел.

пятница, 27 декабря 2013 г.

DeployFolder - Развёртывание мобильного подкаталога

DeployFolder - Развёртывание мобильного подкаталога

Автоматическое прописывание пути развёртывания файла для мобильного приложения.


Для конкурса Embarcadero "Осеннее настроение 2013" исходник выложен в папку с программой, разработка которой без DeployFolder была бы совершенно невозможной: https://subversion.assembla.com/svn/who-is-who/trunk/WhoIsWho

Вы можете отдельно взять исходный код программы здесь: https://db.tt/YAOLB40g. Но я не гарантирую, что оставлю его без изменения.


Итак, с чего началось создание программы - с простого вопроса: как добавить файл в проект мобильного приложения? Казалось бы, есть простой ответ: Project|Deployment и Add Files. Так? Не совсем. Вам ещё следует прописать каталог, в который файл попадёт на мобильном устройстве. Доступ к этому файлу в коде будет выглядеть примерно так:

  DataDir :=
{$IF Defined(MSWINDOWS)}
    '..\..'{runing in windows - <Platform>\<Configuration>}
{$ELSE}
    TPath.GetDocumentsPath
{$ENDIF}
    +PathDelim + 'Data' + PathDelim;


В данном случае 'Data' – это имя подпапки, в которой лежат мои файлы для приложения внутри папки проекта. А что значит TPath.GetDirectory – зависит от мобильной системы. Для iOs – это '.\StartUp\Documents', а для Android – ‘.\assets\internal’.





К сожалению, эти константы вы вынуждены вносить вручную для кучи разных конфигураций. Не вздумайте что-нибудь забыть или перепутать – приложение может не найти ваш файл после запуска на устройстве или в эмуляторе! Это не очень удобно, не правда ли? Давайте немного облегчим себе жизнь и заполним пути автоматически. Тем более, если количество добавленных файлов больше одного!


Итак, где хранятся пути развёртывания файла? Нет, не в <ProjectName>.deployproj, а в <ProjectName>.dproj! По правде говоря, сначала я был немного озадачен файлом <deployproj>. С одной стороны – расширение файла «.deployproj» так и просит заглянуть в этот файл. И – да! – там перечислены все те файлы, которые были добавлены в проект. Но с другой стороны - всё, что я менял в <deployproj>, вдруг исчезало самым таинственным образом - мистика! Если вы читаете эти строки, то вы уже не попадётесь в эту ловушку. А теперь давайте посмотрим внутрь <dproj> и сравним его кусочек с таблицей Deployment <ProjectName> в IDE.

                </Excluded_Packages>
            </Delphi.Personality>
            <Platforms>
                <Platform value="Android">True</Platform>
                <Platform value="iOSDevice">True</Platform>
                <Platform value="iOSSimulator" ActiveMobileDevice="iPad">True</Platform>
                <Platform value="Win32">True</Platform>
            </Platforms>
            <Deployment>
                <DeployFile LocalName="Data\896.jpg" Class="File">
                    <Platform Name="Android">
                        <RemoteDir>.\assets\internal\Data\</RemoteDir>
                        <RemoteName>896.jpg</RemoteName>
                    </Platform>
                    <Platform Name="iOSDevice">
                        <RemoteDir>.\StartUp\Documents\Data\</RemoteDir>
                        <RemoteName>896.jpg</RemoteName>
                    </Platform>
                    <Platform Name="Win32">
                        <RemoteName>896.jpg</RemoteName>
                    </Platform>
                    <Platform Name="iOSSimulator">
                        <RemoteDir>.\StartUp\Documents\Data\</RemoteDir>
                        <RemoteName>896.jpg</RemoteName>
                    </Platform>
                </DeployFile>
                <DeployFile LocalName="Data\816.jpg" Class="File">
                    <Platform Name="Android">


Без труда можно заметить, что добавленные нами файлы имеют тип “File”. В зависимости от платформы у файла может быть разный каталог размещения. Зная, что <dproj> - это в сущности XML, мы можем бросить на форму из палитры Internet компонент TXMLDocument, найти узел “\Project\ProjectExtensions\BorlandProject\Deployment” и расставить теги <RemoteDir>, в которых хранятся пути развёртывания наших файлов.


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

 Ещё отмечу, что новый путь я ставлю только для платформ из списка 'Android','iOSDevice','iOSSimulator'.

Стоит также заметить, что после обработки проекта я вставил ещё несколько действий. Во-первых - вы можете смеяться, но я не знаю, как удалить строку XML-заголовка, которая появляется в файле проекта после сохранения его компонентом XMLDocument1. Поэтому, не сильно задумываясь, я опять считываю файл на этот раз в TStringList и удаляю первую строчку. Во-вторых, моя Delphi (наткнулся на это в XE5 trial) не хочет собирать проект, если остался старый deployproj. Что ж, удалить его не сложно.

DeployFolder очень удобно поместить в IDE как инструмент c параметром $PROJECT. Этот инструмент можно вызывать каждый раз, когда вы добавили файлы в развёртывание проекта. Например так:


После обработки новым инструментом файла проекта Delphi сама обнаруживает изменение, и достаточно будет просто согласиться на предложение, чтобы перегрузить проект.


Но, увы - на больших проектах происходит ошибка занятости файла*. Ещё одна проблема - при открытой странице Project|Deployment новые файлы задваиваются. Поэтому я закрываю проект после добавления новых файлов и вызываю инструмент без параметров. Обычно, когда работа идёт над одним и тем же проектом, никаких неудобств не возникает - имя проекта сохраняется.

Кстати. Я стал задумываться, не сделать ли каталогам псевдонимы, например c:\mo\x=\x. Что вы об этом думаете? Есть у вас ещё предложения? Пишите мне – alhymov@mail.ru

И да, ВНИМАНИЕ, УВАГА, ATTENTION, ACHTUNG: При формировании для Андроид списка дополнительных файлов Delphi (или SDK?) формирует список, где имена каталогов в нижнем регистре! Имена самих каталогов в пакете приложения при этом остаются в том регистре, в котором вы написали на вкладке Project|Deployment. При старте приложения на устройстве происходит выгрузка файлов из пакета приложения в "песочницу". И, поскольку имена каталогов в списке и в пакете не совпадают, приложение даже не проходит строчку begin в проекте. Будьте бдительны! А в моей программе это обстоятельство уже учитывается.
---
ошибка занятости файла судя по всему связана с тем, что проект хранится в DropBpox. Т.е. как только файл изменился, его начинают тащить в сеть и, видимо, для этого блокируют. Т.е. надо перед первой записью самому файл захватить и отпустить только после второй записи. Если кто-то сразу мне кинет ссылку на блокировку файла, буду признателен. Но я скорее всего пойду по другому пути - однократной записи. Т.е. ХМL записать в поток и считать его уже оттуда в StringList, а не из файла. И уже потом сохранить один раз. Пока руки не дошли.**
---
** Руки дошли - см. DropBox против файлов http://alhymov.blogspot.ru/2014/02/dropbox.html