Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new callback interfaces split from DeployGateCallback #95

Merged
merged 23 commits into from
Aug 20, 2024

Conversation

satsukies
Copy link
Member

@satsukies satsukies commented Aug 20, 2024

We will plan to implement a new callback method for the Capture feature.
Expanding the current callback interface is difficult because we cannot avoid causing breaking changes for many users.
We will maybe have the same problem in the future when planning for new features with callbacks.

So, we separate the DeployGateCallback interface into multiple interfaces.
When new interfaces are added in the future, it will be possible to provide them without affecting users who do not need them.

We take care in making changes to avoid breaking changes.

@satsukies satsukies self-assigned this Aug 20, 2024
.setEnabledOnNonDebuggableBuild(true)
.build();

DeployGate.install(this, configuration);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use install(Context, DeployGateSdkConfiguration) because install(Application, DeployGateCallback, boolean) is deprecated.

*/
public interface DeployGateCallback {
@Deprecated
public interface DeployGateCallback extends DeployGateInitializeCallback, DeployGateStatusChangeCallback, DeployGateUpdateAvailableCallback {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate to DeployGateInitializeCallback, DeployGateStatusChangeCallback, DeployGateUpdateAvailableCallback.

Now, we marked it deprecated.
We can still use this interface, but we plan to delete it in the next major update.

Comment on lines 68 to 71
private final HashSet<DeployGateCallback> mCallbacks;
private final HashSet<DeployGateInitializeCallback> mInitializeCallbacks;
private final HashSet<DeployGateStatusChangeCallback> mStatusChangeCallbacks;
private final HashSet<DeployGateUpdateAvailableCallback> mUpdateAvailableCallbacks;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose the implementation to keep mCallbacks to minimize the effect on existing code.
It will be deleted with DeployGateCallback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it means minimizing changes to the current code but not minimizing the effect.

