1

После успешного прохождения руководства Accessing Google APIs, я пытаюсь переместить весь код, связанный с Google+, с моего MainActivity на отдельный пользовательский номер GoogleFragment.Вызвать DialogFragment из пользовательского фрагмента, а затем установить его свойство

Однако я застрял на самом последнем месте - в моем пользовательском фрагменте, я не знаю, как получить доступ к mResolvingError поле после того, как DialogFragment был уволен:

public class GoogleFragment extends Fragment 
     implements GoogleApiClient.OnConnectionFailedListener { 

    private boolean mResolvingError = false; // HOW TO ACCESS? 

    @Override 
    public void onConnectionFailed(ConnectionResult connectionResult) { 
     if (mResolvingError) { 
      // Already attempting to resolve an error. 
      return; 
     } else if (connectionResult.hasResolution()) { 
      try { 
       mResolvingError = true; 
       connectionResult.startResolutionForResult(getActivity(), REQUEST_RESOLVE_ERROR); 
      } catch (IntentSender.SendIntentException e) { 
       // There was an error with the resolution intent. Try again. 
       if (mGoogleApiClient != null) 
        mGoogleApiClient.connect(); 
      } 
     } else { 
      // Show dialog using GoogleApiAvailability.getErrorDialog() 
      showErrorDialog(connectionResult.getErrorCode()); 
      mResolvingError = true; 
     } 
    } 

    private void showErrorDialog(int errorCode) { 
     // Create a fragment for the error dialog 
     ErrorDialogFragment dialogFragment = new ErrorDialogFragment(); 
     // Pass the error that should be displayed 
     Bundle args = new Bundle(); 
     args.putInt(ARGS_DIALOG_ERROR, errorCode); 
     dialogFragment.setArguments(args); 
     dialogFragment.show(getActivity().getSupportFragmentManager(), TAG_DIALOG_ERROR); 
    } 

    public static class ErrorDialogFragment extends DialogFragment { 
     public ErrorDialogFragment() { 
     } 

     @Override 
     public Dialog onCreateDialog(Bundle savedInstanceState) { 
      // Get the error code and retrieve the appropriate dialog 
      int errorCode = this.getArguments().getInt(ARGS_DIALOG_ERROR); 
      return GoogleApiAvailability.getInstance().getErrorDialog(
        this.getActivity(), 
        errorCode, 
        REQUEST_RESOLVE_ERROR); 
     } 

     @Override 
     public void onDismiss(DialogInterface dialog) { 
      mResolvingError = false; // DOES NOT COMPILE 
     } 
    } 
} 

Что я должен делать здесь, пожалуйста, ?

Если я делаю ErrorDialogFragment нестатическим я получаю ошибку компиляции:

This fragment inner class should be static (GoogleFragment.ErrorDialogFragment)

Если я держу его статическим - я не могу получить доступ к переменному либо.

Я имею в виду 2 обходные пути для моей проблемы:

  1. Использование LocalBroadcastManager для отправки пользовательских Intent из ErrorDialogFragment в GoogleFragment
  2. Определить пользовательский метод в GoogleFragment и доступ к нему через getSupportFragmentManager().findFragmentByTag()

Но может быть, есть более простое решение?

UPDATE:

Я изменил mResolvingError поле для общественности и попробовал этот код:

@Override 
    public void onDismiss(DialogInterface dialog) { 
     GoogleFragment f = (GoogleFragment) getActivity().getSupportFragmentManager().findFragmentByTag(GoogleFragment.TAG); 
     if (f != null && f.isVisible()) { 
      f.mResolvingError = false; 
     } 
    } 

, но я не знаю, как проверить это правильно, и если f.isVisible() необходим там ...

UPDATE 2:

Может быть как-то использовать DialogInterface.OnDismissListener с GoogleApiAvailability.getInstance().getErrorDialog в моем коде?

+1

Вы должны сделать это наоборот: вызываемый должен отправить результат получателю. Для этого заставьте caller реализовать интерфейс прослушивателя, чтобы вызывающий использовал его для передачи результата. Если вызывающий объект является Activity, ваш фрагмент вызываемого объекта может получить его с помощью 'getActivity()'. Если вызывающий объект является фрагментом, используйте 'setTargetFragment()' для вызываемого лица при его создании, а 'getTargetFragment()' позволит вам получить вызывающего абонента. – BladeCoder

+0

К сожалению, нельзя использовать 'setTargetFragment()' в моем приложении minSdkLevel 9 ... Кроме того, я запутался: внутренний класс должен быть статическим, так что 'GoogleFragment' (со сложным жизненным циклом) не содержит ссылки на' ErrorDialogFragment '. Но если я использую шаблон интерфейса - тогда между ними будет ссылка через переменную 'mListener'. –

+1

Вы можете использовать каждый метод Фрагмента из API 4, если вы используете библиотеку поддержки (как и должно быть). Все классы фрагментов должны быть статичными, поскольку фреймворк должен быть в состоянии инициировать их, вызывая пустой публичный конструктор. Вы не должны содержать ссылку на слушателя, вместо этого вы должны использовать 'getTargetFragment()' или 'getActivity()'. Ссылки будут очищены, когда фрагмент будет отделен от Activity и повторно инициализирован правильно, когда фрагмент будет восстановлен и повторно подключен. Это происходит, например, при изменении ориентации экрана. – BladeCoder

ответ

1

Комментарии BladeCoder были очень проницательными, спасибо.

Однако я понял, что все хлопоты с сохранением и восстановлением mResolvingError является ненужным, потому что startResolutionForResult() запускает отдельную активность в любом случае и мешает моему приложение - так это на самом деле не имеет значения, если я повернуть устройство или нет.

Вот мой окончательный код, чтобы инициировать GCM и принести Google+ данные пользователя -

screenshot 1

screenshot 2

MainActivity.java:

public static final int REQUEST_GOOGLE_PLAY_SERVICES = 1972; 
public static final int REQUEST_GOOGLE_PLUS_LOGIN = 2015; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (savedInstanceState == null) 
     startRegistrationService(); 
} 

private void startRegistrationService() { 
    GoogleApiAvailability api = GoogleApiAvailability.getInstance(); 
    int code = api.isGooglePlayServicesAvailable(this); 
    if (code == ConnectionResult.SUCCESS) { 
     onActivityResult(REQUEST_GOOGLE_PLAY_SERVICES, Activity.RESULT_OK, null); 
    } else if (api.isUserResolvableError(code) && 
     api.showErrorDialogFragment(this, code, REQUEST_GOOGLE_PLAY_SERVICES)) { 
     // wait for onActivityResult call (see below) 
    } else { 
     String str = GoogleApiAvailability.getInstance().getErrorString(code); 
     Toast.makeText(this, str, Toast.LENGTH_LONG).show(); 
    } 
} 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    switch(requestCode) { 
     case REQUEST_GOOGLE_PLAY_SERVICES: 
      if (resultCode == Activity.RESULT_OK) { 
       Intent i = new Intent(this, RegistrationService.class); 
       startService(i); // OK, init GCM 
      } 
      break; 

     case REQUEST_GOOGLE_PLUS_LOGIN: 
      if (resultCode == Activity.RESULT_OK) { 
       GoogleFragment f = (GoogleFragment) getSupportFragmentManager(). 
        findFragmentByTag(GoogleFragment.TAG); 
       if (f != null && f.isVisible()) 
        f.onActivityResult(requestCode, resultCode, data); 
      } 
      break; 

     default: 
      super.onActivityResult(requestCode, resultCode, data); 
    } 
} 

GoogleFragment.java:

public class GoogleFragment extends Fragment 
     implements View.OnClickListener, 
     GoogleApiClient.ConnectionCallbacks, 
     GoogleApiClient.OnConnectionFailedListener { 

    public final static String TAG = "GoogleFragment"; 

    private GoogleApiClient mGoogleApiClient; 

    private ImageButton mLoginButton; 
    private ImageButton mLogoutButton; 

    public GoogleFragment() { 
     // required empty public constructor 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 

     View v = inflater.inflate(R.layout.fragment_google, container, false); 

     mGoogleApiClient = new GoogleApiClient.Builder(getContext()) 
       .addConnectionCallbacks(this) 
       .addOnConnectionFailedListener(this) 
       .addApi(Plus.API) 
       .addScope(Plus.SCOPE_PLUS_PROFILE) 
       .build(); 

     mLoginButton = (ImageButton) v.findViewById(R.id.login_button); 
     mLoginButton.setOnClickListener(this); 

     mLogoutButton = (ImageButton) v.findViewById(R.id.logout_button); 
     mLogoutButton.setOnClickListener(this); 

     return v; 
    } 

    private void googleLogin() { 
     mGoogleApiClient.connect(); 
    } 

    private void googleLogout() { 
     if (mGoogleApiClient.isConnecting() || mGoogleApiClient.isConnected()) 
      mGoogleApiClient.disconnect(); 
    } 

    @Override 
    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
     if (resultCode == Activity.RESULT_OK) 
      mGoogleApiClient.connect(); 
    } 

    @Override 
    public void onClick(View v) { 
     if (v == mLoginButton) 
      googleLogin(); 
     else 
      googleLogout(); 
    } 

    @Override 
    public void onConnected(Bundle bundle) { 
     Person me = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient); 
     if (me != null) { 
      String id = me.getId(); 
      Person.Name name = me.getName(); 
      String given = name.getGivenName(); 
      String family = name.getFamilyName(); 
      boolean female = (me.hasGender() && me.getGender() == 1); 

      String photo = null; 
      if (me.hasImage() && me.getImage().hasUrl()) { 
       photo = me.getImage().getUrl(); 
       photo = photo.replaceFirst("\\bsz=\\d+\\b", "sz=300"); 
      } 

      String city = "Unknown city"; 
      List<Person.PlacesLived> places = me.getPlacesLived(); 
      if (places != null) { 
       for (Person.PlacesLived place : places) { 
        city = place.getValue(); 
        if (place.isPrimary()) 
         break; 
       } 
      } 

      Toast.makeText(getContext(), "Given: " + given + ", Family: " + family + ", Female: " + female + ", City: " + city, Toast.LENGTH_LONG).show(); 
     } 
    } 

    @Override 
    public void onConnectionSuspended(int i) { 
     // ignore? don't know what to do here... 
    } 

    @Override 
    public void onConnectionFailed(ConnectionResult connectionResult) { 
     if (connectionResult.hasResolution()) { 
      try { 
       connectionResult.startResolutionForResult(getActivity(), MainActivity.REQUEST_GOOGLE_PLUS_LOGIN); 
      } catch (IntentSender.SendIntentException e) { 
       mGoogleApiClient.connect(); 
      } 
     } else { 
      int code = connectionResult.getErrorCode(); 
      String str = GoogleApiAvailability.getInstance().getErrorString(code); 
      Toast.MakeText(getContext(), str, Toast.LENGTH_LONG).show(); 
     } 
    } 
}