All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.pods.reachability.NetworkReachability Maven / Gradle / Ivy

package org.robovm.pods.reachability;

import java.net.InetSocketAddress;
import java.util.Objects;

import org.robovm.apple.dispatch.DispatchQueue;
import org.robovm.apple.foundation.NSNotificationCenter;
import org.robovm.apple.foundation.NSObject;
import org.robovm.apple.foundation.NSString;
import org.robovm.apple.systemconfiguration.SCNetworkReachability;
import org.robovm.apple.systemconfiguration.SCNetworkReachability.ClientCallback;
import org.robovm.apple.systemconfiguration.SCNetworkReachabilityFlags;
import org.robovm.apple.systemconfiguration.SCStatus;

public class NetworkReachability extends NSObject {
    public static NSString REACHABILITY_CHANGE_NOTIFICATION = new NSString("kReachabilityChangedNotification");

    private NetworkReachabilityListener reachabilityListener;

    private final SCNetworkReachability reachability;
    private final DispatchQueue reachabilitySerialQueue;
    private NSObject reachabilityObject;

    private final boolean reachableOnWWAN;

    public static NetworkReachability forHostname(String hostname) {
        return new NetworkReachability(new SCNetworkReachability(hostname));
    }

    public static NetworkReachability forAddress(InetSocketAddress hostAddress) {
        return new NetworkReachability(new SCNetworkReachability(hostAddress));
    }

    public static NetworkReachability forInternetConnection() {
        InetSocketAddress zeroAddress = new InetSocketAddress(0);
        return forAddress(zeroAddress);
    }

    public static NetworkReachability forLocalWiFi() {
        InetSocketAddress localWifiAddress = new InetSocketAddress("169.254.0.0", 0);
        return forAddress(localWifiAddress);
    }

    public NetworkReachability(SCNetworkReachability reachability) {
        Objects.requireNonNull(reachability, "reachability");
        this.reachability = reachability;
        reachableOnWWAN = true;

        // We need to create a serial queue.
        // We allocate this once for the lifetime of the notifier.
        reachabilitySerialQueue = DispatchQueue.create("org.robovm.pods.reachability", null);
    }

    public void setListener(NetworkReachabilityListener listener) {
        this.reachabilityListener = listener;
    }

    @Override
    protected void dispose(boolean finalizing) {
        stopNotifier();
        super.dispose(finalizing);
    }

    public boolean startNotifier() {
        // allow start notifier to be called multiple times
        if (reachabilityObject != null && reachabilityObject == this) {
            return true;
        }

        if (reachability.setCallback(new ClientCallback() {
            @Override
            public void invoke(SCNetworkReachability target, SCNetworkReachabilityFlags flags) {
                reachabilityChanged(flags);
            }
        })) {
            // Set it as our reachability queue, which will retain the queue
            if (reachability.setDispatchQueue(reachabilitySerialQueue)) {
                // this should do a retain on ourself, so as long as we're in
                // notifier mode we shouldn't disappear out from under ourselves
                reachabilityObject = this;
                return true;
            } else {
                System.err.println(SCStatus.getStatusDescription(SCStatus.getLastStatus()));
                // Failure - stop any callbacks!
                reachability.setCallback(null);
            }
        } else {
            System.err.println(SCStatus.getStatusDescription(SCStatus.getLastStatus()));
        }
        // if we get here we fail at the internet
        reachabilityObject = null;
        return false;
    }

    public void stopNotifier() {
        // First stop, any callbacks!
        reachability.setCallback(null);

        // Unregister target from the GCD serial dispatch queue.
        reachability.setDispatchQueue(null);

        reachabilityObject = null;
    }

    private boolean isReachableWithFlags(SCNetworkReachabilityFlags flags) {
        boolean connectionUP = true;

        if (!flags.contains(SCNetworkReachabilityFlags.Reachable)) {
            connectionUP = false;
        }

        if (flags.contains(SCNetworkReachabilityFlags.ConnectionRequired)
                && flags.contains(SCNetworkReachabilityFlags.TransientConnection)) {
            connectionUP = false;
        }

        if (flags.contains(SCNetworkReachabilityFlags.IsWWAN)) {
            // We're on 3G.
            if (!reachableOnWWAN) {
                // We don't want to connect when on 3G.
                connectionUP = false;
            }
        }

        return connectionUP;
    }

