From 6d1331d54c4bd791806b547e975e794c3e366214 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 20 Nov 2020 21:17:52 +0300 Subject: remove webview-based online/offline detection, use native android way instead (and pass it to webview) --- .../src/main/java/org/fox/epube/MainActivity.java | 82 ++++++++++++-- .../java/org/fox/epube/NetworkStateReceiver.java | 118 +++++++++++++++++++++ 2 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 org.fox.epube/src/main/java/org/fox/epube/NetworkStateReceiver.java diff --git a/org.fox.epube/src/main/java/org/fox/epube/MainActivity.java b/org.fox.epube/src/main/java/org/fox/epube/MainActivity.java index c2e7ac8..78a8090 100644 --- a/org.fox.epube/src/main/java/org/fox/epube/MainActivity.java +++ b/org.fox.epube/src/main/java/org/fox/epube/MainActivity.java @@ -2,9 +2,12 @@ package org.fox.epube; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -28,7 +31,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import icepick.State; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements NetworkStateReceiver.NetworkStateReceiverListener { private final String TAG = this.getClass().getSimpleName(); private final String BASE_URL = "https://fakecake.org/books"; @@ -38,6 +41,7 @@ public class MainActivity extends AppCompatActivity { private ProgressBar m_loadingBar; private WebView m_web; private Menu m_menu; + private NetworkStateReceiver m_networkStateReceiver; @State protected boolean m_offlineMode; @State protected AppPage m_currentPage = AppPage.PAGE_UNKNOWN; @@ -46,6 +50,9 @@ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + startNetworkBroadcastReceiver(this); + setContentView(R.layout.activity_main); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); @@ -143,6 +150,10 @@ public class MainActivity extends AppCompatActivity { m_currentPage = page; + // not initialized yet + if (m_menu == null) + return; + m_menu.setGroupVisible(R.id.menu_group_pages, false); m_menu.setGroupVisible(R.id.menu_group_pages_offline, false); m_menu.setGroupVisible(R.id.menu_group_library, false); @@ -182,11 +193,19 @@ public class MainActivity extends AppCompatActivity { } } + @Override + protected void onPause() { + unregisterNetworkBroadcastReceiver(this); + super.onPause(); + } + @Override public void onResume() { - super.onResume(); + registerNetworkBroadcastReceiver(this); + + onOfflineModeChanged(!isNetworkAvailable()); - m_web.evaluateJavascript("App.appCheckOffline()", null); + super.onResume(); } public void toast(String msg) { @@ -280,6 +299,17 @@ public class MainActivity extends AppCompatActivity { m_offlineMode = offline; onPageSwitched(m_currentPage); + + // this would probably be annoying if network access status changes a lot + // never reload if reading a book + /* if (m_web != null && m_currentPage != AppPage.PAGE_READER) + m_web.evaluateJavascript("window.location.reload();", null); */ + + // let's delegate this to webview + + if (m_web != null) { + m_web.evaluateJavascript("Reader.onOfflineModeChanged("+ offline +");", null); + } } class WebAppInterface { @@ -358,13 +388,14 @@ public class MainActivity extends AppCompatActivity { } @JavascriptInterface + @Deprecated public void setOffline(final boolean offline) { - runOnUiThread(new Runnable() { - @Override - public void run() { - MainActivity.this.onOfflineModeChanged(offline); - } - }); + // No longer used, we don't rely on webview for offline/online switching anymore. + } + + @JavascriptInterface + public boolean isOnline() { + return isNetworkAvailable(); } @JavascriptInterface @@ -397,6 +428,14 @@ public class MainActivity extends AppCompatActivity { } + private boolean isNetworkAvailable() { + ConnectivityManager manager = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = manager.getActiveNetworkInfo(); + + return networkInfo != null && networkInfo.isConnected(); + } + @Override public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); @@ -413,4 +452,29 @@ public class MainActivity extends AppCompatActivity { else super.onBackPressed(); } + + @Override + public void networkAvailable() { + onOfflineModeChanged(false); + } + + @Override + public void networkUnavailable() { + onOfflineModeChanged(true); + } + + public void startNetworkBroadcastReceiver(Context context) { + m_networkStateReceiver = new NetworkStateReceiver(); + m_networkStateReceiver.addListener((NetworkStateReceiver.NetworkStateReceiverListener) context); + registerNetworkBroadcastReceiver(context); + } + + public void registerNetworkBroadcastReceiver(Context context) { + context.registerReceiver(m_networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)); + } + + public void unregisterNetworkBroadcastReceiver(Context context) { + context.unregisterReceiver(m_networkStateReceiver); + } + } \ No newline at end of file diff --git a/org.fox.epube/src/main/java/org/fox/epube/NetworkStateReceiver.java b/org.fox.epube/src/main/java/org/fox/epube/NetworkStateReceiver.java new file mode 100644 index 0000000..4afb7d9 --- /dev/null +++ b/org.fox.epube/src/main/java/org/fox/epube/NetworkStateReceiver.java @@ -0,0 +1,118 @@ +package org.fox.epube; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * NetworkStateReceiver defines a BroadcastReceiver which allows us to register for system (i.e. network status) or application events. + * All registered receivers for an event are notified by the Android runtime once this event happens. + * Source: http://stackoverflow.com/questions/6169059/android-event-for-internet-connectivity-state-change + */ +public class NetworkStateReceiver extends BroadcastReceiver { + + protected List listeners; + protected Boolean connected; + private String TAG = "NetworkStateReceiver"; + + public NetworkStateReceiver() { + listeners = new ArrayList<>(); + connected = null; + } + + /** + * Called when the BroadcastReceiver is receiving an Intent broadcast (event for which the broadcast receiver has registered occurs). + * During this time you can use the other methods on BroadcastReceiver to view/modify the current result values. + * NOTE: When it runs on the main thread you should never perform long-running operations in it (there is a timeout of 10 seconds that the system allows before considering the receiver to be blocked and a candidate to be killed). + * NOTE: You cannot launch a popup dialog in your implementation of onReceive(). + * @param context Object to access additional information or to start services or activities + * @param intent Object with action used to register your receiver. This object contains additional information (e.g. extras) + */ + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "Intent broadcast received"); + if(intent == null || intent.getExtras() == null) + return; + + // Retrieve a ConnectivityManager for handling management of network connections + ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + // Details about the currently active default data network. When connected, this network is the default route for outgoing connections + NetworkInfo networkInfo = manager.getActiveNetworkInfo(); + + /** + * NOTE: getActiveNetworkInfo() may return null when there is no default network e.g. Airplane Mode + */ + if(networkInfo != null && networkInfo.getState() == NetworkInfo.State.CONNECTED) { + connected = true; + } else if(intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) { //Boolean that indicates whether there is a complete lack of connectivity + connected = false; + } + + notifyStateToAll(); + } //After the onReceive() of the receiver class has finished, the Android system is allowed to recycle the receiver + + /** + * Notify the state to all needed methods + */ + private void notifyStateToAll() { + Log.i(TAG, "Notifying state to " + listeners.size() + " listener(s)"); + for(NetworkStateReceiverListener eachNetworkStateReceiverListener : listeners) + notifyState(eachNetworkStateReceiverListener); + } + + /** + * Notify the network state, triggering interface functions based on the current state + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + private void notifyState(NetworkStateReceiverListener networkStateReceiverListener) { + if(connected == null || networkStateReceiverListener == null) + return; + + if(connected == true) { + // Triggering function on the interface towards network availability + networkStateReceiverListener.networkAvailable(); + } else { + // Triggering function on the interface towards network being unavailable + networkStateReceiverListener.networkUnavailable(); + } + } + + /** + * Adds a listener to the list so that it will receive connection state change updates + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + public void addListener(NetworkStateReceiverListener networkStateReceiverListener) { + Log.i(TAG, "addListener() - listeners.add(networkStateReceiverListener) + notifyState(networkStateReceiverListener);"); + listeners.add(networkStateReceiverListener); + notifyState(networkStateReceiverListener); + } + + /** + * Removes listener (when no longer necessary) from the list so that it will no longer receive connection state change updates + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + public void removeListener(NetworkStateReceiverListener networkStateReceiverListener) { + listeners.remove(networkStateReceiverListener); + } + + /** + * Inner Interface (i.e. to encapsulate behavior in a generic and re-usable way) which handles connection state changes for classes which registered this receiver (Outer class NetworkStateReceiver) + * This interface implements the 'Strategy Pattern', where an execution strategy is evaluated and applied internally at runtime + */ + public interface NetworkStateReceiverListener { + /** + * When the connection state is changed and there is a connection, this method is called + */ + void networkAvailable(); + + /** + * Connection state is changed and there is not a connection, this method is called + */ + void networkUnavailable(); + } +} \ No newline at end of file -- cgit v1.2.3