понедельник, 25 августа 2014 г.

[Из песочницы] Запуск objective-c кода на Android устройствах

Начало истории




Пришел мне на доработку проект написанный на cocos2d. Игра для детей, в которой необходимо собирать пазлы и учить слова. Работа как работа, но главная проблема заключалась в том, что до меня над проектом работала некая девушка из Индии. И тут у меня начался очень веселый период. Пример того, на что мне пришлось смотреть, что делать и чем все это закончилось, будет под катом.
Индокод обеспечивает настроение на весь день


//SelectPuzzle.h
- (void)imagePickerController:(UIImagePickerController *)pickerView didFinishPickingMediaWithInfo:(NSDictionary *)info
{

[[NSUserDefaults standardUserDefaults]setObject:nil forKey:@"cameraImage"];

UIImage *resiZedImage=[info objectForKey:UIImagePickerControllerOriginalImage];
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(interfaceOrientation))
{
isportrait=YES;
resiZedImage=[self ImageResize:resiZedImage];
}
else
{
isportrait=NO;
resiZedImage=[self imageByCropping:resiZedImage toRect:CGRectMake(800,300,1006,1258)];
}

[[NSUserDefaults standardUserDefaults]setInteger:preDefined6 forKey:imageIndex];
[[NSUserDefaults standardUserDefaults]setObject:[NSData dataWithData:UIImagePNGRepresentation(resiZedImage)] forKey:@"cameraImage"];

[[NSUserDefaults standardUserDefaults]synchronize];
[self performSelector:@selector(dismissCameraView:) withObject:pickerView afterDelay:1.0];

[[CCDirector sharedDirector]replaceScene:[PlayScene scene]];

}

//PlayScene.m
-(void)init{
self = [super init];
if(self){
//...
//...
camera = YES;
NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:@"cameraImage"];
UIImage* cameraImage = [[[UIImage alloc] initWithData:imageData]autorelease];
puzzleObject=[CCSprite spriteWithCGImage:cameraImage.CGImage key:nil];
//...
//...
}
return self;
}





Однако пост не об ошибках, которые мне пришлось исправлять, а совсем о другой проблеме.

Через несколько месяцев работы над проектом, когда основные проблемы были решены, заказчик попросил собрать билд под его Android девайс. Далее шел удивительный разговор о том, что первый разработчик должен был писать под cocos2d-x и приложение должно легко запускаться на любом устройстве. Решать проблему пришлось уже мне и мой выбор пал на Apportable.


Собственно тема




Как утверждает официальный сайт, Apportable SDK — это система, которая позволяет запускать один и тот же xCode проект на iOS и Android устройствах. В первую очередь, это касается игр написаных на cocos2d. C портирование некоторых системных Core фреймворков есть проблемы.

Наша игра была написана практически на чистом кокосе и только в двух местах использовались UIKit элементы.

Ну что же. Приступим.

Первое, что необходимо сделать — установить SDK на Mac. Для этого, следуя инструкциям на сайте, необходимо просто ввести в терминале



(echo; echo PATH=~/.apportable/SDK/bin:$PATH) >> ~/.bash_profile; source ~/.bash_profile


SDK требует около 2гб свободного места на диске и установленного xCode 5.


Пока все это скачивается, можно дописать в наш проект необходимый код.

Сначала установим режим эммулирования экрана. Тут есть несколько варианта.

Например:

UIScreenIPhone3GEmulationMode — используется, если ваше приложение не будет поддерживать графику от retina устройств. Размер виртуального экрана 320px/480px, scale — 1.0

UIScreenScaledAspectFitEmulationMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует не retina графику.

UIScreenBestEmulatedMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует наиболее подходящую графику. Для корректной работы этого режима должна быть графика для всех вариантов iPhone и iPad экранов. Иначе может выглядеть совсем не best.



int main(int argc, char *argv[]) {

@autoreleasepool {
#ifdef ANDROID
[UIScreen mainScreen].currentMode = [UIScreenMode emulatedMode:UIScreenAspectFitEmulationMode];
#endif
int retVal = UIApplicationMain(argc, argv, nil, @"AppController");
return retVal;
}
}




Все варианты


typedef NS_ENUM(NSUInteger, UIScreenEmulationMode) {
UIScreenIPhone3GEmulationMode,
UIScreenIPhone4EmulationMode,
UIScreenIPhone5EmulationMode,
UIScreenIPadEmulationMode,
UIScreenIPadRetinaEmulationMode,
UIScreenAspectFitEmulationMode,
UIScreenScaledAspectFitEmulationMode,
UIScreenBestEmulationMode,
UIScreenBestEmulatedMode = UIScreenBestEmulationMode,
UIScreenNativeMode,
UIScreenNativeRetinaMode,
UIScreenBestNativeMode,
};





Следующее что пришлось переделывать — позиции элементов.



[item setPosition:ccp(400, 600)];




на

[item setPosition:ccp(puzzle.contentSize.width * 0.2, puzzle.contentSize.height * 0.772)];




Тоже самое при отслеживании нажатий.

После этого надо перейти в терминале в папку с проектом и ввести:



apportable load




Эта команда создаст apk файл, загрузит его на подключенное устройство, установит и запустит. В идеальном варианте. Он бывает не всегда. Я не буду рассматривать ошибки линковки файлов, они очень индивидуальны. Однако бывает ситуация когда apportable не видит какой-то определенный девайс. На офисе из 7 устройств, на которых я тестировал приложение, не смог установить на 3.

Что делать в таком случае? Все очень просто. Apportable сначала создает apk файл и только потом пытается загрузить его на устройство. Значит этот файл где-то есть. Если более точно, то вот здесь:

/Users/username/.apportable/SDK/Build/android-armeabi-debug/ProjectName




В этой папке лежит apk файл и его можно просто перекинуть на девайс и установить. Для этого я использовал приложение Android File Transfer(в маке из коробки нельзя обращаться к памяти Android устройств).

Все? Профит? К сожалению нет.

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


Что же это? Для iOS разработчиков

Аналог нашего info.plist. Манифест содержит в себе основные настройки, все права и конфигурацию.



И так. После создания билда с помощью apportable, в директории с проектом появилась папка ProjectName.aproj. В этой папке лежат конфигурационные файлы и файлы, которые не компилируются(например ресурсы).

Нас интересует файл configuration.json. В нем можно изменить настройки компиляции проекта для android. Например, добавить пермишены. Для работы фотокамеры нужны права на запись.

И изменение строки



"FEATURES": ["opengles2","landscape"]



на

"FEATURES": ["opengles2","landscape","write_external_storage"]




волшебным образом решило проблему.

Также есть нюанс с локализацией имени приложения на экране устройства. Для каждого языка необходимо добавить файл strings.xml и положить в папку ProjectName/java/res/values-[lang_description]


strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Localization name</string>
</resources>





И добавить в configuration.json:



//A list of java resource directories
"java_res_dirs": ["java/res"]


Несколько моментов на последок:



  • Рамеров экранов у Android устройств очень много, и кое-где приходилось использовать следующие вставки:

    Скрытый текст


    #ifdef ANDROID
    CGRect frame = [UIScreen mainScreen].applicationFrame;//1.7031 - pad 1.778 - phone
    CGFloat ratio = frame.size.height / frame.size.width;
    if (ratio < 1.72)
    {
    x = 16;
    }
    else{
    x = 29;
    }
    #endif




  • Если в файле configuration.json не стоит флаг «portrait», а Device Orientation в настройках xCode проекта стоит галочка напротив «Portrait», то портретная ориентация поддерживаться будет. Будьте внимательны.

  • Официальная документация docs.apportable.com/

  • В Apportable дописаны многие системные фреймворки. Официальная документация содержит мало информации об этом. Новые .h файлы с новыми доступными для android методами можно посмотреть в папке

    /Users/username/.apportable/SDK/sysroot/System/Library


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


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

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