Запуск objective-c кода на Android устройствах

в 6:13, , рубрики: android, iOS, objective-c

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

Пришел мне на доработку проект написанный на 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

Автор: HexArt

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js