Python для андроид

Искал способ как запаковать свой собственый проект на python в apk файл , без помощи пк и вот наконец- таки такая возможность появилась. Об этом собственно и поговорим в данной статье.
вступление
1.что для этого необходимо. Подготавливаем проект.
2.Структура проекта. Пакуем наше приложение в apk файл.
Положение дел.
Благодаря стараниям отечественных и зарубежных разработчиков, на андроиде появилась возможность писать и собирать свои проекты на java прямо на смартфоне. Все это благодаря интегрированной среде разработки. В небольшой срок появились и начали активно развиваться несколько интересных проектов. Коротко расскажу о двух из них. Первым привлекшим мое внимание стал проект anjedi. В отличии от предыдущих попыток в данной програме был реализован удобный интерфейс ,навигация по проекту и все необходимые инструменты для быстрой запаковки и тестирования собранного приложения. Позднее случайно наткнулся на aide в маркете. Данная ide отличалась двухоконным интерфейсом ,автодополнением и лог ошибок. Так как в свободное время писал на питоне используя sl4a мне стало интересно возможно ли собрать собственный проект на смартфоне. Стал копать в этом направлении.
Как вообще собрать приложение написанное на python для андроида ? На официальном сайте описывается способ запаковки. Качаем шаблон программы , в Eclipse создаем новый проект из готового исходника открываем скрипт и заменяем его содержание своим кодом. Собираем проект , получаем готовый апк, переносим на смартфон любым способом ,устанавливаем ,запускаем и наблюдаем работу приложения(не зыбываем установить питон и компоненты).
В первых версиях anjedi не было возможности паковать сторонние проекты не созданные в самой программе. Такая поддержка появилась недавно. Тогда то я и начал от версии к версии тестировать на предмет сборки шаблона на питоне. Однако никак не получалось скомпилировать. На 40% сборки приложение зависает а через несколько минут закрывается с ошибкой. Пока что с помощью anjedi не палучается собрать, по крайней мере у меня. Отписал разрабу может в дельнейшем поправят.
В последней версии aide 1.0 beta9 так же появилась возможность собирать проекты со стандартной структурой. Первым делом попробовал собрать шаблон и вот все получилось.
что нам понадобится
-Для начала установим sl4a и python. Этот пункт я описывал в своей статье -….. и стандартный шаблон
-далее установим aide. Начиная с версии 1.0 beta9 поддерживается запаковка любых проектов для андроид.(найти можно в маркете бесплатно)
-файловый менеджер. Я использую Total comander и root explorer.
-текстовый редактор. Встроенный в aide редактор тормозит при открытии больших файлов , поэтому предлогаю пользоваться сторонним. Посоветовать могу 920 text editor . Автоопределение кодировок , шустрый,удобный редактор с подсветкой синтаксиса.(найти так же можно в маркете абсолютно бесплатно)
когда все установлено можно приступать к работе. Для начала напишем к примеру простенькую программу на питон используя sl4a. После того скачаем шаблон для упаковки нашего будущего проекта. распакуем архив и папку с исходниками положим по пути sdcard/AppProjects/ .В этой папке по умолчанию располагаются проекты для работы с ними в нашей ide. Рассмотрим структуру проекта подробнее.

Программа содержит все необходимое для работы питон скрипта. Дополнительные библиотеки, описание элементов интерфейса в xml , java код . Остановлюсь на том что пригодится нам.

папка res : в тут располагаются ресурсы программы , иконка и логотип проекта, сам питон скрипт ,xml с описанием некоторых элементов интерфейса. В корне папки располагаются 4 папки.

1. drawable/

-icon.png — иконка программы которая будет отображаться в меню. можно заменить на свою.
-script_logo_48.png — логотип программы. Будет отображаться в статус баре когда программа запущена. так же можно заменить на свой.
-stat_sys_warning.png — иконка отображающаяся в статус-баре если во время работы программы возникла ошибка (код ошибки отображается рядом в строке).
2. layout/

— dialog.xml — в этом файле описывается вид строки в статус-баре появляющейся при запуске программы. Тут мы ничего трогать не будем.
— main.xml — содержание строки в статус баре. Тут тоже трогать ничего не будем.
3. raw/

-script.py — рабочий скрипт нашего приложения. В нем содержится весь исходный код питон части нашего приложения. Заменим имеющийся в скрипте код на свой.
4. values/

-strings.xml — тут нас интересуют две строки .
<string name=»app_name»> тут пишем название нашего приложения которое будет отображаться в списке установленных приложений </string>
<string name=»loading»> пишем текст который будет отображаться в статус баре при загрузке приложения </string>
с данной папкой разобрались идем дальше. Возвращаемся в корневой каталог.

SRC/ тут располагается java часть программы. Взглянем на путь по которому лежат файлы — com/dummy/foofoandroid/ — этот путь и есть название пакета нашего проекта — com.dummy.fooforandroid. То есть если мы запакуем два разных проекта с одним и тем же названием то программы будут устанавливаться друг на друга заменяя собой предыдущую версию. Поэтому нам необходимо будет переименовать эти папки и заменить несколько строк в .java файлах. Переименуем например в com.test.app. откроем последнюю папку, в ней лежат 5 файлов, их мы слегка и будем редактировать. Откроем dialogactivity.java в любом текстовом редакторе, смотрим первую строчку — package com.dummy.fooforandroid; меняем выделенное на com.test.app. сохраняем и закрываем файл. То же самое делаем и с остальными четыремя файлами. Т.е меняем название пакета на свое.
сохраняем все и возвращаемся обратно в корневой каталог. находим там файл androidmanifest.xml , в нем так же меняем название. Собственно и все , можно собирать проект.

Сборка проекта
запускаем ide. В панеле файлов видим папку с нашим проектом , нам нужно открыть проект . для этого необходимо открыть любой файл. Например манифест. появится окно уведомления о том что наш проект открыт. Нажимаем клавишу меню в нем выбираем пункт «run» . Начнется компиляция и сборка. По завершению которой программа установится на телефон и мы можем запустить ее , проверить работу. Готовый апк файл располагается по пути папка с нашим проектом /bin/ . Вот о чем хотел собственно рассказать в данной статье. Спасибо за внимание , успехов вам в ваших начинаниях.

Движок QPython (и QPython 3) для Android – вещь по-прежнему плохо изученная, и особенно что касается его встроенной библиотеки Scripting Layer For Android (SL4A), она же androidhelper. Эту библиотеку написали несколько сотрудников Google по принципу 20% свободного времени, снабдили ее спартанской документацией, которую почти невозможно найти, и отправили в свободное плавание. Я искал информацию об SL4A по крупицам, но со временем нашел практически все, что мне нужно.

SL4A позволяет задействовать практически все возможности консольного Python 3 вплоть до библиотек типа matplotlib, при этом используются стандартные диалоги Android: ввод текста, списки, вопросы, радиокнопки, выбор даты и т.д. Программа не будет поражать красотой, но многие задачи решать сможет. Самое главное, что мы получим доступ к различным функциям устройства. Например, можно:

  • делать телефонные звонки
  • посылать SMS
  • менять громкость
  • включать Wi-Fi и Bluetooth
  • открывать веб-страницы
  • открывать сторонние приложения
  • делать фото- и видеосъемку камерой
  • извлекать контакты из контактной книги
  • посылать системные оповещения
  • определять GPS-координаты устройства
  • определять заряд батареи
  • считывать данные SIM-карты
  • воспроизводить медиафайлы
  • работать с буфером обмена
  • генерировать голосовые сообщения
  • экспортировать данные на внешние активности (share)
  • открывать локальные html-страницы
  • и др.

В нашем примере мы напишем простейший список задач. Мы сможем создавать и удалять задачи, а также экспортировать их. Программа будет вибрировать и разговаривать. Мы будем пользоваться тремя видами диалогов: список, текстовый ввод и вопрос «да/нет». На все про все нам хватит менее 100 строк кода. Интерфейс сделаем английским ради универсальности (и GitHub).

Вот весь код и комментарии к наиболее существенным моментам.

from androidhelper import Android droid = Android()

Создаем объект droid класса Android(), который будет отвечать за взаимодействие с SL4A.

path=droid.environment().index(«/Download»)] + «/qpython/scripts3/tasks.txt»

Переменная path будет содержать абсолютное имя файла, в котором хранятся задачи. Почему так длинно? Дело в том, что SL4A не может работать с локальным путем, поэтому приходится определять абсолютный, а абсолютный может отличаться на разных Android-устройствах. Мы обойдем эту проблему путем определения местоположения папки Download с помощью метода droid.environment(). Затем мы отсекаем Download и добавляем путь Qpython/Scripts3 (он всегда одинаков) плюс имя файла.

def dialog_list(options): droid.dialogCreateAlert(«\ud83d\udcc3 My Tasks (%d)» % len(options)) droid.dialogSetItems(options) droid.dialogSetPositiveButtonText(«\u2795») droid.dialogSetNegativeButtonText(«Exit») droid.dialogSetNeutralButtonText(«\u2702») droid.dialogShow() return droid.dialogGetResponse()

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

def dialog_text(default): droid.dialogCreateInput(«\u2795 New Task», «Enter a new task:», default) droid.dialogSetPositiveButtonText(«Submit») droid.dialogSetNeutralButtonText(«Clear») droid.dialogSetNegativeButtonText(«Cancel») droid.dialogShow() return droid.dialogGetResponse()

Определяем функцию, отвечающую за создание новой задачи. Принцип аналогичен. В аргументе default мы передаем ей текст, который по умолчанию появляется в строке ввода (пустой при «»). В более сложных программах можно передавать различные подписи и кнопки.

def dialog_confirm(message): droid.dialogCreateAlert(«Confirmation», message) droid.dialogSetPositiveButtonText(«Yes») droid.dialogSetNegativeButtonText(«No») droid.dialogShow() return droid.dialogGetResponse().result

Эта функция будет задавать вопрос пользователю, чтобы получить ответ да или нет. Мы передаем ей текст вопроса.

while 1: try: with open(path) as file: tasks=file.readlines() except: droid.makeToast(«File %s not found or opening error» % path) tasks=

Создаем цикл (чтобы скрипт не вышел после первого же действия) и первым делом читаем файл задач и загружаем его в список tasks. Если файла нет, создаем пустой список.

response=dialog_list(tasks)

Выводим список задач. Когда пользователь делает какой-то выбор, метод dialog_list() возвращает это действие в виде значения, которое мы присваиваем переменной response.

if «item» in response: del tasks] droid.vibrate(200) droid.makeToast(«Дело сделано!») droid.ttsSpeak(«Дело сделано!»)

Начинаем обрабатывать действие пользователя. Поскольку метод droid.dialogGetResponse(), который мы используем в функции списка, выдает довольно сложную структуру в виде словаря, его придется препарировать не самым очевидным способом. В данном случае по простому клику на пункт списка он удаляется – мы выполнили дело. Сообщим об этом во всплывающем сообщении и одновременно сделаем (чисто забавы ради) виброзвонок на 200 миллисекунд и сгенерируем голосовую фразу Дело сделано!.

elif «which» in response: if «neutral» in response: choice=dialog_confirm(«Are you sure you want to wipe all tasks?») if choice!=None and «which» in choice and choice==»positive»: tasks=

По нажатию на среднюю (нейтральную) кнопку с ножницами можно разом удалить все дела. При этом будет выведен подтверждающий вопрос.

elif «positive» in response: default=»» while 1: input=dialog_text(default) if «canceled» in input: default=input elif «neutral» in input: default=»» elif «positive» in input: tasks.append(input+»\n») droid.ttsSpeak(«Новое дело!») break else: break else: exit=True

Здесь мы создаем новую задачу. Обратим внимание на переменную cancel – ее выдает droid.dialogGetResponse() в случае клика вне диалога (на пустую область экрана). Чтобы корректно обработать такую ситуацию, мы ввели дополнительное условие. По средней кнопке (neutral) поле ввода будет очищаться. При positive мы создаем новый пункт списка и выходим из цикла. Если нажать на самую правую кнопку, сработает else и мы просто выйдем из цикла, ничего не сохранив (хотя формально это будет значение negative в input). Последняя строка означает, что пользователь нажал на Exit. Тогда мы устанавливаем флаг exit в True.

with open(path, «w») as file: for i in range(len(tasks)): file.write(tasks)

После каждой обработки списка сохраняем список задач в файл.

if exit==True: break

Если пользователь решил выйти, мы выходим из главного цикла while.

choice=dialog_confirm(«Do you want to export tasks?») if choice!=None and «which» in choice and choice==»positive»: droid.sendEmail(«Email», «My Tasks», ».join(tasks), attachmentUri=None)

В самом конце мы спрашиваем у пользователя, надо ли экспортировать все задачи куда-нибудь – на почту, в облако, в мессенджер и т.д. При положительном ответе список задач преобразуется в строку и экспортируется.

На этом всё. Программа будет выглядеть, как на скриншоте выше.

Полный листинг

Окончательный полный листинг (с комментариями на английском):

#!/usr/bin/python # -*- coding: utf-8 -*- # This is a very simple to-do list for Android. Requires QPython3 to run (download it from Google Play Market). from androidhelper import Android droid = Android() # Find absolute path on Android path=droid.environment().index(«/Download»)] + «/qpython/scripts3/tasks.txt» def dialog_list(options): «»»Show tasks»»» droid.dialogCreateAlert(«\ud83d\udcc3 My Tasks (%d)» % len(options)) droid.dialogSetItems(options) droid.dialogSetPositiveButtonText(«\u2795») droid.dialogSetNegativeButtonText(«Exit») droid.dialogSetNeutralButtonText(«\u2702») droid.dialogShow() return droid.dialogGetResponse() def dialog_text(default): «»»Show text input»»» droid.dialogCreateInput(«\u2795 New Task», «Enter a new task:», default) droid.dialogSetPositiveButtonText(«Submit») droid.dialogSetNeutralButtonText(«Clear») droid.dialogSetNegativeButtonText(«Cancel») droid.dialogShow() return droid.dialogGetResponse() def dialog_confirm(message): «»»Confirm yes or no»»» droid.dialogCreateAlert(«Confirmation», message) droid.dialogSetPositiveButtonText(«Yes») droid.dialogSetNegativeButtonText(«No») droid.dialogShow() return droid.dialogGetResponse().result # Run main cycle while 1: # Open file try: with open(path) as file: tasks=file.readlines() except: droid.makeToast(«File %s not found or opening error» % path) tasks= # Show tasks and wait for user response response=dialog_list(tasks) # Process response if «item» in response: # delete individual task del tasks] droid.vibrate(200) droid.makeToast(«Дело сделано!») droid.ttsSpeak(«Дело сделано!») elif «which» in response: if «neutral» in response: # delete all tasks choice=dialog_confirm(«Are you sure you want to wipe all tasks?») if choice!=None and «which» in choice and choice==»positive»: tasks= elif «positive» in response: # create new task default=»» while 1: input=dialog_text(default) if «canceled» in input: default=input elif «neutral» in input: # clear input default=»» elif «positive» in input: # create new task tasks.append(input+»\n») droid.ttsSpeak(«Новое дело!») break else: break else: exit=True # Save tasks to file with open(path, «w») as file: for i in range(len(tasks)): file.write(tasks) # If user chose to exit, break cycle and quit if exit==True: break # Export tasks choice=dialog_confirm(«Do you want to export tasks?») if choice!=None and «which» in choice and choice==»positive»: droid.sendEmail(«Email», «My Tasks», ».join(tasks), attachmentUri=None)

Также вы можете найти его на GitHub.

Пара замечаний. SL4A не позволяет использовать никакую графику, однако можно использовать довольно большое количество всевозможных смайлов и эмодзи как Unicode-символы. Это могут быть хоть домики, хоть собачки, хоть кошечки. В нашем примере мы использовали знак плюс (\u2795), ножницы (\u2702) и листок бумаги (\ud83d\udcc3). C каждой новой версией Unicode их становится все больше, но этим не стоит злоупотреблять – новые смайлы не будут отображаться на более старых версиях Android.

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

Полезные ресурсы по теме

  • QPython 3 в Google Play Market
  • Сайт QPython
  • Документация по androidhelper
  • SL4A на GitHub
  • Небольшое описание SL4A на Python Central
  • Блог человека, иногда пишущего про SL4A с примерами

P.S. Вопросы и замечания лучше писать мне в личку.