I've been looking for a solution to this and I came across several answers that didn't fit the use-case. The sample code from Google works fine if imported as is, but the code sits in an Activity and is messy if you want to integrate into a real project where the code would ideally be in a fragment.
The issue is with the enableAutoManage functionality and what to do in the fresh install case where the googleApiClient is built but the authentication/account-pick screen is not yet done and you end up with connection failed on the googleApiClient. I'm going to answer this in full, below.
Here is a sample Activity that will load the GoogleFit_Fragment into the FrameLayout:
public class Main_Activity extends AppCompatActivity {
public static int USER_AUTHORISED_REQUEST_CODE = 5;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
This will load the GoogleFit_Fragment into the FrameLayout,
which will cause the buildFitnessClient() function to be called from the Fragment,
which will cause this Activity to popup the authentication screen
*/
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
.replace(R.id.fragment_container, new GoogleFit_Fragment()).commit();
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
/*
The result of the account chooser is here,
send it to the fragment so we can handle it inside
*/
fragmentReplace.onActivityResult(USER_AUTHORISED_REQUEST_CODE, resultCode, data);
}
}
The GoogleFit_Fragment:
public class GoogleFit_Fragment extends Fragment {
public static final String TAG = "Sample";
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 1;
private GoogleApiClient googleApiFitnessClient;
private OnDataPointListener mListener;
private View fragmentView;
private Activity parentActivity;
#Override
public void onStop() {
super.onStop();
if (googleApiFitnessClient != null) {
Log.d(TAG, "onStop REACHED, client not null and is connected");
googleApiFitnessClient.stopAutoManage(getActivity());
googleApiFitnessClient.disconnect();
}
}
#Override
public void onResume() {
super.onResume();
if (googleApiFitnessClient != null) {
Log.d(TAG, "onResume REACHED, client not null");
googleApiFitnessClient.stopAutoManage(getActivity());
googleApiFitnessClient.disconnect();
googleApiFitnessClient.connect();
} else {
Log.d(TAG, "onResume REACHED, client null, buildingClient");
buildFitnessClient();
googleApiFitnessClient.connect();
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity) {
parentActivity = (Activity) context;
}
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_google_fit, container, false);
initializeLogging();
if (!checkPermissions()) {
requestPermissions();
}
return fragmentView;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Main_Activity.USER_AUTHORISED_REQUEST_CODE && googleApiFitnessClient != null) {
Log.d(TAG, "Activity result finished authorissation, disconnect the client and reconnect");
googleApiFitnessClient.stopAutoManage(getActivity());
googleApiFitnessClient.disconnect();
googleApiFitnessClient.connect();
}
}
private void buildFitnessClient() {
googleApiFitnessClient = new GoogleApiClient.Builder(parentActivity)
.addApi(Fitness.SENSORS_API)
// .addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
// .addScope(new Scope(Scopes.FITNESS_NUTRITION_READ_WRITE))
// .addScope(new Scope(Scopes.FITNESS_BODY_READ_WRITE))
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected!!!");
// Now you can make calls to the Fitness APIs.
findFitnessDataSources();
}
#Override
public void onConnectionSuspended(int i) {
// If your connection to the sensor gets lost at some point,
// you'll be able to determine the reason and react to it here.
if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
Log.i(TAG, "Connection lost. Cause: Network Lost.");
} else if (i
== GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
Log.i(TAG,
"Connection lost. Reason: Service Disconnected");
}
}
}
)
.addOnConnectionFailedListener(
new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "Connection failed! " + connectionResult.getErrorMessage());
}
}
)
.enableAutoManage((FragmentActivity) parentActivity, 0, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Google Play services connection failed. Cause: " +
result.toString());
}
})
.build();
}
private void findFitnessDataSources() {
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
Fitness.SensorsApi.findDataSources(googleApiFitnessClient, new DataSourcesRequest.Builder()
// At least one datatype must be specified.
.setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
// Can specify whether data type is raw or derived.
.setDataSourceTypes(DataSource.TYPE_RAW)
.build())
.setResultCallback(new ResultCallback<DataSourcesResult>() {
#Override
public void onResult(DataSourcesResult dataSourcesResult) {
Log.i(TAG, "Result: " + dataSourcesResult.getStatus().toString());
for (DataSource dataSource : dataSourcesResult.getDataSources()) {
Log.i(TAG, "Data source found: " + dataSource.toString());
Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());
//Let's register a listener to receive Activity data!
if (dataSource.getDataType().equals(DataType.TYPE_STEP_COUNT_CUMULATIVE)
&& mListener == null) {
Log.i(TAG, "Data source for " + dataSource.getDataType() + " found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_STEP_COUNT_CUMULATIVE);
}
}
}
});
}
/**
* Register a listener with the Sensors API for the provided {#link DataSource} and
* {#link DataType} combo.
*/
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
mListener = new OnDataPointListener() {
#Override
public void onDataPoint(DataPoint dataPoint) {
for (Field field : dataPoint.getDataType().getFields()) {
Value val = dataPoint.getValue(field);
Log.i(TAG, "Detected DataPoint field: " + field.getName());
Log.i(TAG, "Detected DataPoint value: " + val);
}
}
};
Fitness.SensorsApi.add(
googleApiFitnessClient,
new SensorRequest.Builder()
.setDataSource(dataSource) // Optional but recommended for custom data sets.
.setDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE) // Can't be omitted.
.setSamplingRate(1, TimeUnit.SECONDS)
.build(),
mListener)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Listener registered!");
} else {
Log.i(TAG, "Listener not registered.");
}
}
});
}
private void initializeLogging() {
/*
Not really needed, you can just log to Logcat without having a view in this fragment
*/
LogWrapper logWrapper = new LogWrapper();
Log.setLogNode(logWrapper);
MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
logWrapper.setNext(msgFilter);
LogView logView = (LogView) fragmentView.findViewById(R.id.sample_logview);
logView.setTextAppearance(parentActivity, R.style.Log);
logView.setBackgroundColor(Color.WHITE);
msgFilter.setNext(logView);
Log.i(TAG, "Ready");
}
/**
* Return the current state of the permissions needed.
*/
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(parentActivity,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(parentActivity,
Manifest.permission.ACCESS_FINE_LOCATION);
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
fragmentView.findViewById(R.id.main_activity_view),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(parentActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
ActivityCompat.requestPermissions(parentActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
buildFitnessClient();
} else {
Snackbar.make(
fragmentView.findViewById(R.id.main_activity_view),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
}
}
Flow:
Launch app
Google API client is built, causing a connection failed callback, but also causing the Account Picker dialog to appear
Account is picked, onActivityResult from Activity is called, result is sent to onActivityResult in Fragment
Google API client is restarted (stopAutoManage, disconnect, connect)
Logging starts STEP_COUNT_CUMULATIVE or any other data type you choose
This works and is tested a lot, please ask if you're unsure of anything
Related
Can't connect to the google api, the result on the monitor is:
E/GoogleFit: RESULT_CANCELED (this message only appears after select the google account)
Obviously, i have the internet permission on my app.
on the google developer console, the name of the package is the same of my project.
Here is the code:
public class MainActivity extends AppCompatActivity implements OnDataPointListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private static final int REQUEST_OAUTH = 1;
private static final String AUTH_PENDING = "auth_state_pending";
private boolean authInProgress = false;
private GoogleApiClient mApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
authInProgress = savedInstanceState.getBoolean(AUTH_PENDING);
}
mApiClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.SENSORS_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
.addConnectionCallbacks(this).addOnConnectionFailedListener(this)
.build(); {
}
}
#Override
protected void onStart() {
super.onStart();
mApiClient.connect();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
DataSourcesRequest dataSourcesRequest = new DataSourcesRequest.Builder()
.setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
.setDataSourceTypes(DataSource.TYPE_RAW)
.build();
ResultCallback<DataSourcesResult> dataSourcesResultResultCallback = new ResultCallback<DataSourcesResult>() {
#Override
public void onResult(#NonNull DataSourcesResult dataSourcesResult) {
for(DataSource dataSource : dataSourcesResult.getDataSources()){
if(DataType.TYPE_STEP_COUNT_CUMULATIVE.equals(dataSource.getDataType())){
registerFitnessDataListener(dataSource, DataType.TYPE_STEP_COUNT_CUMULATIVE);
}
}
}
};
Fitness.SensorsApi.findDataSources(mApiClient, dataSourcesRequest).setResultCallback(dataSourcesResultResultCallback);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if(!authInProgress){
try{
authInProgress = true;
connectionResult.startResolutionForResult(MainActivity.this, REQUEST_OAUTH);
}catch (IntentSender.SendIntentException e){
}
}else{
Log.e("GoogleFit", "AuthInProgress");
}
}
#Override
public void onDataPoint(DataPoint dataPoint) {
for(final Field field: dataPoint.getDataType().getFields()){
final Value value = dataPoint.getValue(field);
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),"Field"+field.getName()+"Value:"+ value,Toast.LENGTH_SHORT).show();
}
});
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_OAUTH){
authInProgress = false;
if(resultCode == RESULT_OK){
if(!mApiClient.isConnecting() && !mApiClient.isConnected()){
mApiClient.connect();
}
}else if(resultCode == RESULT_CANCELED){
Log.e("GoogleFit", "RESULT_CANCELED");
}
}else {
Log.e("GoogleFit", "requestCode NOT request_oauth");
}
super.onActivityResult(requestCode, resultCode, data);
}
private void registerFitnessDataListener(DataSource dataSource, DataType dataType){
SensorRequest request = new SensorRequest.Builder()
.setDataSource(dataSource)
.setDataType(dataType)
.setSamplingRate(3, TimeUnit.SECONDS)
.build();
Fitness.SensorsApi.add(mApiClient, request, this)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if(status.isSuccess()){
Log.e("GoogleFit", "SensorApi succesfully added");
}
}
});
}
#Override
protected void onStop() {
super.onStop();
Fitness.SensorsApi.remove(mApiClient, this)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if(status.isSuccess()){
mApiClient.disconnect();
}
}
});
}
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outState.putBoolean(AUTH_PENDING, authInProgress);
}
}
Fixed problem:
Well, i've made some investigations and I found a solution.
The code is working good, the problem was on the OAuthClient.
Steps to avoid the problem:
Step 1:
Go to Google development console and enable the API (Fitness API in my case),it's very important i this step, to make ensure that you create the OAuth credentials, just use the command given and write the SHA1
Step 2:
Go to Firebase Console and follow the steps
Step 3:
Download the JSON and put in your app folder (in your project)
Step 4:
Create a metadata like this in the manifest:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="-key of your JSON-" />
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
Hope it helps for the others! this solution worked for me!
Make sure that you are using the correct OAuth 2.0 Client ID code. This may lead to an error if you're using it wrong. Also make sure that you have updated the authentication flow based from the latest documentation for Google Sign-In.
You may refer with these SO threads: Google Oauth 2.0 RESULT_CANCELED while using Google Fit api and GoogleApiClient not connecting while using google fit.
I have login google plus, but when i login success, and in the new activity, i logout,after i login again,it is not success login.
Log:ClearcutLoggerApiImpl: disconnect managed GoogleApiClient
It is code:
GoogleSingleton:
public class GoogleSingleton {
private ILogInGoogleListener mListener = null;
public void setLogInGooglePlusListener(final ILogInGoogleListener listener) {
this.mListener = listener;
}
public interface ILogInGoogleListener {
void logInGooglePlusSuccess(String name,
String email,
String pid);
}
private static GoogleSingleton googleSingleton;
private final GoogleApiClient mGoogleApiClient;
private ConnectionResult mConnectionResult;
public static final int RC_SIGN_IN = 321;
private Activity activity;
private GoogleSingleton(final Activity activity) {
this.activity = activity;
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(final Bundle bundle) {
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
System.out.println(">>>> Log in xong");
Person currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName =
currentPerson.getDisplayName();
String email =
Plus.AccountApi.getAccountName(mGoogleApiClient);
String id = currentPerson.getId();
if (mListener != null) {
mListener.logInGooglePlusSuccess(personName,
email,
id);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// Update the UI after signin
}
#Override
public void onConnectionSuspended(final int i) {
mGoogleApiClient.connect();
System.out.println(">>>> chua log in");
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(final ConnectionResult result) {
if (!result.hasResolution()) {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),
activity,
0)
.show();
}
mConnectionResult = result;
}
})
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.build();
}
public static GoogleSingleton getInstance(Activity activity) {
if (googleSingleton == null) {
googleSingleton = new GoogleSingleton(activity);
}
return googleSingleton;
}
public void connectGoogle() {
mGoogleApiClient.connect();
}
public void disConnectGoogle() {
mGoogleApiClient.disconnect();
}
public boolean isConnecting() {
return mGoogleApiClient.isConnecting();
}
public boolean isConnected() {
return mGoogleApiClient.isConnected();
}
public void login() {
System.out.println(">>>>>> check login Singleton");
if (!mGoogleApiClient.isConnecting()) {
if (mConnectionResult != null && mConnectionResult.hasResolution()) {
System.out.println(">>>>>> check login Singleton Try:" + mGoogleApiClient.isConnected() + "__" + mConnectionResult.hasResolution());
try {
mConnectionResult.startResolutionForResult(activity,
RC_SIGN_IN);
System.out.println(">>>>>> abce");
} catch (IntentSender.SendIntentException e) {
mGoogleApiClient.connect();
}
}
} else {
System.out.println(">>>>>> check login Singleton:" + mGoogleApiClient.isConnected() + "__" + mConnectionResult.hasResolution());
}
}
public void logOut() {
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
//set text button google plus.
}
}
}
Activity 1:
LogIn:
/**
* init google plus.
* - init api client.
* - fill data when success.
* - set text when login success.
*/
private void initGooglePlus() {
GoogleSingleton.getInstance(this)
.connectGoogle();
GoogleSingleton.getInstance(this)
.setLogInGooglePlusListener(new GoogleSingleton.ILogInGoogleListener() {
#Override
public void logInGooglePlusSuccess(final String name,
final String email,
final String pid) {
System.out.println(">>>>> check name:"+name);
logInGooglePlus(name,
email,
pid);
}
});
}
/**
* - Sign in google.
* - check null button,and check have sign in.
* - when logout,set data model user local empty.
* - if have sign in, click sign out,reverse.
*
* #param view
* This is button google.
*/
#OnClick(R.id.btnDangNhapGoogle)
protected void setOnDangNhapClicked(View view) {
//sign in and sign out google.
if (btnDangNhapGooglePlus != null) {
//log out
if (btnDangNhapGooglePlus.getText()
.equals(getResources().getString(R.string.text_log_out))) {
if (GoogleSingleton.getInstance(this)
.isConnected()) {
//Log out.
GoogleSingleton.getInstance(this)
.logOut();
//set text button google plus.
btnDangNhapGooglePlus.setText(getResources().getString(R.string.text_dang_nhap_bang_google_plus));
//set null user model.
LocalStorage.getShareInstance(SignInActivity.this)
.saveUserModel(new UserModel());
}
}//log in
else {
if (!GoogleSingleton.getInstance(this)
.isConnecting()) {
GoogleSingleton.getInstance(this)
.login();
}
}
}
}
/**
* - activity result of facebook, google plus.
* - when logout from account activity, logout all social, and set local data user empty.
*
* #param requestCode
* This is request.
* #param resultCode
* This is result.
* #param data
* This is data.
*/
#Override
protected void onActivityResult(final int requestCode,
final int resultCode,
final Intent data) {
super.onActivityResult(requestCode,
resultCode,
data);
mCallbackManager.onActivityResult(requestCode,
resultCode,
data);
System.out.println(">>>>>> connect requecode"+requestCode);
if (requestCode == GoogleSingleton.RC_SIGN_IN) {
System.out.println(">>>>>> connect RC_SIGN_IN");
if (!GoogleSingleton.getInstance(this)
.isConnecting()) {
System.out.println(">>>>>> connect vvvvv");
mIsCheckLoginGoogle = true;
GoogleSingleton.getInstance(this)
.connectGoogle();
}
}
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_LOGOUT_ALL) {
System.out.println(">>>>>> check result logout code");
logOutFaceBookAndGooglePlusResult();
//set null user model.
LocalStorage.getShareInstance(SignInActivity.this)
.saveUserModel(new UserModel());
}
}
/**
* - check facebook and google plus.
* - logout facebook and google plus.
* - set text google plus.
*/
private void logOutFaceBookAndGooglePlusResult() {
if (GoogleSingleton.getInstance(this)
.isConnected()) {
GoogleSingleton.getInstance(this)
.logOut();
//set text button google plus.
btnDangNhapGooglePlus.setText(getResources().getString(R.string.text_dang_nhap_bang_google_plus));
}
//log out facebook.
if (AccessToken.getCurrentAccessToken() != null) {
LoginManager.getInstance()
.logOut();
}
}
Activity 2:
//init google connect.
GoogleSingleton.getInstance(this).connectGoogle();
//logout google.
GoogleSingleton.getInstance(this).logOut();
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:
Using LocalBroadcastManager to send a custom Intent from ErrorDialogFragment to GoogleFragment
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?
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 -
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();
}
}
}
I'm trying to implement in-app purchases on fragments. I used the tutorial http://twigstechtips.blogspot.com/2013/12/android-setting-up-in-app-billing-api.html including modifying IabHelper. I also needed to modify MainActivity:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
PremiumFragment premiumFragment = new PremiumFragment();
premiumFragment.onActivityResult(requestCode, resultCode, data);
}
to send the result to a fragment. Problem is when google billing dialog box disappears, I need manualy reload fragment, to call OnIabPurchaseFinishedListener. My code:
public class PremiumFragment extends Fragment implements NamedTopBar,
OnClickListener {
static final String ITEM_SKU_SUBSCRIPTION = "test";
static final int RC_REQUEST = 10001;
IabHelper iabHelper;
private Button subscriptionButton;
private boolean mBillingServiceReady;
private boolean subBool;
// Callback for when a purchase is finished
private IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
#Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "mPurchaseFinishedListener");
// if we were disposed of in the meantime, quit.
if (iabHelper == null) {
return;
}
// Don't complain if cancelling
if (result.getResponse() == IabHelper.IABHELPER_USER_CANCELLED) {
Log.d(TAG, "User canceled purchase");
return;
}
if (!result.isSuccess()) {
Log.d(TAG, "Error purchasing: " + result.getMessage());
if (result.getResponse() == 7) {
Toast.makeText(getActivity(), "item already purchased!",
Toast.LENGTH_LONG).show();
}
return;
}
if (!verifyDeveloperPayload(purchase)) {
Log.d(TAG,
"Error purchasing. Authenticity verification failed.");
return;
}
// Purchase was success! Update accordingly
if (purchase.getSku().equals(ITEM_SKU_SUBSCRIPTION)) {
Toast.makeText(getActivity(),
"Thank you for buying SUBSCRIPTION!", Toast.LENGTH_LONG)
.show();
subBool = true;
}
initialiseStuff(subBool);
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
// if we were disposed of in the meantime, quit.
if (iabHelper == null) {
return;
}
if (result.isSuccess()) {
iabHelper.queryInventoryAsync(iabInventoryListener());
} else {
Log.d(TAG, "Error while consuming: " + result);
}
// Update the UI to reflect their latest purchase
initialiseStuff(subBool);
}
};
private View window;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
Log.d(TAG, "onCreate");
initialiseBilling();
}
private void initialiseBilling() {
if (iabHelper != null) {
return;
}
String base64EncodedPublicKey = "";
iabHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
iabHelper.enableDebugLogging(false);
Log.d(TAG, "Starting setup.");
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
// Have we been disposed of in the meantime? If so, quit.
if (iabHelper == null) {
return;
}
// Something went wrong
if (!result.isSuccess()) {
Log.d(TAG,
"Problem setting up in-app billing: "
+ result.getMessage()
);
return;
}
// IAB is fully set up. Now, let's get an inventory of stuff we
// own.
Log.d(TAG, "In-app Billing is set up OK");
mBillingServiceReady = true;
iabHelper.queryInventoryAsync(iabInventoryListener());
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
window = inflater.inflate(R.layout.premium_fragment, null);
initViews();
return window;
}
#Override
public void onDestroy() {
super.onDestroy();
if (iabHelper != null) {
iabHelper.dispose();
iabHelper = null;
}
}
private void initViews() {
subscriptionButton = (Button) window
.findViewById(R.id.buy_premium_button);
subscriptionButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buy_premium_button:
Log.d(TAG, "buy_premium_button");
if (mBillingServiceReady) {
/*
* TODO: for security, generate your payload here for
* verification. See the comments on verifyDeveloperPayload()
* for more info. Since this is a SAMPLE, we just use an empty
* string, but on a production app you should carefully generate
* this.
*/
if (iabHelper != null)
iabHelper.flagEndAsync();
String payload = "";
iabHelper.launchPurchaseFlow(getActivity(),
ITEM_SKU_SUBSCRIPTION, RC_REQUEST,
mPurchaseFinishedListener, payload);
} else {
showToast();
}
break;
}
}
private void showToast() {
Toast.makeText(
getActivity(),
"Purchase requires Google Play Store (billing) on your Android.",
Toast.LENGTH_LONG).show();
}
private IabHelper.QueryInventoryFinishedListener iabInventoryListener() {
return new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
// Have we been disposed of in the meantime? If so, quit.
if (iabHelper == null) {
return;
}
// Something went wrong
if (!result.isSuccess()) {
Log.d(TAG,
"onFailure: QueryInventoryFinishedListener," +
"result: " + result
);
return;
}
Log.d(TAG, "Checking for purchases");
Purchase purchaseSubscription = inventory
.getPurchase(ITEM_SKU_SUBSCRIPTION);
subBool = (purchaseSubscription != null && verifyDeveloperPayload(purchaseSubscription));
initialiseStuff(subBool);
}
};
}
private void initialiseStuff(boolean subBool) {
if (subBool) {
subscriptionButton.setText("You already have premium");
subscriptionButton.setEnabled(false);
} else {
subscriptionButton.setEnabled(true);
}
}
boolean verifyDeveloperPayload(Purchase purchase) {
// String payload = p.getDeveloperPayload();
return true;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (iabHelper == null)
return;
// Pass on the activity result to the helper for handling
if (!iabHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
} else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
How to call mPurchaseFinishedListener automatically after dialog box disappears?
I am working with google+ login to my application and when i done it using a activity its work charm and after that i move my code into a fragment and after that when i try to login to google+ its not working i have to open the fragment activity 2 times to login to the google+ can anyone tell me what happen the code to the fragment is added below
public class GooglePluseFragment extends Fragment implements
ConnectionCallbacks, OnConnectionFailedListener {
private static final int RC_SIGN_IN = 0;
private static final String TAG = "MainActivity";
private static final int PROFILE_PIC_SIZE = 800;
private GoogleApiClient mGoogleApiClient;
private boolean mIntentInProgress;
private boolean mSignInClicked;
private ConnectionResult mConnectionResult;
private SignInButton btnSignIn;
private Button btnSignOut;
private Context mContext;
private Activity mActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = getActivity();
mContext = getActivity().getApplicationContext();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.compund_google_pluse_fragment,
container, false);
btnSignIn = (SignInButton) view.findViewById(R.id.btn_sign_in);
btnSignOut = (Button) view.findViewById(R.id.btn_sign_out);
sharedPref = view.getContext().getSharedPreferences(
Constantz.SHEARED_PREFEREANCE, Context.MODE_PRIVATE);
mGoogleApiClient = new GoogleApiClient.Builder(view.getContext())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).addApi(Plus.API, null)
.addScope(Plus.SCOPE_PLUS_LOGIN).build();
btnSignIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
signInWithGplus();
}
});
btnSignOut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
signOutFromGplus();
}
});
return view;
}
#Override
public void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
public void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
#Override
public void onActivityResult(int requestCode, int responseCode,
Intent intent) {
if (requestCode == RC_SIGN_IN) {
if (responseCode != Activity.RESULT_OK) {
mSignInClicked = false;
}
mIntentInProgress = false;
if (!mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),
mActivity, 0).show();
Log.e(TAG, "" + result.getErrorCode());
return;
}
if (!mIntentInProgress) {
mConnectionResult = result;
if (mSignInClicked) {
Log.e(TAG, "" + result.getErrorCode());
resolveSignInError();
}
}
}
#Override
public void onConnected(Bundle arg0) {
mSignInClicked = false;
getProfileInformation();
updateUI(true);
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
updateUI(false);
}
private void updateUI(boolean isSignedIn) {
if (isSignedIn) {
btnSignIn.setVisibility(View.GONE);
btnSignOut.setVisibility(View.VISIBLE);
} else {
btnSignIn.setVisibility(View.VISIBLE);
btnSignOut.setVisibility(View.GONE);
}
}
/**
* Sign-in into google
* */
private void signInWithGplus() {
if (!mGoogleApiClient.isConnecting()) {
mSignInClicked = true;
resolveSignInError();
}
}
/**
* Method to resolve any signin errors
* */
private void resolveSignInError() {
if (mConnectionResult.hasResolution()) {
try {
mIntentInProgress = true;
mConnectionResult.startResolutionForResult(mActivity,
RC_SIGN_IN);
} catch (SendIntentException e) {
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
}
/**
* Fetching user's information name, email, profile pic
* */
private void getProfileInformation() {
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
Person currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
Log.e(TAG, "Name: " + personName + ", plusProfile: "
+ personGooglePlusProfile + ", email: " + email
+ ", Image: " + personPhotoUrl + " user id:"
+ currentPerson.getId());
} else {
Toast.makeText(mContext, "Person information is null",
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Sign-out from google
* */
private void signOutFromGplus() {
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
updateUI(false);
}
}
}
this is how i added framgent in the fragment activity
pluseFragment = new GooglePluseFragment();
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.pluse_frame_layout, pluseFragment);
transaction.commit();
can somebody tell me what i have done wrong ? why i have to open the activity two times to login thank you
Finally found the answer, Problem was when the result activity call in the fragment was catch by the parent activity so you have to manually redirect the result to your fragment. Just have to add this line in your Parent Fragment Activity
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == GooglePluseFragment.RC_SIGN_IN) {
GooglePluseFragment fragment = (GooglePluseFragment) getSupportFragmentManager()
.findFragmentById(R.id.pluse_frame_layout);
fragment.onActivityResult(requestCode, resultCode, data);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
Easy Solution for this ::
just create the static method in fragment
"
public static myOnActivityResult(int requestCode, int resultCode, Intent data){
.....Enter same code
}
and
call this method from Parant Activity on
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
.....
MyFragment.myOnActivityResult(requestCode,resultCode,data)
}
thats it