В форме логина facebook после введения логина и пароля не работает кнопка "Войти"

Не получается войти в андроид-приложение при помощи кнопки facebook.

В приложении сделал кастомную кнопку так как те длинные кнопки которые предлагает facebook не подходят по размерам, при этом Сделал согласно инструкции в учетке разработчика facebook.
Никаких особых данных не запрашиваю, только email, но он для разработчиков если приложение находится в разработке свободно доступен.
Вписал как и просится Development Key Hash.

В итоге, имею такое поведение:

На эмуляторе отлично проходит логин. После нажатия на кнопку появляется форма ввода логина и пароля, ввожу данные и нажимаю "Войти". Открывается второе окно с сообщением что-то типа “Ранее вы входили под такой учеткой, хотите продолжить?” Кликаю на продолжить и получаю от сервера ответ в callbackManager в onSuccess() где уже обрабатываю результат.

Но вот с реальными устройствами возникает проблема.

На реальном устройстве, при нажатии на кнопку открывается форма логина facebook как и в эмуляторе.

Далее ввожу логин и пароль и нажимаю на кнопку "Войти".

Форма логина facebook

Ожидаю что откроется второе окно с сообщением типа “Ранее вы входили под такой учеткой, хотите продолжить?”. Но после клика кнопка мигает как при нажатии, но ничего дальше не происходит. Вокруг кнопки появляется оранжевая прямоугольная обводка как на обычных виджетах когда они получают фокус. Но на этом все. Дальше сколько не нажимай кнопку, только анимация клика и обводка фокуса, больше ничего не происходит. Само окно активно его можно скролить и можно кликать другие элементы, то есть окно не зависло. Но и нет никаких признаков что происходит процесс обмена данными с сервером или какая-либо длительная работа с задачей.
Никаких ошибок после клика в консоли не появляется.

Последние строки в логе AndroidStudio:

2020-06-22 13:03:48.252 1603-1603/com.example.buttonlogintest I/Timeline: Timeline: Activity_launch_request time:3019160 intent:Intent { cmp=com.example.buttonlogintest/com.facebook.CustomTabMainActivity (has extras) } 
2020-06-22 13:03:48.279 1603-1603/com.example.buttonlogintest I/Timeline: Timeline: Activity_launch_request time:3019187 intent:Intent { act=android.intent.action.VIEW dat=https://m.facebook.com/... flg=0x40000000 pkg=com.android.chrome (has extras) }

Код Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.buttonlogintest">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:screenOrientation="portrait"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:replace="android:icon">

        <meta-data
            android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id" />

        <activity
            android:name="com.facebook.FacebookActivity"
            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name" />

        <activity
            android:name="com.facebook.CustomTabActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="@string/fb_login_protocol_scheme" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivityLoginFB">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

Код Activity:

package com.example.buttonlogintest;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;

import androidx.appcompat.app.AppCompatActivity;

import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.login.LoginBehavior;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;

import java.util.Arrays;


public class MainActivityLoginFB extends AppCompatActivity implements View.OnClickListener {

    CallbackManager callbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_fb_btn);

        ImageButton loginImgBtn = (ImageButton) findViewById(R.id.buttonLoginFB);
        loginImgBtn.setOnClickListener(this);

        initFacebookSdk();

    }

    public void initFacebookSdk() {
        Log.e("MYTAG", "InComeActivity. initFacebookSdk");
//        FacebookSdk.sdkInitialize(this);
        callbackManager = CallbackManager.Factory.create();

        LoginManager.getInstance().registerCallback(callbackManager,
                new FacebookCallback<LoginResult>() {
                    @Override
                    public void onSuccess(LoginResult loginResult) {
                        Log.d("MYTAG", "Login");
                        Log.d("MYTAG", "Facebook getUserId: " + loginResult.getAccessToken().getUserId());
                    }

                    @Override
                    public void onCancel() {
                        Log.e("MYTAG", "InComeActivity. initFacebookSdk: onCancel");
                    }

                    @Override
                    public void onError(FacebookException exception) {
                        Log.e("MYTAG", "InComeActivity. initFacebookSdk: onError");
                    }
                });

    }

    @Override
    public void onClick(View view) {
        LoginManager loginManager = LoginManager.getInstance();
        loginManager.setLoginBehavior(LoginBehavior.NATIVE_WITH_FALLBACK);
        loginManager.logInWithReadPermissions(this, Arrays.asList("email"));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        callbackManager.onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Код разметки:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.buttonlogintest.MainActivity">

    <ImageButton
        android:id="@+id/buttonLoginFB"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginTop="32dp"
        android:elevation="4dp"
        android:padding="4dp"
        android:scaleType="fitXY"
        android:src="@drawable/ic_fb_logo"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Предыстория. Изначально такое поведение появилось в уже работающем приложении. Грешил на то что не поменял Key Hash после того как подписывал приложение (для публикации в GooglePlay) на другой машине. Но, после обновления хэшей ничего не помогло. В новом созданом для теста приложении и на еще одном рабочем приложении (ранее там не было кнопки входа, потому сделал специально эксперимент) тоже самое поведение логин-формы фейсбук. Что дев версия что релизная – не работают на реальных устройствах, но на эмуляторах – работают.
Уж не знаю на что и думать.

Есть ли у кого какие идеи что это может быть? Может у кого есть рабочие приложения с логином через кастомную кнопку фб на java что-бы сопоставить что я сделал неверно?


Ответы (1 шт):

Автор решения: V.March

Результаты проб и ошибок:

Как выявилось проблема в том, что у меня в браузере Chrome на смартфоне стоит запрет на сохранение куки(как впрочем и у многих тех, кто то не сильно горит желанием шарить направо и налево своими данными). А форма которая открывалась для авторизации через Facebook была как раз на основе WebView от Chrome. И пока не дашь доступ на хранение и чтение куки в Chrome - тебе возможности авторизироваться не дадут. Что ж причина найдена, facebook желает иметь доступ к куки. Что есть немного возмутительно (но это лично мое мнение).

Поиск решения. (Злоключения и море "позитива" от клиентоориентированности facebook )

Фактически, сейчас для авторизации через Facebook используются 2 варианта. Либо через WebView системы (как правило, на себя эту функцию перебирает WebView предоставляемое Chrome-браузером) либо через приложение от facebook. За эти варианты отвечает такая строчка при запуске процесса логина:

LoginManager.getInstance().setLoginBehavior(LoginBehavior.NATIVE_WITH_FALLBACK);

NATIVE_WITH_FALLBACK этот флаг означет "Сначала попробуй войти через приложение от facebook, если приложения нет, то войди через WebView."

Вот как раз здесь и загвоздка. Если нет приложения, и в браузере запрет на куки - то ожидается, что прилетит ошибка в CallbackManager которую можно обработать и вывести пользователю, к примеру, попап с просьбой дать доступ к куки. Но facebook взял на себя обработку ошибки и разработчик не получит НИКАКОГО ответа от сервера, который смог бы обработать. Вместо этого в форме логина будет красоваться текст мелким шрифтом вверху экрана типа: "У вас нет доступа на куки, а мы очень хотим куки хранить".

Что сразу подумалось: ну хотите хранить куки, ну так ХОТИТЕ себе дальше! А я хочу авторизироваться вводя только логин и пароль от своей учетки.

Потому данный баннер как правило игнорируется. Что я благополучно и делал несколько раз. Если же нажать на ссылку в этом тексте, то перекидывает на страничку в хроме о том, "что такое куки и с чем их едят". Возможно где-то в этой тонне "полезной и познавательной" информации прилежный пользователь и найдет рекомендацию или инструкцию как подключить в хроме куки, если появилось желание это сделать. Фактически, пользователь может за это время раз 50 войти в приложение обычным способом, который как правило предлагается самым первым.

Таким образом, лозунг "Авторизация в 2 клика! Проще некуда!" превращается в эпопею из многократных попыток залогиниться, перезагрузок телефона, сноса и повторной установки приложения (что ну ооочень негативно сказывается на Статистике в GooglePlay). Появляется естественное чувство что тебя хотят обмануть.

Само решение:

В SDK facebook кроме флага NATIVE_WITH_FALLBACK есть еще несколько. 2 из них здесь и пригодились.

WEB_VIEW_ONLY - Вход через WebView пропуская момент проверки установлено ли приложение. Таким образом вы всегда будете входить через WebView. Но не от Chrome, а системное.

NATIVE_ONLY - Вход только через приложение. Нет приложения - логин не получится.

С этими флагами мы в onError() CallbackManager-а получим ошибки, которые можем самостоятельно обработать.

Скомбинировав эти варианты и сделав вилку из if-else можно получить решение.

public class MainActivityLoginFB extends AppCompatActivity implements View.OnClickListener {

    private CallbackManager callbackManager;
    private LoginManager loginManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_fb_btn);

        initFacebookSdk();
        ImageButton loginImgBtn = (ImageButton) findViewById(R.id.buttonLoginFB);
        loginImgBtn.setOnClickListener(this);

    }

    public void initFacebookSdk() {
        Log.e("MYTAG", "InComeActivity. initFacebookSdk");
        // FacebookSdk.sdkInitialize(this); //В обновленном facebook-SDK это не нужно.

        callbackManager = CallbackManager.Factory.create();

        getLoginManager().registerCallback(callbackManager,
                new FacebookCallback<LoginResult>() {
                    @Override
                    public void onSuccess(LoginResult loginResult) {
                        Log.d("MYTAG", "Login: onSuccess.");
                        Log.d("MYTAG", "Facebook getUserId: " + loginResult.getAccessToken().getUserId());
                    }

                    @Override
                    public void onCancel() {
                        Log.e("MYTAG", "Login: onCancel");
                    }

                    @Override
                    public void onError(FacebookException exception) {
                        Toast.makeText(getApplicationContext(), "onError: " + exception.toString(), Toast.LENGTH_LONG).show();
                        Log.e("MYTAG", "Login: onError. Error: " + exception.toString());
                        loginWithoutApp();
                    }
                });
    }

    @Override
    public void onClick(View view) {
        loginWithApp();
    }

    LoginManager getLoginManager() {
        if (loginManager == null) {
            loginManager = LoginManager.getInstance();
        }
        return loginManager;
    }

    private void loginWithApp() {
        LoginManager.getInstance().setLoginBehavior(LoginBehavior.NATIVE_ONLY);
        loginManager.logInWithReadPermissions(this, Arrays.asList("email"));
    }

    private void loginWithoutApp() {

        getLoginManager().setLoginBehavior(LoginBehavior.WEB_VIEW_ONLY);
        loginManager.logInWithReadPermissions(this, Arrays.asList("email"));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        callbackManager.onActivityResult(requestCode, resultCode, data);
    }
}

P.S.: Буду рад конструктивной критике и предложениям оптимизации кода, или альтернативного решения проблеммы.

→ Ссылка