  • The current changes mean we are going to keep maintaining two callback mechanisms until we can completely delete the deprecated entities, even tough the new mechanism is compatible with the legacy one.
  • The current changes allows at most two callbacks like DeployGateInitializeCallback at the initialization time. It's not a planned feature.

Please remove mCallbacks property and dispatch the callback registration to each property. It will be completely the same behaviour as the current behaviour i.e. it can minimize the effect.

Copy link
Member Author

@satsukies satsukies Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on minimizing code changes.
I understood it is important to minimize behavior changes same behavior as the current behavior in this case.

I'll fix it.

Comment on lines 198 to 212
// call onInitialized on each callbacks
for (DeployGateCallback callback : mCallbacks) {
callback.onInitialized(true);
}
for (DeployGateInitializeCallback callback : mInitializeCallbacks) {
callback.onInitialized(true);
}

// after call onInitialized, then call onStatusChanged
for (DeployGateCallback callback : mCallbacks) {
callback.onStatusChanged(isManaged, isAuthorized, loginUsername, isStopped);
}
for (DeployGateStatusChangeCallback callback : mStatusChangeCallbacks) {
callback.onStatusChanged(isManaged, isAuthorized, loginUsername, isStopped);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep order of call callback methods.
We call onInitializad() first, after that call onStatusCahnged().

Comment on lines 307 to 321
// call onInitialized on each callbacks
for (DeployGateCallback callback : mCallbacks) {
callback.onInitialized(false);
}
for (DeployGateInitializeCallback callback : mInitializeCallbacks) {
callback.onInitialized(false);
}

// after call onInitialized, then call onStatusChanged
for (DeployGateCallback callback : mCallbacks) {
callback.onStatusChanged(false, false, null, false);
}
for (DeployGateStatusChangeCallback callback : mStatusChangeCallbacks) {
callback.onStatusChanged(false, false, null, false);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added new testcases for DeployGateSdkConfigration.
It checks all public methods are defined each modules(sdk, sdk-mock).

@satsukies satsukies requested a review from jmatsu August 20, 2024 01:18
Comment on lines 20 to 24
* @deprecated since 4.9.0. Use {@link DeployGateInitializeCallback#onInitialized(boolean)} instead.
*/
@Deprecated
@Override
public void onInitialized(boolean isServiceAvailable);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also delete this method and delegate it to each extended class.
In this case, we cannot mark deprecated to methods in X.

Copy link
Contributor

@jmatsu jmatsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decomposing the current Callback class sounds great.

Please fix unwanted breaking changes when deprecating methods.

@@ -0,0 +1,23 @@
package com.deploygate.sdk;

public interface DeployGateStatusChangeCallback {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add javadocs to new classes.

@@ -62,6 +73,9 @@ public static final class Builder {
private boolean isCaptureEnabled = true;

private DeployGateCallback callback = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove here.

*/
@Deprecated
public Builder setCallback(DeployGateCallback callback) {
this.callback = callback;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the current behaviour, at most one callback for each should be allowed.

Suggested change
this.callback = callback;
this.initializeCallback = callback;
this.statusChangeCallback = callback;
this.updateAvailableCallback = callback;

@@ -12,8 +12,10 @@ public final class DeployGateSdkConfiguration {

final boolean isCrashReportingEnabled;


final DeployGateCallback callback;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove here.

*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Please deprecate these methods in sdk-mock as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't add a comment to registerCallbackInternal method so let me write it here.

Please fix registerCallbackInternal to register a DeployGateCallback to each property.

*/
public static void registerInitializeCallback(
DeployGateInitializeCallback callback,
boolean refreshImmediately
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to provide methods with boolean refreshImmediately anymore. Alternatively, users should call refresh manually. It will also be able to avoid leading users to many refresh requests hell.

Comment on lines 68 to 71
private final HashSet<DeployGateCallback> mCallbacks;
private final HashSet<DeployGateInitializeCallback> mInitializeCallbacks;
private final HashSet<DeployGateStatusChangeCallback> mStatusChangeCallbacks;
private final HashSet<DeployGateUpdateAvailableCallback> mUpdateAvailableCallbacks;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it means minimizing changes to the current code but not minimizing the effect.

  • The current changes mean we are going to keep maintaining two callback mechanisms until we can completely delete the deprecated entities, even tough the new mechanism is compatible with the legacy one.
  • The current changes allows at most two callbacks like DeployGateInitializeCallback at the initialization time. It's not a planned feature.

Please remove mCallbacks property and dispatch the callback registration to each property. It will be completely the same behaviour as the current behaviour i.e. it can minimize the effect.

Comment on lines 75 to 77
// with setAuthor method when creating DeployGate SdkConfiguration. like:
//
// DeployGate.install(this, "YOURUSERNAME");
// builder.setAuthor("YOURUSERNAME")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

describe to use setAuthor(String) method defined in DeployGateSdkConfuration.Builder class because DeployGate.install(Application, String) is deprecated.

@satsukies
Copy link
Member Author

@jmatsu
thank you for the review.
I fixed it according to your comments. Please re-check my code.

@satsukies satsukies requested a review from jmatsu August 20, 2024 04:08
Copy link
Contributor

@jmatsu jmatsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you wrap a DeployGateCallback by anonymous classes?

@Override
public void onInitialized(boolean isServiceAvailable) {
DeployGateSdkConfiguration configuration = new DeployGateSdkConfiguration.Builder()
// Please note that these callback is called if you have removed the content provider.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iff is not a typo by the way. It's an abbreviation of if and only if.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh.. reverted this change.

Comment on lines 969 to 986
mInitializeCallbacks.add(new DeployGateInitializeCallback() {
@Override
public void onInitialized(boolean isServiceAvailable) {
listener.onInitialized(isServiceAvailable);
}
});
mStatusChangeCallbacks.add(new DeployGateStatusChangeCallback() {
@Override
public void onStatusChanged(boolean isManaged, boolean isAuthorized, String loginUsername, boolean isStopped) {
listener.onStatusChanged(isManaged, isAuthorized, loginUsername, isStopped);
}
});
mUpdateAvailableCallbacks.add(new DeployGateUpdateAvailableCallback() {
@Override
public void onUpdateAvailable(int revision, String versionName, int versionCode) {
listener.onUpdateAvailable(revision, versionName, versionCode);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be compilable. What happened?

Suggested change
mInitializeCallbacks.add(new DeployGateInitializeCallback() {
@Override
public void onInitialized(boolean isServiceAvailable) {
listener.onInitialized(isServiceAvailable);
}
});
mStatusChangeCallbacks.add(new DeployGateStatusChangeCallback() {
@Override
public void onStatusChanged(boolean isManaged, boolean isAuthorized, String loginUsername, boolean isStopped) {
listener.onStatusChanged(isManaged, isAuthorized, loginUsername, isStopped);
}
});
mUpdateAvailableCallbacks.add(new DeployGateUpdateAvailableCallback() {
@Override
public void onUpdateAvailable(int revision, String versionName, int versionCode) {
listener.onUpdateAvailable(revision, versionName, versionCode);
}
});
mInitializeCallbacks.add(listener);
mStatusChangeCallbacks.add(listener);
mUpdateAvailableCallbacks.add(listener);

The current code breaks unregister methods because their identities have been changed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry.
I added new testcases and checked keeping the behavior between before and after.

if (sInstance == null) {
return;
}
if (listener == null) {
return;
}

sInstance.mCallbacks.remove(listener);
sInstance.mInitializeCallbacks.remove(new DeployGateInitializeCallback() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These codes are completely wrong. Please re-check what this code expects.

By the way, the test job passed so the test codes of unregister methods are also broken or not tested. Could you please fix them as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No testcase for register/unregister methods exists.
Added it and fixed my code causing unexpected behavior.

/**
* A callback interface to receive DeployGate events. Implement this and pass to
* {@link DeployGate#registerCallback(DeployGateCallback, boolean)} to listen.
*
* @author tnj
* @deprecated since 4.9.0. Use each extended callback interfaces instead.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @deprecated since 4.9.0. Use each extended callback interfaces instead.
* @deprecated since 4.9.0. Use fine-grained callback interfaces for each purpose.

Comment on lines 4 to 5
* A callback interface to receive DeployGate service initialization events.Implement this and pass to
* {@link DeployGate#registerInitializeCallback(DeployGateInitializeCallback)} to listen.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be hard to maintain comments if you embedded the detailed usages. see annotation is enough.

Suggested change
* A callback interface to receive DeployGate service initialization events.Implement this and pass to
* {@link DeployGate#registerInitializeCallback(DeployGateInitializeCallback)} to listen.
* A callback interface to receive DeployGate service initialization events.

this.callback = callback;
@Deprecated
public Builder setCallback(final DeployGateCallback callback) {
this.setInitializeCallback(new DeployGateInitializeCallback() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@satsukies
Copy link
Member Author

@jmatsu
Sorry, the behavior was broken as mentioned in the comments from you.

I added some testcases for check behavior of DeployGate#register, DeployGate#unregister, and DeployGateSdkConfiguration.Builder class.

1d45993...190ef4c

Please re-check this PR. Thanks.

@satsukies satsukies requested a review from jmatsu August 20, 2024 10:41
Comment on lines 1654 to 1664
static HashSet<DeployGateInitializeCallback> getInitializeCallbacks() {
return sInstance != null ? sInstance.mInitializeCallbacks : null;
}

static HashSet<DeployGateStatusChangeCallback> getStatusChangeCallbacks() {
return sInstance != null ? sInstance.mStatusChangeCallbacks : null;
}

static HashSet<DeployGateUpdateAvailableCallback> getUpdateAvailableCallbacks() {
return sInstance != null ? sInstance.mUpdateAvailableCallbacks : null;
}
Copy link
Member Author

@satsukies satsukies Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some test cases use these package-private methods.

Copy link
Contributor

@jmatsu jmatsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost great. Only a few fix is required.

Comment on lines 1654 to 1664
static HashSet<DeployGateInitializeCallback> getInitializeCallbacks() {
return sInstance != null ? sInstance.mInitializeCallbacks : null;
}

static HashSet<DeployGateStatusChangeCallback> getStatusChangeCallbacks() {
return sInstance != null ? sInstance.mStatusChangeCallbacks : null;
}

static HashSet<DeployGateUpdateAvailableCallback> getUpdateAvailableCallbacks() {
return sInstance != null ? sInstance.mUpdateAvailableCallbacks : null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static HashSet<DeployGateInitializeCallback> getInitializeCallbacks() {
return sInstance != null ? sInstance.mInitializeCallbacks : null;
}
static HashSet<DeployGateStatusChangeCallback> getStatusChangeCallbacks() {
return sInstance != null ? sInstance.mStatusChangeCallbacks : null;
}
static HashSet<DeployGateUpdateAvailableCallback> getUpdateAvailableCallbacks() {
return sInstance != null ? sInstance.mUpdateAvailableCallbacks : null;
}
/**
* Not a public API. This is only for testing.
*/
static HashSet<DeployGateInitializeCallback> getInitializeCallbacks() {
return sInstance != null ? sInstance.mInitializeCallbacks : null;
}
/**
* Not a public API. This is only for testing.
*/
static HashSet<DeployGateStatusChangeCallback> getStatusChangeCallbacks() {
return sInstance != null ? sInstance.mStatusChangeCallbacks : null;
}
/**
* Not a public API. This is only for testing.
*/
static HashSet<DeployGateUpdateAvailableCallback> getUpdateAvailableCallbacks() {
return sInstance != null ? sInstance.mUpdateAvailableCallbacks : null;
}

import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class DeployGateTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@satsukies satsukies requested a review from jmatsu August 20, 2024 11:08
Copy link
Contributor

@jmatsu jmatsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done!

@satsukies
Copy link
Member Author

Thanks!

@satsukies satsukies merged commit b4aa1bc into master Aug 20, 2024
6 checks passed
@satsukies satsukies deleted the feat/separate-callback-interface branch August 20, 2024 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants