mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-28 06:03:00 +00:00
Add fallback to native location provider
This commit is contained in:
parent
a2b5181128
commit
50c81f4a24
4 changed files with 296 additions and 36 deletions
|
@ -2,5 +2,8 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.google.android.gms">
|
package="com.google.android.gms">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="5" />
|
<uses-sdk android:minSdkVersion="9" />
|
||||||
|
<application>
|
||||||
|
<receiver android:name="org.microg.gms.location.NativeLocationClientImpl$NativePendingIntentForwarder" />
|
||||||
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.android.gms.common.ConnectionResult;
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
|
||||||
import com.google.android.gms.common.api.GoogleApiClient;
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
||||||
|
@ -21,9 +20,9 @@ public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
private static final String TAG = "GmsClient";
|
private static final String TAG = "GmsClient";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final GoogleApiClient.ConnectionCallbacks callbacks;
|
protected final GoogleApiClient.ConnectionCallbacks callbacks;
|
||||||
private final GoogleApiClient.OnConnectionFailedListener connectionFailedListener;
|
protected final GoogleApiClient.OnConnectionFailedListener connectionFailedListener;
|
||||||
private ConnectionState state = ConnectionState.NOT_CONNECTED;
|
protected ConnectionState state = ConnectionState.NOT_CONNECTED;
|
||||||
private ServiceConnection serviceConnection;
|
private ServiceConnection serviceConnection;
|
||||||
private I serviceInterface;
|
private I serviceInterface;
|
||||||
|
|
||||||
|
@ -46,18 +45,21 @@ public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
Log.d(TAG, "connect()");
|
Log.d(TAG, "connect()");
|
||||||
if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) return;
|
if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) return;
|
||||||
state = ConnectionState.CONNECTING;
|
state = ConnectionState.CONNECTING;
|
||||||
if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(context) !=
|
if (serviceConnection != null) {
|
||||||
ConnectionResult.SUCCESS) {
|
MultiConnectionKeeper.getInstance(context)
|
||||||
state = ConnectionState.NOT_CONNECTED;
|
.unbind(getActionString(), serviceConnection);
|
||||||
} else {
|
|
||||||
if (serviceConnection != null) {
|
|
||||||
MultiConnectionKeeper.getInstance(context)
|
|
||||||
.unbind(getActionString(), serviceConnection);
|
|
||||||
}
|
|
||||||
serviceConnection = new GmsServiceConnection();
|
|
||||||
MultiConnectionKeeper.getInstance(context).bind(getActionString(),
|
|
||||||
serviceConnection);
|
|
||||||
}
|
}
|
||||||
|
serviceConnection = new GmsServiceConnection();
|
||||||
|
if (!MultiConnectionKeeper.getInstance(context).bind(getActionString(),
|
||||||
|
serviceConnection)) {
|
||||||
|
state = ConnectionState.ERROR;
|
||||||
|
handleConnectionFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleConnectionFailed() {
|
||||||
|
connectionFailedListener.onConnectionFailed(new ConnectionResult(ConnectionResult
|
||||||
|
.API_UNAVAILABLE, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,7 +80,7 @@ public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return state == ConnectionState.CONNECTED;
|
return state == ConnectionState.CONNECTED || state == ConnectionState.PSEUDO_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,6 +88,10 @@ public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
return state == ConnectionState.CONNECTING;
|
return state == ConnectionState.CONNECTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasError() {
|
||||||
|
return state == ConnectionState.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
@ -94,8 +100,8 @@ public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
return serviceInterface;
|
return serviceInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ConnectionState {
|
protected enum ConnectionState {
|
||||||
NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR
|
NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR, PSEUDO_CONNECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GmsServiceConnection implements ServiceConnection {
|
private class GmsServiceConnection implements ServiceConnection {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.microg.gms.location;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -20,6 +21,9 @@ import java.util.Map;
|
||||||
|
|
||||||
public class LocationClientImpl extends GoogleLocationManagerClient {
|
public class LocationClientImpl extends GoogleLocationManagerClient {
|
||||||
private static final String TAG = "GmsLocationClientImpl";
|
private static final String TAG = "GmsLocationClientImpl";
|
||||||
|
private NativeLocationClientImpl nativeLocation = null;
|
||||||
|
private Map<LocationListener, ILocationListener> listenerMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks,
|
public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks,
|
||||||
GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
@ -35,50 +39,89 @@ public class LocationClientImpl extends GoogleLocationManagerClient {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<LocationListener, ILocationListener> listenerMap = new HashMap<>();
|
|
||||||
|
|
||||||
public Location getLastLocation() throws RemoteException {
|
public Location getLastLocation() throws RemoteException {
|
||||||
Log.d(TAG, "getLastLocation()");
|
Log.d(TAG, "getLastLocation()");
|
||||||
return getServiceInterface().getLastLocation();
|
if (nativeLocation != null) {
|
||||||
|
return nativeLocation.getLastLocation();
|
||||||
|
} else {
|
||||||
|
return getServiceInterface().getLastLocation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestLocationUpdates(LocationRequest request, final LocationListener listener)
|
public void requestLocationUpdates(LocationRequest request, final LocationListener listener)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
ILocationListener iLocationListener = new ILocationListener.Stub() {
|
if (nativeLocation != null) {
|
||||||
@Override
|
nativeLocation.requestLocationUpdates(request, listener);
|
||||||
public void onLocationChanged(Location location) throws RemoteException {
|
} else {
|
||||||
listener.onLocationChanged(location);
|
if (!listenerMap.containsKey(listener)) {
|
||||||
|
listenerMap.put(listener, new ILocationListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location location) throws RemoteException {
|
||||||
|
listener.onLocationChanged(location);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
getServiceInterface().requestLocationUpdatesWithPackage(request,
|
||||||
listenerMap.put(listener, iLocationListener);
|
listenerMap.get(listener), getContext().getPackageName());
|
||||||
getServiceInterface().requestLocationUpdatesWithPackage(request,
|
}
|
||||||
iLocationListener, getContext().getPackageName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent)
|
public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent);
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.requestLocationUpdates(request, pendingIntent);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestLocationUpdates(LocationRequest request, LocationListener listener,
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener,
|
||||||
Looper looper) throws RemoteException {
|
Looper looper) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.requestLocationUpdates(request, listener, looper);
|
||||||
|
}
|
||||||
requestLocationUpdates(request, listener); // TODO
|
requestLocationUpdates(request, listener); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLocationUpdates(LocationListener listener) throws RemoteException {
|
public void removeLocationUpdates(LocationListener listener) throws RemoteException {
|
||||||
getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener));
|
if (nativeLocation != null) {
|
||||||
listenerMap.remove(listener);
|
nativeLocation.removeLocationUpdates(listener);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException {
|
public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException {
|
||||||
getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent);
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.removeLocationUpdates(pendingIntent);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMockMode(boolean isMockMode) throws RemoteException {
|
public void setMockMode(boolean isMockMode) throws RemoteException {
|
||||||
getServiceInterface().setMockMode(isMockMode);
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.setMockMode(isMockMode);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().setMockMode(isMockMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMockLocation(Location mockLocation) throws RemoteException {
|
public void setMockLocation(Location mockLocation) throws RemoteException {
|
||||||
getServiceInterface().setMockLocation(mockLocation);
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.setMockLocation(mockLocation);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().setMockLocation(mockLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleConnectionFailed() {
|
||||||
|
// DO NOT call super here, because fails are not really problems :)
|
||||||
|
nativeLocation = new NativeLocationClientImpl(this);
|
||||||
|
state = ConnectionState.PSEUDO_CONNECTED;
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean("fallback_to_native_active", true);
|
||||||
|
callbacks.onConnected(bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
208
src/org/microg/gms/location/NativeLocationClientImpl.java
Normal file
208
src/org/microg/gms/location/NativeLocationClientImpl.java
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014-2015 µg Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Criteria;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.location.LocationListener;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NativeLocationClientImpl {
|
||||||
|
private final static String TAG = "GmsToNativeLocationClient";
|
||||||
|
private final static Criteria DEFAULT_CRITERIA = new Criteria();
|
||||||
|
private final static Map<PendingIntent, Integer> pendingCount = new HashMap<>();
|
||||||
|
private final static Map<PendingIntent, PendingIntent> nativePendingMap = new HashMap<>();
|
||||||
|
private static final String EXTRA_PENDING_INTENT = "pending_intent";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final LocationManager locationManager;
|
||||||
|
private final Map<LocationListener, NativeListener> nativeListenerMap = new HashMap<>();
|
||||||
|
|
||||||
|
public NativeLocationClientImpl(LocationClientImpl client) {
|
||||||
|
context = client.getContext();
|
||||||
|
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Criteria makeNativeCriteria(LocationRequest request) {
|
||||||
|
Criteria criteria = new Criteria();
|
||||||
|
switch (request.getPriority()) {
|
||||||
|
case LocationRequest.PRIORITY_HIGH_ACCURACY:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_HIGH);
|
||||||
|
break;
|
||||||
|
case LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY:
|
||||||
|
default:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
|
||||||
|
break;
|
||||||
|
case LocationRequest.PRIORITY_NO_POWER:
|
||||||
|
case LocationRequest.PRIORITY_LOW_POWER:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_LOW);
|
||||||
|
}
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLastLocation() {
|
||||||
|
Log.d(TAG,"getLastLocation()");
|
||||||
|
return locationManager.getLastKnownLocation(
|
||||||
|
locationManager.getBestProvider(DEFAULT_CRITERIA, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener) {
|
||||||
|
requestLocationUpdates(request, listener, Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) {
|
||||||
|
Log.d(TAG,"requestLocationUpdates()");
|
||||||
|
Intent i = new Intent(context, NativePendingIntentForwarder.class);
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent);
|
||||||
|
i.putExtras(bundle);
|
||||||
|
pendingCount.put(pendingIntent, request.getNumUpdates());
|
||||||
|
nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0));
|
||||||
|
locationManager.requestLocationUpdates(request.getInterval(),
|
||||||
|
request.getSmallestDesplacement(), makeNativeCriteria(request),
|
||||||
|
nativePendingMap.get(pendingIntent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper
|
||||||
|
looper) {
|
||||||
|
Log.d(TAG,"requestLocationUpdates()");
|
||||||
|
if (nativeListenerMap.containsKey(listener)) {
|
||||||
|
removeLocationUpdates(listener);
|
||||||
|
}
|
||||||
|
nativeListenerMap.put(listener, new NativeListener(listener, request.getNumUpdates()));
|
||||||
|
locationManager.requestLocationUpdates(request.getInterval(),
|
||||||
|
request.getSmallestDesplacement(), makeNativeCriteria(request),
|
||||||
|
nativeListenerMap.get(listener), looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(LocationListener listener) {
|
||||||
|
Log.d(TAG,"removeLocationUpdates()");
|
||||||
|
locationManager.removeUpdates(nativeListenerMap.get(listener));
|
||||||
|
nativeListenerMap.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(PendingIntent pendingIntent) {
|
||||||
|
Log.d(TAG,"removeLocationUpdates()");
|
||||||
|
locationManager.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockMode(boolean isMockMode) {
|
||||||
|
Log.d(TAG,"setMockMode()");
|
||||||
|
// not yet supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockLocation(Location mockLocation) {
|
||||||
|
Log.d(TAG,"setMockLocation()");
|
||||||
|
// not yet supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NativePendingIntentForwarder extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) {
|
||||||
|
PendingIntent pendingIntent = intent.getExtras().getParcelable
|
||||||
|
(EXTRA_PENDING_INTENT);
|
||||||
|
try {
|
||||||
|
pendingIntent.send(context, 0, intent);
|
||||||
|
pendingCount.put(pendingIntent, pendingCount.get(pendingIntent) - 1);
|
||||||
|
if (pendingCount.get(pendingIntent) == 0) {
|
||||||
|
((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||||
|
.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||||
|
.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NativeListener implements android.location.LocationListener {
|
||||||
|
|
||||||
|
private final LocationListener listener;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
private NativeListener(LocationListener listener, int count) {
|
||||||
|
this.listener = listener;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location location) {
|
||||||
|
listener.onLocationChanged(location);
|
||||||
|
count--;
|
||||||
|
if (count == 0) {
|
||||||
|
locationManager.removeUpdates(this);
|
||||||
|
nativeListenerMap.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderEnabled(String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderDisabled(String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
NativeListener that = (NativeListener) o;
|
||||||
|
|
||||||
|
if (!listener.equals(that.listener)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return listener.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue