суббота, 7 июня 2014 г.

Автоматический аплоад фото в flicker с помощью TheBat

Я использую flicker, чтобы заливать скриншоты в закрытые альбомы. Несмотря на то, что альбомы закрытые, можно получить прямую ссылку на исходную картинку.
Для этого надо сперва в настройках flicker найти поле "Your Flickr upload email" и запомнить указанный email.

Создаём в какой-нибудь папке типа windows/system батник. Нужно только правильно задать аккаунт в TheBat, с которого будет отправлены скрины, и email для аплоада:
@echo on
@set path=%cd%
"<путь к The Bat!>\thebat.exe" /MAILU=MyEmailAccount@mail-server.com;TO=<Your Flickr upload email>@photos.flickr.com;SEND;ATTACH="%path%\*.png"
Теперь переходим в папку со скринами (у меня они только png) и запускаем данный батник. Плюс я настроил в TheBat, чтобы отправленные такие письма удалялись, а в аккаунте gmail складывались в отдельную папку. Минусом является то, что нельзя задать конечный альбом для аплоада и сразу получить прямые ссылки. Приходится открывать flicker в вебе и задавать альбом. Но это ограничение самого flicker.По хорошему, конечно, надо сделать тулзу, чтобы она автоматом заливала, помещала в нужный альбом и получала прямую ссылку, тем более какие-то готовые либы уже есть. Но на это надо время...

вторник, 26 ноября 2013 г.

HListView и ViewPager

Есть такой хороший контрол для андроида: HListView (либа: HorizontalVariableListView). Это горизонтальный ListView.

Есть, конечно Gallery. Но этот контрол считается deprecated, начиная с апи 16, плюс его логика такова, что он будет центровать дочерние элементы. Да и баг есть. Не очень корректно отрабатывается detachViewsFromParent, но это уже совсем другая история.

Главное, что HorizontalVariableListView со своей задачей справляется. Я как понимаю это просто взяли код ListView и адаптировали его. Т.е. там своя цепочка классов: HListView - AbsHListView - AdapterView, ну а дальше уже стандартный ViewGroup. Но жить это не мешает.

Интересная ситуация возникла, когда я поместил HListView внутри ViewPager. Были реализованы табы и на одном из них был такой контрол. Была даже более сложно: обычный ListView, каждый элемент которого - это в свою очередь HListView.

Всё работало хорошо на 4-ке. Когда же попробовал на 2-ке. Балалайка: вместо скролинга элемента ListView, переворачивалась другая страница ViewPager. Проблема была бы у любого девайса с апи < 14.

Одно из решений: перегрузить canScroll. Вот источник на SO
@Override
 protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
  return super.canScroll(v, checkV, dx, x, y) || (checkV && customCanScroll(v));
 }

 protected boolean customCanScroll(View v) {
  return (v instanceof HorizontalScrollView || v instanceof AbsHListView);
 }

В данном случае решение SO чуть подправлено для правильный работы.

Суть проблемы заключается в том, что в реализации canScroll ViewPager есть строка:
ViewCompat.canScrollHorizontally(v, -dx);
canScrollHorizontally всегда возвращает false, и только с 14 сдк метод перегружен и возвращается View::canScrollHorizontally

суббота, 8 июня 2013 г.

Вертикальный TextView в Android

VerticalTextView.java

public class VerticalTextView extends TextView {

 private int _width, _height;
 private final Rect _bounds = new Rect();

 public VerticalTextView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
 }

 public VerticalTextView(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

 public VerticalTextView(Context context) {
  super(context);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  // vise versa
  _height = getMeasuredWidth();
  _width = getMeasuredHeight();
  setMeasuredDimension(_width, _height);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  canvas.save();

  canvas.translate(_width, _height);
  canvas.rotate(-90);

  TextPaint paint = getPaint();
  paint.setColor(getTextColors().getDefaultColor());

  String text = text();

  paint.getTextBounds(text, 0, text.length(), _bounds);
  canvas.drawText(text, getCompoundPaddingLeft(), (_bounds.height() - _width) / 2, paint);

  canvas.restore();
 }

 private String text() {
  return super.getText().toString();
 }
}

xml:

    <com.my.namespace.VerticalTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left|center_vertical"
        android:background="@color/feedback_background"
        android:padding="4dip"
        android:text="@string/feedback"
        android:textColor="@color/feedback_text_color"
        android:textSize="@dimen/text_xlarge" />

PS: начиная с API11 никакого огорода городить не надо. Используем setRotation

Поисковые системы в моём Google Chrome


Открываем chrome://settings/.  Затем управление поисковыми системами.

4pda: http://4pda.ru/forum/index.php?act=Search&source=all&forums[]=281&query=%s&x=17&y=16&subforums=1
dns: http://smolensk.dns-shop.ru/search/?q=%s
enter: http://www.enter.ru/search?q=%s
elmall67: http://elmall67.ru/search?l=1&search_field=%s&search_button=%CD%E0%E9%F2%E8
nnm-club: http://nnm-club.ru/forum/search.php?mode=results&show_results=topics&search_keywords=%s
pc67: http://pc67.ru/search?str=%s
rutracker: http://rutracker.org/forum/tracker.php?max=1&nm=%s
so: http://stackoverflow.com/search?q=%s
wiki: http://ru.wikipedia.org/wiki/%s
Перевод en > ru: http://translate.google.com/#en|ru|%s
Перевод ru > en http://translate.google.com/#ru|en|%s
yandex maps: http://maps.yandex.ru/?text=%s
yandex market: http://market.yandex.ru/search.xml?text=%s&from=os
Google play: https://play.google.com/store/search?q=%s

суббота, 11 мая 2013 г.

ASUS RT-N56U

Тема на хоботе: http://forum.ixbt.com/topic.cgi?id=14:58751-94
FAQ: http://forum.ixbt.com/topic.cgi?id=14:58751
Firmare: https://code.google.com/p/rt-n56u/downloads/list
Wiki: https://code.google.com/p/rt-n56u/w/list

Подготовка HDD:
cat /proc/partitions
fdisk -lu /dev/sda
fdisk /dev/sda:  d n p 1 w q
mkfs.ext4 -m 0 -T largefile /dev/sda1

mkdir /media/AiDisk_a1/opt




Скрипты монтирования/размонтирования:

/usr/bin/opt-mount.sh
/usr/bin/opt-umount.sh (2 параметра: devname - в виде /dev/sd[a-z][0-9], mpname - точка монтирования).

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

В папке /etc/storage делаем файл, скажем persisted-files. туда складываем абсолютные пути от / всех файлов и папок, которые хотим восстанавливать после перезагрузки. например, мы добавили пользователя username и положили в его домашнюю директорию /home/username rsa ключи для беспарольного логина. Тогда содержимое файла persisted-files будет

код
/etc/passwd
/etc/group
/home/username

В файл /etc/storage/started_script.sh добавляем такие три строчки:

if [ -r /etc/storage/persisted.tar.bz2 ]; then
    tar -x -j -f persisted.tar.bz2 -C
fi

Запускаем команду

tar -c -f /etc/storage/persisted.tar.bz2 -j -T /etc/storage/persisted-list

Запускаем команду mtd_storage.sh save




Что сохраняется при вызове /sbin/mtd_storage.sh save?
Все содержимое /etc/storage. Оно также автоматом сохраняется при перезагрузке (если были изменения).
authorized_keys должно быть там, оно автоматически копируется туда куда нужно при старте. Но для основного пользователя.
Хотите добавить свою логику? В /etc/storage есть скрипты, в том числе 2 стартовых, там можете написать любую логику.

воскресенье, 7 апреля 2013 г.

Facebook API

Речь пойдет про использование facebook api для мобильного приложения под андроид. Если вы взялись за api, причём не важно для чего, знайте - вы вляпались. Много багов, ограничений, нюансов.

Я не буду останавливаться на элементарных вещах, как получение api key, отправка запросов и прочее, про которые можно узнать, почитав доки. Интересны нюансы, обычно, в них проблемы.

Использую в подавляющем большинстве FQL, а не Graph API. Потому что FQL позволяет запрашивать только нужные поля, которые надо, или достаточно сложные запросы, как: получить все альбомы моих друзей или получить все мероприятия, в которых среди участников есть определённый юзер. Но совсем без Graph API не обойтись. К примеру я не знаю, как получить документы групп с помощью FQL.

Первое, что надо уяснить, свести к минимуму количество запросов к серверу. Потому что у facebook есть определённые ограничения на максимальное количество запросов в определенный интервал времени. В один прекрасный момент просто начнут приходить json-ы с ошибкой. И придется ждать время. Понятно, что на родное приложение facebook с api key 350685531728 это не распространяется.

Часто встречаются ограничения на количество возвращаемых данных, limit в этом случае игнорируется:
- список тредов (50)?
- список альбомов, где фиды альбомов получаются выборкой из другой batch данного запроса. Возвращает только 200 альбомов.
"fids": SELECT object_id FROM album WHERE owner = ...
"albums: "SELECT object_id,name,... FROM album WHERE object_id IN (SELECT object_id FROM #fids)
- список фото по списку фидов альбомов: нельзя указывать больше 50 фидов.
SELECT album_object_id, object_id, caption,... FROM photo WHERE album_object_id IN (...)
- список всех альбомов по списку юзеров. На тестовом аккаунте: друзей ~800. Альбомов ~9000. Если же сделать запрос ниже, то вернёт ~2000 альбомов для ~70 юзеров.
SELECT object_id,owner FROM album WHERE owner IN (SELECT uid2 FROM friend WHERE uid1=me()) OR owner = me()

Мне нужно было получить документы всех групп. Для этого использовал batch requests, где каждая из 50 batch запрашивала документы какой-то одной группы. По непонятной причине, иногда ответ получается "урезанный", большинство batch пустые. Если же следом отправить такой же запрос, то всё корректно.

Не корректно работают тэги для видео. Если запрашивать их через graph api как: video_id/tags, то возвращает не всех юзеров. Хотя, если использовать fql: SELECT subject FROM video_tag WHERE vid = video_id, то вижу всех юзеров.

 недописано...

вторник, 5 марта 2013 г.

Как добавлял поддержку GDrive в android проект

Была уже поддержка dropbox, box.com. Настала очередь и google drive. Как никак продукт от самого гугла, а они умеют продвигать, что им надо. Правда потом так же успешно закрывать. Но не об это сейчас.

Все эти ребята как любят делать. Генеришь определенный урлик с app_id и открываешь в браузере, вводишь свои данные, а на выходе тебе токен. Хорошо, конечно, но хотелось бы лучше, когда просто открывается активити, юзер вводит логин, пароль, кликает "логин", в фоне логиниться и продолжает работать. Фактически надо повторить определенную последовательность GET/POST запросов и дело в шляпе. Так было с обоими первыми хранилищами (да и с facebook тоже).

С гуглом такое не прошло. Они юзают js, а через обычный явовский http клиент такое не с имитировать. Есть конечно, htmlunit и selenium, которые позволяют заюзать, вроде бы, без gui-шный браузер. Но сам не проверял, да и весят прилично. На данный момент отказался.
Можно было пойти законным путем для андроида. Но, честно не хотелось тянуть их либы, которые постоянно обновляются, потом какие-то api помечаются как устаревшие. Ведь фактически gdrive вышел из google docs. Тем более, что мне не так и много надо было операций (dir, upload, download, create folder, delete file/folder). Вполне можно было обойтись обычными GET/POST/DELETE запросами с передачей токенов.

Как ни странно, столкнулся с проблемой. Токен получался из аккаунта гугла, что подключен на девайсе, но он не был привязан к app_id. Пример гугловский оказался не рабочий, там так же вываливалась ошибка о не настроенном доступе (впрочем так часто бывает с примерами, гугл здесь оказался не исключением). Поиск на stackoverflow показал, что дело всё-таки в бабине. Плюс нашелся на github рабочий вариант.

Но как-то уже расхотелось юзать андроидовский вариант. Тем более нужны были тесты на яве. В итоге авторизацию стал делать как для stand alone application. Кратко: открываете в браузере определенный урлик, логинитесь, в ответ получить в теле html некоторый код, потом POST-запрос с этим кодом, в ответ же долгожданный accessToken и refreshToken. Токен живет 10 минут, потом его надо обновлять. Интересно, что после обновления приходит только accessToken, refreshToken остается прежним ( в том же box.com и refreshToken приходит новый). В моём выборе я был не одинок. Expert Explorer и ES Explorer использовали такой же механизм.
Ну а дальше уже юзал простые api.

Единственным нюансом оказалось удаление файлов/папок. Сперва использовал вариант через /files. Но был какой-то полукривой. Вроде объект удаляется, повторный вызов апи показывает, что объекта уже нет, а на вебе висит, хотя с ним ничего сделать нельзя. Через некоторое время он пропал и в вебе, но осадок остался. Второй вариант /children уже не доставил никаких неудобств.

В общем не без затыков, но задача была решена. Как часто бывает, спасибо докам гугла, достаточно информативно и понятно, плюс "Try it!" тоже хорошая вещь для отладки, посмотреть, какой там json генерится.