Android FacebookSDK для входа в систему с помощью встроенного приложения Facebook запрашивает разрешения даже после того, как пользователь уже дал им

Состояние: Android-устройство с встроенным Facebook apk установлено, но вышло из системы.

Каждый раз, когда это условие выполняется, и пользователь пытается войти с помощью Facebook LoginButton или делать это вручную (см. ниже), после во время входа в систему, SDK Facebook всегда запрашивает разрешение на доступ к пользовательским данным, даже если пользователь уже дал разрешение.

Вот код, который я реализую в своем приложении:

Версия 1 - Кнопка входа в систему:

XML:

<com.facebook.widget.LoginButton
    android:id="@+id/login_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Java:

private static final List<String> PERMISSIONS = Arrays.asList("email", "user_groups");
private UiLifecycleHelper uiHelper;

@Override
protected void onCreate(Bundle savedInstanceState){
    uiHelper = new UiLifecycleHelper(this, callback);
    uiHelper.onCreate(savedInstanceState);
    loginButton = (LoginButton) findViewById(R.id.login_button);
    loginButton.setReadPermissions(PERMISSIONS);
}

private Session.StatusCallback callback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
        //modify interface or something
    }
};

Версия 2 - вручную

XML:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/button"
    android:onClick="buttonOnClick"
    android:text="Login" />

Java:

private static final List<String> PERMISSIONS = Arrays.asList("email", "user_groups");

public void buttonOnClick(View v) {
    System.out.println("botaoOnClick");
    Session session = Session.getActiveSession();
    if (!session.isOpened() && !session.isClosed()) {
        System.out.println("session: " + session);
        session.openForRead(new Session.OpenRequest(this).setPermissions(PERMISSIONS).setCallback(callback));
    }
    else {
        Session.openActiveSession(this, true, callback);
    }
}

private Session.StatusCallback callback = new Session.StatusCallback() {
    @Override
    public void call(Session session, SessionState state, Exception exception) {
        //modify interface or something
    }
};

Даже примеры приложений, которые поставляются с sdk, работают таким образом, но я уже видел некоторые другие приложения (например, Foursquare), работающие так, как я считаю, это естественное поведение (только запрашивает разрешения, если пользователь hasn ' t уже дал им).

Итак, кто-нибудь знает способ достижения желаемого результата? Предпочтительно без редактирования самого SDK Facebook.

Спасибо заранее!

Изменить 1. Дополнительная информация: при проверке переменной сеанса на методе обратного вызова разрешения всегда становятся пустыми, когда выполняется условие выше. Если пользователь отказывается давать разрешение (не в первый раз) и пытается снова войти в систему, сеанс приходит с разрешениями, которые пользователь уже дал в прошлом, как и ожидалось.

Изменить 2 (в 2013-06-28). Я решил загрузить видео, воспроизводя проблему, так как я не уверен, что все понимают, что я имел в виду в своем объяснении.

Ссылка здесь: http://www.youtube.com/watch?v=w4qJfoiVSsU

Ответ 1

Это возможно потому, что у вас есть несоответствие подписи между вашим приложением (которое, как я полагаю, находится в режиме отладки), и ваше приложение facebook, связанное с вашим appID.

В основном вы должны убедиться, что:

  • Подпись вашего приложения для Android, использующего facebook sdk, добавляется в панель инструментов facebook app Facebook App Dashboard вы можете легко получить свою подпись приложения с помощью keytool или добавив этот код в свою деятельность по методу WithCreate:

    @Override
    protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    
    try {
        PackageInfo info = getPackageManager().getPackageInfo(
                getPackageName(), 
                PackageManager.GET_SIGNATURES);
        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(signature.toByteArray());
            Log.d("TAG", "Hash to copy ==> "+ Base64.encodeToString(md.digest(), Base64.DEFAULT));
            }
    } catch (NameNotFoundException e) {
    
    } 
    catch (NoSuchAlgorithmException e) {
    
    }
    }
    
  • Флажок входа в Facebook включен

  • Если в вашем небезопасном режиме песочницы вам нужно выбрать категорию на странице сведений о приложении

  • По умолчанию в loginbutton используется имя входа SingleSignOn, которое будет в основном регистрироваться в качестве владельца приложения facebook, поэтому я предлагаю вам использовать тот, который позволяет любому пользователю входить в систему от имени вашего приложения facebook. Для этого просто добавьте эту строку после того, как вы установите разрешение на кнопку входа:

    loginButton.setReadPermissions(PERMISSIONS);
    loginButton.setLoginBehavior(SessionLoginBehavior.SUPPRESS_SSO);
    

Если у вас все еще есть проблемы, я предлагаю вам прочитать этот документ устранение неполадок в facebook login

PS: И FYI. Если вы используете UiLifecycleHelper, не забудьте переопределить основные методы жизненного цикла активности/фрагмента и перенаправить вызовы на UiLifecycleHelper, как указано в javadoc.

При использовании этого класса клиенты ДОЛЖНЫ вызывать все общедоступные методы из  * соответствующие методы либо в Деятельности, либо в фрагменте. Невыполнение всех  * методы могут привести к неправильно инициализированным или неинициализированным сеансам.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
protected void onResume() {
    super.onResume();
    uiHelper.onResume();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    uiHelper.onSaveInstanceState(outState);
}

@Override
protected void onPause() {
    super.onPause();
    uiHelper.onPause();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    uiHelper.onDestroy();
}

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

Ответ 2

Я думаю, что разрешения на чтение запрашиваются каждый раз (или просто показывают всплывающее высказывание, которое они уже предоставили), если приложение Facebook не установлено на устройстве.

Это должно быть поведение с последним Android SDK.

Вы заметили, что с установленным приложением тоже?

Ответ 3

Я использую ручную версию с небольшим завихрением для оператора if в вашей функции buttonOnClick.

Session session = Session.getActiveSession();
boolean isReadAllowed = checkPermissions(Arrays.asList(PERMISSIONS), session.getPermissions());
if (!session.isOpened() && !session.isClosed()) {
    session.openForRead(new Session.OpenRequest(this).setPermissions(PERMISSIONS).setCallback(callback));
} else {
    if (isReadAllowed) {
        Session.openActiveSession(this, true, callback);
    } else {                                    
        session.requestNewReadPermissions(new Session.NewPermissionsRequest(this, Arrays.asList(PERMISSIONS)).setCallback(callback));
    }
}

И моя функция checkPermissions:

public static boolean checkPermissions(List<String> needed, List<String> available) {
    boolean ret = true;
    for (String s : needed) {
        if (!available.contains(s)) {
            ret = false;
            break;
        }
    }
    return ret;
}

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

EDIT: Спасибо за видео. Сначала я не понял проблему. Я видел это поведение раньше, и это ошибка с помощью SDK для Facebook. Я подал ошибку на свой трекер (ссылка) месяц назад, похоже, что они не исправят это.

Ответ 4

Я определил две причины этой проблемы.

  • Необходимо передать разрешения в блоке else в ручной версии вашего кода, то есть openActiveSession(), для этого я использую openForRead (op) с разрешениями.
  • KeyHash не подходит, и в этом случае также успешно выполняется вход в систему по умолчанию, но каждый раз отображается диалог разрешений.

Я создал KeyHash с помощью debug.keystore со следующей командой

keytool -exportcert -alia
s androiddebugkey -keystore debug.keystore | ope
nssl sha1 -binary | openssl base64

private static final List<String> PERMISSIONS = Arrays.asList("email", "user_groups");

public void buttonOnClick(View v) 
{
    Log.e("SKFBDemo", "buttonOnClick");

    Session session = Session.getActiveSession();
    if (session != null && !session.isOpened() && !session.isClosed()) 
    {

        Log.e("SKFBDemo", "Session is in Active");
        session.openForRead(new Session.OpenRequest(this).setPermissions(PERMISSIONS).setCallback(callback));
    }
    else 
    {
        Log.e("SKFBDemo", "Session is null or not opened or closed");

        OpenRequest op = new Session.OpenRequest(this);
        op.setPermissions(PERMISSIONS);

        session = new Builder(this).build();
        session.addCallback(callback);
        Session.setActiveSession(session);
        session.openForRead(op);
    }
}

private Session.StatusCallback callback = new Session.StatusCallback() 
{
    @Override
    public void call(Session session, SessionState state, Exception exception) 
    {
        Log.e("SKFBDemo", "Session.StatusCallback state="+state.toString());
    }
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    Log.e("SKFBDemo", "onActivityResult resultCode="+resultCode);

    if(resultCode == RESULT_OK && requestCode == Session.DEFAULT_AUTHORIZE_ACTIVITY_CODE)
    {
        Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);

        final Session session = Session.getActiveSession(); 
        if (session != null && !session.isClosed()) 
        {
            Log.e("SKFBDemo", "Login successful");
        }
        else
        {
            Log.e("SKFBDemo", "Facebook session not opened");
        }
    }
}

Я пробовал с этим кодом, его запрос разрешения только один раз и работает нормально.