пятница, 23 марта 2012 г.

Air Native Process (.exe and .jar)


Начиная с версии 2.0 в Adobe Air появилась возможность общаться со сторонними приложениями через специальный встроенный класс NativeProcess. Это делает возможным написание "ядра" любого приложения на "нативном" языке портируемой платформы (Objective-C, Java, C#), в то время как сам интерфейс и анимацию можно сделать в удобных инструментах Flash-платформы (Flash Professional, Flex, Flash Catalyst). Здесь мы рассмотрим простейший пример взаимодействия Adobe Air и приложения Windows написанного на C#, а также, аналогичного приложения написанного на Java. В результате сделаем AIR приложение для создания "снимков экрана" - Screenshooter.


Для начала немного про AIRAdobe Integrated Runtime, платформа разработки приложений для широкого круга устройств, под различные операционные системы (PC, MAC, Linux, Android, iOS). При компиляции приложения Adobe AIR создается универсальный пакет, который может быть установлен на любые платформы. 



Программирование, в основном, осуществляется на языке ActionScript 3.0 (платформы Flash Player) с введение дополнительных патеков доступа к функционалу Air. Также, приложения Air могу быть собраны на основе HTML/CSS/JS (AJAX), но об этом уже в другой раз. Ниже приведены рисунки поясняющие работу Adobe Integrated Runtime.


 

Adobe Air охватывает все необходимые "возможности" для разработки полноценных, многофункциональных приложений, в том числе и мобильных. 

Конечно можно получать доступ к сторонним файлам через ExternalInterface, он в основном применяется для вызова JavaScript файлов в браузере (к примеру, получения информации из социальной сети). Пример работы с JavaScript можно посмотреть тут. А также пример взаимодействия C# и Flash здесь.

Разработку я буду проводить в FlashBuilder 4.5, использовать flex sdk 4.5 - Air 2.6. Результат можно скачать по ссылке на картинке ниже (.executable). После установки его в Windows, появиться стандартный .exe файл, выполнение которого можно назначить на горячую клавишу, к примеру Ctrl+Alt+0, делается это в свойствах .exe файла (картинка пример).



ОПИСАНИЕ
1. Screenshooter.as
Начнем с описания необходимых нам переменных
Parameters


Здесь, хочу отметить только массив coorArr, элементы которого будут "ассоциироваться" с координатами и передаваться в стороннее приложение. _nativeProcess - тот самый экземпляр класса NativeProcess позволяющий запустить стороннее приложение и обмениваться с ним данными. _nativeProcessInfo - экземпляр класса NativeProcessStartupInfo, в котором содержится информация о стороннем файле (_nativeProcessInfo.executable) и начальные параметры его запуска (_nativeProcessInfo.arguments).

Также, чтобы приложение правильно работало, необходимо изменить .xml файл, в котором находятся настройки запуска AIR приложения. В FlashBuilder этот файл находиться в основной папке проекта src, называется: Screenshooter-app.xml. В нем нужно найти и выставить следующие параметры (их описание можно найти в комментариях):




Initialization
"Определение", инициализация необходимых объектов.


1. init_Stage() - определяем параметры сцены. Важно, чтобы 
.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE - определяет, что сцена "развернута" на весь экран и на ней возможны нажатия клавиатуры. Поддерживается только в AIR приложениях.
.align = StageAlign.TOP_LEFT - выравнивание положения сцены от верхнего левого края экрана.
.scaleMode = StageScaleMode.NO_SCALE - определяет, что размер сцены будет фиксированным, при этом сцена (и внутренний контент) не будет реагировать на событие изменения размера окна приложения (но будет его прослушивать).
2. init_Cover() - закрываем весь экран слоем, на котором будем выделять область для сохранения. Поэтому делаем слой прозрачным, с альфой 0.01, чтобы на нем можно было отлавливать события:
_stageCover.graphics.beginFill(0xffffff, 0.01); 
_stageCover.graphics.drawRect(0,0, Capabilities.screenResolutionX, Capabilities.screenResolutionY);
На строчке (81) происходит отрисовка слоя на весь экран используя Capabilities.screenResolutionX и Capabilities.screenResolutionY . Эти два параметра определяются только при запуске приложения.

3. init_CapruteRect() - определяем то, как будет отрисовываться область выделения экрана. 
_captureGraph = _captureRect.graphics;
Делается это для того, чтобы ускорить дальнейшую отрисовку области выделения, т.е. лишний раз не обращаться к объекту _captureRect. Сама отрисовка области будет происходить в обработчике события перемещения мыши, где, каждый раз при смешении мыши, будет перерисовываться выделенная область. Поэтому в _captureGraph не завершаем отрисовку (.endFill()).



Handling Mouse and Key Events
Обработка событий от "мыши" и клавиатуры.


Строчка
_thisStage.nativeWindow.close();
обеспечивает выход из приложения, через закрытие нативного окна в котором находится Stage (возможно только в AIR).

1. handler_MouseDown - при нажатии левой клавиши "мыши" запоминаем координаты курсора и начинаем "прослушку" событий движения (MOUSE_MOVE) и "отпускания" клавиши мыши (MOUSE_UP).

2. handler_MouseMove - во время движения мышкой, каждый раз перерисовываем область смещения в виде прямоугольника (.drawRect(_sx, _sy, mouseX - _sx, mouseY - _sy)).

3. handler_MouseUp - выполняется после отпускания левой клавиши мыши. 
Во-первых удаляем всех слушателей (строчки 123-125), хотя, это можно было не делать, если прописаны useWeakReference = true ("слабые ссылки") при назначение слушателей. Затем формируем массив отправляемых данных, из массива coordArr, ассоциируя значения массива с начальными и конечными координатами мыши. Здесь используется встроенный в объект Array специальный метод .forEach( function (element:String, num:uint, arr:Array) :void {}, thisObject=null), где в строковой вектор args последовательно заносятся идентификатор и значение ("left", 100, "right", 100, ...)
coordArr.forEach( function (element:String, num:uint, arr:Array) :void {
    var formattedData:Object = utils_CompareCoord(element) as Object;
    args.push(formattedData.param);
    args.push(formattedData.value);
 }); 
В функции utils_CompareCoord(what:String):Object - происходит проверка координат: выделяем слева на право или справа налево. В зависимости от направления будут меняться местами координаты верхнего и нижнего углов.


Также заносим в отправляемые аргументы (args) дополнительный аттрибут - "out", который соответствует пути сохранения конечного файла, в нашем случае это Desktop:
args.push("out");
args.push(File.desktopDirectory.nativePath + "\\");

4. NativeProcess.isSupported - Проверяем возможен ли вызов стороннего файла в нашей системе и если да, то начинаем подготовку к его запуску. Здесь можно выбрать один из двух вариантов запуска: 
  • .exe файл написанный на C# (только на Windows платформе) 
  • .jar файл написанный на Java (везде где установлена виртуальная машина Java Runtime)
Ниже рассмотрим особенности запуска этих двух типов файлов.



out_PassDataToNativeProcess...
Приготавливаем NativeProcess и NativeProcessInfo к запуску стороннего файла.


 1. out_PassDataToNativeProcessNET - подготавливает и запускает .exe файл. 
Изначально, задаем аттрибуты (передаваемые при запуске .exe файлу), которые в C# будут интерпретироваться как массив (строчка 150). Поскольку .NET, обычно, уже интергирован в Windows и запускается автоматически, то нам нет необходимости вносить какие либо дополнительные параметры, C# - это так называемый Common Intermediate Language (CIL). Нужно обязательно указать путь к "выполняемому" файлу (строчка 151). Файл ScreenshooterNET.exe находиться в той же папке, что и .swf файл. 
Также, хочу отметить одну особенность: имя "выполняемого" файла обязательно должно отличаться от имени .swf файла, иначе возникнет конфликт при сборке приложения.
// The command line arguments that will be passed to the process on startup.
_nativeProcessInfo.arguments = data;
// The File object that references an executable on the host operating system.
_nativeProcessInfo.executable = File.applicationDirectory.resolvePath("ScreenshooterNET.exe");
Далее ставим прослушку событий на объект взаиможействующий с "сторонним" файлом -  _nativeProcess:
  • NativeProcessExitEvent.EXIT - запускается при завершении работы "стороннего" файла. В нашем случае, будет завершать работу всего приложения - удалять "слушателей", закрывать окно приложения.

  • ProgressEvent.STANDARD_OUTPUT_DATA - запускается при передаче сообщения из "стороннего" файл в Air приложение. К примеру: Console.WriteLine("message") в С#. Именно это событие ответственно за связь между сторонним файлом и приложением AIR.

  •  ProgressEvent.STANDARD_INPUT_PROGRESS - запускается при отправке сообщения из Air приложения с помощью _nativeProcess.standardInput.writeUTFBytes("message").

  • ProgressEvent.STANDARD_ERROR_DATA - сообщение о ошибках при обмене данными между "сторонним" файлом и Air приложением.
И наконец - ЗАПУСКАЕМ NativeProcess c параметрами:
_nativeProcess.start(_nativeProcessInfo);

2. out_PassDataToNativeProcessJAR - подготавливает к запуску .jar файл.
С .jar файлом не так все просто как с windows executable. Выполняемые .jar файлы требуют запуска виртуальной машины Java Runtime, так же как .swf файлы требуются для своего запуска Flash Player. Это так называемые "интерпретируемые" языки программирования (Interpreted Language) и для них нужен интерпретатор. Если открыть .jar файл с помощью WinRar, то можно увидеть java .class файл с инструкцией его запуска через виртуальную машину (в отдельной папке). Поэтому выполнение .jar файла нужно произвести через запуск виртуальную машину Java Runtime. Стандартно располагается она в системной папке "C:\Program Files (x86)\Java\jre6\bin\javaw.exe". И именно этот файл нужно передать как "сторонний" (выполняемый) для NativeProcess (строчки 166-167). В аргументы нужно добавить специальный "флаг" -jar, а затем имя .jar файла, который будет выполняться виртуальной машиной Java Runtime (строчки 169-172).
var file:File = new File("C:\\Program Files (x86)\\Java\\jre6");
file = file.resolvePath("bin/javaw.exe");
  
var args:Vector.String = new Vector.String;
args.push("-jar");
args.push(File.applicationDirectory.resolvePath("screenshooterJAR.jar").nativePath);
args.push(data.join("-"));

_nativeProcessInfo.arguments = args;
_nativeProcessInfo.executable = file;
Ну чтож, ставим "слушателей" событий на _nativeProcess и запускаем Native Process:
_nativeProcess.start(_nativeProcessInfo);



Handling Native Process Events
Рассмотрим как отправлять и обробатывать пришедшие сообщения от файла запущенного через Native Process.


1. handler_IncomeData - обработчик событий на входящие сообщения от "стороннего" файла запущенного через Native Process (ProgressEvent.STANDARD_OUTPUT_DATA). Входящие сообщения читаются из _nativeProcess.standardOutput методом .readUTFBytes(...) как последовательность байт в формате UTF-8, которые преобразуются в строчку и запоминаются в переменной income.
var income:String = String(_nativeProcess.standardOutput.readUTFBytes
(
   _nativeProcess.standardOutput.bytesAvailable
));
Все запускаемые "сторонние" файлы, в нашем случае, проверяются на готовность выполняться. Поэтому, как только "сторонний" файл готов к работе, он отсылает сообщение "Ready", приняв которое мы отправляем ответное сообщение о запуске работы этого "стороннего" файла - "Start/n"
Обязательно заканчивать отправляемое сообщение
символом перехода на новую строчку - "/n".
if (income.indexOf("READY") == 0)
{
     trace("Start Native Process");
     _nativeProcess.standardInput.writeUTFBytes("START/n");
}
Здесь хочу отметить одну особенность. При получении и преобразовании сообщения от "стороннего" файла не получается просто проверить это значение как, income == "READY". Это может быть объяснено появлением дополнительных символов в входящем сообщении, так как, иногда, длина сообщения не соответствует ожидаемой длине всего на пару символов ("READY".length = 5, income.length = 7). Поэтому проверка на входящее значение делается по сопоставлению положения фразы в строке if (income.indexOf("READY") == 0). Эта проблема есть и на стороне запускаемого файла, и решается она таким же способом.
Также здесь делаем еще одну проверку на окончание работы от "стороннего" файла - сообщение "Complete", которое форсирует завершение работы всего приложения.
if(income.indexOf("omplete")==1) 
{
    handler_OnExit(null); // handler for NativeProcessExitEvent.EXIT
}

2. handler_ProgressData - вызывается когда произошла отправка сообщения к "стороннему" файлу запущенному через Native Process - ProgressEvent.STANDARD_INPUT_PROGRESS. Если мы хотим полностью завершить обмен сообщениями между "сторонним" файлом и Air Native Process, то нам нужно вызвать метод _nativeProcess.closeInput(), который закрывает входяший поток данных в "сторонний" файл, при этом этот поток не может быть заново открыт, необходимо перезапускать Native Process (но прежде нужно из него выйти _nativeProcess.exit()).

3. handler_OnExit - обработчик события завершения работы "стороннего" файла, выход - NativeProcessExitEvent.EXIT. Здесь удаляются все слушатели событий с объекта NativeProcess, сам он завершается и приложение закрывается.
Тестирование AIR приложения нужно производить закомментировав _nativeProcess.start(nativeProcessInfo);
На этом завершается ActionScript 3.0 часть и мы переходим к рассмотрению кода С# и Java в Visual Studio и Eclipse соответственно.


2. ScreenshooterNET.exe
Этот файл будет ответственнен за копирование области экрана в картинку (Bitmap) и сохранение ейо на рабочий стол. Именно он будет запускаться как "сторонний" файл в Air Native Process. Разработку кода я буду вести в Microsoft Visual Studio 2010 C# Express, которую можно бесплатно скачать отсюда.

Project
В этой небольшой программе нам нет надобности в каком либо пользовательском интерфейсе, она всего лишь будет копировать и сохранять информацию, и лучше всего, если это будет делаться в фоновом, незаметном для пользователя, режиме. Поэтому проект который мы создаем будет консольным приложением (Console Application).




Program.cs - Solution, namespace, Main
Общая информация о Visual Studio и C#.

1. Solution - это набор проектов объединенных одной идеей (.sln). В проекты можно добавлять любые элементы, они здесь называются Items. Совсем недавно попробовал работать с Local Database (.sdf) и мне очень понравилось, так просто и интуитивно понятно создавать новые таблицы, особенно классно то, что есть DataSet (.xsd).

2. namespace - пространство имен, включает в себя набор классов, тоже самое, что и package в ActionScript 3.0. Для добавления в файл функционала платформы .NET нужно подключать его через "ссылку" (References) на нужный namespace, к примеру, using System.Drawing - в этом случае мы получаем доступ ко всем классам расположенным внутри этого "пакета". Пространство имен, это начальная структура любой программы на языке C#. Внутри одного файла .cs может быть сколько угодно namespace, к примеру, можно добавить namespace Screenshooter.Save:
namespace Screenshooter.Save
{ 
    class Class1 {}
    class Class2 {}
    ...
}

3. Main - так называемая "точка входа программы" (entry point for the program). Статическая функция Main - первая функция, которая будет вызвана, при запуске программы. Если в классе несколько статических функции, то только Main будет считаться точкой входа. Такая же система присутствует в виртуальной машине платформы Java. 



READY and START Messages from AIR Native Process
При начале работы этого приложение, из функции Main класса Program будем отсылать сообщение о готовности файла к выполнению команд - "READY". Затем будем ожидать команду на запуск - "START".


1. Метод Console.WriteLine("message") будет передавать сообщение в Air Native Process, а с помощью Console.ReadLine() можно принять входящее сообщение (необходимо, чтобы входящее сообщение имело в конце символ перехода на новую строку - "\n").

2. После получение команда начать - "START" мы создаем новый класс и дальнейшие действия будут происходить в нем. Здесь я хочу отметить одну особенность языка C# - это Named Parameters (на рисунке выше, отмечены пунктирной линией), то есть можно указывать имя параметра которому передаешь значение.
void myFunction(int a, int b){}
myFunction(b:2, a:7); // Работает
myFunction(3, b:11);  // Работает, a=3
myFunction(b:11, 3);  // ERROR - Не работает



class ScreenShooter
Начнем по порядку.
Сначала объявляем и инициализируем переменные.


Типы данных, которые я здесь использую, чисто C#, хотя можно использовать имена .NET - ссылка на сопоставление имен.
Основная работа будет проходить в функции MakeShoot(). Но прежде, чем начать нужно подготовить приложение к тестированию и симулировать входящие аругменты при запуске. Сделать это можно в настройках проекта.



Simulated Parameters
Симулируем параметры передаваемые из Air приложения в Visual Studio.


Здесь, важно чтобы параметры имели теже имена, что и в Air приложении, последовательность не важна, после имени параметра через пробел должно идти значение:
 top 0 left 0 right 800 bottom 600 out C:\Users\DQvsRA\Desktop\screenshooter.png
При запуске программы эти параметры будут переданы в функцию Main в виде массива строк - String[] args. Конечно, можно не вводить дополнительные имена, а определиться с последовательностью параметров и в дальнейшем вынимать их из массива по заранее известным индексам.



MakeShoot()
Первое, что мы должны сделать с входными данными это разделить их в переменные.


Разделение значений по переменным делается с помощью цикла foreach(string arg in args) {...}, в котором каждый элемент массива проверяется по ключу "right", "left", "bottom", "top", "out", а в соответствующую переменную заноситься следующий аргумент - top = int.Parse(args[count+1]), т.к. в входящем массиве последовательно идут key, value, key, value ... Затем высчитывается ширина и высота картинки (iWidth, iHeight):
int iWidth = right - left;
int iHeight = bottom - top;

Теперь, у нас есть все необходимое чтобы сделать снимок той области экрана, которую мы получили из Air приложения. Нужно только воспользоваться специальными .NET классами, об этом дальше.


1. На этом этапе, мы подготавливаем необходимые для дальнейшей работы объекты.
  • Rectangle rect = new Rectangle(left, top, iWidth, iHeigth) - объект прямоугольный, который хранит в себе область будущего снимка экрана.
  • Bitmap bitmap = new Bitmap(iWidth, iHeigth) - объект хранящий в себе графическую информации о пискелях рисунка, а также различные аттрибуты и методы работы с ней (к примеру PixelFormat и Save); bitmap - "вещественная" картинка, которая дальше будет сохранена на жесткий диск, пока что пустая.
  • Graphics graphic = Graphics.fromImage(bitmap) - определяем графический объект graphics и помещаем в него ссылку на новую графику созданную в bitmap с помощью статического метода Graphics.fromImage(bitmap). Только таким образом можно создать объект "графику", так как класс не имеет конструктора и не помет быть наследуем.
Следующий шаг будет копирование графики с экрана:
graphic.CopyFromScreen(rect.Location, new Point(0, 0), rect.Size);

В языке C# возможна перегрузка функций (Function Overloading), поэтому некоторые методы можно вызывать с различным набором параметров. Пример:
interface ITest
{
   void F();                     // F()
   void F(int x);                // F(int)
   void F(ref int x);            // F(ref int)
   void F(int x, int y);         // F(int, int)
   int F(string s);              // F(string)
   int F(int x);                 // F(int)           error
   void F(string[] a);           // F(string[])
   void F(params string[] a);    // F(string[])      error
}
При перегрузке есть несколько правил.
  1. Использование параметра ссылки (ref - отправка параметра как reference) или ссылки на параметр (out - возвращение функцией значения в этот параметр) не изменяет сигнатуру метода, т.е. F(int a) и F(ref int a) не одно и тоже.
  2. Изменение возвращаемого типа при перегрузке и модификатор params в параметрах не допустимы.
    public void abc() { ... } и public int abc() { ... } - Result: Compiler Error error CS0111 ...
Ключевое слово params позволяет передавать любое количество параметров в функцию, после запятой, params обязательно должен быть последним параметром функции. К примеру (link):
// START
abc(1,"hi","bye","no"); 
Console.WriteLine();
abc(2,"hi");
Console.WriteLine();
abc(3);
1) void abc(int i , string [] b) { ... }        
    Result: Compiler Error;

2) void abc(int i , string [] b, int j) { ... } 
    Result: Compiler Error error CS0231: A params or __arglist parameter must be 
                                         the last parameter in a formal parameter list

3) void abc(int i , params string [] b)
   { 
       Console.Write("{0} = ", i);
       foreach (string s in b) Console.Write(s + " ");
   } 
   Result:
        1 = hi bye no
        2 = hi
        3 = 
// END

2. Здесь мы получаем путь к рабочему столу через специальный класс платформы .NET Environment, который позволяет получить информацию о системе пользователя.
outPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
Похожая возможность есть и в Air - Capabilities.
Также составляем имя будущего файла с помощью DateTime:
name = "/scrn_" + string.Format("{0:HH_mm_ss tt}", DateTime.Now);
3. Сохраняем картинку (bitmap) на жесткий диск в формате .png, с помощью функции bitmap.Save используя ImageFormat.Png.
try
{
   bitmap.Save(outPath + name + EXTENSION, ImageFormat.Png);
   Console.WriteLine("Complete");
}
catch (Exception e)
{
   Console.WriteLine("********* ERROR! ********** \n" + e);
   Console.WriteLine("Complete");
} 
Для проверки, что создание прошло успешно, используем конструкцию try{}catch{} ( позволяющая "отлавливать" исключительные ситуации). Затем отсылаем сообщение "Complete", которое останавливает работу NativeProcess в Air и форсирует закрытие приложение.



Теперь можно запустить проект и проверить его на работоспособность.


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

Осталось собрать окончательную версию, из главного меню: Build - Build Solution. При этом, если вы ничего не меняли в настройках проекта, релиз будет находиться в папке \bin\Release\Screenshooter.exe. Файл .exe нужно переименовать, почему смотри выше в подразделе out_PassDataToNativeProcessNET, и переместить в папку где находиться .swf файл. После этого можно тестировать приложение из AIR

 В конце статьи есть ссылка на скачивание всех проектов в FlashBuilder, VisualStudio и Java.


3. ScreenshooterJAR.jar
Мне стало интересно, можно ли также общаться между AIR и Java файлами, что для этого нужно и как это происходит. Ниже я опишу этот эксперимент, его особенности и реализацию.
Разработку проекта я буду вести в FlashBuilder, так как он построен на Java IDE Eclipse (плагин к нему). В FB можно свободно создавать, компилировать и удобно экспортировать Java проекты.

Project
Создание Java проекта в FlashBuilder правильно начать с переключения в Java Perspective, чтобы иметь возможность создавать классы и другие элементы, иметь подсветку кода и экспорта. Ниже на рисунке показано как это можно сделать.


Единственное, что я поменял это Java Runtime Environment: JRE-1.x или "Use Default JRE":





class ScreenShooterJAR, main.
Реализация программы на Java отличается от того, что мы делали на C#. Здесь я буду использовать отдельный "поток" (Thread), и поэтому имплементирую в основной класс ScreenShooterJAR функционал из интерфейса Runnable.

1. Подготовка необходимых нам переменных и констант. Статические константы (private static final) обязательно должны быть инициализированны при своем объявление, они хранят в себе имена параметров передаваемых из AIR приложения, а также здесь добавлена константа определяющая секретное слово (SECRET), проверка которого запускает выполнение основной части программы. Дальше идут стандартные переменные (как в C#, только "глобальные", принадлежат всему классу), в них будут сохраняться значения переданных параметров. Одна дополнительная переменная - private String stringToConvert будет служить строкой для отправки сообщений обратно в AIR приложение.

2. public static void main(String[] args) - "точка входа программы" (entry point for the program). Смысл ее такой же как в .NET, это первая функция которая выполняется при запуске программы.
При старте NativeProcess в качестве параметра мы передаем одни параметр, в котором все интересующие нас аттрибуты "сшиты" в строку через тире (см. out_PassDataToNativeProcess...). Здесь мы разбираем эту строку обратно в массив параметров.
String[] input = args[0].split("-");
Далее, обращаемся к конструктору класса и передаем ему полученный выше массив с данными.

3. В конструкторе эти входящие данные сохраняются в "глобальную" переменную params для дальнейшего использования, а затем создается новый поток для обработки входящих событий и отсылается сообщение о готовности продолжить работу ("READY").
Помимо стандартного метода отправки сообщения, через системную консоль:
System.out.println(String message);
можно также использовать встроенный с Java класс DataOutputStream, который можно "отследить" на появление ошибок (я имею ввиду try{}catch{}). Реализация есть в функции public void utils_sendToAir(String message).


public void run()
Функция работающая на отдельном "потоке" (Thread). На самом деле можно обойтись без нее, и все что в ней написано перенести в отдельную функцию, вызываемую из конструктора.


Переменная String incomeingString будет хранить в себе входящее сообщение, ее мы будем проверять на соответствие секретному слову. 
BufferedReader in  - читает и "накапливает" текст из входящего "стрима" (input stream). Метод in.readLine() возвращает строку текста, только в том случае если встретился символ "\n" или "\t", до этого программа будет находиться в режиме "ожидания" (дальнейшие инструкции выполняться не будут). Таким образом переменная incomeingString никогда не будет пустой. Поэтому если мы хотим принять много входящих сообщений, то нам нужно использовать цикл while, который будет выполняться до тех пор пока в считываемом сообщении есть какие либо символы, то есть всегда. Если мы хотим считать сообщение только один раз тогда нужно использовать следующий вариант без цикла while:
incomeingString = in.readLine();
а затем соответствующие проверки. Если в входящем сообщении на нулевой позиции присутствует секретное слово SECRET = "START", то мы запускаем функцию инициализирующую необходимые переменные Initialize() и продолжаем дальнейщее выполнение программы.



Initialize
Здесь мы "разбираем" массив данных, занося значения в соответствующие переменные. Тоже самое, что для .NET.


После задания значений переменным, мы высчитываем ширину и высоту будующей картинки (imageWidth, imageHeight), а также передаем эти данные обратно в AIR приложение.

На этом этапе нужно симулировать входящие переменные и протестировать программу.


В процессе "дебага", все что вы будете вводить в console, воспринимается как входные данные. Именно там нужно вводить ключевое слово "START"




MakeShootAndSave
Функция, в которой будет происходить создание и сохранение области рабочего стола.


1. Для работы с датами в Java есть специальный класс SimpleDateFormat, с помощью которого можно быстро задать нужный формат отображения времени. Можно использовать различные шаблоны, к примеру:
Date and Time PatternResult
"yyyy.MM.dd G 'at' HH:mm:ss z"2001.07.04 AD at 12:08:56 PDT
"EEE, MMM d, ''yy"Wed, Jul 4, '01
"h:mm a"12:08 PM
"hh 'o''clock' a, zzzz"12 o'clock PM, Pacific Daylight Time
"K:mm a, z"0:08 PM, PDT
"yyyyy.MMMMM.dd GGG hh:mm aaa"02001.July.04 AD 12:08 PM
"EEE, d MMM yyyy HH:mm:ss Z"Wed, 4 Jul 2001 12:08:56 -0700
"yyMMddHHmmssZ"010704120856-0700
2. В этом блоке кода происходит создание картинки области экрана.
Прежде всего, я набрал в гугле "screen capture java" и одна из ссылок вывела меня на эту конструкцию, может быть есть и другие возможности сделать картинку с экрана, но здесь я использую класс Robot, в котором есть встроенный метод createScreenCapture(rect), куда нужно передать область экрана, в виде объекта Rectangle:
Robot robot = new Robot();
Rectangle rect = new Rectangle(top, left, imageWidth, imageHeight);
BufferedImage image = robot.createScreenCapture(rect); // return BufferedImage 
Самое просто решение. Далее полученное изображение помещаем в специальный объект BufferedImage image, который в дальнейшем будет записан в файл.

3. Записываем полученную картинку в файл:
// write(RenderedImage im, String formatName, ImageOutputStream output)
ImageIO.write(image, "png", outputFile); 
Также здесь "отлавливаем"  IOException.

4. System.out.println("Complete") - форсирует завершение работы NativeProcess в AIR приложении и закрывает его.



Export .JAR File
Чтобы экпортировать выполняемый Java Runtime файл (.jar) нужно сделать следующие действия.


Затем нужно переместить этот .jar файл в папку где находиться .swf файл. Далее можно запускать его в NativeProcess.



PROJECT FILES
Проекты можно скачать отсюда.


*********** THE END ***********

Комментариев нет:

Отправить комментарий