суббота, 12 января 2013 г.

Двигаем мышью


    При опробовании беспроводного тачпада Logitech в качестве альтернативы мыши возникла мысль – ведь рядом лежит вполне готовый беспроводной тачпад – смартфон. Мышь он, конечно, не заменит,  но кое-что сделать,  комфортно развалившись в кресле,  вполне можно.
Идея эта не нова, есть достаточно приложений для удаленного управления ПК с андроид-устройства.  Решил реализовать минималистический андроид-тачпад с настройкой чувствительности по осям и сглаживания движения, который в дальнейшем, при желании, можно наполнить разнообразными возможностями удаленного управления.

В качестве отправной точки используем ранее слепленную программу рисования каракулей на экране. Только теперь эти каракули и прочие околоэкранные конвульсии надо будет передать на ПК.

Серверную часть можно посмотреть тут:

Двигаемся плавно и естественно.

Хочу более подробно остановиться на эмуляции движения мыши на ПК.  Для эмуляции мышиных бегов используем класс Robot и его методы mouseMove, mousePress, mouseRelease. Больше всего проблем возникло при попытке заставить курсор мыши перемещаться достаточно быстро и плавно.

Опробовал вот такие методы:

1. Просто mouseMove – быстрые рывки, практически неприменимо.
2. Алгоритм Брезенхэма – при использовании задержки 1мс на каждом шаге отрабатывает слишком медленно.
3. Вычисление координат с использованием формулы отрезка прямой с заданным шагом – все равно дергается курсор.
4. Еще один пошаговый алгоритм с количеством шагов, вычисляемым как функция от кода сглаживания. Использовал задержку 1 мс на каждом втором шаге.

Самый лучший, но совсем не идеальный, результат получился при использовании варианта 4. Буду благодарен если кто-то подскажет более красивый вариант.

/**
 * Compute mouse step to move mouse
 * @param len Move distance
 * @param smooth Smoothing mode
 * @return Mouse step
 */
private static int getMouseStep(double len, int smooth){
    int a = 30;
    switch (smooth)
    {
        case 1:
            a = 30;
            if (len < 5) a = 2;
            break;
        case 2:
            a = 40;
            if (len < 5) a = 2;
            break;
        case 3:
            a = 50;
            if (len < 5) a = 2;
            break;
        case 4:
            a = 50;
            if (len > 50) a = 70;
            if (len < 5) a = 2;
            break;
        case 5:
            a = 70;
            if (len < 50) a = 50;           
            if (len < 5) a = 2;
            break;
        case 6:
            a = 90;
            if (len < 50) a = 70;           
            if (len < 5) a = 5;
            break;
        }
        return a;
    }
   

/**
 * Move mouse (actual version)
 * @param xstart Start X coordinate
 * @param ystart Start Y coordinate
 * @param xend Finish X coordinate
 * @param yend Finish Y coordinate
 * @param sm Smoothing
 * @param rb Mouse Robot
 */
private static void MouseMove5(int xstart, int ystart, int xend, int yend, int sm, Robot rb) {
   
    int a = 30;
    boolean flag;
   
    double len = Math.sqrt((xend-xstart)*(xend-xstart)+(yend-ystart)*(yend-ystart));
   
    if (len < 3){
        rb.mouseMove(xend, yend);
        rb.delay(1);
    }
       
    a=getMouseStep(len, sm);
    flag = true;
    for (int i = 0; i < a; i++) {
        int mov_x = ((xend * i) / a) + (xstart * (a - i) / a);
        int mov_y = ((yend * i) / a) + (ystart * (a - i) / a);
        rb.mouseMove(mov_x, mov_y);
        if (flag){
            rb.delay(1);
            flag = false;
        }
        else flag = true;
    }
    rb.mouseMove(xend, yend);
}
                                                                                                                                                                 

Вопросы безопасности.

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

При подключении к серверу  будем передавать хеш-код пароля. Серверное приложение при первом старте (и в режиме настройки) запросит ввод пароля и сохранит его хеш-код. Для вычисления хеш-кода используем алгоритм MD5:

**
 * Encrypt password
 * Create MD5 hash
 * @param input Input String
 * @return MD5 Hash String
 */
    public static String md5Encrypt(String input){
        String md5 = null;
       
        if (input == null)
            return null;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(input.getBytes(), 0, input.length());
            md5 = new BigInteger(1, digest.digest()).toString(16);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return md5;
    }

В любом случае, я бы рекомендовал использовать такую программу исключительно в домашней wifi сети.