Статьи

Создание приложения чата с кодовым названием One Part 2

Во второй части этого урока мы рассмотрим процесс входа в Google и получения уникального идентификатора. Мы попытаемся написать общий код, который позже сможем использовать для процесса входа в Facebook. Но сначала давайте рассмотрим, что на самом деле означает «вход в систему»…

Когда вы обрабатываете свой собственный список пользователей и пользователь регистрируется в процессе регистрации, вы обычно можете спросить этого пользователя о чем угодно. Тем не менее, когда пользователь входит в систему через Facebook, Google или любую другую службу, вы получаете доступ к этой службе для получения сведений о пользователе … Это до боли понятно с такими службами, которые по умолчанию не предоставляют даже адрес электронной почты при входе в систему. Иногда он доступен в Facebook, но только для пользователей, которые не хотят его скрывать.

Хуже того, одной из основных причин использования такого сервиса является доступ к контактам … Однако Facebook больше не позволяет разработчикам получать доступ к вашим друзьям на Facebook. Разработчик приложения Facebook может получить доступ только к списку друзей, у которых установлено приложение, и для этого нам нужно будет «пригласить» людей использовать / установить приложение.

Начало работы — Конфигурация

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

Начните с перехода на консоль разработчика Google: https://console.developers.google.com/

Создайте новое приложение, нажав кнопку «Создать»:

чат-приложение-учебник-Google-Логин-1

Просто введите название приложения, например, в этом случае его SocialChat и нажмите «Создать»:

чат-приложение-учебник-Google-Логин-2

Теперь можно выбрать раздел API, где вы должны увидеть страницу нового проекта:

чат-приложение-учебник-Google-Логин-3

В этом проекте вы должны нажать Google+ API в разделе «Социальные сети»:

чат-приложение-учебник-Google-Логин-4

В разделе учетных данных создайте новый идентификатор клиента, сначала нам нужно создать его для веб-приложения. Это будет использоваться симулятором, чтобы мы могли отлаживать приложение на рабочем столе. Он также будет использоваться портами для JavaScript, рабочего стола и эффективно для всего, кроме iOS и Android:

чат-приложение-учебник-Google-Логин-5

Экран концентрации используется, чтобы запрашивать у пользователей разрешения, обычно вы должны правильно его заполнять для приложения «реального мира», но в этом случае мы оставили его в основном пустым для простоты:

чат-приложение-учебник-Google-Логин-6

Теперь нам нужно добавить привязки нативных приложений для Android / iOS практически так же, как мы делали веб-приложение:

чат-приложение-учебник-Google-Логин-7

Чтобы создать собственное приложение для Android, убедитесь, что вы правильно настроили хранилище ключей для своего приложения. Если у вас нет сертификата Android, вы можете воспользоваться нашим визуальным мастером или использовать командную строку, указанную в нашем руководстве по подписыванию .

Теперь, когда у вас есть сертификат, вам нужно два значения для вашего приложения, первое — это имя пакета, которое должно совпадать с именем пакета вашего основного класса (с методами start, stop). Он должен быть указан в разделе свойств Codename One. Убедитесь, что вы используете абсолютно уникальное имя и используете свой собственный домен, не используйте префикс com.codename1 или что-нибудь подобное …

Вам также нужно значение SHA1 для вашего сертификата, это объясняется Google здесь: https://developers.google.com/+/mobile/android/getting-started

Эффективно вам нужно запустить эту командную строку:

1
$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v

Это запросит пароль, а затем напечатает несколько строк данных, одна из которых должна начинаться с SHA1 то есть значения, которое вам понадобится для приложения Android.

чат-приложение-учебник-Google-Логин-8

Приложение iOS должно иметь то же имя пакета, что и идентификатор пакета. Ему также нужен идентификатор appstore, который вы можете получить из itunes, он должен отображаться в качестве префикса для профиля обеспечения ваших приложений. Если ваше приложение правильно настроено, например, с помощью мастера сертификатов Codename One, вы должны увидеть его в свойствах проекта в разделе Codename One → iOS.

чат-приложение-учебник-Google-Логин-9

После того, как все будет завершено, вы должны увидеть нечто похожее на это:

чат-приложение-учебник-Google-Логин-10

Конфигурация проекта

Теперь нам нужно установить несколько важных советов по сборке в проекте, чтобы он работал правильно. Чтобы установить подсказки для сборки, просто щелкните правой кнопкой мыши проект, выберите свойства проекта и в разделе Codename One выберите вторую вкладку. Добавьте эти записи в таблицу:

1
2
android.includeGPlayServices=true
ios.gplus.clientId=your ios client ID

чат-приложение-учебник-Google-Логин-11

Код

Так что теперь, когда все это на месте, мы бы хотели, чтобы оно работало с нашей сборкой …

Сначала, чтобы сделать код более общим, мы определим этот интерфейс, который мы можем реализовать как для Facebook, так и для Google, таким образом обобщая код входа в систему:

1
2
3
4
5
6
static interface UserData {
    public String getName();
    public String getId();
    public String getImage();
    public void fetchData(String token, Runnable callback);
}

Способ работы этого интерфейса заключается в том, что мы вызываем fetchData после завершения процесса входа в систему, чтобы получить данные из Google / Facebook, а затем предоставить нам имя / идентификатор и изображение. Обратите внимание, что, поскольку Facebook не всегда предоставляет электронную почту, мы не можем надежно использовать ее в качестве уникального идентификатора для пользователя …

Также обратите внимание, что fetchData является асинхронным и будет вызывать обратный вызов после его завершения. Мы могли бы сделать это синхронно и использовать invokeAndBlock .

Код, который привязывает это к кнопке, выглядит следующим образом:

1
2
3
4
5
6
7
loginWithGoogle.addActionListener((e) -> {
    Login gc = GoogleConnect.getInstance();
    gc.setClientId("1013232201263-lf4aib14r7g6mln58v1e36ibhktd79db.apps.googleusercontent.com");
    gc.setRedirectURI("https://www.codenameone.com/oauth2callback");
    gc.setClientSecret("-------------------");
    doLogin(gc, new GoogleData());
});

Обратите внимание, что мы спрятали клиент в GoogleData и использовали класс GoogleData который фактически является реализацией интерфейса UserData . Класс GoogleData выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
static class GoogleData extends ConnectionRequest implements UserData {
    private Runnable callback;
    private Map<String, Object> parsedData;
 
    @Override
    public String getName() {
        return (String) parsedData.get("displayName");
    }
 
    @Override
    public String getId() {
        java.util.List<Map<String, String>> emailList = (java.util.List<Map<String, String>>) parsedData.get("emails");
        return emailList.get(0).get("value").toLowerCase();
    }
 
    @Override
    public String getImage() {
        Map<String, Object> imageMeta = ((Map<String, Object>) parsedData.get("image"));
        return (String)imageMeta.get("url");
    }
 
    @Override
    public void fetchData(String token, Runnable callback) {
        this.callback = callback;
        addRequestHeader("Authorization", "Bearer " + token);
        setPost(false);
        NetworkManager.getInstance().addToQueue(this);
    }
 
    @Override
    protected void readResponse(InputStream input) throws IOException {
        JSONParser parser = new JSONParser();
        parsedData = parser.parseJSON(new InputStreamReader(input, "UTF-8"));
    }
 
    @Override
    protected void postResponse() {
        callback.run();
    }
}

Как правило, в классе не так уж много. fetchData использует запрос на подключение для подключения к URL-адресу в Google+ API, который возвращает сведения о пользователе, когда токен установлен. Эти детали возвращаются в виде строки JSON, которую мы затем анализируем и устанавливаем для правильных переменных.

Класс JSONParser возвращает данные JSON в виде дерева списков и карт, через которое мы можем пройти, чтобы извлечь нужные нам данные.

Одно значение, которое может быть непонятным для случайного наблюдателя, — это token , это строка, содержащая «ключ» к учетной записи пользователя. Facebook / Google и т. Д. Хранят пароли своих пользователей в своей базе данных (фактически, даже если они не знают паролей), поэтому, чтобы доказать, что мы получили разрешение пользователя на доступ к его данным, мы получаем уникальный токен, который выглядит следующим образом: длинная строка тарабарщины, и мы можем использовать это при доступе к их соответствующим API, таким образом проверяя себя. Это очень распространенная практика … Однако токены иногда теряют силу, и поэтому вам может потребоваться «обновить» свой токен, выполнив вход снова, как показано в следующем блоке кода.

Теперь мы можем перейти к фактическому процессу входа в систему, который является общим для входа в Google и Facebook благодаря классу выше и встроенным абстракциям в Codename One:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private String fullName;
private String uniqueId;
private String imageURL;
void doLogin(Login lg, UserData data) {
    if(lg.isUserLoggedIn()) {
        showContactsForm();
        return;
    }
 
    // if the user already logged in previously and we have a token
    String t = Preferences.get("token", (String)null);
    if(t != null) {
        // we check the expiration of the token which we previously stored as System time
        long tokenExpires = Preferences.get("tokenExpires", (long)-1);
        if(tokenExpires < 0 || tokenExpires > System.currentTimeMillis()) {
            // we are still logged in
            showContactsForm();
            return;
        }
    }
 
    lg.setCallback(new LoginCallback() {
        @Override
        public void loginFailed(String errorMessage) {
            Dialog.show("Error Logging In", "There was an error logging in: " + errorMessage, "OK", null);
        }
 
        @Override
        public void loginSuccessful() {
            // when login is successful we fetch the full data
            data.fetchData(lg.getAccessToken().getToken(), ()-> {
                // we store the values of result into local variables
                uniqueId = data.getId();
                fullName = data.getName();
                imageURL = data.getImage();
 
                // we then store the data into local cached storage so they will be around when we run the app next time
                Preferences.set("fullName", fullName);
                Preferences.set("uniqueId", uniqueId);
                Preferences.set("imageURL", imageURL);
                Preferences.set("token", lg.getAccessToken().getToken());
 
                // token expiration is in seconds from the current time, we convert it to a System.currentTimeMillis value so we can
                // reference it in the future to check expiration
                Preferences.set("tokenExpires", tokenExpirationInMillis(lg.getAccessToken()));
                showContactsForm();
            });
        }
    });
    lg.doLogin();
}

Теперь это большой блок кода, но на самом деле это не так уж и много. Все, что он делает — это делегирует интерфейс UserData и проверяет токен. Он также хранит возвращенные данные и показывает следующую форму (которую мы не будем сейчас рассматривать).

Последний фрагмент кода для этого раздела — это небольшой служебный метод, который мы использовали для определения истечения срока действия токена:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/**
 * token expiration is in seconds from the current time, we convert it to a System.currentTimeMillis value so we can
 * reference it in the future to check expiration
 */
long tokenExpirationInMillis(AccessToken token) {
    String expires = token.getExpires();
    if(expires != null && expires.length() > 0) {
        try {
            // when it will expire in seconds
            long l = Long.parseLong(expires) * 1000;
            return System.currentTimeMillis() + l;
        } catch(NumberFormatException err) {
            // ignore invalid input
        }
    }
    return -1;
}

Это эффективно позволяет нам определить, истек ли токен в будущем выполнении приложения.

Другие сообщения в этой серии

Это непрерывная серия постов, включающая следующие части: