import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import fi.iki.elonen.NanoHTTPD;
public class WebViewActivity extends AppCompatActivity {
private WebView webView;
private static final int REQUEST_INTERNET_PERMISSION = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webView = findViewById(R.id.webView);
// Check and request internet permission if needed
if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET}, REQUEST_INTERNET_PERMISSION);
} else {
// Load WebView content
loadWebViewContent();
}
}
private void loadWebViewContent() {
// Check network connectivity
String baseUrl = isNetworkAvailable() ? "http://localhost:8080/index.html" : "file:///android_asset/index.html";
// Use WebView to load the content
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(baseUrl);
}
// Check network connectivity
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
// Handle permission request result
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_INTERNET_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, load WebView content
loadWebViewContent();
} else {
// Permission denied, handle accordingly (e.g., show a message or exit the app)
Toast.makeText(this, "Internet permission denied. The app may not work properly.", Toast.LENGTH_SHORT).show();
}
}
}
// NanoHTTPD server for serving local files
private class LocalWebServer extends NanoHTTPD {
public LocalWebServer() {
super(8080);
}
@Override
public Response serve(IHTTPSession session) {
// Your file-serving logic here
// ...
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
}
}
}
Note: This example provides a basic structure, and you may need to modify it according to your specific requirements. Additionally, make sure to implement proper error handling, security measures, and comply with Google Play policies in your actual app.
In an advanced setup, you may want to enhance the app with features such as improved security, error handling, and a more dynamic server for serving local files. Below is an example that incorporates these considerations:
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;
public class AdvancedWebViewActivity extends AppCompatActivity {
private WebView webView;
private static final int REQUEST_INTERNET_PERMISSION = 1;
private LocalWebServer localWebServer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webView = findViewById(R.id.webView);
// Check and request internet permission if needed
if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET}, REQUEST_INTERNET_PERMISSION);
} else {
// Start NanoHTTPD server for serving local files
startLocalServer();
// Load WebView content
loadWebViewContent();
}
}
private void startLocalServer() {
localWebServer = new LocalWebServer();
try {
localWebServer.start();
} catch (IOException e) {
e.printStackTrace();
// Handle server start failure
}
}
private void stopLocalServer() {
if (localWebServer != null) {
localWebServer.stop();
}
}
private void loadWebViewContent() {
// Check network connectivity
String baseUrl = isNetworkAvailable() ? "http://localhost:8080/index.html" : "file:///android_asset/index.html";
// Use WebView to load the content
webView.setWebViewClient(new AdvancedWebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(baseUrl);
}
// Check network connectivity
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
// Handle permission request result
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_INTERNET_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, start the local server and load WebView content
startLocalServer();
loadWebViewContent();
} else {
// Permission denied, handle accordingly (e.g., show a message or exit the app)
Toast.makeText(this, "Internet permission denied. The app may not work properly.", Toast.LENGTH_SHORT).show();
}
}
}
// NanoHTTPD server for serving local files
private class LocalWebServer extends NanoHTTPD {
public LocalWebServer() {
super(8080);
}
@Override
public Response serve(IHTTPSession session) {
// Your file-serving logic here
// Example: Serve files from the assets folder
String uri = session.getUri();
String fileName = uri.substring(1); // Remove the leading '/'
String mimeType = getMimeType(fileName);
try {
InputStream inputStream = getAssets().open(fileName);
return newChunkedResponse(Response.Status.OK, mimeType, inputStream);
} catch (IOException e) {
e.printStackTrace();
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
}
}
// Get MIME type based on file extension
private String getMimeType(String fileName) {
// Implement your own logic or use a library for MIME type detection
// For simplicity, a basic implementation is provided here
if (fileName.endsWith(".html")) {
return "text/html";
} else if (fileName.endsWith(".css")) {
return "text/css";
} else if (fileName.endsWith(".js")) {
return "application/javascript";
} else if (fileName.endsWith(".json")) {
return "application/json";
} else if (fileName.endsWith(".png")) {
return "image/png";
} else if (fileName.endsWith(".mp3")) {
return "audio/mpeg";
} else if (fileName.endsWith(".m4a")) {
return "audio/mp4";
} else {
return "application/octet-stream";
}
}
}
// Custom WebViewClient for advanced handling
private class AdvancedWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
// Override URL loading for custom handling
// Example: Open external links in the device's default browser
String url = request.getUrl().toString();
if (!url.startsWith("http://localhost:8080")) {
// External link, open in the default browser
// You can customize this behavior based on your requirements
// ...
return true; // Return true to indicate that the WebView handles the URL
}
// Internal link, load in the WebView
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
// Handle WebView errors
// Example: Display a custom error page
view.loadUrl("file:///android_asset/error.html");
}
}
@Override
protected void onDestroy() {
// Stop the local server when the activity is destroyed
stopLocalServer();
super.onDestroy();
}
}
This example incorporates a more dynamic MIME type detection, custom WebViewClient for handling URL loading, and improved error handling. Keep in mind that this is a complex example, and you should adapt it based on your specific use case and requirements.
Let's go through the code in detail:
Permissions Handling and Initialization:
Internet Permission:
- The app checks if it has the
INTERNET permission. If not, it requests the permission from the user.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INTERNET}, REQUEST_INTERNET_PERMISSION);
} else {
// Start NanoHTTPD server for serving local files
startLocalServer();
// Load WebView content
loadWebViewContent();
}
- If the permission is granted, it starts the NanoHTTPD server and loads the WebView content.
Local Server Initialization:
- The
startLocalServer() method initializes the NanoHTTPD server for serving local files.
private void startLocalServer() {
localWebServer = new LocalWebServer();
try {
localWebServer.start();
} catch (IOException e) {
e.printStackTrace();
// Handle server start failure
}
}
WebView Content Loading:
Network Availability Check:
- The
isNetworkAvailable() method checks if the device is connected to the internet.
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
Load WebView Content:
- The
loadWebViewContent() method loads the content into the WebView.
private void loadWebViewContent() {
String baseUrl = isNetworkAvailable() ? "http://localhost:8080/index.html" : "file:///android_asset/index.html";
webView.setWebViewClient(new AdvancedWebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(baseUrl);
}
NanoHTTPD Local Server:
LocalWebServer Class:
- This inner class extends NanoHTTPD and is responsible for serving local files.
private class LocalWebServer extends NanoHTTPD {
public LocalWebServer() {
super(8080);
}
// Serve local files based on the requested URI
@Override
public Response serve(IHTTPSession session) {
// Your file-serving logic here
// ...
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
}
// Get MIME type based on file extension
private String getMimeType(String fileName) {
// Implementation for MIME type detection
// ...
}
}
- The
serve method handles incoming HTTP requests, and the getMimeType method determines the MIME type of the requested file.
WebViewClient for Advanced Handling:
AdvancedWebViewClient Class:
- This inner class extends WebViewClient and provides advanced handling of WebView events.
private class AdvancedWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
// Override URL loading for custom handling
// ...
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
// Handle WebView errors
// ...
}
}
- The
shouldOverrideUrlLoading method allows custom handling of URL loading, and the onReceivedError method handles WebView errors.
Activity Lifecycle:
onDestroy Method:
- The
onDestroy method stops the local server when the activity is destroyed.
@Override
protected void onDestroy() {
// Stop the local server when the activity is destroyed
stopLocalServer();
super.onDestroy();
}
Note:
- This code provides an advanced structure for handling WebView content, server initialization, permissions, and more.
- The
LocalWebServer class and AdvancedWebViewClient class can be further extended based on specific requirements. - For production use, additional error handling, security considerations, and testing should be incorporated.
- Make sure to adjust the code according to your app's specific needs and architecture.