    public boolean isReachable() {
        return isReachableWithFlags(getReachabilityFlags());
    }

    public boolean isReachableViaWWAN() {
        SCNetworkReachabilityFlags flags = getReachabilityFlags();
        // Check we're REACHABLE
        if (flags.contains(SCNetworkReachabilityFlags.Reachable)) {
            // Now, check we're on WWAN
            if (flags.contains(SCNetworkReachabilityFlags.IsWWAN)) {
                return true;
            }
        }

        return false;
    }

    public boolean isReachableViaWiFi() {
        SCNetworkReachabilityFlags flags = getReachabilityFlags();
        // Check we're REACHABLE
        if (flags.contains(SCNetworkReachabilityFlags.Reachable)) {
            // Now, check we're NOT on WWAN
            if (flags.contains(SCNetworkReachabilityFlags.IsWWAN)) {
                return false;
            }
            return true;
        }
        return false;
    }

    public boolean isConnectionRequired() {
        return getReachabilityFlags().contains(SCNetworkReachabilityFlags.ConnectionRequired);
    }

    public boolean isConnectionOnDemand() {
        SCNetworkReachabilityFlags flags = getReachabilityFlags();
        return flags.contains(SCNetworkReachabilityFlags.ConnectionRequired)
                && (flags.contains(SCNetworkReachabilityFlags.ConnectionOnTraffic) || flags
                        .contains(SCNetworkReachabilityFlags.ConnectionOnDemand));
    }

    public boolean isInterventionRequired() {
        SCNetworkReachabilityFlags flags = getReachabilityFlags();
        return flags.contains(SCNetworkReachabilityFlags.ConnectionRequired)
                && flags.contains(SCNetworkReachabilityFlags.InterventionRequired);
    }

    public NetworkStatus getCurrentReachabilityStatus() {
        if (isReachable()) {
            if (isReachableViaWiFi()) {
                return NetworkStatus.ReachableViaWiFi;
            }
            return NetworkStatus.ReachableViaWWAN;
        }
        return NetworkStatus.NotReachable;
    }

    public SCNetworkReachabilityFlags getReachabilityFlags() {
        return reachability.getFlags();
    }

    public String getCurrentReachabilityString() {
        NetworkStatus status = getCurrentReachabilityStatus();
        switch (status) {
        case ReachableViaWWAN:
            return "Cellular";
        case ReachableViaWiFi:
            return "WiFi";
        default:
            return "No Connection";
        }
    }

    public String getCurrentReachabilityFlagsAsString() {
        return getReachabilityFlagsAsString(getReachabilityFlags());
    }

    private static String getReachabilityFlagsAsString(SCNetworkReachabilityFlags flags) {
        return String.format("%s%s %s%s%s%s%s%s%s",
                flags.contains(SCNetworkReachabilityFlags.IsWWAN) ? 'W' : '-',
                flags.contains(SCNetworkReachabilityFlags.Reachable) ? 'R' : '-',
                flags.contains(SCNetworkReachabilityFlags.ConnectionRequired) ? 'c' : '-',
                flags.contains(SCNetworkReachabilityFlags.TransientConnection) ? 't' : '-',
                flags.contains(SCNetworkReachabilityFlags.InterventionRequired) ? 'i' : '-',
                flags.contains(SCNetworkReachabilityFlags.ConnectionOnTraffic) ? 'C' : '-',
                flags.contains(SCNetworkReachabilityFlags.ConnectionOnDemand) ? 'D' : '-',
                flags.contains(SCNetworkReachabilityFlags.IsLocalAddress) ? 'l' : '-',
                flags.contains(SCNetworkReachabilityFlags.IsDirect) ? 'd' : '-'
                );
    }

    private void reachabilityChanged(SCNetworkReachabilityFlags flags) {
        if (reachabilityListener != null) {
            if (isReachableWithFlags(flags)) {
                reachabilityListener.onReachable(this);
            } else {
                reachabilityListener.onUnreachable(this);
            }
            reachabilityListener.onChange(this, flags);
        }

        // this makes sure the change notification happens on the MAIN THREAD
        DispatchQueue.getMainQueue().async(
                () -> NSNotificationCenter.getDefaultCenter().postNotification(REACHABILITY_CHANGE_NOTIFICATION, this));
    }

    @Override
    public String toString() {
        return getCurrentReachabilityFlagsAsString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy