Search code examples
androidandroid-fragmentsgoogle-play-servicesandroid-dialogfragmentgoogle-plus-signin

Call DialogFragment from a custom Fragment, then set its property


After having successfully followed the guide Accessing Google APIs, I am trying to move all the Google+ related code from my MainActivity to a separate custom GoogleFragment.

However I am stuck at the very last spot - in my custom Fragment, I don't know how to access the mResolvingError field after the DialogFragment has been dismissed:

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
        }
    }
}

What should I do here please?

If I make the ErrorDialogFragment non-static I get compile error:

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

If I keep it static - I can not access the variable either.

I am thinking of 2 workarounds for my problem:

  1. Using LocalBroadcastManager to send a custom Intent from ErrorDialogFragment to GoogleFragment
  2. Define a custom method in GoogleFragment and access it through getSupportFragmentManager().findFragmentByTag()

But is there maybe a simpler solution?

UPDATE:

I've changed the mResolvingError field to public and have tried this code:

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

but I am not sure how to test this properly and if f.isVisible() is needed there...

UPDATE 2:

Maybe I should somehow use DialogInterface.OnDismissListener with GoogleApiAvailability.getInstance().getErrorDialog in my code?


Solution

  • BladeCoder's comments have been very insightful, thanks.

    However I have realized, that all the hassle with saving and restoring mResolvingError is unnecessary, because startResolutionForResult() starts a separate Activity anyway and obstructs my app - so it doesn't really matter if I rotate device or not.

    Here is my final code to initiate GCM and fetch Google+ user data -

    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();
            }
        }
    }