diff --git a/.gitignore b/.gitignore index a2a645e..f712c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -.gradle -.idea -build/ -app/build/ -app/release/ -local.properties -gradle.properties +.gradle/ +.idea/ +.stignore +.stfolder/ +build/ +app/build/ +app/release/ +local.properties +gradle.properties diff --git a/README.md b/README.md index 60de73f..43acc7d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,27 @@ +
+ +App Logo

Wi-Fi Info

-

A powerful network toolset packed into an Android app, which gathers and displays the information about the Wi-Fi network you are connected to!

+

+A powerful network toolset packed into an Android app, which gathers and displays the information about the Wi-Fi network you are connected to! +

--- -

GitHub latest release - - GitHub all releases

+

+ + GitHub Release Downloads + + Play Store Downloads + +

Github repo size @@ -43,9 +52,26 @@

+
+ +

Screenshots

+ +
+ + + + + + + + +
+ +
+

Downloads

-

+

Play Store @@ -54,7 +80,7 @@

or

Releases

-

+

Features

@@ -117,7 +143,7 @@ For contributor guidelines, see CONTRIBUTING.md<

-
Copyright 2022 TrueMLGPro
+
Copyright 2020-2024 TrueMLGPro
 
 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
 
diff --git a/app/build.gradle b/app/build.gradle
index 80d8e0b..4105fd6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,8 +2,8 @@ apply plugin: 'com.android.application'
 
 android {
 	namespace "com.truemlgpro.wifiinfo"
-	compileSdk 33
-	buildToolsVersion '33.0.2'
+	compileSdk 34
+	buildToolsVersion = '34.0.0'
 
 	signingConfigs {
 		release {
@@ -14,15 +14,15 @@ android {
 		}
 	}
 	compileOptions {
-		sourceCompatibility 1.8
-		targetCompatibility 1.8
+		sourceCompatibility JavaVersion.VERSION_17
+		targetCompatibility JavaVersion.VERSION_17
 	}
 	defaultConfig {
 		applicationId "com.truemlgpro.wifiinfo"
 		minSdkVersion 21
-		targetSdkVersion 33
-		versionCode 1600
-		versionName "1.6"
+		targetSdkVersion 34
+		versionCode 1610
+		versionName "1.6.1"
 	}
 	buildTypes {
 		release {
@@ -47,9 +47,9 @@ android {
 			proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 		}
 	}
-	applicationVariants.all { variant ->
-		variant.outputs.all {
-			outputFileName = "${APP_NAME}_${defaultConfig.versionName}_${variant.buildType.name}.apk"
+	applicationVariants.configureEach { variant ->
+		variant.outputs.configureEach {
+			outputFileName = "${APP_NAME}_v${defaultConfig.versionName}_${variant.buildType.name}.apk"
 		}
 	}
 	lint {
@@ -62,10 +62,11 @@ android {
 }
 
 dependencies {
-	implementation 'androidx.appcompat:appcompat:1.7.0-alpha01'
-	implementation 'com.google.android.material:material:1.9.0'
+	implementation 'androidx.appcompat:appcompat:1.7.0-alpha03'
+	implementation 'com.google.android.material:material:1.11.0'
 	implementation 'androidx.cardview:cardview:1.0.0'
-	implementation 'androidx.preference:preference:1.2.0'
+	implementation "androidx.recyclerview:recyclerview:1.3.2"
+	implementation 'androidx.preference:preference:1.2.1'
 	implementation 'androidx.core:core-splashscreen:1.0.1'
 
 	implementation 'com.github.AnwarShahriar:Calligrapher:master'
@@ -73,8 +74,11 @@ dependencies {
 	implementation 'com.github.sougata-chatterjee:FloatingActionButton:1.7.1'
 
 	implementation 'com.github.TrueMLGPro:AndroidNetworkTools:76b8feabb6' // Fork of a Kotlin rewrite of stealthcopter/AndroidNetworkTools
-	implementation 'com.github.100rabhkr:TCWHOIS:master' // Minimum SDK version is 22, ignored in AndroidManifest.xml
-	implementation 'org.minidns:minidns-hla:1.0.4'
+	implementation 'com.github.100rabhkr:TCWHOIS:master' // Whois lookups (minimum SDK version is 22, ignored in AndroidManifest.xml)
+	implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1' // Helper for working with databases located in assets folder
+//	implementation 'org.fourthline.cling:cling-core:2.1.2' // Library for locating UPnP devices on the local network; for 1.6.2
+	implementation 'eu.agno3.jcifs:jcifs-ng:2.1.9' // Library used for locating NetBIOS devices on the local network
+	implementation 'org.minidns:minidns-hla:1.0.4' // DNS lookups
 	implementation 'org.minidns:minidns-android21:1.0.4'
 
 	implementation fileTree(dir: 'libs', include: ['*.jar'])
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index ebce3e3..a180306 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -15,3 +15,5 @@
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
+
+-dontwarn org.slf4j.impl.StaticLoggerBinder
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 17d447e..0c15c4c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
 
 	
 
@@ -15,25 +15,28 @@
 	
 	
 	
+	
+	
 	
 
 	
+		android:localeConfig="@xml/locale_config"
+		tools:targetApi="tiramisu">
 
 		
 
 		
 			
 				
@@ -44,77 +47,77 @@
 			
 		
 	    
+			android:parentActivityName=".ui.SettingsActivity">
 		
 		
+			android:parentActivityName=".ui.MainActivity">
 		
 		
+			android:parentActivityName=".ui.MainActivity">
 			
+				android:value="com.truemlgpro.wifiinfo.ui.MainActivity" />
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 		
+			android:parentActivityName=".ui.ToolsActivity">
 		
 	    
+		    android:parentActivityName=".ui.ToolsActivity">
 		
 
 		
 
 		
+			android:name=".services.ConnectionStateService"
+			android:enabled="true"
+			android:foregroundServiceType="specialUse">
+			
 		
 
 		
+			android:name=".services.NotificationService"
+			android:enabled="true"
+			android:foregroundServiceType="specialUse">
+			
 		
 
 		
 		
 
-		
 			
 				
 			
 		
 
-		
 			
 				
diff --git a/app/src/main/assets/databases/iana_ports.db b/app/src/main/assets/databases/iana_ports.db
new file mode 100644
index 0000000..38d35f2
Binary files /dev/null and b/app/src/main/assets/databases/iana_ports.db differ
diff --git a/app/src/main/assets/databases/oui.db b/app/src/main/assets/databases/oui.db
new file mode 100644
index 0000000..c5d6ec6
Binary files /dev/null and b/app/src/main/assets/databases/oui.db differ
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/App.java b/app/src/main/java/com/truemlgpro/wifiinfo/App.java
new file mode 100644
index 0000000..377c197
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/App.java
@@ -0,0 +1,135 @@
+package com.truemlgpro.wifiinfo;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceManager;
+
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.services.ConnectionStateService;
+import com.truemlgpro.wifiinfo.services.NotificationService;
+import com.truemlgpro.wifiinfo.ui.SettingsActivity;
+
+public class App extends Application {
+	private SharedPreferences.OnSharedPreferenceChangeListener sharedPrefChangeListener;
+	Intent connectionStateServiceIntent;
+	Intent notificationServiceIntent;
+
+	@Override
+	public void onCreate() {
+		super.onCreate();
+		registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
+			@Override
+			public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
+				// Stub
+			}
+
+			@Override
+			public void onActivityStarted(Activity activity) {
+				// Stub
+			}
+
+			@Override
+			public void onActivityResumed(Activity activity) {
+				if (activity.getClass().equals(SettingsActivity.class))
+					initSharedPrefs(activity);
+			}
+
+			@Override
+			public void onActivityPaused(Activity activity) {
+				if (activity.getClass().equals(SettingsActivity.class)) {
+					SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+					prefs.unregisterOnSharedPreferenceChangeListener(sharedPrefChangeListener);
+				}
+			}
+
+			@Override
+			public void onActivityStopped(Activity activity) {
+				// Stub
+			}
+
+			@Override
+			public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+				// Stub
+			}
+
+			@Override
+			public void onActivityDestroyed(Activity activity) {
+				// Stub
+			}
+		});
+	}
+
+	private void initSharedPrefs(Activity activity) {
+		connectionStateServiceIntent = new Intent(this, ConnectionStateService.class);
+		notificationServiceIntent = new Intent(this, NotificationService.class);
+		sharedPrefChangeListener = (prefs, key) -> {
+			switch (key) {
+				case PreferenceKeys.KEY_PREF_DARK_MODE,
+					PreferenceKeys.KEY_PREF_AMOLED_MODE,
+					PreferenceKeys.KEY_PREF_APP_FONT,
+					PreferenceKeys.KEY_PREF_KEEP_SCREEN_ON ->
+						restartSettingsActivity(activity);
+				case PreferenceKeys.KEY_PREF_APP_LANGUAGE -> {
+					restartSettingsActivity(activity);
+					if (prefs.getBoolean(PreferenceKeys.KEY_PREF_SHOW_NTFC, PreferenceDefaults.SHOW_NTFC))
+						restartServices(this);
+				}
+				case PreferenceKeys.KEY_PREF_SHOW_NTFC -> {
+					if (prefs.getBoolean(PreferenceKeys.KEY_PREF_SHOW_NTFC, PreferenceDefaults.SHOW_NTFC)) {
+						startServices(this);
+					} else {
+						stopServices(this);
+					}
+				}
+				case PreferenceKeys.KEY_PREF_START_STOP_SVC -> restartServices(this);
+			}
+		};
+
+		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+		prefs.registerOnSharedPreferenceChangeListener(sharedPrefChangeListener);
+	}
+
+	private void startServices(Context context) {
+		if (!ConnectionStateService.isConnectionStateServiceRunning) {
+			if (Build.VERSION.SDK_INT < 26) {
+				context.startService(connectionStateServiceIntent);
+			} else {
+				context.startForegroundService(connectionStateServiceIntent);
+			}
+		}
+	}
+
+	private void stopServices(Context context) {
+		context.stopService(notificationServiceIntent);
+		context.stopService(connectionStateServiceIntent);
+	}
+
+	private void restartServices(Context context) {
+		context.stopService(notificationServiceIntent);
+		context.stopService(connectionStateServiceIntent);
+		if (Build.VERSION.SDK_INT < 26) {
+			context.startService(connectionStateServiceIntent);
+		} else {
+			context.startForegroundService(connectionStateServiceIntent);
+		}
+	}
+
+	private void restartSettingsActivity(Activity activity) {
+		if (activity.getClass().equals(SettingsActivity.class)) {
+			Intent activityRestartIntent = new Intent(this, SettingsActivity.class);
+			activity.finish();
+			activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+			activity.startActivity(activityRestartIntent);
+			activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+		}
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/BootReceiver.java b/app/src/main/java/com/truemlgpro/wifiinfo/BootReceiver.java
deleted file mode 100644
index dab1332..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/BootReceiver.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class BootReceiver extends BroadcastReceiver {
-	@Override
-	public void onReceive(Context context, Intent intent) {
-		boolean keyBoot = new SharedPreferencesManager(context).retrieveBoolean(SettingsActivity.KEY_PREF_BOOT_SWITCH, MainActivity.startOnBoot);
-		if (keyBoot) {
-			Intent ServiceIntent = new Intent(context, ConnectionStateService.class);
-			if (android.os.Build.VERSION.SDK_INT < 26) {
-				context.startService(ServiceIntent);
-			} else {
-				context.startForegroundService(ServiceIntent);
-			}
-		}
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ConnectionStateService.java b/app/src/main/java/com/truemlgpro/wifiinfo/ConnectionStateService.java
deleted file mode 100644
index 9d0e23d..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/ConnectionStateService.java
+++ /dev/null
@@ -1,476 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-
-import androidx.annotation.RequiresApi;
-import androidx.core.content.ContextCompat;
-
-public class ConnectionStateService extends Service {
-	private BroadcastReceiver ConnectionStateReceiver;
-	private Notification.Builder builder;
-	private String state_online = "";
-	private String state_offline = "";
-
-	private ScreenStateReceiver screenStateReceiver;
-	private IntentFilter intentFilter;
-	private boolean isScreenStateReceiverRegistered;
-	private boolean isHandlerPosted;
-
-	public static boolean isConnectionStateServiceRunning;
-	public static boolean isNotificationServiceRunning;
-
-	@Override
-	public void onCreate() {
-		IntentFilter filter = new IntentFilter();
-		filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
-		ConnectionStateReceiver = new ConnectionStateReceiver();
-		registerReceiver(ConnectionStateReceiver, filter);
-
-		intentFilter = new IntentFilter();
-		intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-		intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-		screenStateReceiver = new ScreenStateReceiver();
-
-		isConnectionStateServiceRunning = true;
-		super.onCreate();
-	}
-
-	@Override
-	public void onDestroy() {
-		unregisterReceiver(ConnectionStateReceiver);
-		if (isScreenStateReceiverRegistered) {
-			unregisterReceiver(screenStateReceiver);
-			isScreenStateReceiverRegistered = false;
-		}
-
-		if (isHandlerPosted) {
-			handler.removeCallbacks(runnable);
-			isHandlerPosted = false;
-		}
-
-		isConnectionStateServiceRunning = false;
-		super.onDestroy();
-	}
-
-	@Override
-	public int onStartCommand(Intent intent, int flags, int startId) {
-		state_online = getString(R.string.connection_status_online);
-		state_offline = getString(R.string.connection_status_offline);
-		ConnectivityManager CM = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-		NetworkInfo WiFi_NI = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-		boolean isConnected = WiFi_NI != null && WiFi_NI.isConnected();
-
-		if (isConnected) {
-			if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
-				/// ANDROID 8 - ANDROID 9 ///
-				int NOTIFICATION_ID = 1004;
-
-				NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-				String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-				builder = new Notification.Builder(this, channelID);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-					.setContentTitle(state_online)
-					.setWhen(System.currentTimeMillis())
-					.setChannelId(channelID)
-					.setColor(ContextCompat.getColor(this, R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			} else if (Build.VERSION.SDK_INT >= 29) {
-				/// ANDROID 10 & higher ///
-				int NOTIFICATION_ID = 1005;
-
-				NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-				String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-				builder = new Notification.Builder(this, channelID);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-					.setContentTitle(state_online)
-					.setWhen(System.currentTimeMillis())
-					.setChannelId(channelID)
-					.setColor(ContextCompat.getColor(this, R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			} else if (Build.VERSION.SDK_INT < 26) {
-				/// ANDROID 5 - ANDROID 7 ///
-				int NOTIFICATION_ID = 1006;
-
-				NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-				builder = new Notification.Builder(this);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-					.setContentTitle(state_online)
-					.setWhen(System.currentTimeMillis())
-					.setPriority(Notification.PRIORITY_MIN)
-					.setColor(getResources().getColor(R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			}
-		} else {
-			if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
-				/// ANDROID 8 - ANDROID 9 ///
-				int NOTIFICATION_ID = 1004;
-
-				NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-				String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-				builder = new Notification.Builder(this, channelID);
-
-				Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
-				intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-				PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10041, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-					.setContentTitle(state_offline)
-					.setWhen(System.currentTimeMillis())
-					.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-					.setChannelId(channelID)
-					.setColor(ContextCompat.getColor(this, R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			} else if (Build.VERSION.SDK_INT >= 29) {
-				/// ANDROID 10 & higher ///
-				int NOTIFICATION_ID = 1005;
-
-				NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-				String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-				builder = new Notification.Builder(this, channelID);
-
-				Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
-				intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-				PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10051, intentActionStop, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-					.setContentTitle(state_offline)
-					.setWhen(System.currentTimeMillis())
-					.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-					.setChannelId(channelID)
-					.setColor(ContextCompat.getColor(this, R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			} else if (Build.VERSION.SDK_INT < 26) {
-				/// ANDROID 5 - ANDROID 7 ///
-				int NOTIFICATION_ID = 1006;
-
-				builder = new Notification.Builder(this);
-
-				Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
-				intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-				PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10061, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
-
-				Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-					.setContentTitle(state_offline)
-					.setWhen(System.currentTimeMillis())
-					.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-					.setPriority(Notification.PRIORITY_MIN)
-					.setColor(getResources().getColor(R.color.ntfcColor))
-					.setCategory(Notification.CATEGORY_SERVICE)
-					.setOngoing(true)
-					.setOnlyAlertOnce(true)
-					.setAutoCancel(false)
-					.build();
-				startForeground(NOTIFICATION_ID, notification);
-			}
-		}
-		return START_STICKY;
-	}
-
-	public class ConnectionStateReceiver extends BroadcastReceiver {
-		@Override
-		public void onReceive(final Context context, final Intent intent)
-		{
-			ConnectivityManager CM = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-			NetworkInfo WiFi_NI = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-			boolean isConnected = WiFi_NI != null && WiFi_NI.isConnected();
-
-			Intent ServiceIntent = new Intent(ConnectionStateService.this, NotificationService.class);
-			if (isConnected) {
-				if (Build.VERSION.SDK_INT < 26) {
-					startService(ServiceIntent);
-				} else {
-					startForegroundService(ServiceIntent);
-				}
-
-				if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
-					showOnlineNotificationAPI26_28(context);
-				} else if (Build.VERSION.SDK_INT >= 29) {
-					showOnlineNotificationAPI29(context);
-				} else if (Build.VERSION.SDK_INT < 26) {
-					showOnlineNotificationAPI21(context);
-				}
-
-				boolean keyStartStopScrnStateNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_STRT_STOP_SRVC_CHECK, MainActivity.startStopSrvcScrnState);
-
-				if (keyStartStopScrnStateNtfc) {
-					registerReceiver(screenStateReceiver, intentFilter);
-					isScreenStateReceiverRegistered = true;
-					handler.post(runnable);
-					isHandlerPosted = true;
-				} else {
-					if (isScreenStateReceiverRegistered) {
-						unregisterReceiver(screenStateReceiver);
-						isScreenStateReceiverRegistered = false;
-					}
-
-					if (isHandlerPosted) {
-						handler.removeCallbacks(runnable);
-						isHandlerPosted = false;
-					}
-				}
-				isNotificationServiceRunning = true;
-			} else {
-				if (isNotificationServiceRunning) {
-					stopService(ServiceIntent);
-					isNotificationServiceRunning = false;
-				}
-
-				if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
-					showOfflineNotificationAPI26_28(context);
-				} else if (Build.VERSION.SDK_INT >= 29) {
-					showOfflineNotificationAPI29(context);
-				} else if (Build.VERSION.SDK_INT < 26) {
-					showOfflineNotificationAPI21(context);
-				}
-
-				if (isScreenStateReceiverRegistered) {
-					unregisterReceiver(screenStateReceiver);
-					isScreenStateReceiverRegistered = false;
-				}
-
-				if (isHandlerPosted) {
-					handler.removeCallbacks(runnable);
-					isHandlerPosted = false;
-				}
-			}
-		}
-
-		/// ONLINE NOTIFICATIONS ///
-
-		/// ANDROID 8 - ANDROID 9 ///
-
-		@RequiresApi(api = Build.VERSION_CODES.O)
-		public void showOnlineNotificationAPI26_28(Context context) {
-			int NOTIFICATION_ID = 1004;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-			builder = new Notification.Builder(context, channelID);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-				.setContentTitle(state_online)
-				.setWhen(System.currentTimeMillis())
-				.setChannelId(channelID)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// ANDROID 10 & higher ///
-
-		@RequiresApi(api = Build.VERSION_CODES.O)
-		public void showOnlineNotificationAPI29(Context context) {
-			int NOTIFICATION_ID = 1005;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-			builder = new Notification.Builder(context, channelID);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-				.setContentTitle(state_online)
-				.setWhen(System.currentTimeMillis())
-				.setChannelId(channelID)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// ANDROID 5 - ANDROID 7 ///
-
-		public void showOnlineNotificationAPI21(Context context) {
-			int NOTIFICATION_ID = 1006;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			builder = new Notification.Builder(context);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_success_24px)
-				.setContentTitle(state_online)
-				.setWhen(System.currentTimeMillis())
-				.setPriority(Notification.PRIORITY_MIN)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// END ///
-
-		/// OFFLINE NOTIFICATIONS ///
-
-		/// ANDROID 8 - ANDROID 9 ///
-
-		@RequiresApi(api = Build.VERSION_CODES.O)
-		public void showOfflineNotificationAPI26_28(Context context) {
-			int NOTIFICATION_ID = 1004;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-			builder = new Notification.Builder(context, channelID);
-
-			Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
-			intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-			PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, 10041, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-				.setContentTitle(state_offline)
-				.setWhen(System.currentTimeMillis())
-				.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// ANDROID 10 & higher ///
-
-		@RequiresApi(api = Build.VERSION_CODES.O)
-		public void showOfflineNotificationAPI29(Context context) {
-			int NOTIFICATION_ID = 1005;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-			builder = new Notification.Builder(context, channelID);
-
-			Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
-			intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-			PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, 10051, intentActionStop, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-				.setContentTitle(state_offline)
-				.setWhen(System.currentTimeMillis())
-				.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-				.setChannelId(channelID)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// ANDROID 5 - ANDROID 7 ///
-
-		public void showOfflineNotificationAPI21(Context context) {
-			int NOTIFICATION_ID = 1006;
-
-			NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-			builder = new Notification.Builder(context);
-
-			Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
-			intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
-			PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, 10061, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
-
-			Notification notification = builder.setSmallIcon(R.drawable.wifi_fail_24px)
-				.setContentTitle(state_offline)
-				.setWhen(System.currentTimeMillis())
-				.addAction(R.drawable.stop_24px, getString(R.string.stop_service), pIntentActionStop)
-				.setPriority(Notification.PRIORITY_MIN)
-				.setColor(getResources().getColor(R.color.ntfcColor))
-				.setCategory(Notification.CATEGORY_SERVICE)
-				.setOngoing(true)
-				.setOnlyAlertOnce(true)
-				.setAutoCancel(false)
-				.build();
-			notificationManager.notify(NOTIFICATION_ID, notification);
-		}
-
-		/// END ///
-	}
-
-	private final Handler handler = new Handler(Looper.getMainLooper());
-	private final Runnable runnable = new Runnable() {
-		@Override
-		public void run() {
-			Intent ServiceIntent = new Intent(ConnectionStateService.this, NotificationService.class);
-			if (ScreenStateReceiver.screenState) {
-				if (!isNotificationServiceRunning) {
-					if (android.os.Build.VERSION.SDK_INT < 26) {
-						startService(ServiceIntent);
-					} else {
-						startForegroundService(ServiceIntent);
-					}
-					isNotificationServiceRunning = true;
-				}
-			} else {
-				if (isNotificationServiceRunning) {
-					stopService(ServiceIntent);
-					isNotificationServiceRunning = false;
-				}
-			}
-			handler.postDelayed(runnable, 1000);
-		}
-	};
-
-	@RequiresApi(Build.VERSION_CODES.O)
-	private String createNotificationChannel(NotificationManager notificationManager) {
-		String channelID = "connection_state_service";
-		CharSequence channelName = "Connection State Service";
-		NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_MIN);
-		channel.setDescription("Wi-Fi Info Connection Listener Service Notification");
-		channel.setShowBadge(false);
-		notificationManager.createNotificationChannel(channel);
-		return channelID;
-	}
-
-	@Override
-	public IBinder onBind(Intent intent) {
-		return null;
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ScreenStateReceiver.java b/app/src/main/java/com/truemlgpro/wifiinfo/ScreenStateReceiver.java
deleted file mode 100644
index 563e468..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/ScreenStateReceiver.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class ScreenStateReceiver extends BroadcastReceiver {
-	public static Boolean screenState = false;
-
-	@Override
-	public void onReceive(Context context, Intent intent) {
-		String action = intent.getAction();
-		if (action.equals(Intent.ACTION_SCREEN_ON)) {
-			screenState = true;
-		}
-
-		if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-			screenState = false;
-		}
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/SettingsActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/SettingsActivity.java
deleted file mode 100644
index 9573a65..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/SettingsActivity.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-
-import me.anwarshahriar.calligrapher.Calligrapher;
-
-public class SettingsActivity extends AppCompatActivity {
-	public static final String KEY_PREF_DARK_MODE_SWITCH = "theme_switch";
-	public static final String KEY_PREF_AMOLED_MODE_CHECK = "amoled_theme_checkbox";
-	public static final String KEY_PREF_APP_FONT = "app_font_list";
-	public static final String KEY_PREF_APP_LANGUAGE = "app_language_list";
-	public static final String KEY_PREF_KEEP_SCREEN_ON_SWITCH = "keep_screen_on_checkbox";
-	public static final String KEY_PREF_CARD_FREQ = "card_update_freq";
-	public static final String KEY_PREF_BOOT_SWITCH = "boot_switch";
-	public static final String KEY_PREF_NTFC_SWITCH = "notification_switch";
-	public static final String KEY_PREF_CLR_CHECK = "colorize_ntfc_checkbox";
-	public static final String KEY_PREF_VIS_SIG_STRG_CHECK = "visualize_signal_strength_ntfc_checkbox";
-	public static final String KEY_PREF_STRT_STOP_SRVC_CHECK = "start_stop_service_screen_state_ntfc_checkbox";
-	public static final String KEY_PREF_NTFC_FREQ = "notification_update_freq";
-
-	@Override
-	protected void onCreate(Bundle savedInstanceState)
-	{
-		ThemeManager.initializeThemes(this, getApplicationContext());
-
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.settings_activity);
-
-		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
-
-		setSupportActionBar(toolbar);
-		final ActionBar actionbar = getSupportActionBar();
-		actionbar.setDisplayHomeAsUpEnabled(true);
-		actionbar.setDisplayShowHomeEnabled(true);
-		actionbar.setElevation(20);
-
-		toolbar.setNavigationOnClickListener(v -> {
-			// Back button pressed
-			restartActivity();
-		});
-
-		getSupportFragmentManager().beginTransaction()
-			.replace(R.id.content_frame, new SettingsFragment())
-			.commit();
-	}
-
-	private void restartActivity() {
-		Intent mainActivityIntent = new Intent(SettingsActivity.this, MainActivity.class);
-		finish();
-		overridePendingTransition(0, 0);
-		startActivity(mainActivityIntent);
-		overridePendingTransition(0, 0);
-	}
-
-	@Override
-	public void onBackPressed() {
-		super.onBackPressed();
-		restartActivity();
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/SettingsFragment.java b/app/src/main/java/com/truemlgpro/wifiinfo/SettingsFragment.java
deleted file mode 100644
index cbfe0ec..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/SettingsFragment.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import static android.content.Context.CLIPBOARD_SERVICE;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.widget.Toast;
-
-import androidx.preference.CheckBoxPreference;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
-
-public class SettingsFragment extends PreferenceFragmentCompat {
-	private PackageInfo pi;
-
-	@Override
-	public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-		setPreferencesFromResource(R.xml.preferences, rootKey);
-
-		if (android.os.Build.VERSION.SDK_INT >= 26) {
-			CheckBoxPreference colorizeNtfcPref = findPreference("colorize_ntfc_checkbox");
-			assert colorizeNtfcPref != null;
-			colorizeNtfcPref.setVisible(true);
-			colorizeNtfcPref.setIcon(R.drawable.format_color_fill_24px);
-		}
-
-		try {
-			pi = requireActivity().getPackageManager().getPackageInfo(requireActivity().getPackageName(), 0);
-		} catch (PackageManager.NameNotFoundException e) {
-			e.printStackTrace();
-		}
-
-		setPreferenceSummary("app_version_pref", pi.versionName);
-		setPreferenceSummary("android_version_pref", Build.VERSION.RELEASE);
-		setPreferenceSummary("sdk_version_code_pref", String.valueOf(Build.VERSION.SDK_INT));
-		setPreferenceSummary("device_model_pref", Build.MODEL);
-		setPreferenceSummary("product_name_pref", Build.PRODUCT);
-
-		findPreference("app_version_pref").setOnPreferenceClickListener(preference -> {
-			copyToClipboard((String) preference.getTitle(), (String) preference.getSummary());
-			return true;
-		});
-
-		findPreference("android_version_pref").setOnPreferenceClickListener(preference -> {
-			copyToClipboard((String) preference.getTitle(), (String) preference.getSummary());
-			return true;
-		});
-
-		findPreference("sdk_version_code_pref").setOnPreferenceClickListener(preference -> {
-			copyToClipboard((String) preference.getTitle(), (String) preference.getSummary());
-			return true;
-		});
-
-		findPreference("device_model_pref").setOnPreferenceClickListener(preference -> {
-			copyToClipboard((String) preference.getTitle(), (String) preference.getSummary());
-			return true;
-		});
-
-		findPreference("product_name_pref").setOnPreferenceClickListener(preference -> {
-			copyToClipboard((String) preference.getTitle(), (String) preference.getSummary());
-			return true;
-		});
-	}
-
-	private void setPreferenceSummary(String key, CharSequence summary) {
-		Preference prefObj = findPreference(key);
-		assert prefObj != null;
-		prefObj.setSummary(summary);
-	}
-
-	private void copyToClipboard(String label, String text) {
-		ClipboardManager cbm = (ClipboardManager) requireActivity().getSystemService(CLIPBOARD_SERVICE);
-		ClipData clip = ClipData.newPlainText(label, text);
-		cbm.setPrimaryClip(clip);
-		Toast.makeText(requireContext(), getString(R.string.copied_to_clipboard) + ": " + text, Toast.LENGTH_SHORT).show();
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ThemeManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/ThemeManager.java
deleted file mode 100644
index fe5de9f..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/ThemeManager.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import android.app.Activity;
-import android.content.Context;
-
-/**
- * A helper class for managing themes
- */
-public class ThemeManager {
-	/**
-	 * Initializes themes for Activities based on selected Theme preference
-	 * @param activity an activity which called this method
-	 * @param appContext a context to pass, has to be an Application Context
-	 */
-	public static void initializeThemes(Activity activity, Context appContext) {
-		boolean keyTheme = new SharedPreferencesManager(appContext).retrieveBoolean(SettingsActivity.KEY_PREF_DARK_MODE_SWITCH, MainActivity.darkMode);
-		boolean keyAmoledTheme = new SharedPreferencesManager(appContext).retrieveBoolean(SettingsActivity.KEY_PREF_AMOLED_MODE_CHECK, MainActivity.amoledMode);
-
-		if (keyAmoledTheme) {
-			if (keyTheme) {
-				activity.setTheme(R.style.AmoledDarkTheme);
-			}
-		} else {
-			activity.setTheme(R.style.DarkTheme);
-		}
-
-		if (!keyTheme) {
-			activity.setTheme(R.style.LightTheme);
-		}
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/URLandIPConverter.java b/app/src/main/java/com/truemlgpro/wifiinfo/URLandIPConverter.java
deleted file mode 100644
index 0adf6b1..0000000
--- a/app/src/main/java/com/truemlgpro/wifiinfo/URLandIPConverter.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.truemlgpro.wifiinfo;
-
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.UnknownHostException;
-
-public class URLandIPConverter {
-	public static String convertUrl(String url) throws MalformedURLException, UnknownHostException {
-		try {
-			InetAddress ipFromURL = InetAddress.getByName(new URL(url).getHost());
-			return ipFromURL.getHostAddress();
-		} catch (MalformedURLException | UnknownHostException e) {
-			e.printStackTrace();
-		}
-		return "";
-	}
-}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/adapters/PortScannerAdapter.java b/app/src/main/java/com/truemlgpro/wifiinfo/adapters/PortScannerAdapter.java
new file mode 100644
index 0000000..d943e1f
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/adapters/PortScannerAdapter.java
@@ -0,0 +1,64 @@
+package com.truemlgpro.wifiinfo.adapters;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.models.DiscoveredPort;
+
+import java.util.ArrayList;
+
+public class PortScannerAdapter extends RecyclerView.Adapter {
+	private final ArrayList discoveredPortsArrayList;
+
+	public PortScannerAdapter(ArrayList discoveredPortsArrayList) {
+		this.discoveredPortsArrayList = discoveredPortsArrayList;
+	}
+
+	public static class ViewHolder extends RecyclerView.ViewHolder {
+		public final TextView textview_open_port;
+		public final TextView textview_port_service_name;
+		public final TextView textview_port_service_desc;
+		public final TextView textview_port_service_protocol;
+
+		public ViewHolder(View itemView) {
+			super(itemView);
+			textview_open_port = itemView.findViewById(R.id.port_scanner_item_open_port);
+			textview_port_service_name = itemView.findViewById(R.id.port_scanner_item_port_service_name);
+			textview_port_service_desc = itemView.findViewById(R.id.port_scanner_item_port_service_desc);
+			textview_port_service_protocol = itemView.findViewById(R.id.port_scanner_item_port_service_protocol);
+		}
+	}
+
+	@NonNull
+	@Override
+	public PortScannerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+		View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.port_scanner_recycler_item, parent, false);
+		return new PortScannerAdapter.ViewHolder(view);
+	}
+
+	@Override
+	public void onBindViewHolder(@NonNull PortScannerAdapter.ViewHolder holder, int position) {
+		DiscoveredPort discoveredPort = discoveredPortsArrayList.get(position);
+		holder.textview_open_port.setText(discoveredPort.openPort());
+		holder.textview_port_service_name.setText(discoveredPort.portServiceName());
+		holder.textview_port_service_desc.setText(discoveredPort.portServiceDescription());
+		holder.textview_port_service_protocol.setText(discoveredPort.portServiceProtocol());
+	}
+
+	@Override
+	public int getItemCount() {
+		return discoveredPortsArrayList.size();
+	}
+
+	public void clear() {
+		int size = discoveredPortsArrayList.size();
+		discoveredPortsArrayList.clear();
+		notifyItemRangeRemoved(0, size);
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/adapters/SubnetScannerAdapter.java b/app/src/main/java/com/truemlgpro/wifiinfo/adapters/SubnetScannerAdapter.java
new file mode 100644
index 0000000..80fea5c
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/adapters/SubnetScannerAdapter.java
@@ -0,0 +1,168 @@
+package com.truemlgpro.wifiinfo.adapters;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.text.SpannableStringBuilder;
+import android.text.style.StyleSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.models.SubnetDevice;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+
+import java.util.ArrayList;
+
+public class SubnetScannerAdapter extends RecyclerView.Adapter {
+	private final ArrayList subnetDevicesArrayList;
+	private final Context context;
+
+	public SubnetScannerAdapter(ArrayList subnetDevicesArrayList, Context context) {
+		this.subnetDevicesArrayList = subnetDevicesArrayList;
+		this.context = context;
+	}
+
+	public static class ViewHolder extends RecyclerView.ViewHolder {
+		public final RelativeLayout relative_layout_vendor;
+		public final TextView textview_ip;
+		public final TextView textview_mac;
+		public final TextView textview_vendor;
+		public final TextView textview_device_name;
+		public final TextView textview_device_type;
+		public final TextView textview_device_ping;
+
+		public ViewHolder(View itemView) {
+			super(itemView);
+			relative_layout_vendor = itemView.findViewById(R.id.subnet_scanner_item_relative_layout_vendor);
+			textview_ip = itemView.findViewById(R.id.subnet_scanner_item_ip);
+			textview_mac = itemView.findViewById(R.id.subnet_scanner_item_mac);
+			textview_vendor = itemView.findViewById(R.id.subnet_scanner_item_vendor);
+			textview_device_name = itemView.findViewById(R.id.subnet_scanner_item_device_name);
+			textview_device_type = itemView.findViewById(R.id.subnet_scanner_item_device_type);
+			textview_device_ping = itemView.findViewById(R.id.subnet_scanner_item_device_ping);
+		}
+	}
+
+	@NonNull
+	@Override
+	public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+		View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.subnet_scanner_recycler_item, parent, false);
+		return new ViewHolder(view);
+	}
+
+	@Override
+	public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+		SubnetDevice subnetDevice = subnetDevicesArrayList.get(position);
+		String ipAddress = subnetDevice.getIP();
+		String macAddress = subnetDevice.getMAC();
+		String deviceVendor = subnetDevice.getDeviceVendor();
+		String deviceName = subnetDevice.getDeviceName();
+		String deviceType = subnetDevice.getDeviceType();
+		String pingTimeString = subnetDevice.getDevicePingTime();
+
+		boolean darkTheme = ThemeManager.isDarkTheme(context.getApplicationContext());
+
+		SpannableStringBuilder ipAddressStyled = new SpannableStringBuilder(ipAddress);
+		int lastDotIndex = ipAddress.lastIndexOf(".");
+		// Set the bold style for the last integer in the IP address
+		ipAddressStyled.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
+				lastDotIndex + 1, ipAddress.length(), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
+		holder.textview_ip.setText(ipAddressStyled);
+
+		if (macAddress != null && !macAddress.equals(context.getString(R.string.na))) {
+			holder.relative_layout_vendor.setVisibility(View.VISIBLE); // Make the device vendor layout visible and adjust the padding of the TextViews
+			holder.textview_mac.setPadding(dpToPixels(8), dpToPixels(4), dpToPixels(8), dpToPixels(4));
+			holder.textview_device_name.setPadding(dpToPixels(8), dpToPixels(4), dpToPixels(8), dpToPixels(4));
+
+			holder.textview_mac.setText(macAddress.toUpperCase());
+			if (deviceVendor != null && !deviceVendor.isEmpty()) {
+				holder.textview_vendor.setText(deviceVendor);
+			} else {
+				holder.textview_vendor.setText(context.getString(R.string.na));
+			}
+
+			if (deviceName != null && !deviceName.isEmpty()) {
+				if (deviceType != null && !deviceType.equals(context.getString(R.string.your_device))) {
+					holder.textview_device_name.setText(deviceName);
+				}
+			} else {
+				holder.textview_device_name.setText("");
+			}
+		} else {
+			holder.relative_layout_vendor.setVisibility(View.GONE); // Hide the device vendor layout and adjust the padding of the TextViews
+			holder.textview_mac.setPadding(dpToPixels(8), dpToPixels(4), dpToPixels(8), dpToPixels(16));
+			holder.textview_device_name.setPadding(dpToPixels(8), dpToPixels(4), dpToPixels(8), dpToPixels(16));
+
+			if (deviceName != null && !deviceName.isEmpty()) {
+				holder.textview_mac.setText(deviceName);
+			} else if (deviceType != null && !deviceType.equals(context.getString(R.string.your_device))) {
+				holder.textview_mac.setText(context.getString(R.string.na));
+			}
+		}
+		holder.textview_device_type.setText(deviceType);
+		holder.textview_device_ping.setText(pingTimeString);
+
+		float pingTime = extractPingTime(pingTimeString);
+		if (pingTime <= 100) {
+			if (darkTheme) {
+				holder.textview_device_ping.setTextColor(Color.GREEN);
+			} else {
+				holder.textview_device_ping.setTextColor(Color.parseColor("#11B828"));
+			}
+		} else if (pingTime > 100 && pingTime <= 250) {
+			if (darkTheme) {
+				holder.textview_device_ping.setTextColor(Color.YELLOW);
+			} else {
+				holder.textview_device_ping.setTextColor(Color.parseColor("#FFBB00"));
+			}
+		} else {
+			if (darkTheme) {
+				holder.textview_device_ping.setTextColor(Color.RED);
+			} else {
+				holder.textview_device_ping.setTextColor(Color.parseColor("#C22517"));
+			}
+		}
+	}
+
+	public void updateNetBiosName(String ipAddress, String netBiosName) {
+		for (int i = 0; i < subnetDevicesArrayList.size(); i++) {
+			SubnetDevice subnetDevice = subnetDevicesArrayList.get(i);
+			if (subnetDevice.getIP().equals(ipAddress)) {
+				if (netBiosName != null && !netBiosName.isEmpty()) {
+					if (!subnetDevice.getDeviceType().equals(context.getString(R.string.your_device))) {
+						subnetDevice.setDeviceName(netBiosName);
+						notifyItemChanged(i);
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	private float extractPingTime(String pingString) {
+		String timeString = pingString.replaceAll("[^\\d.]", "");
+		return Float.parseFloat(timeString);
+	}
+
+	private int dpToPixels(int dp) {
+		final float scale = context.getResources().getDisplayMetrics().density;
+		return (int) (dp * scale + 0.5f);
+	}
+
+	@Override
+	public int getItemCount() {
+		return subnetDevicesArrayList.size();
+	}
+
+	public void clear() {
+		int size = subnetDevicesArrayList.size();
+		subnetDevicesArrayList.clear();
+		notifyItemRangeRemoved(0, size);
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceDefaults.java b/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceDefaults.java
new file mode 100644
index 0000000..744802c
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceDefaults.java
@@ -0,0 +1,18 @@
+package com.truemlgpro.wifiinfo.interfaces;
+
+public interface PreferenceDefaults {
+	boolean DARK_MODE = true;
+	boolean AMOLED_MODE = false;
+	boolean KEEP_SCREEN_ON = true;
+	boolean START_ON_BOOT = false;
+	boolean SHOW_NTFC = true;
+	boolean VISUALIZE_SIGNAL_STRENGTH = false;
+	boolean START_STOP_SRVC_SCRN_STATE = false;
+	boolean COLORIZE_NTFC = false;
+	boolean NEVER_SHOW_GEO_DIALOG = false;
+	boolean NEVER_SHOW_PERMISSION_REQ_DIALOG = false;
+	String NTFC_UPDATE_INTERVAL = "1000";
+	String CARD_UPDATE_INTERVAL = "1000";
+	String APP_FONT = "fonts/Gilroy-Semibold.ttf";
+	String APP_LANG = "default_lang";
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceKeys.java b/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceKeys.java
new file mode 100644
index 0000000..7bc06bc
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/interfaces/PreferenceKeys.java
@@ -0,0 +1,18 @@
+package com.truemlgpro.wifiinfo.interfaces;
+
+public interface PreferenceKeys {
+	String KEY_PREF_DARK_MODE = "theme_switch";
+	String KEY_PREF_AMOLED_MODE = "amoled_theme_checkbox";
+	String KEY_PREF_APP_FONT = "app_font_list";
+	String KEY_PREF_APP_LANGUAGE = "app_language_list";
+	String KEY_PREF_KEEP_SCREEN_ON = "keep_screen_on_checkbox";
+	String KEY_PREF_CARD_FREQ = "card_update_freq";
+	String KEY_PREF_START_ON_BOOT = "boot_switch";
+	String KEY_PREF_SHOW_NTFC = "notification_switch";
+	String KEY_PREF_COLORIZE_NTFC = "colorize_ntfc_checkbox";
+	String KEY_PREF_VISUALIZE_SIGNAL_STRENGTH = "visualize_signal_strength_ntfc_checkbox";
+	String KEY_PREF_START_STOP_SVC = "start_stop_service_screen_state_ntfc_checkbox";
+	String KEY_PREF_NTFC_FREQ = "notification_update_freq";
+	String KEY_NEVER_SHOW_GEO_DIALOG = "NeverShowGeoDialog";
+	String KEY_NEVER_SHOW_PERMISSION_REQ_DIALOG = "NeverShowPermissionDialog";
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/models/DiscoveredPort.java b/app/src/main/java/com/truemlgpro/wifiinfo/models/DiscoveredPort.java
new file mode 100644
index 0000000..95318ab
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/models/DiscoveredPort.java
@@ -0,0 +1,3 @@
+package com.truemlgpro.wifiinfo.models;
+
+public record DiscoveredPort(String openPort, String portServiceName, String portServiceDescription, String portServiceProtocol) { }
\ No newline at end of file
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/models/SubnetDevice.java b/app/src/main/java/com/truemlgpro/wifiinfo/models/SubnetDevice.java
new file mode 100644
index 0000000..5250960
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/models/SubnetDevice.java
@@ -0,0 +1,47 @@
+package com.truemlgpro.wifiinfo.models;
+
+public class SubnetDevice {
+	private final String ip;
+	private final String mac;
+	private final String vendor;
+	private String deviceName;
+	private final String deviceType;
+	private final String devicePing;
+
+	public SubnetDevice(String ip, String mac, String vendor, String deviceName, String deviceType, String devicePing) {
+		this.ip = ip;
+		this.mac = mac;
+		this.vendor = vendor;
+		this.deviceName = deviceName;
+		this.deviceType = deviceType;
+		this.devicePing = devicePing;
+	}
+
+	public String getIP() {
+		return ip;
+	}
+
+	public String getMAC() {
+		return mac;
+	}
+
+	public String getDeviceVendor() {
+		return vendor;
+	}
+
+	public String getDeviceName() {
+		return deviceName;
+	}
+
+	public void setDeviceName(String deviceName) {
+		this.deviceName = deviceName;
+	}
+
+	public String getDeviceType() {
+		return deviceType;
+	}
+
+	public String getDevicePingTime() {
+		return devicePing;
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ActionButtonReceiver.java b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/ActionButtonReceiver.java
similarity index 72%
rename from app/src/main/java/com/truemlgpro/wifiinfo/ActionButtonReceiver.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/receivers/ActionButtonReceiver.java
index 231bdac..f1f0297 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/ActionButtonReceiver.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/ActionButtonReceiver.java
@@ -1,15 +1,20 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.receivers;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
+import com.truemlgpro.wifiinfo.services.ConnectionStateService;
+import com.truemlgpro.wifiinfo.services.NotificationService;
+import com.truemlgpro.wifiinfo.ui.MainActivity;
+
 public class ActionButtonReceiver extends BroadcastReceiver {
 	@Override
 	public void onReceive(Context context, Intent intent)
 	{
 		if (intent.getAction() != null) {
 			if (intent.getAction().equals("ACTION_STOP")) {
+				NotificationService.shouldPostAnUpdate = false;
 				context.stopService(new Intent(context, NotificationService.class));
 				context.stopService(new Intent(context, ConnectionStateService.class));
 				MainActivity.isServiceRunning = false;
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/receivers/BootReceiver.java b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/BootReceiver.java
new file mode 100644
index 0000000..4c906d7
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/BootReceiver.java
@@ -0,0 +1,29 @@
+package com.truemlgpro.wifiinfo.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.services.ConnectionStateService;
+import com.truemlgpro.wifiinfo.utils.SharedPreferencesManager;
+
+public class BootReceiver extends BroadcastReceiver {
+	@Override
+	public void onReceive(Context context, Intent intent) {
+		String action = intent.getAction();
+		boolean keyBoot = new SharedPreferencesManager(context).retrieveBoolean(PreferenceKeys.KEY_PREF_START_ON_BOOT, PreferenceDefaults.START_ON_BOOT);
+
+		if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+			if (keyBoot) {
+				Intent serviceIntent = new Intent(context, ConnectionStateService.class);
+				if (android.os.Build.VERSION.SDK_INT < 26) {
+					context.startService(serviceIntent);
+				} else {
+					context.startForegroundService(serviceIntent);
+				}
+			}
+		}
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/receivers/ScreenStateReceiver.java b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/ScreenStateReceiver.java
new file mode 100644
index 0000000..fe698ba
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/receivers/ScreenStateReceiver.java
@@ -0,0 +1,29 @@
+package com.truemlgpro.wifiinfo.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.truemlgpro.wifiinfo.services.NotificationService;
+
+public class ScreenStateReceiver extends BroadcastReceiver {
+	@Override
+	public void onReceive(Context context, Intent intent) {
+		String action = intent.getAction();
+		Intent serviceIntent = new Intent(context, NotificationService.class);
+
+		if (action.equals(Intent.ACTION_SCREEN_ON)) {
+			if (!NotificationService.isNotificationServiceRunning) {
+				if (android.os.Build.VERSION.SDK_INT < 26) {
+					context.startService(serviceIntent);
+				} else {
+					context.startForegroundService(serviceIntent);
+				}
+			}
+		} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+			if (NotificationService.isNotificationServiceRunning) {
+				context.stopService(serviceIntent);
+			}
+		}
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/services/ConnectionStateService.java b/app/src/main/java/com/truemlgpro/wifiinfo/services/ConnectionStateService.java
new file mode 100644
index 0000000..c958bbf
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/services/ConnectionStateService.java
@@ -0,0 +1,346 @@
+package com.truemlgpro.wifiinfo.services;
+
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Build;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.StringRes;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.receivers.ActionButtonReceiver;
+import com.truemlgpro.wifiinfo.receivers.ScreenStateReceiver;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.SharedPreferencesManager;
+
+import java.util.Locale;
+
+public class ConnectionStateService extends Service {
+	private Notification notification21_25;
+	private Notification notification26_28;
+	private Notification notification29;
+	private BroadcastReceiver ConnectionStateReceiver;
+	private Notification.Builder builder;
+	private String state_online = "";
+	private String state_offline = "";
+
+	private static final String CHANNEL_ID = "connection_state_service";
+	private static final int NOTIFICATION_ID_API21_25 = 1004;
+	private static final int NOTIFICATION_ID_API26_28 = 1005;
+	private static final int NOTIFICATION_ID_API29 = 1006;
+	private static final int SRVC_STOP_REQUEST_CODE_API21_25 = 10041;
+	private static final int SRVC_STOP_REQUEST_CODE_API26_28 = 10051;
+	private static final int SRVC_STOP_REQUEST_CODE_API29 = 10061;
+
+	private ScreenStateReceiver screenStateReceiver;
+	private IntentFilter intentFilter;
+	private boolean isScreenStateReceiverRegistered;
+
+	private Context localizedContext;
+
+	public static boolean isConnectionStateServiceRunning;
+
+	@Override
+	public void onCreate() {
+		super.onCreate();
+
+		IntentFilter filter = new IntentFilter();
+		filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
+		ConnectionStateReceiver = new ConnectionStateReceiver();
+		registerReceiver(ConnectionStateReceiver, filter);
+
+		intentFilter = new IntentFilter();
+		intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+		intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+		screenStateReceiver = new ScreenStateReceiver();
+
+		isConnectionStateServiceRunning = true;
+		initLocaleConfig();
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		unregisterReceiver(ConnectionStateReceiver);
+		if (isScreenStateReceiverRegistered) {
+			unregisterReceiver(screenStateReceiver);
+			isScreenStateReceiverRegistered = false;
+		}
+
+		isConnectionStateServiceRunning = false;
+	}
+
+	@Override
+	public int onStartCommand(Intent intent, int flags, int startId) {
+		state_online = getLocalizedString(R.string.connection_status_online);
+		state_offline = getLocalizedString(R.string.connection_status_offline);
+		ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+		NetworkInfo wifiCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+		boolean isConnected = wifiCheck != null && wifiCheck.isConnected();
+
+		if (isConnected) {
+			if (Build.VERSION.SDK_INT < 26) {
+				/// ANDROID 5 - ANDROID 7 ///
+				showOnlineNotificationAPI21_25(this);
+				startForeground(NOTIFICATION_ID_API21_25, notification21_25);
+			} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+				/// ANDROID 8 - ANDROID 9 ///
+				showOnlineNotificationAPI26_28(this);
+				startForeground(NOTIFICATION_ID_API26_28, notification26_28);
+			} else if (Build.VERSION.SDK_INT >= 29) {
+				/// ANDROID 10 & higher ///
+				showOnlineNotificationAPI29(this);
+				startForeground(NOTIFICATION_ID_API29, notification29, Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ? ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE : 0);
+			}
+		} else {
+			if (Build.VERSION.SDK_INT < 26) {
+				/// ANDROID 5 - ANDROID 7 ///
+				showOfflineNotificationAPI21_25(this);
+				startForeground(NOTIFICATION_ID_API21_25, notification21_25);
+			} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+				/// ANDROID 8 - ANDROID 9 ///
+				showOfflineNotificationAPI26_28(this);
+				startForeground(NOTIFICATION_ID_API26_28, notification26_28);
+			} else if (Build.VERSION.SDK_INT >= 29) {
+				/// ANDROID 10 & higher ///
+				showOfflineNotificationAPI29(this);
+				startForeground(NOTIFICATION_ID_API29, notification29, Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ? ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE : 0);
+			}
+		}
+		return START_STICKY;
+	}
+
+	private void initLocaleConfig() {
+		String localePref = new SharedPreferencesManager(getApplicationContext()).retrieveString(PreferenceKeys.KEY_PREF_APP_LANGUAGE, PreferenceDefaults.APP_LANG);
+		if (localePref.equals(PreferenceDefaults.APP_LANG))
+			localePref = LocaleManager.getDefaultSystemLocale();
+		Locale currentAppLocale = Locale.forLanguageTag(localePref);
+		Configuration conf = getApplicationContext().getResources().getConfiguration();
+		conf.setLocale(currentAppLocale);
+		localizedContext = getApplicationContext().createConfigurationContext(conf);
+	}
+
+	@NonNull
+	private String getLocalizedString(@StringRes int stringRes) {
+		return localizedContext.getResources().getString(stringRes);
+	}
+
+	/// ONLINE NOTIFICATIONS ///
+	/// ANDROID 5 - ANDROID 7 ///
+
+	public void showOnlineNotificationAPI21_25(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		builder = new Notification.Builder(context);
+
+		notification21_25 = builder.setSmallIcon(R.drawable.wifi_success_24px)
+				.setContentTitle(state_online)
+				.setWhen(System.currentTimeMillis())
+				.setPriority(Notification.PRIORITY_MIN)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API21_25, notification21_25);
+	}
+
+	/// ANDROID 8 - ANDROID 9 ///
+
+	@RequiresApi(api = Build.VERSION_CODES.O)
+	public void showOnlineNotificationAPI26_28(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(context, channelID);
+
+		notification26_28 = builder.setSmallIcon(R.drawable.wifi_success_24px)
+				.setContentTitle(state_online)
+				.setWhen(System.currentTimeMillis())
+				.setChannelId(channelID)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API26_28, notification26_28);
+	}
+
+	/// ANDROID 10 & higher ///
+
+	@RequiresApi(api = Build.VERSION_CODES.O)
+	public void showOnlineNotificationAPI29(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(context, channelID);
+
+		notification29 = builder.setSmallIcon(R.drawable.wifi_success_24px)
+				.setContentTitle(state_online)
+				.setWhen(System.currentTimeMillis())
+				.setChannelId(channelID)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API29, notification29);
+	}
+
+	/// END ///
+
+	/// OFFLINE NOTIFICATIONS ///
+	/// ANDROID 5 - ANDROID 7 ///
+
+	public void showOfflineNotificationAPI21_25(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		builder = new Notification.Builder(context);
+
+		Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
+		intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
+		@SuppressLint("UnspecifiedImmutableFlag")
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, SRVC_STOP_REQUEST_CODE_API21_25, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
+
+		notification21_25 = builder.setSmallIcon(R.drawable.wifi_fail_24px)
+				.setContentTitle(state_offline)
+				.setWhen(System.currentTimeMillis())
+				.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_service), pIntentActionStop)
+				.setPriority(Notification.PRIORITY_MIN)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API21_25, notification21_25);
+	}
+
+	/// ANDROID 8 - ANDROID 9 ///
+
+	@RequiresApi(api = Build.VERSION_CODES.O)
+	public void showOfflineNotificationAPI26_28(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(context, channelID);
+
+		Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
+		intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
+		@SuppressLint("UnspecifiedImmutableFlag")
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, SRVC_STOP_REQUEST_CODE_API26_28, intentActionStop, PendingIntent.FLAG_ONE_SHOT);
+
+		notification26_28 = builder.setSmallIcon(R.drawable.wifi_fail_24px)
+				.setContentTitle(state_offline)
+				.setWhen(System.currentTimeMillis())
+				.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_service), pIntentActionStop)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API26_28, notification26_28);
+	}
+
+	/// ANDROID 10 & higher ///
+
+	@RequiresApi(api = Build.VERSION_CODES.O)
+	public void showOfflineNotificationAPI29(Context context) {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(context, channelID);
+
+		Intent intentActionStop = new Intent(context, ActionButtonReceiver.class);
+		intentActionStop.setAction("ACTION_STOP_CONN_STATE_SERVICE");
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(context, SRVC_STOP_REQUEST_CODE_API29, intentActionStop, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
+
+		notification29 = builder.setSmallIcon(R.drawable.wifi_fail_24px)
+				.setContentTitle(state_offline)
+				.setWhen(System.currentTimeMillis())
+				.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_service), pIntentActionStop)
+				.setChannelId(channelID)
+				.setColor(getResources().getColor(R.color.ntfcColor))
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
+		notificationManager.notify(NOTIFICATION_ID_API29, notification29);
+	}
+
+	/// END ///
+
+	public class ConnectionStateReceiver extends BroadcastReceiver {
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+			NetworkInfo wifiCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+			boolean isConnected = wifiCheck != null && wifiCheck.isConnected();
+
+			Intent ServiceIntent = new Intent(ConnectionStateService.this, NotificationService.class);
+			if (isConnected) {
+				if (Build.VERSION.SDK_INT < 26) {
+					startService(ServiceIntent);
+				} else {
+					startForegroundService(ServiceIntent);
+				}
+
+				if (Build.VERSION.SDK_INT < 26) {
+					showOnlineNotificationAPI21_25(ConnectionStateService.this);
+				} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+					showOnlineNotificationAPI26_28(ConnectionStateService.this);
+				} else if (Build.VERSION.SDK_INT >= 29) {
+					showOnlineNotificationAPI29(ConnectionStateService.this);
+				}
+
+				boolean keyStartStopScrnStateNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_START_STOP_SVC, PreferenceDefaults.START_STOP_SRVC_SCRN_STATE);
+
+				if (keyStartStopScrnStateNtfc) {
+					registerReceiver(screenStateReceiver, intentFilter);
+					isScreenStateReceiverRegistered = true;
+				} else {
+					if (isScreenStateReceiverRegistered) {
+						unregisterReceiver(screenStateReceiver);
+						isScreenStateReceiverRegistered = false;
+					}
+				}
+			} else {
+				if (NotificationService.isNotificationServiceRunning) {
+					stopService(ServiceIntent);
+				}
+
+				if (Build.VERSION.SDK_INT < 26) {
+					showOfflineNotificationAPI21_25(ConnectionStateService.this);
+				} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+					showOfflineNotificationAPI26_28(ConnectionStateService.this);
+				} else if (Build.VERSION.SDK_INT >= 29) {
+					showOfflineNotificationAPI29(ConnectionStateService.this);
+				}
+
+				if (isScreenStateReceiverRegistered) {
+					unregisterReceiver(screenStateReceiver);
+					isScreenStateReceiverRegistered = false;
+				}
+			}
+		}
+	}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return null;
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/NotificationService.java b/app/src/main/java/com/truemlgpro/wifiinfo/services/NotificationService.java
similarity index 54%
rename from app/src/main/java/com/truemlgpro/wifiinfo/NotificationService.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/services/NotificationService.java
index 71122ae..b0059ca 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/NotificationService.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/services/NotificationService.java
@@ -1,12 +1,14 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.services;
 
+import android.annotation.SuppressLint;
 import android.app.Notification;
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Build;
@@ -16,7 +18,17 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.StringRes;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.receivers.ActionButtonReceiver;
+import com.truemlgpro.wifiinfo.ui.MainActivity;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.SharedPreferencesManager;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -24,31 +36,36 @@
 import java.net.SocketException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 
 public class NotificationService extends Service {
+	private Notification notification21_25;
 	private Notification notification26_28;
 	private Notification notification29;
-	private Notification notification21_25;
 	private Notification.Builder builder;
 
-	private final int NOTIFICATION_ID_API26_28 = 1001;
-	private final int NOTIFICATION_ID_API29 = 1002;
-	private final int NOTIFICATION_ID_API21_25 = 1003;
+	private final String CHANNEL_ID = "wifi_info";
+	private final int NOTIFICATION_ID_API21_25 = 1001;
+	private final int NOTIFICATION_ID_API26_28 = 1002;
+	private final int NOTIFICATION_ID_API29 = 1003;
+	private int visualizeSignalStrengthNtfcColor;
 
-	private boolean shouldPostAnUpdate = true;
+	private Context localizedContext;
 
-	private int visSigStrgNtfcColor;
+	public static boolean shouldPostAnUpdate = true;
+	public static boolean isNotificationServiceRunning;
 
 	@Override
 	public void onCreate() {
 		super.onCreate();
 		handler.post(runnable);
+		isNotificationServiceRunning = true;
+		initLocaleConfig();
 	}
 
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
-		shouldPostAnUpdate = false;
 		handler.removeCallbacks(runnable);
 		if (Build.VERSION.SDK_INT < 24) {
 			stopForeground(true);
@@ -56,54 +73,68 @@ public void onDestroy() {
 			stopForeground(STOP_FOREGROUND_REMOVE);
 		}
 		stopSelf();
+		isNotificationServiceRunning = false;
 	}
 
 	@Override
 	public int onStartCommand(Intent intent, int flags, int startId) {
-		if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+		if (Build.VERSION.SDK_INT < 26) {
+			/// Android 5 - Android 7 ///
+			showNotificationAPI21_25();
+			startForeground(NOTIFICATION_ID_API21_25, notification21_25);
+		} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
 			/// Android 8 - Android 9 ///
 			showNotificationAPI26_28();
 			startForeground(NOTIFICATION_ID_API26_28, notification26_28);
 		} else if (Build.VERSION.SDK_INT >= 29) {
 			/// Android 10 & higher ///
 			showNotificationAPI29();
-			startForeground(NOTIFICATION_ID_API29, notification29);
-		} else if (Build.VERSION.SDK_INT < 26) {
-			/// Android 5 - Android 7 ///
-			showNotificationAPI21_25();
-			startForeground(NOTIFICATION_ID_API21_25, notification21_25);
+			startForeground(NOTIFICATION_ID_API29, notification29, Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ? ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE : 0);
 		}
 		return START_NOT_STICKY;
 	}
-	
-	/// Handler for Notification Updates ///
+
+	private void initLocaleConfig() {
+		String localePref = new SharedPreferencesManager(getApplicationContext()).retrieveString(PreferenceKeys.KEY_PREF_APP_LANGUAGE, PreferenceDefaults.APP_LANG);
+		if (localePref.equals(PreferenceDefaults.APP_LANG))
+			localePref = LocaleManager.getDefaultSystemLocale();
+		Locale currentAppLocale = Locale.forLanguageTag(localePref);
+		Configuration conf = getApplicationContext().getResources().getConfiguration();
+		conf.setLocale(currentAppLocale);
+		localizedContext = getApplicationContext().createConfigurationContext(conf);
+	}
+
+	@NonNull
+	private String getLocalizedString(@StringRes int stringRes) {
+		return localizedContext.getResources().getString(stringRes);
+	}
+
+	/// Handler for notification updates ///
 	private final Handler handler = new Handler(Looper.getMainLooper());
 	private final Runnable runnable = new Runnable() {
 		@Override
 		public void run() {
-			if (!shouldPostAnUpdate) {
-				return;
-			}
-			int keyNtfcFreqFormatted = Integer.parseInt(new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_NTFC_FREQ, MainActivity.ntfcUpdateInterval));
-			if (Build.VERSION.SDK_INT >= 29) {
-				showNotificationAPI29();
-			} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
-				showNotificationAPI26_28();
-			} else if (Build.VERSION.SDK_INT < 26) {
-				showNotificationAPI21_25();
+			if (shouldPostAnUpdate) {
+				int keyNtfcFreqFormatted = Integer.parseInt(new SharedPreferencesManager(getApplicationContext()).retrieveString(PreferenceKeys.KEY_PREF_NTFC_FREQ, PreferenceDefaults.NTFC_UPDATE_INTERVAL));
+				if (Build.VERSION.SDK_INT < 26) {
+					showNotificationAPI21_25();
+				} else if (Build.VERSION.SDK_INT >= 26 && Build.VERSION.SDK_INT < 29) {
+					showNotificationAPI26_28();
+				} else if (Build.VERSION.SDK_INT >= 29) {
+					showNotificationAPI29();
+				}
+				handler.postDelayed(runnable, keyNtfcFreqFormatted);
 			}
-			handler.postDelayed(runnable, keyNtfcFreqFormatted);
 		}
 	};
 
 	private Intent getNtfcSettingsActivityIntent() {
 		Intent intentActionSettings = new Intent();
+		intentActionSettings.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
 		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-			intentActionSettings.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
 			intentActionSettings.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
 			intentActionSettings.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 		} else {
-			intentActionSettings.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
 			intentActionSettings.putExtra("app_package", getPackageName());
 			intentActionSettings.putExtra("app_uid", getApplicationInfo().uid);
 			intentActionSettings.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -111,108 +142,103 @@ private Intent getNtfcSettingsActivityIntent() {
 		return intentActionSettings;
 	}
 
-	/// ANDROID 8 - ANDROID 9 ///
-	@RequiresApi(api = Build.VERSION_CODES.O)
-	public void showNotificationAPI26_28() {
+	/// ANDROID 5 - ANDROID 7 ///
+	public void showNotificationAPI21_25() {
 		Intent notificationIntent = new Intent(this, MainActivity.class);
-		PendingIntent content_intent = PendingIntent.getActivity(this, 10011, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+		@SuppressLint("UnspecifiedImmutableFlag")
+		PendingIntent content_intent = PendingIntent.getActivity(this, 10031, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
 
 		Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
 		intentActionStop.setAction("ACTION_STOP");
-		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10012, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT);
-		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10013, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
-
-		NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-		builder = new Notification.Builder(this, channelID);
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10032, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT);
+		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10033, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
 
-		boolean keyNtfcColor = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_CLR_CHECK, MainActivity.colorizeNtfc);
-		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK, MainActivity.visualizeSigStrg);
+		NotificationManager notificationManager;
+		builder = new Notification.Builder(this);
+		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_VISUALIZE_SIGNAL_STRENGTH, PreferenceDefaults.VISUALIZE_SIGNAL_STRENGTH);
 
 		WifiManager mainWifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 		WifiInfo wInfo = mainWifi.getConnectionInfo();
 		String ip = getIPv4Address();
 		String ssid = wInfo.getSSID();
 		if (ssid.equals("")) {
-			ssid = getString(R.string.na);
+			ssid = getLocalizedString(R.string.na);
 		} else {
 			ssid = ssid.replaceAll("^\"|\"$", "");
 		}
-		String bssid = "";
+		String bssid;
 		if (wInfo.getBSSID() != null) {
 			bssid = wInfo.getBSSID().toUpperCase();
-		}
-		if (bssid.contains("02:00:00:00:00:00")) {
-			bssid = getString(R.string.na);
+		} else {
+			bssid = getLocalizedString(R.string.na);
 		}
 		int rssi = wInfo.getRssi();
 		int RSSIconv = WifiManager.calculateSignalLevel(rssi, 101);
 		if (keyVisSigStrgNtfc) {
 			if (RSSIconv >= 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
 			} else if (RSSIconv >= 50 && RSSIconv < 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
 			} else if (RSSIconv >= 1 && RSSIconv < 50) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
 			}
 		} else {
-			visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColor);
+			visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColor);
 		}
 		int freq = wInfo.getFrequency();
 		String distanceFromRssiRounded = String.format("~%.1fm", freqRssiToDistance(freq, rssi));
 		int channel = convertFrequencyToChannel(freq);
 		int networkSpeed = wInfo.getLinkSpeed();
 
-		String collapsedInfo = getString(R.string.ssid) + ": " + ssid + " | " + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
-		String extendedInfo = getString(R.string.ssid) + ": " + ssid + "\n" + getString(R.string.bssid) + ": " + bssid + "\n" + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
-			getString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getString(R.string.network_channel) + ": " + channel + "\n" +
-			getString(R.string.network_speed) + ": " + networkSpeed + " / " + networkSpeed + " Mbps";
+		String collapsedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + " | " + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
+		String extendedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + "\n" + getLocalizedString(R.string.bssid) + ": " + bssid + "\n" + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
+				getLocalizedString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getLocalizedString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getLocalizedString(R.string.network_channel) + ": " + channel + "\n" +
+				getLocalizedString(R.string.network_speed) + ": " + networkSpeed + " / " + networkSpeed + " Mbps";
 
-		notification26_28 = builder.setContentIntent(content_intent)
-			.setSmallIcon(R.drawable.wifi_24px)
-			.setContentTitle(getString(R.string.local_ip) + ": " + ip)
-			.setContentText(collapsedInfo)
-			.setWhen(System.currentTimeMillis())
-			.addAction(R.drawable.stop_24px, getString(R.string.stop_services), pIntentActionStop)
-			.addAction(R.drawable.settings_24px, getString(R.string.notification_settings), pIntentActionSettings)
-			.setChannelId(channelID)
-			.setColorized(keyNtfcColor)
-			.setColor(visSigStrgNtfcColor)
-			.setCategory(Notification.CATEGORY_SERVICE)
-			.setStyle(new Notification.BigTextStyle().bigText(extendedInfo))
-			.setOngoing(true)
-			.setOnlyAlertOnce(true)
-			.setAutoCancel(false)
-			.build();
+		notification21_25 = builder.setContentIntent(content_intent)
+				.setSmallIcon(R.drawable.wifi_24px)
+				.setContentTitle(getLocalizedString(R.string.local_ip) + ": " + ip)
+				.setContentText(collapsedInfo)
+				.setWhen(System.currentTimeMillis())
+				.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_services), pIntentActionStop)
+				.addAction(R.drawable.settings_24px, getLocalizedString(R.string.notification_settings), pIntentActionSettings)
+				.setPriority(Notification.PRIORITY_LOW)
+				.setColor(visualizeSignalStrengthNtfcColor)
+				.setCategory(Notification.CATEGORY_SERVICE)
+				.setStyle(new Notification.BigTextStyle().bigText(extendedInfo))
+				.setOngoing(true)
+				.setOnlyAlertOnce(true)
+				.setAutoCancel(false)
+				.build();
 
-			notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-			notificationManager.notify(NOTIFICATION_ID_API26_28, notification26_28);
+		notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+		notificationManager.notify(NOTIFICATION_ID_API21_25, notification21_25);
 	}
 
-	/// ANDROID 10 & higher ///
-	@RequiresApi(api = Build.VERSION_CODES.Q)
-	public void showNotificationAPI29() {
+	/// ANDROID 8 - ANDROID 9 ///
+	@RequiresApi(api = Build.VERSION_CODES.O)
+	public void showNotificationAPI26_28() {
 		Intent notificationIntent = new Intent(this, MainActivity.class);
-		PendingIntent content_intent = PendingIntent.getActivity(this, 10021, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+		@SuppressLint("UnspecifiedImmutableFlag")
+		PendingIntent content_intent = PendingIntent.getActivity(this, 10011, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
 
 		Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
 		intentActionStop.setAction("ACTION_STOP");
-		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10022, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10023, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10012, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT);
+		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10013, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
 
-		NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-		String channelID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
-		builder = new Notification.Builder(this, channelID);
+		String ntfcChannelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(this, ntfcChannelId);
 
-		boolean keyNtfcColor = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_CLR_CHECK, MainActivity.colorizeNtfc);
-		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK, MainActivity.visualizeSigStrg);
+		boolean keyNtfcColor = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_COLORIZE_NTFC, PreferenceDefaults.COLORIZE_NTFC);
+		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_VISUALIZE_SIGNAL_STRENGTH, PreferenceDefaults.VISUALIZE_SIGNAL_STRENGTH);
 
 		WifiManager mainWifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 		WifiInfo wInfo = mainWifi.getConnectionInfo();
 		String ip = getIPv4Address();
 		String ssid = wInfo.getSSID();
 		if (ssid.equals("")) {
-			ssid = getString(R.string.na);
+			ssid = getLocalizedString(R.string.na);
 		} else {
 			ssid = ssid.replaceAll("^\"|\"$", "");
 		}
@@ -221,43 +247,41 @@ public void showNotificationAPI29() {
 			bssid = wInfo.getBSSID().toUpperCase();
 		}
 		if (bssid.contains("02:00:00:00:00:00")) {
-			bssid = getString(R.string.na);
+			bssid = getLocalizedString(R.string.na);
 		}
 		int rssi = wInfo.getRssi();
 		int RSSIconv = WifiManager.calculateSignalLevel(rssi, 101);
 		if (keyVisSigStrgNtfc) {
 			if (RSSIconv >= 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
 			} else if (RSSIconv >= 50 && RSSIconv < 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
 			} else if (RSSIconv >= 1 && RSSIconv < 50) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
 			}
 		} else {
-			visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColor);
+			visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColor);
 		}
 		int freq = wInfo.getFrequency();
 		String distanceFromRssiRounded = String.format("~%.1fm", freqRssiToDistance(freq, rssi));
 		int channel = convertFrequencyToChannel(freq);
-		int TXLinkSpd = wInfo.getTxLinkSpeedMbps();
-		int RXLinkSpd = wInfo.getRxLinkSpeedMbps();
-		String networkSpeed = RXLinkSpd + " / " + TXLinkSpd + " Mbps";
+		int networkSpeed = wInfo.getLinkSpeed();
 
-		String collapsedInfo = getString(R.string.ssid) + ": " + ssid + " | " + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
-		String extendedInfo = getString(R.string.ssid) + ": " + ssid + "\n" + getString(R.string.bssid) + ": " + bssid + "\n" + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
-			getString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getString(R.string.network_channel) + ": " + channel + "\n" +
-			getString(R.string.network_speed) + ": " + networkSpeed;
+		String collapsedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + " | " + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
+		String extendedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + "\n" + getLocalizedString(R.string.bssid) + ": " + bssid + "\n" + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
+			getLocalizedString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getLocalizedString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getLocalizedString(R.string.network_channel) + ": " + channel + "\n" +
+			getLocalizedString(R.string.network_speed) + ": " + networkSpeed + " / " + networkSpeed + " Mbps";
 
-		notification29 = builder.setContentIntent(content_intent)
+		notification26_28 = builder.setContentIntent(content_intent)
 			.setSmallIcon(R.drawable.wifi_24px)
-			.setContentTitle(getString(R.string.local_ip) + ": " + ip)
+			.setContentTitle(getLocalizedString(R.string.local_ip) + ": " + ip)
 			.setContentText(collapsedInfo)
 			.setWhen(System.currentTimeMillis())
-			.addAction(R.drawable.stop_24px, getString(R.string.stop_services), pIntentActionStop)
-			.addAction(R.drawable.settings_24px, getString(R.string.notification_settings), pIntentActionSettings)
-			.setChannelId(channelID)
+			.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_services), pIntentActionStop)
+			.addAction(R.drawable.settings_24px, getLocalizedString(R.string.notification_settings), pIntentActionSettings)
+			.setChannelId(ntfcChannelId)
 			.setColorized(keyNtfcColor)
-			.setColor(visSigStrgNtfcColor)
+			.setColor(visualizeSignalStrengthNtfcColor)
 			.setCategory(Notification.CATEGORY_SERVICE)
 			.setStyle(new Notification.BigTextStyle().bigText(extendedInfo))
 			.setOngoing(true)
@@ -265,71 +289,78 @@ public void showNotificationAPI29() {
 			.setAutoCancel(false)
 			.build();
 
-		notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-		notificationManager.notify(NOTIFICATION_ID_API29, notification29);
+			NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+			notificationManager.notify(NOTIFICATION_ID_API26_28, notification26_28);
 	}
 
-	/// ANDROID 5 - ANDROID 7 ///
-	public void showNotificationAPI21_25() {
+	/// ANDROID 10 & higher ///
+	@RequiresApi(api = Build.VERSION_CODES.Q)
+	public void showNotificationAPI29() {
 		Intent notificationIntent = new Intent(this, MainActivity.class);
-		PendingIntent content_intent = PendingIntent.getActivity(this, 10031, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+		PendingIntent content_intent = PendingIntent.getActivity(this, 10021, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
 		Intent intentActionStop = new Intent(this, ActionButtonReceiver.class);
 		intentActionStop.setAction("ACTION_STOP");
-		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10032, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT);
-		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10033, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
+		PendingIntent pIntentActionStop = PendingIntent.getBroadcast(this, 10022, intentActionStop, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+		PendingIntent pIntentActionSettings = PendingIntent.getActivity(this, 10023, getNtfcSettingsActivityIntent(), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
-		NotificationManager notificationManager;
-		builder = new Notification.Builder(this);
-		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK, MainActivity.visualizeSigStrg);
+		String ntfcChannelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? CHANNEL_ID : "";
+		builder = new Notification.Builder(this, ntfcChannelId);
+
+		boolean keyNtfcColor = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_COLORIZE_NTFC, PreferenceDefaults.COLORIZE_NTFC);
+		boolean keyVisSigStrgNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_VISUALIZE_SIGNAL_STRENGTH, PreferenceDefaults.VISUALIZE_SIGNAL_STRENGTH);
 
 		WifiManager mainWifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
 		WifiInfo wInfo = mainWifi.getConnectionInfo();
 		String ip = getIPv4Address();
 		String ssid = wInfo.getSSID();
 		if (ssid.equals("")) {
-			ssid = getString(R.string.na);
+			ssid = getLocalizedString(R.string.na);
 		} else {
 			ssid = ssid.replaceAll("^\"|\"$", "");
 		}
-		String bssid;
+		String bssid = "";
 		if (wInfo.getBSSID() != null) {
 			bssid = wInfo.getBSSID().toUpperCase();
-		} else {
-			bssid = getString(R.string.na);
+		}
+		if (bssid.contains("02:00:00:00:00:00")) {
+			bssid = getLocalizedString(R.string.na);
 		}
 		int rssi = wInfo.getRssi();
 		int RSSIconv = WifiManager.calculateSignalLevel(rssi, 101);
 		if (keyVisSigStrgNtfc) {
 			if (RSSIconv >= 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalHigh);
 			} else if (RSSIconv >= 50 && RSSIconv < 75) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalAvg);
 			} else if (RSSIconv >= 1 && RSSIconv < 50) {
-				visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
+				visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColorSignalLow);
 			}
 		} else {
-			visSigStrgNtfcColor = getResources().getColor(R.color.ntfcColor);
+			visualizeSignalStrengthNtfcColor = getResources().getColor(R.color.ntfcColor);
 		}
 		int freq = wInfo.getFrequency();
 		String distanceFromRssiRounded = String.format("~%.1fm", freqRssiToDistance(freq, rssi));
 		int channel = convertFrequencyToChannel(freq);
-		int networkSpeed = wInfo.getLinkSpeed();
+		int TXLinkSpd = wInfo.getTxLinkSpeedMbps();
+		int RXLinkSpd = wInfo.getRxLinkSpeedMbps();
+		String networkSpeed = RXLinkSpd + " / " + TXLinkSpd + " Mbps";
 
-		String collapsedInfo = getString(R.string.ssid) + ": " + ssid + " | " + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
-		String extendedInfo = getString(R.string.ssid) + ": " + ssid + "\n" + getString(R.string.bssid) + ": " + bssid + "\n" + getString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
-			getString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getString(R.string.network_channel) + ": " + channel + "\n" +
-			getString(R.string.network_speed) + ": " + networkSpeed + " / " + networkSpeed + " Mbps";
+		String collapsedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + " | " + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + " | " + freq + " MHz " + "(Ch: " + channel + ")";
+		String extendedInfo = getLocalizedString(R.string.ssid) + ": " + ssid + "\n" + getLocalizedString(R.string.bssid) + ": " + bssid + "\n" + getLocalizedString(R.string.rssi) + ": " + RSSIconv + "%" + " (" + rssi + "dBm" + ")" + "\n" +
+			getLocalizedString(R.string.distance) + ": " + distanceFromRssiRounded + "\n" + getLocalizedString(R.string.frequency) + ": " + freq + "MHz" + "\n" + getLocalizedString(R.string.network_channel) + ": " + channel + "\n" +
+			getLocalizedString(R.string.network_speed) + ": " + networkSpeed;
 
-		notification21_25 = builder.setContentIntent(content_intent)
+		notification29 = builder.setContentIntent(content_intent)
 			.setSmallIcon(R.drawable.wifi_24px)
-			.setContentTitle(getString(R.string.local_ip) + ": " + ip)
+			.setContentTitle(getLocalizedString(R.string.local_ip) + ": " + ip)
 			.setContentText(collapsedInfo)
 			.setWhen(System.currentTimeMillis())
-			.addAction(R.drawable.stop_24px, getString(R.string.stop_services), pIntentActionStop)
-			.addAction(R.drawable.settings_24px, getString(R.string.notification_settings), pIntentActionSettings)
-			.setPriority(Notification.PRIORITY_LOW)
-			.setColor(visSigStrgNtfcColor)
+			.addAction(R.drawable.stop_24px, getLocalizedString(R.string.stop_services), pIntentActionStop)
+			.addAction(R.drawable.settings_24px, getLocalizedString(R.string.notification_settings), pIntentActionSettings)
+			.setChannelId(ntfcChannelId)
+			.setColorized(keyNtfcColor)
+			.setColor(visualizeSignalStrengthNtfcColor)
 			.setCategory(Notification.CATEGORY_SERVICE)
 			.setStyle(new Notification.BigTextStyle().bigText(extendedInfo))
 			.setOngoing(true)
@@ -337,12 +368,10 @@ public void showNotificationAPI21_25() {
 			.setAutoCancel(false)
 			.build();
 
-		notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-		notificationManager.notify(NOTIFICATION_ID_API21_25, notification21_25);
+		NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+		notificationManager.notify(NOTIFICATION_ID_API29, notification29);
 	}
 
-	/// END ///
-
 	private String getIPv4Address() {
 		try {
 			List allNetworkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
@@ -356,8 +385,8 @@ private String getIPv4Address() {
 					}
 				}
 			}
-		} catch (SocketException ex) {
-			Log.e("getIPv4Address()", ex.toString());
+		} catch (SocketException e) {
+			Log.e("getIPv4Address()", e.toString());
 		}
 		return null;
 	}
@@ -386,17 +415,6 @@ private double freqRssiToDistance(int frequency, int rssi) {
 		return Math.pow(10.0D, (27.55D - 20 * Math.log10((double) frequency) + Math.abs(rssi)) / 20.0D);
 	}
 
-	@RequiresApi(Build.VERSION_CODES.O)
-	private String createNotificationChannel(NotificationManager notificationManager) {
-		String channelID = "wifi_info";
-		CharSequence channelName = "Notification Service";
-		NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_LOW);
-		channel.setDescription("Main Wi-Fi Info Notification");
-		channel.setShowBadge(false);
-		notificationManager.createNotificationChannel(channel);
-		return channelID;
-	}
-
 	@Override
 	public IBinder onBind(Intent intent) {
 		return null;
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/QSTileService.java b/app/src/main/java/com/truemlgpro/wifiinfo/services/QSTileService.java
similarity index 65%
rename from app/src/main/java/com/truemlgpro/wifiinfo/QSTileService.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/services/QSTileService.java
index 2e1661d..b1d436b 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/QSTileService.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/services/QSTileService.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.services;
 
 import android.graphics.drawable.Icon;
 import android.net.ConnectivityManager;
@@ -12,7 +12,12 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.truemlgpro.wifiinfo.R;
+
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -20,23 +25,20 @@
 import java.net.SocketException;
 import java.net.URL;
 import java.util.Enumeration;
-import java.util.Scanner;
 
 @RequiresApi(api = Build.VERSION_CODES.N)
 public class QSTileService extends TileService {
-	Tile qs_tile;
+	private static Tile qs_tile;
 
-	NetworkInfo wifiCheck;
-	NetworkInfo cellularCheck;
+	private NetworkInfo wifiCheck;
+	private NetworkInfo cellularCheck;
 
-	String publicIPFetched;
-	boolean siteReachable = false;
-	boolean switchIP = true;
+	private boolean switchIP = true;
 
-	Icon wifiDefaultIcon;
-	Icon wifiSuccessIcon;
-	Icon wifiFailIcon;
-	Icon updateIcon;
+	private static Icon wifiSuccessIcon;
+	private static Icon wifiFailIcon;
+	private Icon wifiDefaultIcon;
+	private Icon updateIcon;
 
 	@Override
 	public void onStartListening() {
@@ -54,6 +56,7 @@ public void onStartListening() {
 	@Override
 	public void onStopListening() {
 		super.onStopListening();
+		qs_tile = null;
 		wifiDefaultIcon = null;
 		wifiSuccessIcon = null;
 		wifiFailIcon = null;
@@ -89,7 +92,7 @@ private void showIPAddress() {
 	}
 
 	private void showLocalIP() {
-		SystemClock.sleep(500);
+		SystemClock.sleep(250);
 		if (wifiCheck.isConnected() || cellularCheck.isConnected()) {
 			qs_tile.setLabel(getIPv4Address());
 			qs_tile.setIcon(wifiSuccessIcon);
@@ -102,12 +105,11 @@ private void showLocalIP() {
 	}
 
 	private void showPublicIP() {
-		SystemClock.sleep(500);
+		SystemClock.sleep(250);
 		qs_tile.setState(Tile.STATE_ACTIVE);
 		qs_tile.updateTile();
 		if (wifiCheck.isConnected() || cellularCheck.isConnected()) {
-			PublicIPAsyncTask publicIPAsyncTask = new PublicIPAsyncTask();
-			publicIPAsyncTask.execute();
+			new PublicIPAsyncTask().execute();
 		} else {
 			qs_tile.setLabel("No Connection");
 			qs_tile.setIcon(wifiFailIcon);
@@ -127,68 +129,55 @@ private String getIPv4Address() {
 					}
 				}
 			}
-		} catch (SocketException ex) {
-			Log.e("getIPv4Address()", ex.toString());
+		} catch (SocketException e) {
+			Log.e("getIPv4Address()", e.toString());
 		}
 		return null;
 	}
 
-	private static String getPublicIPAddress() {
-		String publicIP = "";
-		try {
-			Scanner scanner = new Scanner(new URL("https://api.ipify.org").openStream(), "UTF-8").useDelimiter("\\A");
-			publicIP = scanner.next();
-			scanner.close();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		return publicIP;
-	}
-
-	private static boolean isReachable(String url) {
-		boolean reachable;
-		int code;
-
-		try {
-			URL siteURL = new URL(url);
-			HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection();
-			connection.setRequestMethod("GET");
-			connection.setConnectTimeout(3000);
-			connection.connect();
-			code = connection.getResponseCode();
-			connection.disconnect();
-			reachable = code == 200;
-		} catch (Exception e) {
-			reachable = false;
-		}
-		return reachable;
-	}
-
-	class PublicIPAsyncTask extends AsyncTask {
+	private static class PublicIPAsyncTask extends AsyncTask {
 		@Override
-		protected Void doInBackground(String[] voids) {
-			String url = "https://api.ipify.org";
-			siteReachable = isReachable(url);
-			if (siteReachable) {
-				publicIPFetched = getPublicIPAddress();
-			} else {
-				publicIPFetched = "N/A";
+		protected String doInBackground(Void... params) {
+			HttpURLConnection urlConnection = null;
+			String ipAddress = null;
+			try {
+				URL url = new URL("https://public-ip-api.vercel.app/api/ip/");
+				urlConnection = (HttpURLConnection) url.openConnection();
+				urlConnection.setRequestMethod("GET");
+
+				int responseCode = urlConnection.getResponseCode();
+				if (responseCode == HttpURLConnection.HTTP_OK) {
+					StringBuilder responseBuilder = new StringBuilder();
+					InputStream inputStream = urlConnection.getInputStream();
+					try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+						String line;
+						while ((line = reader.readLine()) != null) {
+							responseBuilder.append(line);
+						}
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+					ipAddress = responseBuilder.toString();
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			} finally {
+				if (urlConnection != null)
+					urlConnection.disconnect();
 			}
-			return null;
+			return ipAddress;
 		}
 
 		@Override
-		protected void onPostExecute(Void aVoid) {
-			super.onPostExecute(aVoid);
-			if (siteReachable) {
-				qs_tile.setLabel(publicIPFetched);
-				qs_tile.setIcon(wifiSuccessIcon);
-				qs_tile.updateTile();
-			} else {
-				qs_tile.setLabel("No Connection");
+		protected void onPostExecute(String ipAddress) {
+			if (ipAddress == null) {
+				ipAddress = "No Connection";
 				qs_tile.setIcon(wifiFailIcon);
-				qs_tile.updateTile();
+			} else {
+				qs_tile.setIcon(wifiSuccessIcon);
 			}
+			qs_tile.setLabel(ipAddress);
+			qs_tile.updateTile();
 		}
 	}
 }
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/CellularDataIPActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/CellularDataIPActivity.java
similarity index 62%
rename from app/src/main/java/com/truemlgpro/wifiinfo/CellularDataIPActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/CellularDataIPActivity.java
index 04d3beb..07b4943 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/CellularDataIPActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/CellularDataIPActivity.java
@@ -1,6 +1,5 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
-import android.annotation.SuppressLint;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -11,6 +10,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.telephony.TelephonyManager;
 import android.view.View;
 import android.widget.TextView;
 
@@ -20,8 +20,17 @@
 import androidx.cardview.widget.CardView;
 
 import com.github.clans.fab.FloatingActionButton;
-
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.AppClipboardManager;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -31,9 +40,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.Scanner;
-
-import me.anwarshahriar.calligrapher.Calligrapher;
 
 public class CellularDataIPActivity extends AppCompatActivity {
 	private TextView textview_nocellconn;
@@ -44,15 +50,13 @@ public class CellularDataIPActivity extends AppCompatActivity {
 	private TextView textview_local_ipv6_cell;
 	private FloatingActionButton fab_update_ip;
 
-	private String publicIPFetched;
-	private boolean siteReachable = false;
-
 	private BroadcastReceiver CellularDataConnectivityReceiver;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.cellular_data_ip_activity);
@@ -67,88 +71,71 @@ protected void onCreate(Bundle savedInstanceState)
 		fab_update_ip = (FloatingActionButton) findViewById(R.id.fab_update_ip);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
+		initCopyableText();
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.cellular_data_ip));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
 			finish();
 		});
 
-		fab_update_ip.setOnClickListener(v -> {
-			fab_update_ip.setEnabled(false);
-			PublicIPRunnable runnableIP = new PublicIPRunnable();
-			new Thread(runnableIP).start();
-		});
+		fab_update_ip.setOnClickListener(v -> new PublicIPAsyncTask().execute());
 
 		checkCellularConnectivity();
 	}
 
-	private boolean isReachable(String url) {
-		boolean reachable;
-		int code;
-
-		try {
-			URL siteURL = new URL(url);
-			HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection();
-			connection.setRequestMethod("GET");
-			connection.setConnectTimeout(3000);
-			connection.connect();
-			code = connection.getResponseCode();
-			connection.disconnect();
-			reachable = code == 200;
-		} catch (Exception e) {
-			reachable = false;
-		}
-		return reachable;
-	}
-
-	private String getPublicIPAddress() {
-		String publicIP = "";
-		try {
-			Scanner scanner = new Scanner(new URL("https://api.ipify.org").openStream(), "UTF-8").useDelimiter("\\A");
-			publicIP = scanner.next();
-			scanner.close();
-		} catch (IOException e) {
-			e.printStackTrace();
+	private class PublicIPAsyncTask extends AsyncTask {
+		@Override
+		protected void onPreExecute() {
+			super.onPreExecute();
+			fab_update_ip.setEnabled(false);
 		}
-		return publicIP;
-	}
 
-	@SuppressWarnings("deprecation")
-	class PublicIPRunnable implements Runnable {
 		@Override
-		public void run() {
-			new AsyncTask() {
-				@Override
-				protected Void doInBackground(String[] voids) {
-					String url = "https://api.ipify.org";
-					siteReachable = isReachable(url);
-					if (siteReachable) {
-						publicIPFetched = getPublicIPAddress();
-					} else {
-						publicIPFetched = getString(R.string.na);
+		protected String doInBackground(Void... params) {
+			HttpURLConnection urlConnection = null;
+			String ipAddress = null;
+			try {
+				URL url = new URL("https://public-ip-api.vercel.app/api/ip/");
+				urlConnection = (HttpURLConnection) url.openConnection();
+				urlConnection.setRequestMethod("GET");
+
+				int responseCode = urlConnection.getResponseCode();
+				if (responseCode == HttpURLConnection.HTTP_OK) {
+					StringBuilder responseBuilder = new StringBuilder();
+					InputStream inputStream = urlConnection.getInputStream();
+					try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+						String line;
+						while ((line = reader.readLine()) != null) {
+							responseBuilder.append(line);
+						}
+					} catch (IOException e) {
+						e.printStackTrace();
 					}
-					return null;
+					ipAddress = responseBuilder.toString();
 				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			} finally {
+				if (urlConnection != null)
+					urlConnection.disconnect();
+			}
+			return ipAddress;
+		}
 
-				@Override
-				protected void onPostExecute(Void aVoid) {
-					super.onPostExecute(aVoid);
-					textview_public_ip_cell.setText(String.format(getString(R.string.your_ip), publicIPFetched));
-				}
-			}.execute();
-
-			Handler handlerEnableFAB = new Handler(Looper.getMainLooper());
-			handlerEnableFAB.postDelayed(() -> fab_update_ip.setEnabled(true), 5000);
+		@Override
+		protected void onPostExecute(String ipAddress) {
+			if (ipAddress == null)
+				ipAddress = getString(R.string.na);
+			textview_public_ip_cell.setText(String.format(getString(R.string.your_ip), ipAddress));
+			new Handler(Looper.getMainLooper()).postDelayed(() -> fab_update_ip.setEnabled(true), 5000);
 		}
 	}
 
@@ -159,24 +146,16 @@ public void onReceive(Context context, Intent intent) {
 		}
 	}
 
-	private void showWidgets() {
-		cardview_ip.setVisibility(View.VISIBLE);
-		cardview_local_ip.setVisibility(View.VISIBLE);
-		textview_nocellconn.setVisibility(View.GONE);
-	}
-
-	private void hideWidgets() {
-		cardview_ip.setVisibility(View.GONE);
-		cardview_local_ip.setVisibility(View.GONE);
-		textview_nocellconn.setVisibility(View.VISIBLE);
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
 	}
 
-	@SuppressLint("SetTextI18n")
 	private void checkCellularConnectivity() {
 		ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
 		NetworkInfo cellularCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
-		if (cellularCheck.isConnected()) {
+		if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) {
 			showWidgets();
 			textview_local_ipv4_cell.setText(getCellularIPv4Address());
 			textview_local_ipv6_cell.setText(getCellularIPv6Address());
@@ -199,8 +178,8 @@ private String getCellularIPv4Address() {
 					}
 				}
 			}
-		} catch (Exception ex) {
-			ex.printStackTrace();
+		} catch (Exception e) {
+			e.printStackTrace();
 		}
 		return getString(R.string.na);
 	}
@@ -220,12 +199,41 @@ private String getCellularIPv6Address() {
 					}
 				}
 			}
-		} catch (Exception ex) {
-			ex.printStackTrace();
+		} catch (Exception e) {
+			e.printStackTrace();
 		}
 		return getString(R.string.na);
 	}
 
+	private void initCopyableText() {
+		textview_public_ip_cell.setOnLongClickListener(v -> {
+			AppClipboardManager.copyToClipboard(this, getString(R.string.public_ip_address), textview_public_ip_cell.getText().toString());
+			return true;
+		});
+
+		textview_local_ipv4_cell.setOnLongClickListener(v -> {
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ipv4), textview_local_ipv4_cell.getText().toString());
+			return true;
+		});
+
+		textview_local_ipv6_cell.setOnLongClickListener(v -> {
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ipv6), textview_local_ipv6_cell.getText().toString());
+			return true;
+		});
+	}
+
+	private void showWidgets() {
+		cardview_ip.setVisibility(View.VISIBLE);
+		cardview_local_ip.setVisibility(View.VISIBLE);
+		textview_nocellconn.setVisibility(View.GONE);
+	}
+
+	private void hideWidgets() {
+		cardview_ip.setVisibility(View.GONE);
+		cardview_local_ip.setVisibility(View.GONE);
+		textview_nocellconn.setVisibility(View.VISIBLE);
+	}
+
 	@Override
 	protected void onStart() {
 		super.onStart();
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/DNSLookupActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/DNSLookupActivity.java
similarity index 89%
rename from app/src/main/java/com/truemlgpro/wifiinfo/DNSLookupActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/DNSLookupActivity.java
index 2d63b70..2e9112c 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/DNSLookupActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/DNSLookupActivity.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -8,6 +8,7 @@
 import android.net.NetworkInfo;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -25,6 +26,11 @@
 import androidx.appcompat.widget.Toolbar;
 
 import com.google.android.material.textfield.TextInputLayout;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
 
 import org.minidns.dnsserverlookup.android21.AndroidUsingLinkProperties;
 import org.minidns.hla.ResolverApi;
@@ -33,10 +39,9 @@
 import org.minidns.record.Record;
 
 import java.io.IOException;
+import java.util.Objects;
 import java.util.Set;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
-
 public class DNSLookupActivity extends AppCompatActivity {
 	private TextView textview_nonetworkconn;
 	private ProgressBar dns_lookup_progress_bar;
@@ -54,15 +59,13 @@ public class DNSLookupActivity extends AppCompatActivity {
 	private String url_ip;
 	private String dns_record_type;
 
-	public Boolean wifi_connected;
-	public Boolean cellular_connected;
-
-	String lineSeparator = "\n---------------------\n";
+	final String lineSeparator = "\n---------------------\n";
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.dns_lookup_activity);
@@ -78,16 +81,14 @@ protected void onCreate(Bundle savedInstanceState)
 		dns_lookup_textview = (TextView) findViewById(R.id.dns_lookup_textview);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.dns_lookup));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -123,6 +124,7 @@ class DNSLookupTask extends AsyncTask {
 		protected void onPreExecute() {
 			super.onPreExecute();
 			setEnabled(get_dns_info_button, false);
+			setEnabled(edit_text_dns, false);
 			dns_lookup_progress_bar.setVisibility(View.VISIBLE);
 		}
 
@@ -162,19 +164,12 @@ protected String doInBackground(String... params) {
 		protected void onPostExecute(String result) {
 			super.onPostExecute(result);
 			setEnabled(get_dns_info_button, true);
+			setEnabled(edit_text_dns, true);
 			dns_lookup_progress_bar.setVisibility(View.INVISIBLE);
 			appendResultsText(result);
 		}
 	}
 
-	private void setEnabled(final View view, final boolean enabled) {
-		runOnUiThread(() -> {
-			if (view != null) {
-				view.setEnabled(enabled);
-			}
-		});
-	}
-
 	private void appendResultsText(final String text) {
 		runOnUiThread(() -> {
 			dns_lookup_textview.append(text + "\n");
@@ -197,30 +192,31 @@ public void onReceive(Context context, Intent intent) {
 		}
 	}
 
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
+	}
+
 	public void checkNetworkConnectivity(Boolean shouldClearLog) {
 		ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
 		NetworkInfo wifiCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
 		NetworkInfo cellularCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
-		if (wifiCheck.isConnected() && !cellularCheck.isConnected()) { // Wi-Fi Connectivity Check
+		if (wifiCheck.isConnected()) { // Wi-Fi Connectivity Check
 			showWidgets();
 			if (toolbarDnsMenu != null) {
 				if (!toolbarDnsMenu.findItem(R.id.clear_dns_log).isEnabled()) {
 					setToolbarItemEnabled(R.id.clear_dns_log, true);
 				}
 			}
-			wifi_connected = true;
-			cellular_connected = false;
-		} else if (cellularCheck.isConnected() && !wifiCheck.isConnected()) { // Cellular Connectivity Check
+		} else if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) { // Cellular Connectivity Check
 			showWidgets();
 			if (toolbarDnsMenu != null) {
 				if (!toolbarDnsMenu.findItem(R.id.clear_dns_log).isEnabled()) {
 					setToolbarItemEnabled(R.id.clear_dns_log, true);
 				}
 			}
-			wifi_connected = false;
-			cellular_connected = true;
-		} else if (!wifiCheck.isConnected() && !cellularCheck.isConnected()) {
+		} else {
 			if (shouldClearLog) { dns_lookup_textview.setText(""); }
 			if (toolbarDnsMenu != null) {
 				if (toolbarDnsMenu.findItem(R.id.clear_dns_log).isEnabled()) {
@@ -228,8 +224,6 @@ public void checkNetworkConnectivity(Boolean shouldClearLog) {
 				}
 			}
 			hideWidgets();
-			wifi_connected = false;
-			cellular_connected = false;
 		}
 	}
 
@@ -253,6 +247,14 @@ public void hideWidgets() {
 		textview_nonetworkconn.setVisibility(View.VISIBLE);
 	}
 
+	private void setEnabled(final View view, final boolean enabled) {
+		runOnUiThread(() -> {
+			if (view != null) {
+				view.setEnabled(enabled);
+			}
+		});
+	}
+
 	@Override
 	protected void onStart() {
 		super.onStart();
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/MainActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/MainActivity.java
similarity index 65%
rename from app/src/main/java/com/truemlgpro/wifiinfo/MainActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/MainActivity.java
index c434e6d..e3abc34 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/MainActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/MainActivity.java
@@ -1,14 +1,13 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
@@ -40,6 +39,8 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.appcompat.app.ActionBar;
@@ -54,9 +55,22 @@
 
 import com.github.clans.fab.FloatingActionButton;
 import com.github.clans.fab.FloatingActionMenu;
-
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.services.ConnectionStateService;
+import com.truemlgpro.wifiinfo.services.NotificationService;
+import com.truemlgpro.wifiinfo.utils.AppClipboardManager;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.SharedPreferencesManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -71,16 +85,12 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
-import java.util.Scanner;
-
-import me.anwarshahriar.calligrapher.Calligrapher;
 
 public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
 	private Toolbar toolbar;
 	private Menu toolbarMenu;
 	private TextView textview_public_ip;
 	private TextView textview_ssid;
-	private TextView textview_hidden_ssid;
 	private TextView textview_bssid;
 	private TextView textview_ipv4;
 	private TextView textview_ipv6;
@@ -131,7 +141,6 @@ public class MainActivity extends AppCompatActivity implements ActivityCompat.On
 
 	// Strings for getAllNetworkInformation()
 	String info_ssid = "";
-	String info_hidden_ssid = "";
 	String info_bssid = "";
 	String info_ipv4 = "";
 	String info_ipv6 = "";
@@ -165,34 +174,16 @@ public class MainActivity extends AppCompatActivity implements ActivityCompat.On
 	String info_wpa3_sae_support = "";
 	String info_wpa3_suite_b_support = "";
 
-	private final int LocationPermissionCode = 123;
+	private final int LOCATION_PERMISSION_CODE = 123;
 
 	private ConnectivityManager connectivityManager;
 	private NetworkInfo WiFiCheck;
 	private DhcpInfo dhcpInfo;
 	private WifiInfo wifiInfo;
 	private WifiManager wifiManager;
-	private BroadcastReceiver WiFiConnectivityReceiver;
+	private BroadcastReceiver wifiConnectivityReceiver;
 	public static Boolean isServiceRunning = false;
-	public static final Boolean darkMode = true;
-	public static final Boolean amoledMode = false;
-	public static final Boolean keepScreenOn = true;
-	public static final Boolean startOnBoot = false;
-	public static final Boolean showNtfc = true;
-	public static final Boolean visualizeSigStrg = false;
-	public static final Boolean startStopSrvcScrnState = false;
-	public static final Boolean colorizeNtfc = false;
-	private final boolean neverShowGeoDialog = false;
-	private final boolean neverShowPermissionReqDialog = false;
-	private final String keyNeverShowGeoDialog = "NeverShowGeoDialog";
-	private final String keyNeverShowPermissionReqDialog = "NeverShowPermissionDialog";
 	private boolean isHandlerRunning = false;
-	public static final String ntfcUpdateInterval = "1000";
-	private final String cardUpdateInterval = "1000";
-	public static final String appFont = "fonts/Gilroy-Semibold.ttf";
-	public static final String appLang = "default_lang";
-	private String publicIPFetched;
-	private boolean siteReachable = false;
 	private final double megabyte = 1024 * 1024;
 	private final double gigabyte = 1024 * 1024 * 1024;
 
@@ -201,15 +192,14 @@ public class MainActivity extends AppCompatActivity implements ActivityCompat.On
 
 	private int keyCardFreqFormatted = 1000;
 
-	private SharedPreferences.OnSharedPreferenceChangeListener sharedPrefChangeListener;
-
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
-		/// Shared Preferences ///
 		ThemeManager.initializeThemes(this, getApplicationContext());
 		LocaleManager.initializeLocale(getApplicationContext());
-		initSharedPrefs();
+
+		/// Set default preferences ///
+		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
 		/// Splash Screen API ///
 		SplashScreen.installSplashScreen(this);
@@ -222,53 +212,46 @@ protected void onCreate(Bundle savedInstanceState)
 		initCopyableText();
 
 		/// Handler Init ///
-		keyCardFreqFormatted = Integer.parseInt(new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_CARD_FREQ, cardUpdateInterval));
+		keyCardFreqFormatted = Integer.parseInt(new SharedPreferencesManager(getApplicationContext()).retrieveString(PreferenceKeys.KEY_PREF_CARD_FREQ, PreferenceDefaults.CARD_UPDATE_INTERVAL));
 
 		/// Request permissions ///
 		if (Build.VERSION.SDK_INT >= 26) {
+			createNotificationChannels();
 			if (!isLocationPermissionGranted()) {
 				// If user didn't choose to hide the permission request dialog
-				if (!new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(keyNeverShowPermissionReqDialog, neverShowPermissionReqDialog)) {
+				if (!new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_NEVER_SHOW_PERMISSION_REQ_DIALOG, PreferenceDefaults.NEVER_SHOW_PERMISSION_REQ_DIALOG)) {
 					requestPermissionsOnStart();
 				}
 			} else {
 				/// Notify if GPS is disabled ///
 				// If user didn't choose to hide the GPS request dialog
-				if (!new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(keyNeverShowGeoDialog, neverShowGeoDialog)) {
+				if (!new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_NEVER_SHOW_GEO_DIALOG, PreferenceDefaults.NEVER_SHOW_GEO_DIALOG)) {
 					requestGPSFeature();
 				}
 			}
-		}
-
-		/// Services ///
-		initForegroundServices();
 
-		/// Create dynamic shortcuts ///
-		if (Build.VERSION.SDK_INT >= 26) {
+			/// Create dynamic shortcuts ///
 			createShortcuts();
 		}
 
 		/// Keep screen on ///
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
 
+		/// Services ///
+		initForegroundServices();
+
 		/// Initialize font and ActionBar ///
 		initFontAndActionbar();
 
 		/// Set up FloatingActionMenu ///
 		fam.setClosedOnTouchOutside(true);
 
-		/// Set default preferences ///
-		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
-
 		/// Check Wi-Fi Connectivity ///
 		initConnectivityCheck();
 	}
 
 	private void initFontAndActionbar() {
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, appFont);
-		calligrapher.setFont(this, font, true);
-
+		FontManager.init(this, getApplicationContext(), true);
 		setSupportActionBar(toolbar);
 		ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(false);
@@ -279,7 +262,6 @@ private void initViews() {
 		toolbar = findViewById(R.id.toolbar);
 		textview_public_ip = findViewById(R.id.textview_public_ip);
 		textview_ssid = findViewById(R.id.textview_ssid_value);
-		textview_hidden_ssid = findViewById(R.id.textview_hidden_ssid_value);
 		textview_bssid = findViewById(R.id.textview_bssid_value);
 		textview_ipv4 = findViewById(R.id.textview_ipv4_value);
 		textview_ipv6 = findViewById(R.id.textview_ipv6_value);
@@ -380,7 +362,6 @@ private void getAllNetworkInformation() {
 		String dns2 = intToIp(dhcpInfo.dns2);
 		String subnetMask = getSubnetMask();
 		String broadcastAddr = getBroadcastAddr();
-		String networkId = String.valueOf(wifiInfo.getNetworkId());
 		// Apps cannot access MAC address on Android 11+
 		String macAddr;
 		if (Build.VERSION.SDK_INT > 29) {
@@ -389,6 +370,7 @@ private void getAllNetworkInformation() {
 			macAddr = getMACAddress();
 		}
 		String networkInterface = getNetworkInterface();
+		String networkId = String.valueOf(wifiInfo.getNetworkId());
 		String loopbackAddr = String.valueOf(InetAddress.getLoopbackAddress());
 		String localhostAddr = getLocalhostAddress();
 		int leaseTime = dhcpInfo.leaseDuration;
@@ -402,13 +384,7 @@ private void getAllNetworkInformation() {
 			info_ssid = ssid.replaceAll("^\"|\"$", "");
 		}
 
-		if (wifiInfo.getHiddenSSID()) {
-			info_hidden_ssid = getString(R.string.yes);
-		} else {
-			info_hidden_ssid = getString(R.string.no);
-		}
-
-		if (bssid.contains("02:00:00:00:00:00")) {
+		if (bssid.equals("02:00:00:00:00:00")) {
 			info_bssid = getString(R.string.na);
 		} else {
 			info_bssid = bssid;
@@ -436,7 +412,7 @@ private void getAllNetworkInformation() {
 			info_broadcast_addr = broadcastAddr;
 		}
 
-		if (networkId.contains("-1")) {
+		if (networkId.equals("-1")) {
 			info_network_id = getString(R.string.na);
 		} else {
 			info_network_id = networkId;
@@ -525,7 +501,6 @@ private void getAllNetworkInformation() {
 
 	private void updateTextviews() {
 		textview_ssid.setText(info_ssid);
-		textview_hidden_ssid.setText(info_hidden_ssid);
 		textview_bssid.setText(info_bssid);
 		textview_ipv4.setText(info_ipv4);
 		textview_ipv6.setText(info_ipv6);
@@ -650,8 +625,8 @@ private void checkWiFiConnectivity(Boolean shouldStartHandlerThread) {
 
 	private void showWidgets() {
 		textview_noconn.setVisibility(View.GONE);
-		cardview_1.setVisibility(View.VISIBLE);
 		cardview_ip.setVisibility(View.VISIBLE);
+		cardview_1.setVisibility(View.VISIBLE);
 		cardview_2.setVisibility(View.VISIBLE);
 		cardview_3.setVisibility(View.VISIBLE);
 		cardview_4.setVisibility(View.VISIBLE);
@@ -662,8 +637,8 @@ private void showWidgets() {
 
 	private void hideWidgets() {
 		textview_noconn.setVisibility(View.VISIBLE);
-		cardview_1.setVisibility(View.GONE);
 		cardview_ip.setVisibility(View.GONE);
+		cardview_1.setVisibility(View.GONE);
 		cardview_2.setVisibility(View.GONE);
 		cardview_3.setVisibility(View.GONE);
 		cardview_4.setVisibility(View.GONE);
@@ -672,67 +647,51 @@ private void hideWidgets() {
 		fab_update.setVisibility(View.GONE);
 	}
 
-	private String getPublicIPAddress() {
-		String publicIP = "";
-		try {
-			InputStream apiInputStream = new URL("https://api.ipify.org").openStream();
-			Scanner scanner = new Scanner(apiInputStream, "UTF-8").useDelimiter("\\A");
-			publicIP = scanner.next();
-			apiInputStream.close();
-			scanner.close();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-		return publicIP;
-	}
-
-	private boolean isReachable(String url) {
-		boolean reachable;
-		int response_code;
-
-		try {
-			URL siteURL = new URL(url);
-			HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection();
-			connection.setRequestMethod("GET");
-			connection.setConnectTimeout(3000);
-			connection.connect();
-			response_code = connection.getResponseCode();
-			connection.disconnect();
-			reachable = response_code == 200;
-		} catch (Exception e) {
-			reachable = false;
+	private class PublicIPAsyncTask extends AsyncTask {
+		@Override
+		protected void onPreExecute() {
+			super.onPreExecute();
+			fab_update.setEnabled(false);
 		}
-		return reachable;
-	}
 
-	@SuppressWarnings("deprecation")
-	class PublicIPRunnable implements Runnable {
-		@SuppressLint("StaticFieldLeak")
 		@Override
-		public void run() {
-			new AsyncTask() {
-				@Override
-				protected Void doInBackground(String[] voids) {
-					String url = "https://api.ipify.org";
-					siteReachable = isReachable(url);
-					if (siteReachable) {
-						publicIPFetched = getPublicIPAddress();
-					} else {
-						publicIPFetched = getString(R.string.na);
+		protected String doInBackground(Void... params) {
+			HttpURLConnection urlConnection = null;
+			String ipAddress = null;
+			try {
+				URL url = new URL("https://public-ip-api.vercel.app/api/ip/");
+				urlConnection = (HttpURLConnection) url.openConnection();
+				urlConnection.setRequestMethod("GET");
+
+				int responseCode = urlConnection.getResponseCode();
+				if (responseCode == HttpURLConnection.HTTP_OK) {
+					StringBuilder responseBuilder = new StringBuilder();
+					InputStream inputStream = urlConnection.getInputStream();
+					try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+						String line;
+						while ((line = reader.readLine()) != null) {
+							responseBuilder.append(line);
+						}
+					} catch (IOException e) {
+						e.printStackTrace();
 					}
-					return null;
+					ipAddress = responseBuilder.toString();
 				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			} finally {
+				if (urlConnection != null)
+					urlConnection.disconnect();
+			}
+			return ipAddress;
+		}
 
-				@SuppressLint("SetTextI18n")
-				@Override
-				protected void onPostExecute(Void aVoid) {
-					super.onPostExecute(aVoid);
-					textview_public_ip.setText(String.format(getString(R.string.your_ip), publicIPFetched));
-				}
-			}.execute();
-
-			Handler handlerEnableFAB = new Handler(Looper.getMainLooper());
-			handlerEnableFAB.postDelayed(() -> fab_update.setEnabled(true), 5000);
+		@Override
+		protected void onPostExecute(String ipAddress) {
+			if (ipAddress == null)
+				ipAddress = getString(R.string.na);
+			textview_public_ip.setText(String.format(getString(R.string.your_ip), ipAddress));
+			new Handler(Looper.getMainLooper()).postDelayed(() -> fab_update.setEnabled(true), 5000);
 		}
 	}
 
@@ -744,18 +703,15 @@ private String getMACAddress() {
 					continue;
 
 				byte[] macBytes = networkInterface.getHardwareAddress();
-				if (macBytes == null) {
+				if (macBytes == null)
 					return "";
-				}
 
 				StringBuilder macAddressStringBuilder = new StringBuilder();
-				for (byte b : macBytes) {
+				for (byte b : macBytes)
 					macAddressStringBuilder.append(String.format("%02X:", b));
-				}
 
-				if (macAddressStringBuilder.length() > 0) {
+				if (macAddressStringBuilder.length() > 0)
 					macAddressStringBuilder.deleteCharAt(macAddressStringBuilder.length() - 1);
-				}
 
 				return macAddressStringBuilder.toString();
 			}
@@ -766,9 +722,8 @@ private String getMACAddress() {
 	}
 
 	private String getGatewayIP() {
-		if (!WiFiCheck.isConnected()) {
+		if (!WiFiCheck.isConnected())
 			return "0.0.0.0";
-		}
 		wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
 		DhcpInfo dhcp = wifiManager.getDhcpInfo();
 		int gatewayIP = dhcp.gateway;
@@ -783,13 +738,12 @@ private String getIPv4Address() {
 					continue;
 				List allInetAddresses = Collections.list(networkInterface.getInetAddresses());
 				for (InetAddress inetAddr : allInetAddresses) {
-					if (!inetAddr.isLoopbackAddress() && inetAddr instanceof Inet4Address) {
+					if (!inetAddr.isLoopbackAddress() && inetAddr instanceof Inet4Address)
 						return inetAddr.getHostAddress();
-					}
 				}
 			}
-		} catch (SocketException ex) {
-			Log.e("getIPv4Address()", ex.toString());
+		} catch (SocketException e) {
+			Log.e("getIPv4Address()", e.toString());
 		}
 		return null;
 	}
@@ -804,15 +758,14 @@ private String getIPv6Address() {
 				for (InetAddress inetAddr : allInetAddresses) {
 					if (!inetAddr.isLoopbackAddress() && inetAddr instanceof Inet6Address) {
 						int index = String.valueOf(inetAddr).indexOf("%");
-						if (index != -1) {
+						if (index != -1)
 							return Objects.requireNonNull(inetAddr.getHostAddress()).substring(0, index-1);
-						}
 						return inetAddr.getHostAddress();
 					}
 				}
 			}
-		} catch (SocketException ex) {
-			Log.e("getIPv6Address()", ex.toString());
+		} catch (SocketException e) {
+			Log.e("getIPv6Address()", e.toString());
 		}
 		return null;
 	}
@@ -820,7 +773,7 @@ private String getIPv6Address() {
 	private String getHostname() {
 		try {
 			InetAddress hostnameAddr = InetAddress.getByName(getGatewayIP());
-			return hostnameAddr.getHostName();
+			return hostnameAddr.getCanonicalHostName();
 		} catch (UnknownHostException e) {
 			e.printStackTrace();
 		}
@@ -830,31 +783,16 @@ private String getHostname() {
 	@RequiresApi(api = Build.VERSION_CODES.R)
 	private String getWifiStandard() {
 		int wifiStandard = wifiInfo.getWifiStandard();
-		String wifiStandardHumanReadable = getString(R.string.na);
-		switch (wifiStandard) {
-			case ScanResult.WIFI_STANDARD_LEGACY:
-				wifiStandardHumanReadable = "Wi-Fi 1/2/3 (802.11a/b/g)";
-				break;
-			case ScanResult.WIFI_STANDARD_11N:
-				wifiStandardHumanReadable = "Wi-Fi 4 (802.11n)";
-				break;
-			case ScanResult.WIFI_STANDARD_11AC:
-				wifiStandardHumanReadable = "Wi-Fi 5 (802.11ac)";
-				break;
-			case ScanResult.WIFI_STANDARD_11AX:
-				wifiStandardHumanReadable = "Wi-Fi 6 (802.11ax)";
-				break;
-			case ScanResult.WIFI_STANDARD_11AD:
-				wifiStandardHumanReadable = "WiGig (802.11ad)";
-				break;
-			case ScanResult.WIFI_STANDARD_11BE:
-				wifiStandardHumanReadable = "Wi-Fi 7 (802.11be)";
-				break;
-			case ScanResult.WIFI_STANDARD_UNKNOWN:
-				wifiStandardHumanReadable = getString(R.string.na);
-				break;
-		}
-		return wifiStandardHumanReadable;
+		return switch (wifiStandard) {
+			case ScanResult.WIFI_STANDARD_LEGACY -> "Wi-Fi 1/2/3 (802.11a/b/g)";
+			case ScanResult.WIFI_STANDARD_11N -> "Wi-Fi 4 (802.11n)";
+			case ScanResult.WIFI_STANDARD_11AC -> "Wi-Fi 5 (802.11ac)";
+			case ScanResult.WIFI_STANDARD_11AX -> "Wi-Fi 6 (802.11ax)";
+			case ScanResult.WIFI_STANDARD_11AD -> "WiGig (802.11ad)";
+			case ScanResult.WIFI_STANDARD_11BE -> "Wi-Fi 7 (802.11be)";
+			case ScanResult.WIFI_STANDARD_UNKNOWN -> getString(R.string.na);
+			default -> getString(R.string.na);
+		};
 	}
 
 	private String getNetworkInterface() {
@@ -881,36 +819,32 @@ private String netPrefixLengthToSubnetMask(int netPrefixLength) {
 
 	private String getSubnetMask() {
 		try {
-			if (getIPv4Address() == null) {
+			if (getIPv4Address() == null)
 				return null;
-			}
 			InetAddress inetAddress = InetAddress.getByName(getIPv4Address());
 			NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
 			for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
-				if (Patterns.IP_ADDRESS.matcher(String.valueOf(address.getAddress()).replaceFirst("/", "")).matches()) {
+				if (Patterns.IP_ADDRESS.matcher(String.valueOf(address.getAddress()).replaceFirst("/", "")).matches())
 					return netPrefixLengthToSubnetMask(address.getNetworkPrefixLength());
-				}
 			}
-		} catch (IOException ex) {
-			Log.e("getSubnetMask()", ex.toString());
+		} catch (IOException e) {
+			Log.e("getSubnetMask()", e.toString());
 		}
 		return null;
 	}
 
 	private String getBroadcastAddr() {
 		try {
-			if (getIPv4Address() == null) {
+			if (getIPv4Address() == null)
 				return null;
-			}
 			InetAddress inetAddress = InetAddress.getByName(String.valueOf(getIPv4Address()));
 			NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
 			for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
-				if (Patterns.IP_ADDRESS.matcher(String.valueOf(address.getAddress()).replaceFirst("/", "")).matches()) {
+				if (Patterns.IP_ADDRESS.matcher(String.valueOf(address.getAddress()).replaceFirst("/", "")).matches())
 					return String.valueOf(address.getBroadcast()).replaceFirst("/", "");
-				}
 			}
-		} catch (IOException ex) {
-			Log.e("getBroadcastAddr()", ex.toString());
+		} catch (IOException e) {
+			Log.e("getBroadcastAddr()", e.toString());
 		}
 		return null;
 	}
@@ -956,22 +890,15 @@ private int convertFrequencyToChannel(int freq) {
 		}
 	}
 
-	private void copyToClipboard(String label, String text) {
-		ClipboardManager cbm = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
-		ClipData clip = ClipData.newPlainText(label, text);
-		cbm.setPrimaryClip(clip);
-		Toast.makeText(getBaseContext(), getString(R.string.copied_to_clipboard) + ": " + text, Toast.LENGTH_SHORT).show();
-	}
-
 	private boolean isLocationEnabled() {
 		LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
 		return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
 	}
 
-	private boolean hasPermissions(Context context, String... permissions) {
-		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
-			for (String permission: permissions) {
-				if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
+	private boolean hasPermissions(String... permissions) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
+			for (String permission : permissions) {
+				if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
 					return false;
 				}
 			}
@@ -1000,7 +927,7 @@ private void requestGPSFeature() {
 					}
 					dialog.cancel();
 				})
-				.setNeutralButton(getString(R.string.dont_show_again), (dialog, id) -> new SharedPreferencesManager(getApplicationContext()).storeBoolean(keyNeverShowGeoDialog, true));
+				.setNeutralButton(getString(R.string.dont_show_again), (dialog, id) -> new SharedPreferencesManager(getApplicationContext()).storeBoolean(PreferenceKeys.KEY_NEVER_SHOW_GEO_DIALOG, true));
 			if (Build.VERSION.SDK_INT == 26) {
 				builder.setMessage(getString(R.string.wifi_info_needs_location_api_26));
 			} else if (Build.VERSION.SDK_INT >= 27) {
@@ -1017,18 +944,18 @@ private void requestPermissionsOnStart() {
 		builder.setTitle(getString(R.string.permission_required))
 			.setMessage(getString(R.string.location_permission_is_required_android_8_1_plus))
 			.setPositiveButton(getString(R.string.yes), (dialog, id) -> {
-				// Android 8.1 - Android 10
-				String[] ForegroundCoarseLocationPermission_API27 = {Manifest.permission.ACCESS_COARSE_LOCATION};
-				// Android 11+
-				String[] ForegroundFineLocationPermission_API30 = {Manifest.permission.ACCESS_FINE_LOCATION};
-				if (Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT < 30) {
-					ActivityCompat.requestPermissions(MainActivity.this, ForegroundCoarseLocationPermission_API27, LocationPermissionCode);
-				} else if (Build.VERSION.SDK_INT >= 30) {
-					ActivityCompat.requestPermissions(MainActivity.this, ForegroundFineLocationPermission_API30, LocationPermissionCode);
+				// Android 8.1 - Android 11
+				String[] foregroundCoarseLocationPermission_API27 = {Manifest.permission.ACCESS_COARSE_LOCATION};
+				// Android 12+
+				String[] foregroundFineLocationPermission_API30 = {Manifest.permission.ACCESS_FINE_LOCATION};
+				if (Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT < 31) {
+					ActivityCompat.requestPermissions(MainActivity.this, foregroundCoarseLocationPermission_API27, LOCATION_PERMISSION_CODE);
+				} else if (Build.VERSION.SDK_INT >= 31) {
+					ActivityCompat.requestPermissions(MainActivity.this, foregroundFineLocationPermission_API30, LOCATION_PERMISSION_CODE);
 				}
 			})
 			.setNegativeButton(getString(R.string.no_thanks), null)
-			.setNeutralButton(getString(R.string.dont_show_again), (dialog, id) -> new SharedPreferencesManager(getApplicationContext()).storeBoolean(keyNeverShowPermissionReqDialog, true))
+			.setNeutralButton(getString(R.string.dont_show_again), (dialog, id) -> new SharedPreferencesManager(getApplicationContext()).storeBoolean(PreferenceKeys.KEY_NEVER_SHOW_PERMISSION_REQ_DIALOG, true))
 			.setCancelable(false);
 		AlertDialog alert = builder.create();
 		alert.show();
@@ -1040,10 +967,10 @@ private boolean isLocationPermissionGranted() {
 		boolean permissionGranted = false;
 		if (Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT < 31) {
 			// Android 8.1 - Android 11
-			permissionGranted = hasPermissions(this, Manifest.permission.ACCESS_COARSE_LOCATION);
+			permissionGranted = hasPermissions(Manifest.permission.ACCESS_COARSE_LOCATION);
 		} else if (Build.VERSION.SDK_INT >= 31) {
 			// Android 12+
-			permissionGranted = hasPermissions(this, Manifest.permission.ACCESS_FINE_LOCATION);
+			permissionGranted = hasPermissions(Manifest.permission.ACCESS_FINE_LOCATION);
 		}
 		return permissionGranted;
 	}
@@ -1068,175 +995,15 @@ private void createShortcuts() {
 		shortcutManager.setDynamicShortcuts(Arrays.asList(githubShortcut, releasesShortcut));
 	}
 
-	private void initSharedPrefs() {
-		sharedPrefChangeListener = (prefs, key) -> {
-			if (key.equals(SettingsActivity.KEY_PREF_DARK_MODE_SWITCH)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_DARK_MODE_SWITCH, prefs.getBoolean(SettingsActivity.KEY_PREF_DARK_MODE_SWITCH, true));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_AMOLED_MODE_CHECK)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_AMOLED_MODE_CHECK, prefs.getBoolean(SettingsActivity.KEY_PREF_AMOLED_MODE_CHECK, false));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_BOOT_SWITCH)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_BOOT_SWITCH, prefs.getBoolean(SettingsActivity.KEY_PREF_BOOT_SWITCH, false));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_NTFC_SWITCH)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_NTFC_SWITCH, prefs.getBoolean(SettingsActivity.KEY_PREF_NTFC_SWITCH, true));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_CLR_CHECK)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_CLR_CHECK, prefs.getBoolean(SettingsActivity.KEY_PREF_CLR_CHECK, false));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK, prefs.getBoolean(SettingsActivity.KEY_PREF_VIS_SIG_STRG_CHECK, false));
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_STRT_STOP_SRVC_CHECK)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_STRT_STOP_SRVC_CHECK, prefs.getBoolean(SettingsActivity.KEY_PREF_STRT_STOP_SRVC_CHECK, false));
-				Intent restartConnectionStateService = new Intent(MainActivity.this, ConnectionStateService.class);
-				Intent restartNotificationService = new Intent(MainActivity.this, NotificationService.class);
-				if (ConnectionStateService.isNotificationServiceRunning) {
-					stopService(restartNotificationService);
-				}
-				if (ConnectionStateService.isConnectionStateServiceRunning) {
-					stopService(restartConnectionStateService);
-					if (Build.VERSION.SDK_INT < 26) {
-						startService(restartConnectionStateService);
-					} else {
-						startForegroundService(restartConnectionStateService);
-					}
-				}
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_NTFC_FREQ)) {
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("500")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "500");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("1000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("2000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "2000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("3000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "3000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("4000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "4000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("5000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "5000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_NTFC_FREQ, "1000").equals("10000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_NTFC_FREQ, "10000");
-				}
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_CARD_FREQ)) {
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("500")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "500");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("1000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("2000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "2000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("3000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "3000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("4000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "4000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("5000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "5000");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_CARD_FREQ, "1000").equals("10000")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_CARD_FREQ, "10000");
-				}
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_APP_FONT)) {
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/Gilroy-Semibold.ttf").equals("fonts/Gilroy-Semibold.ttf")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/Gilroy-Semibold.ttf");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/Gilroy-Semibold.ttf").equals("fonts/CircularStd-Bold.ttf")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/CircularStd-Bold.ttf");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/Gilroy-Semibold.ttf").equals("fonts/Nunito-Bold.ttf")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_FONT, "fonts/Nunito-Bold.ttf");
-				}
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_APP_LANGUAGE)) {
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("default_lang")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("en")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "en");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("fr")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "fr");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("de")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "de");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("pt")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "pt");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("ru")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "ru");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("es")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "es");
-				}
-
-				if (prefs.getString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "default_lang").equals("uk")) {
-					new SharedPreferencesManager(getApplicationContext()).storeString(SettingsActivity.KEY_PREF_APP_LANGUAGE, "uk");
-				}
-			}
-
-			if (key.equals(SettingsActivity.KEY_PREF_KEEP_SCREEN_ON_SWITCH)) {
-				new SharedPreferencesManager(getApplicationContext()).storeBoolean(SettingsActivity.KEY_PREF_KEEP_SCREEN_ON_SWITCH, prefs.getBoolean(SettingsActivity.KEY_PREF_KEEP_SCREEN_ON_SWITCH, true));
-			}
-		};
-
-		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
-		prefs.registerOnSharedPreferenceChangeListener(sharedPrefChangeListener);
-	}
-
 	private void initForegroundServices() {
-		boolean keyNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(SettingsActivity.KEY_PREF_NTFC_SWITCH, showNtfc);
+		boolean keyNtfc = new SharedPreferencesManager(getApplicationContext()).retrieveBoolean(PreferenceKeys.KEY_PREF_SHOW_NTFC, PreferenceDefaults.SHOW_NTFC);
 		if (keyNtfc) {
 			if (!isServiceRunning) {
-				Intent ConnectionStateServiceIntent = new Intent(MainActivity.this, ConnectionStateService.class);
+				Intent connectionStateServiceIntent = new Intent(MainActivity.this, ConnectionStateService.class);
 				if (Build.VERSION.SDK_INT < 26) {
-					startService(ConnectionStateServiceIntent);
+					startService(connectionStateServiceIntent);
 				} else {
-					startForegroundService(ConnectionStateServiceIntent);
+					startForegroundService(connectionStateServiceIntent);
 				}
 				isServiceRunning = true;
 			}
@@ -1245,14 +1012,47 @@ private void initForegroundServices() {
 				ConnectivityManager CM = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
 				WiFiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
 
-				Intent ConnectionStateServiceIntent = new Intent(MainActivity.this, ConnectionStateService.class);
-				Intent NotificationServiceIntent = new Intent(MainActivity.this, NotificationService.class);
+				Intent connectionStateServiceIntent = new Intent(MainActivity.this, ConnectionStateService.class);
+				Intent notificationServiceIntent = new Intent(MainActivity.this, NotificationService.class);
 
-				stopService(ConnectionStateServiceIntent);
-				isServiceRunning = false;
+				stopService(connectionStateServiceIntent);
 				if (WiFiCheck.isConnected()) {
-					stopService(NotificationServiceIntent);
+					stopService(notificationServiceIntent);
 				}
+				isServiceRunning = false;
+			}
+		}
+	}
+
+	@RequiresApi(Build.VERSION_CODES.O)
+	private void createNotificationChannels() {
+		NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+		// ConnectionStateService notification
+		String connStateNtfcChannelId = "connection_state_service";
+		CharSequence connStateNtfcChannelName = "Connection State Service";
+		NotificationChannel connStateNtfcChannel = new NotificationChannel(connStateNtfcChannelId, connStateNtfcChannelName, NotificationManager.IMPORTANCE_MIN);
+		connStateNtfcChannel.setDescription("Wi-Fi Info Connection Listener Service Notification");
+		connStateNtfcChannel.setShowBadge(false);
+		notificationManager.createNotificationChannel(connStateNtfcChannel);
+
+		// NotificationService notification
+		String mainNtfcChannelId = "wifi_info";
+		CharSequence mainNtfcChannelName = "Notification Service";
+		NotificationChannel mainNtfcChannel = new NotificationChannel(mainNtfcChannelId, mainNtfcChannelName, NotificationManager.IMPORTANCE_LOW);
+		mainNtfcChannel.setDescription("Main Wi-Fi Info Notification");
+		mainNtfcChannel.setShowBadge(false);
+		notificationManager.createNotificationChannel(mainNtfcChannel);
+
+		if (Build.VERSION.SDK_INT >= 33) {
+			String[] postNotificationsPermission = {Manifest.permission.POST_NOTIFICATIONS};
+			ActivityResultLauncher requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
+				if (isGranted) {
+					initForegroundServices();
+				}
+			});
+
+			if (!hasPermissions(postNotificationsPermission)) {
+				requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
 			}
 		}
 	}
@@ -1278,194 +1078,184 @@ private void initOnClickListeners() {
 
 		fab_settings.setOnClickListener(v -> {
 			Intent intent_settings = new Intent(MainActivity.this, SettingsActivity.class);
-			intent_settings.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
 			startActivity(intent_settings);
 			fam.close(true);
 		});
 
-		fab_update.setOnClickListener(v -> {
-			fab_update.setEnabled(false);
-			PublicIPRunnable runnableIP = new PublicIPRunnable();
-			new Thread(runnableIP).start();
-		});
+		fab_update.setOnClickListener(v -> new PublicIPAsyncTask().execute());
 	}
 
 	private void initCopyableText() {
 		textview_public_ip.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.public_ip_address), textview_public_ip.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.public_ip_address), textview_public_ip.getText().toString());
 			return true;
 		});
 
 		textview_ssid.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.ssid), textview_ssid.getText().toString());
-			return true;
-		});
-
-		textview_hidden_ssid.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.hidden_ssid), textview_hidden_ssid.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ssid), textview_ssid.getText().toString());
 			return true;
 		});
 
 		textview_bssid.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.bssid), textview_bssid.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.bssid), textview_bssid.getText().toString());
 			return true;
 		});
 
 		textview_ipv4.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.ipv4), textview_ipv4.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ipv4), textview_ipv4.getText().toString());
 			return true;
 		});
 
 		textview_ipv6.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.ipv6), textview_ipv6.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ipv6), textview_ipv6.getText().toString());
 			return true;
 		});
 
 		textview_gateway_ip.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.gateway_ip), textview_gateway_ip.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.gateway_ip), textview_gateway_ip.getText().toString());
 			return true;
 		});
 
 		textview_hostname.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.hostname), textview_hostname.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.hostname), textview_hostname.getText().toString());
 			return true;
 		});
 
 		textview_wifi_standard.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.wifi_standard), textview_wifi_standard.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.wifi_standard), textview_wifi_standard.getText().toString());
 			return true;
 		});
 
 		textview_frequency.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.frequency), textview_frequency.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.frequency), textview_frequency.getText().toString());
 			return true;
 		});
 
 		textview_network_channel.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.network_channel), textview_network_channel.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.network_channel), textview_network_channel.getText().toString());
 			return true;
 		});
 
 		textview_rssi.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.rssi_signal_strength), textview_rssi.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.rssi_signal_strength), textview_rssi.getText().toString());
 			return true;
 		});
 
 		textview_distance.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.distance), textview_distance.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.distance), textview_distance.getText().toString());
 			return true;
 		});
 
 		textview_lease_duration.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.ip_lease_duration), textview_lease_duration.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.ip_lease_duration), textview_lease_duration.getText().toString());
 			return true;
 		});
 
 		if (Build.VERSION.SDK_INT >= 29) {
 			textview_network_speed.setOnLongClickListener(v -> {
-				copyToClipboard(getString(R.string.network_speed), textview_network_speed.getText().toString());
+				AppClipboardManager.copyToClipboard(this, getString(R.string.network_speed), textview_network_speed.getText().toString());
 				return true;
 			});
 		} else {
 			textview_network_speed_legacy.setOnLongClickListener(v -> {
-				copyToClipboard(getString(R.string.network_speed), textview_network_speed_legacy.getText().toString());
+				AppClipboardManager.copyToClipboard(this, getString(R.string.network_speed), textview_network_speed_legacy.getText().toString());
 				return true;
 			});
 		}
 
 		textview_transmitted_data.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.transmitted_mbs_gbs), textview_transmitted_data.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.transmitted_mbs_gbs), textview_transmitted_data.getText().toString());
 			return true;
 		});
 
 		textview_received_data.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.received_mbs_gbs), textview_received_data.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.received_mbs_gbs), textview_received_data.getText().toString());
 			return true;
 		});
 
 		textview_dns1.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.dns_1), textview_dns1.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.dns_1), textview_dns1.getText().toString());
 			return true;
 		});
 
 		textview_dns2.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.dns_2), textview_dns2.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.dns_2), textview_dns2.getText().toString());
 			return true;
 		});
 
 		textview_subnet_mask.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.subnet_mask), textview_subnet_mask.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.subnet_mask), textview_subnet_mask.getText().toString());
 			return true;
 		});
 
 		textview_broadcast_address.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.broadcast_address), textview_broadcast_address.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.broadcast_address), textview_broadcast_address.getText().toString());
 			return true;
 		});
 
 		textview_network_id.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.network_id), textview_network_id.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.network_id), textview_network_id.getText().toString());
 			return true;
 		});
 
 		textview_mac_address.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.mac_address), textview_mac_address.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.mac_address), textview_mac_address.getText().toString());
 			return true;
 		});
 
 		textview_network_interface.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.network_interface), textview_network_interface.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.network_interface), textview_network_interface.getText().toString());
 			return true;
 		});
 
 		textview_loopback_address.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.loopback_address), textview_loopback_address.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.loopback_address), textview_loopback_address.getText().toString());
 			return true;
 		});
 
 		textview_localhost.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.localhost_address), textview_localhost.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.localhost_address), textview_localhost.getText().toString());
 			return true;
 		});
 
 		textview_wpa_supplicant_state.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.wpa_supplicant_state), textview_wpa_supplicant_state.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.wpa_supplicant_state), textview_wpa_supplicant_state.getText().toString());
 			return true;
 		});
 
 		textview_5ghz_support.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string._5ghz_band_support), textview_5ghz_support.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string._5ghz_band_support), textview_5ghz_support.getText().toString());
 			return true;
 		});
 
 		textview_6ghz_support.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string._6ghz_band_support), textview_6ghz_support.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string._6ghz_band_support), textview_6ghz_support.getText().toString());
 			return true;
 		});
 
 		textview_60ghz_support.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string._60ghz_band_support), textview_60ghz_support.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string._60ghz_band_support), textview_60ghz_support.getText().toString());
 			return true;
 		});
 
 		textview_wifi_direct_support.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.wifi_direct_support), textview_wifi_direct_support.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.wifi_direct_support), textview_wifi_direct_support.getText().toString());
 			return true;
 		});
 
 		textview_tdls_support.setOnLongClickListener(v -> {
-			copyToClipboard(getString(R.string.tdls_support), textview_tdls_support.getText().toString());
+			AppClipboardManager.copyToClipboard(this, getString(R.string.tdls_support), textview_tdls_support.getText().toString());
 			return true;
 		});
 
 		if (Build.VERSION.SDK_INT >= 29) {
 			textview_wpa3_sae_support.setOnLongClickListener(v -> {
-				copyToClipboard(getString(R.string.wpa3_sae_support), textview_wpa3_sae_support.getText().toString());
+				AppClipboardManager.copyToClipboard(this, getString(R.string.wpa3_sae_support), textview_wpa3_sae_support.getText().toString());
 				return true;
 			});
 
 			textview_wpa3_suite_b_support.setOnLongClickListener(v -> {
-				copyToClipboard(getString(R.string.wpa3_suite_b_support), textview_wpa3_suite_b_support.getText().toString());
+				AppClipboardManager.copyToClipboard(this, getString(R.string.wpa3_suite_b_support), textview_wpa3_suite_b_support.getText().toString());
 				return true;
 			});
 		}
@@ -1473,13 +1263,12 @@ private void initCopyableText() {
 
 	private void copyAllTextviews() {
 		StringBuilder strB = new StringBuilder();
-		strB.append(getString(R.string.ssid) + ": " + info_ssid).append("\n").append(getString(R.string.hidden_ssid) + ": " + info_hidden_ssid).append("\n")
-			.append(getString(R.string.bssid) + ": " + info_bssid).append("\n").append(getString(R.string.ipv4) + ": " + info_ipv4).append("\n")
-			.append(getString(R.string.ipv6) + ": " + info_ipv6).append("\n").append(getString(R.string.gateway_ip) + ": " + info_gateway_ip).append("\n")
-			.append(getString(R.string.hostname) + ": " + info_hostname).append("\n").append(getString(R.string.wifi_standard) + ": " + info_wifi_standard).append("\n")
-			.append(getString(R.string.frequency) + ": " + info_frequency).append("\n").append(getString(R.string.network_channel) + ": " + info_network_channel).append("\n")
-			.append(getString(R.string.rssi_signal_strength) + ": " + info_rssi).append("\n").append(getString(R.string.distance) + ": " + info_distance).append("\n")
-			.append(getString(R.string.ip_lease_duration) + ": " + info_lease_time).append("\n");
+		strB.append(getString(R.string.ssid) + ": " + info_ssid).append("\n").append(getString(R.string.bssid) + ": " + info_bssid).append("\n")
+			.append(getString(R.string.ipv4) + ": " + info_ipv4).append("\n").append(getString(R.string.ipv6) + ": " + info_ipv6).append("\n")
+			.append(getString(R.string.gateway_ip) + ": " + info_gateway_ip).append("\n").append(getString(R.string.hostname) + ": " + info_hostname).append("\n")
+			.append(getString(R.string.wifi_standard) + ": " + info_wifi_standard).append("\n").append(getString(R.string.frequency) + ": " + info_frequency).append("\n")
+			.append(getString(R.string.network_channel) + ": " + info_network_channel).append("\n").append(getString(R.string.rssi_signal_strength) + ": " + info_rssi).append("\n")
+			.append(getString(R.string.distance) + ": " + info_distance).append("\n").append(getString(R.string.ip_lease_duration) + ": " + info_lease_time).append("\n");
 		if (Build.VERSION.SDK_INT >= 29) {
 			strB.append(getString(R.string.network_speed) + ": " + info_network_speed).append("\n");
 		} else {
@@ -1495,13 +1284,13 @@ private void copyAllTextviews() {
 			.append(getString(R.string._60ghz_band_support) + ": " + info_60ghz_support).append("\n").append(getString(R.string.wifi_direct_support) + ": " + info_p2p_support).append("\n")
 			.append(getString(R.string.tdls_support) + ": " + info_tdls_support).append("\n").append(getString(R.string.wpa3_sae_support) + ": " + info_wpa3_sae_support).append("\n")
 			.append(getString(R.string.wpa3_suite_b_support) + ": " + info_wpa3_suite_b_support);
-		copyToClipboard("all_info_text", String.valueOf(strB));
+		AppClipboardManager.copyToClipboard(this, "all_info_text", String.valueOf(strB));
 	}
 
 	@Override
 	protected void onPause() {
 		super.onPause();
-		unregisterReceiver(WiFiConnectivityReceiver);
+		unregisterReceiver(wifiConnectivityReceiver);
 	}
 
 	@Override
@@ -1509,8 +1298,8 @@ protected void onResume() {
 		super.onResume();
 		IntentFilter filter = new IntentFilter();
 		filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
-		WiFiConnectivityReceiver = new WiFiConnectivityReceiver();
-		registerReceiver(WiFiConnectivityReceiver, filter);
+		wifiConnectivityReceiver = new WiFiConnectivityReceiver();
+		registerReceiver(wifiConnectivityReceiver, filter);
 	}
 
 	@Override
@@ -1531,18 +1320,11 @@ protected void onStop() {
 		}
 	}
 
-	@Override
-	protected void onDestroy() {
-		super.onDestroy();
-		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
-		prefs.unregisterOnSharedPreferenceChangeListener(sharedPrefChangeListener);
-	}
-
 	@Override
 	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
 		super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 		if (Build.VERSION.SDK_INT >= 30) {
-			if (requestCode == LocationPermissionCode) {
+			if (requestCode == LOCATION_PERMISSION_CODE) {
 				if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 					AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
 					builder.setTitle(getString(R.string.background_location_permission))
@@ -1564,6 +1346,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
 		}
 	}
 
+	@SuppressLint("MissingSuperCall")
 	@Override
 	public void onBackPressed() {
 		AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/PingToolActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/PingToolActivity.java
similarity index 68%
rename from app/src/main/java/com/truemlgpro/wifiinfo/PingToolActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/PingToolActivity.java
index 328362c..578d5d2 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/PingToolActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/PingToolActivity.java
@@ -1,6 +1,5 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
-import android.annotation.SuppressLint;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -12,6 +11,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -30,12 +30,15 @@
 import com.stealthcopter.networktools.Ping;
 import com.stealthcopter.networktools.ping.PingResult;
 import com.stealthcopter.networktools.ping.PingStats;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+import com.truemlgpro.wifiinfo.utils.URLandIPConverter;
 
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
-
-import me.anwarshahriar.calligrapher.Calligrapher;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class PingToolActivity extends AppCompatActivity {
 	private TextView textview_nonetworkconn;
@@ -65,17 +68,26 @@ public class PingToolActivity extends AppCompatActivity {
 	private HandlerThread pingHandlerThread;
 	private Handler pingHandler;
 
-	public Boolean wifi_connected;
-	public Boolean cellular_connected;
+	private Boolean wifi_connected;
+	private Boolean cellular_connected;
 
 	private String url_ip = "";
 	private final String lineSeparator = "\n----------------------------\n";
 	private int sentPackets = 0;
 
+	private String ping_timeout_string = "";
+	private String ping_ttl_string = "";
+	private String ping_times_string = "";
+
+	private final String DEFAULT_TIMEOUT = "3000";
+	private final String DEFAULT_TTL = "30";
+	private final String DEFAULT_PACKETS = "5";
+
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.ping_activity);
@@ -84,12 +96,12 @@ protected void onCreate(Bundle savedInstanceState)
 		textview_nonetworkconn = (TextView) findViewById(R.id.textview_nonetworkconn);
 		ping_progress_bar = (ProgressBar) findViewById(R.id.ping_progress_bar);
 		text_input_layout_ping = (TextInputLayout) findViewById(R.id.input_layout_ping);
-		edit_text_ping = (EditText) findViewById(R.id.edit_text_ping);
 		text_input_layout_timeout = (TextInputLayout) findViewById(R.id.input_layout_timeout);
-		edit_text_timeout = (EditText) findViewById(R.id.edit_text_timeout);
 		text_input_layout_ttl = (TextInputLayout) findViewById(R.id.input_layout_ttl);
-		edit_text_ttl = (EditText) findViewById(R.id.edit_text_ttl);
 		text_input_layout_times = (TextInputLayout) findViewById(R.id.input_layout_times);
+		edit_text_ping = (EditText) findViewById(R.id.edit_text_ping);
+		edit_text_timeout = (EditText) findViewById(R.id.edit_text_timeout);
+		edit_text_ttl = (EditText) findViewById(R.id.edit_text_ttl);
 		edit_text_times = (EditText) findViewById(R.id.edit_text_times);
 		ping_button = (Button) findViewById(R.id.ping_button);
 		ping_button_cancel = (Button) findViewById(R.id.ping_button_cancel);
@@ -97,16 +109,14 @@ protected void onCreate(Bundle savedInstanceState)
 		ping_text = (TextView) findViewById(R.id.ping_textview);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.ping_tool));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -132,12 +142,58 @@ private String getGateway() {
 		return String.format("%d.%d.%d.%d", (ip & 0xff), (ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff));
 	}
 
-	@SuppressLint("SetTextI18n")
+	private void validateField(EditText editText, String input, String defaultValue) {
+		if (TextUtils.isEmpty(input)) {
+			if (editText.equals(edit_text_timeout)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.timeout_field_empty));
+				ping_timeout_string = defaultValue;
+			} else if (editText.equals(edit_text_ttl)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.ttl_field_empty));
+				ping_ttl_string = defaultValue;
+			} else if (editText.equals(edit_text_times)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.packet_amount_not_defined));
+				ping_times_string = defaultValue;
+			}
+		} else if (!isStringInt(input)) {
+			if (editText.equals(edit_text_timeout)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.timeout_not_integer));
+				ping_timeout_string = defaultValue;
+			} else if (editText.equals(edit_text_ttl)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.ttl_not_integer));
+				ping_ttl_string = defaultValue;
+			} else if (editText.equals(edit_text_times)) {
+				setInputToDefault(editText, defaultValue, String.format(getString(R.string.packet_amount_not_integer), input));
+				ping_times_string = defaultValue;
+			}
+		} else if (Integer.parseInt(input) < 1) {
+			if (editText.equals(edit_text_timeout)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.timeout_lower_than_1_ms));
+				ping_timeout_string = defaultValue;
+			} else if (editText.equals(edit_text_ttl)) {
+				setInputToDefault(editText, defaultValue, getString(R.string.ttl_lower_than_1));
+				ping_ttl_string = defaultValue;
+			} else if (editText.equals(edit_text_times)) {
+				setInputToDefault(editText, defaultValue, String.format(getString(R.string.packet_amount_lower_than_1), input));
+				ping_times_string = defaultValue;
+			}
+		}
+	}
+
+	private void setInputToDefault(EditText editText, String defaultStr, String errorMessage) {
+		appendResultsText(errorMessage);
+		appendResultsText(getString(R.string.resetting));
+		appendResultsText(lineSeparator);
+		editText.setText(defaultStr);
+	}
+
 	private void preparePinger() {
-		setEnabled(ping_button, false);
-		setEnabled(ping_button_cancel, true);
-		url_ip = edit_text_ping.getText().toString();
+		ping_timeout_string = edit_text_timeout.getText().toString();
+		ping_ttl_string = edit_text_ttl.getText().toString();
+		ping_times_string = edit_text_times.getText().toString();
+
+		disableViews();
 
+		url_ip = edit_text_ping.getText().toString();
 		if (TextUtils.isEmpty(url_ip)) {
 			if (wifi_connected) {
 				url_ip = getGateway();
@@ -147,102 +203,56 @@ private void preparePinger() {
 			edit_text_ping.setText(url_ip);
 		}
 
-		if (TextUtils.isEmpty(edit_text_timeout.getText().toString())) {
-			appendResultsText(getString(R.string.timeout_field_empty));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_timeout.setText("3000");
-		}
+		validateField(edit_text_timeout, ping_timeout_string, DEFAULT_TIMEOUT);
+		validateField(edit_text_ttl, ping_ttl_string, DEFAULT_TTL);
+		validateField(edit_text_times, ping_times_string, DEFAULT_PACKETS);
 
-		if (!isStringInt(edit_text_timeout.getText().toString())) {
-			appendResultsText(getString(R.string.timeout_not_integer));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_timeout.setText("3000");
-		}
+		int ping_timeout = Integer.parseInt(ping_timeout_string);
+		int ping_ttl = Integer.parseInt(ping_ttl_string);
+		int ping_times = Integer.parseInt(ping_times_string);
 
-		if (Integer.parseInt(edit_text_timeout.getText().toString()) < 1) {
-			appendResultsText(getString(R.string.timeout_lower_than_1_ms));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_timeout.setText("3000");
-		}
-
-		if (TextUtils.isEmpty(edit_text_ttl.getText().toString())) {
-			appendResultsText(getString(R.string.ttl_field_empty));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_ttl.setText("30");
-		}
-
-		if (!isStringInt(edit_text_ttl.getText().toString())) {
-			appendResultsText(getString(R.string.ttl_not_integer));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_ttl.setText("30");
-		}
-
-		if (Integer.parseInt(edit_text_ttl.getText().toString()) < 1) {
-			appendResultsText(getString(R.string.ttl_lower_than_1));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_ttl.setText("30");
-		}
-
-		if (TextUtils.isEmpty(edit_text_times.getText().toString())) {
-			appendResultsText(getString(R.string.packets_amount_not_defined));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_times.setText("5");
-		}
-
-		if (!isStringInt(edit_text_times.getText().toString())) {
-			appendResultsText(getString(R.string.packets_not_integer, edit_text_times.getText().toString()));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_times.setText("5");
-		}
-
-		if (Integer.parseInt(edit_text_times.getText().toString()) < 1) {
-			appendResultsText(getString(R.string.packets_lower_than_1, edit_text_times.getText().toString()));
-			appendResultsText(getString(R.string.resetting));
-			edit_text_times.setText("5");
-		}
-
-		int ping_timeout = Integer.parseInt(edit_text_timeout.getText().toString());
-		int ping_ttl = Integer.parseInt(edit_text_ttl.getText().toString());
-		int ping_times = Integer.parseInt(edit_text_times.getText().toString());
-
-		pingHandlerThread = new HandlerThread("BackgroundPingHandlerThread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+		pingHandlerThread = new HandlerThread("PingBackgroundHandlerThread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
 		pingHandlerThread.start();
 		pingHandler = new Handler(pingHandlerThread.getLooper());
 
 		pingHandler.post(() -> {
 			try {
-				String pingHostAddress = URLandIPConverter.convertUrl("https://" + url_ip);
-				InetAddress inetAddress = InetAddress.getByName(url_ip);
-				String pingHostname = inetAddress.getHostName();
-				appendResultsText(String.format(getString(R.string.ping_log_ip), pingHostAddress));
-				appendResultsText(String.format(getString(R.string.ping_log_hostname), pingHostname));
-			} catch (UnknownHostException | MalformedURLException e) {
+				AtomicReference pingHostAddress = new AtomicReference<>("");
+				AtomicReference pingHostname = new AtomicReference<>("");
+				URLandIPConverter.convertUrlToIp(url_ip, result -> {
+					pingHostAddress.set(result);
+					appendResultsText(String.format(getString(R.string.ping_log_ip), pingHostAddress.get()));
+				});
+
+				URLandIPConverter.convertIpToUrl(url_ip, result -> {
+					pingHostname.set(result);
+					appendResultsText(String.format(getString(R.string.ping_log_hostname), pingHostname.get()));
+					appendResultsText(getString(R.string.time_to_live_ttl) + ": " + ping_ttl);
+					startPinger(url_ip, ping_timeout, ping_ttl, ping_times);
+				});
+			} catch (Exception e) {
 				e.printStackTrace();
-				setEnabled(ping_button, true);
-				setEnabled(ping_button_cancel, false);
+				enableViews();
 				appendResultsText(lineSeparator);
 			}
 		});
-		startPinger(url_ip, ping_timeout, ping_ttl, ping_times);
 	}
 
 	private void startPinger(String url_ip, int timeout, int ttl, int times) {
 		runOnUiThread(() -> ping_progress_bar.setVisibility(View.VISIBLE));
 		pinger = Ping.Companion.onAddress(url_ip).setTimeOutMillis(timeout).setDelayMillis(500).setTimeToLive(ttl).setTimes(times).doPing(new Ping.PingListener() {
 			final long startTime = System.currentTimeMillis();
-			@SuppressLint("DefaultLocale")
 			@Override
 			public void onResult(PingResult pingResult) {
 				sentPackets++;
 				if (pingResult.isReachable()) {
 					appendResultsText(String.format(getString(R.string.ping_successful_response),
-							sentPackets, pingResult.getAddress(), pingResult.getTimeTaken(), ttl));
+							sentPackets, pingResult.getAddress(), pingResult.getTimeTaken()));
 				} else {
 					appendResultsText(String.format(getString(R.string.connection_timeout), sentPackets));
 				}
 			}
 
-			@SuppressLint("DefaultLocale")
 			@Override
 			public void onFinished(PingStats pingStats) {
 				long endTime = System.currentTimeMillis();
@@ -258,18 +268,17 @@ public void onFinished(PingStats pingStats) {
 				appendResultsText(lineSeparator);
 				sentPackets = 0;
 				runOnUiThread(() -> ping_progress_bar.setVisibility(View.INVISIBLE));
-				setEnabled(ping_button, true);
-				setEnabled(ping_button_cancel, false);
+				enableViews();
 			}
 
 			@Override
 			public void onError(Exception e) {
 				e.printStackTrace();
 				appendResultsText(e.getMessage());
+				appendResultsText(lineSeparator);
 				sentPackets = 0;
 				runOnUiThread(() -> ping_progress_bar.setVisibility(View.INVISIBLE));
-				setEnabled(ping_button, true);
-				setEnabled(ping_button_cancel, false);
+				enableViews();
 			}
 		});
 	}
@@ -280,13 +289,18 @@ public void onReceive(Context context, Intent intent) {
 			checkNetworkConnectivity(true);
 		}
 	}
-	
+
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
+	}
+
 	public void checkNetworkConnectivity(Boolean shouldClearLog) {
 		CM = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
 		wifiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
 		cellularCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
-		if (wifiCheck.isConnected() && !cellularCheck.isConnected()) { // Wi-Fi Connectivity Check
+		if (wifiCheck.isConnected()) { // Wi-Fi Connectivity Check
 			showWidgets();
 			if (toolbarPingMenu != null) {
 				if (!toolbarPingMenu.findItem(R.id.clear_ping_log).isEnabled()) {
@@ -295,7 +309,7 @@ public void checkNetworkConnectivity(Boolean shouldClearLog) {
 			}
 			wifi_connected = true;
 			cellular_connected = false;
-		} else if (cellularCheck.isConnected() && !wifiCheck.isConnected()) { // Cellular Connectivity Check
+		} else if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) { // Cellular Connectivity Check
 			showWidgets();
 			if (toolbarPingMenu != null) {
 				if (!toolbarPingMenu.findItem(R.id.clear_ping_log).isEnabled()) {
@@ -304,7 +318,7 @@ public void checkNetworkConnectivity(Boolean shouldClearLog) {
 			}
 			wifi_connected = false;
 			cellular_connected = true;
-		} else if (!wifiCheck.isConnected() && !cellularCheck.isConnected()) {
+		} else {
 			if (shouldClearLog) { ping_text.setText(""); }
 			if (toolbarPingMenu != null) {
 				if (toolbarPingMenu.findItem(R.id.clear_ping_log).isEnabled()) {
@@ -351,6 +365,16 @@ private void setEnabled(final View view, final boolean enabled) {
 		});
 	}
 
+	private void enableViews() {
+		setEnabled(ping_button, true);
+		setEnabled(ping_button_cancel, false);
+	}
+
+	private void disableViews() {
+		setEnabled(ping_button, false);
+		setEnabled(ping_button_cancel, true);
+	}
+
 	private void appendResultsText(final String text) {
 		runOnUiThread(() -> {
 			ping_text.append(text + "\n");
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/PortScannerActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/PortScannerActivity.java
similarity index 60%
rename from app/src/main/java/com/truemlgpro/wifiinfo/PortScannerActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/PortScannerActivity.java
index 6da94dd..995f3fc 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/PortScannerActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/PortScannerActivity.java
@@ -1,21 +1,23 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
+import android.annotation.SuppressLint;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
+import android.net.DhcpInfo;
 import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.view.View;
-import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.EditText;
-import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.Spinner;
 import android.widget.TextView;
@@ -23,19 +25,29 @@
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
 import com.google.android.material.textfield.TextInputLayout;
 import com.stealthcopter.networktools.PortScan;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.adapters.PortScannerAdapter;
+import com.truemlgpro.wifiinfo.models.DiscoveredPort;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.IANADatabaseHelper;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
 
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
-
 public class PortScannerActivity extends AppCompatActivity {
 	private TextView textview_nonetworkconn;
 	private ProgressBar port_scanner_progress_bar;
@@ -50,30 +62,35 @@ public class PortScannerActivity extends AppCompatActivity {
 	private TextView ports_closed_text;
 	private Button port_scan_button;
 	private Button port_scan_stop_button;
-	private ListView listview_open_ports;
+	private RecyclerView recyclerview_open_ports;
 
 	private PortScan portScanner;
+	private IANADatabaseHelper ianaDbHelper;
 
-	private ArrayList portsArrayList;
-	private ArrayAdapter adapter;
+	private ArrayList portsArrayList;
+	private PortScannerAdapter recyclerAdapter;
 
 	private BroadcastReceiver NetworkConnectivityReceiver;
 
 	private HandlerThread portScannerHandlerThread;
 	private Handler portScannerHandler;
 
-	public Boolean wifi_connected;
-	public Boolean cellular_connected;
+	private NetworkInfo wifiCheck;
+	private NetworkInfo cellularCheck;
+
+	private Boolean wifi_connected;
+	private Boolean cellular_connected;
 
 	private String url_ip = "";
 	private String ports = "";
-	private int threads = 8;
+	private int threads = 64;
 	private int closedPorts = 0;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.port_scanner_activity);
@@ -92,23 +109,27 @@ protected void onCreate(Bundle savedInstanceState)
 		ports_closed_text = (TextView) findViewById(R.id.ports_closed_text);
 		port_scan_button = (Button) findViewById(R.id.port_scan_button);
 		port_scan_stop_button = (Button) findViewById(R.id.port_scan_stop_button);
-		listview_open_ports = (ListView) findViewById(R.id.listview_open_ports);
+		recyclerview_open_ports = (RecyclerView) findViewById(R.id.recyclerview_open_ports);
 
 		portsArrayList = new ArrayList<>();
-		adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, portsArrayList);
-		listview_open_ports.setAdapter(adapter);
+		LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
+		DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerview_open_ports.getContext(), linearLayoutManager.getOrientation());
+		recyclerview_open_ports.addItemDecoration(dividerItemDecoration);
+		recyclerview_open_ports.setLayoutManager(linearLayoutManager);
+		recyclerAdapter = new PortScannerAdapter(portsArrayList);
+		recyclerview_open_ports.setAdapter(recyclerAdapter);
 
-		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		ianaDbHelper = new IANADatabaseHelper(this);
 
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.port_scanner));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -118,7 +139,7 @@ protected void onCreate(Bundle savedInstanceState)
 		port_scan_button.setOnClickListener(v -> {
 			startPortScanner();
 			ports_open_text.setText(getString(R.string.ports_open_none));
-			adapter.clear();
+			recyclerAdapter.clear();
 		});
 
 		port_scan_stop_button.setOnClickListener(v -> {
@@ -130,37 +151,42 @@ protected void onCreate(Bundle savedInstanceState)
 		});
 	}
 
-	private void setEnabled(final View view, final boolean enabled) {
-        runOnUiThread(() -> {
-	        if (view != null) {
-		        view.setEnabled(enabled);
-	        }
-        });
-    }
+	private String getGateway() {
+		if (!wifiCheck.isConnected()) {
+			return "0.0.0.0";
+		}
+		WifiManager mainWifi = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
+		DhcpInfo dhcp = mainWifi.getDhcpInfo();
+		int ip = dhcp.gateway;
+		return String.format("%d.%d.%d.%d", (ip & 0xff), (ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff));
+	}
 
-	private void addPortsToList(final String text) {
-		Comparator portComparator = (port1, port2) -> Integer.parseInt(port1) - Integer.parseInt(port2);
-		int index = Collections.binarySearch(portsArrayList, text, portComparator);
+	private void addPortsToList(final String port, final String portServiceName, final String portServiceDescription, final String portServiceProtocol) {
+		DiscoveredPort discoveredPort = new DiscoveredPort(port, portServiceName, portServiceDescription, portServiceProtocol);
+		Comparator portComparator = (itemOne, itemNext) -> Integer.parseInt(itemOne.openPort()) - Integer.parseInt(itemNext.openPort());
+		int index = Collections.binarySearch(portsArrayList, discoveredPort, portComparator);
+		int insertedItemPosition = (index < 0) ? (-index - 1) : index;
 
 		runOnUiThread(() -> {
-			if (!portsArrayList.contains(text)) {
-				portsArrayList.add((index < 0) ? (-index - 1) : index, text);
+			if (!portsArrayList.contains(port)) {
+				portsArrayList.add(insertedItemPosition, discoveredPort);
+				recyclerAdapter.notifyItemInserted(insertedItemPosition);
+				recyclerview_open_ports.smoothScrollToPosition(portsArrayList.size() - 1);
 			}
-			adapter.notifyDataSetChanged();
 		});
 	}
 
-	public void sortListByPort() {
-		Collections.sort(portsArrayList, (port1, port2) -> Integer.parseInt(port1) - Integer.parseInt(port2));
+	private void sortListByPort() {
+		Collections.sort(portsArrayList, (itemOne, itemNext) -> Integer.parseInt(itemOne.openPort()) - Integer.parseInt(itemNext.openPort()));
 	}
 
-	public boolean isPortRangeValid(String portRange) {
+	private boolean isPortRangeValid(String portRange) {
 		String regex = "^(?:6553[0-5]|655[0-2]\\d|65[0-4]\\d\\d|6[0-4]\\d{3}|[0-5]\\d{4}|\\d{1,5})(?:-(?:6553[0-5]|655[0-2]\\d|65[0-4]\\d\\d|6[0-4]\\d{3}|[0-5]\\d{4}|\\d{1,5}))?(?:,(?:6553[0-5]|655[0-2]\\d|65[0-4]\\d\\d|6[0-4]\\d{3}|[0-5]\\d{4}|\\d{1,5})(?:-(?:6553[0-5]|655[0-2]\\d|65[0-4]\\d\\d|6[0-4]\\d{3}|[0-5]\\d{4}|\\d{1,5}))?|\\d{1,5}-\\d{1,5})*(?:,\\s*\\d{1,5}(?:-\\d{1,5})?)*$";
 		Pattern pattern = Pattern.compile(regex);
 		Matcher matcher = pattern.matcher(portRange);
 
 		if (matcher.matches()) {
-			String[] ranges = portRange.replaceAll("[a-zA-Z]", "").split(",");
+			String[] ranges = portRange.split(",");
 
 			for (String range : ranges) {
 				if (range.contains("-")) {
@@ -171,10 +197,16 @@ public boolean isPortRangeValid(String portRange) {
 					int firstPort = Integer.parseInt(numbers[0]);
 					int secondPort = Integer.parseInt(numbers[1]);
 
-					// Check if the first number is lower than the second number
+					// Check if the first port number is lower than the second port number
 					if (firstPort >= secondPort) {
 						return false;
 					}
+				} else {
+					int singlePort = Integer.parseInt(range);
+					// Check if the single port is greater than the max allowed value
+					if (singlePort > 65535) {
+						return false;
+					}
 				}
 			}
 			return true;
@@ -183,61 +215,68 @@ public boolean isPortRangeValid(String portRange) {
 	}
 
 	private void startPortScanner() {
-		setEnabled(port_scan_button, false);
-		setEnabled(port_scan_stop_button, true);
+		String threads_string = edittext_threads.getText().toString();
+
+		disableViews();
 
 		url_ip = edittext_ip.getText().toString();
 		if (TextUtils.isEmpty(url_ip)) {
-			url_ip = "google.com";
+			if (wifi_connected) {
+				url_ip = getGateway();
+			} else if (cellular_connected) {
+				url_ip = "google.com";
+			}
 			edittext_ip.setText(url_ip);
 		}
 
-		if (TextUtils.isEmpty(edittext_threads.getText().toString()) || !isStringInt(edittext_threads.getText().toString()) || threads <= 0) {
-			edittext_threads.setText("64");
+		if (TextUtils.isEmpty(threads_string) || !isStringInt(threads_string) || threads <= 0) {
+			threads_string = "64";
+			edittext_threads.setText(threads_string);
 		}
-		threads = Integer.parseInt(edittext_threads.getText().toString());
+		threads = Integer.parseInt(threads_string);
 
 		ports = edittext_ports.getText().toString().replaceAll("[a-zA-Z]", "");
-		edittext_ports.setText(ports);
 		if (TextUtils.isEmpty(ports) || !isPortRangeValid(ports)) {
-			edittext_ports.setText("1-65535");
+			ports = "1-65535";
+			edittext_ports.setText(ports);
 		}
-		ports = edittext_ports.getText().toString();
 
 		runOnUiThread(() -> port_scanner_progress_bar.setVisibility(View.VISIBLE));
 
-		portScannerHandlerThread = new HandlerThread("BackgroundPortScannerHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
+		portScannerHandlerThread = new HandlerThread("PortScannerBackgroundHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
 		portScannerHandlerThread.start();
 		portScannerHandler = new Handler(portScannerHandlerThread.getLooper());
 
 		portScannerHandler.post(() -> {
 			int scanMethodIndex = spinner_packet_types.getSelectedItemPosition();
+			String scanProtocol = spinner_packet_types.getSelectedItem().toString();
 			try { // setMethod -> 0 (TCP), 1 (UDP)
-				portScanner = PortScan.Companion.onAddress(url_ip).setTimeOutMillis(1000).setPorts(ports).setNoThreads(threads).setMethod(scanMethodIndex == 0 ? 0 : 1).doScan(new PortScan.PortListener() {
+				portScanner = PortScan.Companion.onAddress(url_ip).setTimeOutMillis(1000).setPorts(ports).setNoThreads(threads).setMethod(scanMethodIndex).doScan(new PortScan.PortListener() {
 					@Override
 					public void onResult(int portNo, boolean open) {
 						String portNoString = String.valueOf(portNo);
 						if (open) {
-							addPortsToList(portNoString);
+							addPortsToList(
+								portNoString,
+								ianaDbHelper.getServiceName(portNo, scanProtocol.toLowerCase()),
+								ianaDbHelper.getServiceDescription(portNo, scanProtocol.toLowerCase()),
+								scanProtocol);
+							runOnUiThread(() -> ports_open_text.setText(String.format(getString(R.string.ports_open_amount), portsArrayList.size())));
 						} else {
 							closedPorts++;
+							runOnUiThread(() -> ports_closed_text.setText(String.format(getString(R.string.ports_closed_amount), closedPorts)));
 						}
-
-						runOnUiThread(() -> {
-							ports_open_text.setText(String.format(getString(R.string.ports_open_amount), portsArrayList.size()));
-							ports_closed_text.setText(String.format(getString(R.string.ports_closed_amount), closedPorts));
-						});
 					}
 
+					@SuppressLint("NotifyDataSetChanged")
 					@Override
 					public void onFinished(ArrayList openPorts) {
-						setEnabled(port_scan_button, true);
-						setEnabled(port_scan_stop_button, false);
+						enableViews();
 						runOnUiThread(() -> {
 							ports_open_text.setText(String.format(getString(R.string.ports_open_amount), portsArrayList.size()));
 							ports_closed_text.setText(String.format(getString(R.string.ports_closed_amount), closedPorts));
 							sortListByPort();
-							adapter.notifyDataSetChanged();
+							recyclerAdapter.notifyDataSetChanged();
 							port_scanner_progress_bar.setVisibility(View.INVISIBLE);
 							closedPorts = 0;
 						});
@@ -249,6 +288,7 @@ public void onFinished(ArrayList openPorts) {
 					portScanner.cancel();
 				}
 				runOnUiThread(() -> port_scanner_progress_bar.setVisibility(View.INVISIBLE));
+				enableViews();
 			}
 		});
     }
@@ -260,22 +300,27 @@ public void onReceive(Context context, Intent intent) {
 		}
 	}
 
-	public void checkNetworkConnectivity() {
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
+	}
+
+	private void checkNetworkConnectivity() {
 		ConnectivityManager CM = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
-		NetworkInfo wifiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-		NetworkInfo cellularCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+		wifiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+		cellularCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
-		if (wifiCheck.isConnected() && !cellularCheck.isConnected()) { // WI-FI Connectivity Check
+		if (wifiCheck.isConnected()) { // WI-FI Connectivity Check
 			showWidgets();
 			wifi_connected = true;
 			cellular_connected = false;
-		} else if (cellularCheck.isConnected() && !wifiCheck.isConnected()) { // Cellular Connectivity Check
+		} else if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) { // Cellular Connectivity Check
 			showWidgets();
 			wifi_connected = false;
 			cellular_connected = true;
-		} else if (!wifiCheck.isConnected() && !cellularCheck.isConnected()) {
+		} else {
 			ports_open_text.setText(getString(R.string.ports_open_none));
-			adapter.clear();
+			recyclerAdapter.clear();
 			hideWidgets();
 			wifi_connected = false;
 			cellular_connected = false;
@@ -291,7 +336,7 @@ private boolean isStringInt(String s)  {
 		}
 	}
 
-	public void showWidgets() {
+	private void showWidgets() {
 		text_input_layout_ip.setVisibility(View.VISIBLE);
 		text_input_layout_threads.setVisibility(View.VISIBLE);
 		text_input_layout_ports.setVisibility(View.VISIBLE);
@@ -303,12 +348,12 @@ public void showWidgets() {
 		ports_closed_text.setVisibility(View.VISIBLE);
 		port_scan_button.setVisibility(View.VISIBLE);
 		port_scan_stop_button.setVisibility(View.VISIBLE);
-		listview_open_ports.setVisibility(View.VISIBLE);
+		recyclerview_open_ports.setVisibility(View.VISIBLE);
 		port_scanner_progress_bar.setVisibility(View.INVISIBLE);
 		textview_nonetworkconn.setVisibility(View.GONE);
 	}
 
-	public void hideWidgets() {
+	private void hideWidgets() {
 		text_input_layout_ip.setVisibility(View.GONE);
 		text_input_layout_threads.setVisibility(View.GONE);
 		text_input_layout_ports.setVisibility(View.GONE);
@@ -320,11 +365,29 @@ public void hideWidgets() {
 		ports_closed_text.setVisibility(View.GONE);
 		port_scan_button.setVisibility(View.GONE);
 		port_scan_stop_button.setVisibility(View.GONE);
-		listview_open_ports.setVisibility(View.GONE);
+		recyclerview_open_ports.setVisibility(View.GONE);
 		port_scanner_progress_bar.setVisibility(View.GONE);
 		textview_nonetworkconn.setVisibility(View.VISIBLE);
 	}
 
+	private void setEnabled(final View view, final boolean enabled) {
+		runOnUiThread(() -> {
+			if (view != null) {
+				view.setEnabled(enabled);
+			}
+		});
+	}
+
+	private void enableViews() {
+		setEnabled(port_scan_button, true);
+		setEnabled(port_scan_stop_button, false);
+	}
+
+	private void disableViews() {
+		setEnabled(port_scan_button, false);
+		setEnabled(port_scan_stop_button, true);
+	}
+
 	@Override
 	protected void onStart() {
 		super.onStart();
@@ -350,4 +413,10 @@ protected void onStop() {
 		}
 		unregisterReceiver(NetworkConnectivityReceiver);
 	}
+
+	@Override
+	protected void onDestroy() {
+		super.onDestroy();
+		recyclerview_open_ports.setAdapter(null);
+	}
 }
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/RouterSetupActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/RouterSetupActivity.java
similarity index 82%
rename from app/src/main/java/com/truemlgpro/wifiinfo/RouterSetupActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/RouterSetupActivity.java
index 1c0ac3d..2276be1 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/RouterSetupActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/RouterSetupActivity.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.annotation.SuppressLint;
 import android.app.Dialog;
@@ -31,9 +31,13 @@
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 
-import java.util.Objects;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
+import java.util.Objects;
 
 public class RouterSetupActivity extends AppCompatActivity {
 	private Toolbar toolbar;
@@ -63,6 +67,7 @@ public class RouterSetupActivity extends AppCompatActivity {
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.router_setup_activity);
@@ -75,16 +80,14 @@ protected void onCreate(Bundle savedInstanceState)
 		progressBarLoading = (ProgressBar) findViewById(R.id.router_setup_progress_bar);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.router_setup));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -118,20 +121,25 @@ public void initLoginDialog() {
 	}
 
 	public void showLoginDialog() {
-		alert.show();
+		if (!isFinishing()) {
+			alert.show();
+		}
 	}
 
 	@SuppressLint("SetJavaScriptEnabled")
 	public void loadWebview() {
-		String userAgent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 Firefox/4.0";
+		String userAgent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1";
 		WebSettings ws = webview_main.getSettings();
 		ws.setJavaScriptEnabled(true);
+		ws.setDomStorageEnabled(true);
 		ws.setSupportZoom(true);
 		ws.setBuiltInZoomControls(true);
 		ws.setDisplayZoomControls(false);
 		ws.setLoadWithOverviewMode(true);
 		ws.setUseWideViewPort(true);
 		ws.setUserAgentString(userAgent);
+		if (Build.VERSION.SDK_INT >= 33)
+			ws.setAlgorithmicDarkeningAllowed(true);
 		webview_main.loadUrl("http://" + getGatewayIP());
 		webview_main.setWebChromeClient(new WebChromeClient() {
 			@Override
@@ -200,42 +208,20 @@ public void onReceivedError(WebView view, int errorCode, String description, Str
 	}
 
 	private void showErrorToast(Context mContext, int errorCode) {
-		String message = null;
-		switch (errorCode) {
-			case WebViewClient.ERROR_AUTHENTICATION:
-				message = getString(R.string.auth_error);
-				break;
-			case WebViewClient.ERROR_TIMEOUT:
-				message = getString(R.string.timeout_error);
-				break;
-			case WebViewClient.ERROR_TOO_MANY_REQUESTS:
-				message = getString(R.string.too_many_requests_error);
-				break;
-			case WebViewClient.ERROR_UNKNOWN:
-				message = getString(R.string.unknown_error);
-				break;
-			case WebViewClient.ERROR_CONNECT:
-				message = getString(R.string.connect_error);
-				break;
-			case WebViewClient.ERROR_HOST_LOOKUP:
-				message = getString(R.string.host_lookup_error);
-				break;
-			case WebViewClient.ERROR_PROXY_AUTHENTICATION:
-				message = getString(R.string.proxy_auth_error);
-				break;
-			case WebViewClient.ERROR_REDIRECT_LOOP:
-				message = getString(R.string.redirect_loop_error);
-				break;
-			case WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME:
-				message = getString(R.string.unsupported_auth_scheme_error);
-				break;
-			case WebViewClient.ERROR_UNSUPPORTED_SCHEME:
-				message = getString(R.string.unsupported_scheme_error);
-				break;
-			case WebViewClient.ERROR_IO:
-				message = getString(R.string.io_error);
-				break;
-		}
+		String message = switch (errorCode) {
+			case WebViewClient.ERROR_AUTHENTICATION -> getString(R.string.auth_error);
+			case WebViewClient.ERROR_TIMEOUT -> getString(R.string.timeout_error);
+			case WebViewClient.ERROR_TOO_MANY_REQUESTS -> getString(R.string.too_many_requests_error);
+			case WebViewClient.ERROR_UNKNOWN -> getString(R.string.unknown_error);
+			case WebViewClient.ERROR_CONNECT -> getString(R.string.connect_error);
+			case WebViewClient.ERROR_HOST_LOOKUP -> getString(R.string.host_lookup_error);
+			case WebViewClient.ERROR_PROXY_AUTHENTICATION -> getString(R.string.proxy_auth_error);
+			case WebViewClient.ERROR_REDIRECT_LOOP -> getString(R.string.redirect_loop_error);
+			case WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME -> getString(R.string.unsupported_auth_scheme_error);
+			case WebViewClient.ERROR_UNSUPPORTED_SCHEME -> getString(R.string.unsupported_scheme_error);
+			case WebViewClient.ERROR_IO -> getString(R.string.io_error);
+			default -> null;
+		};
 
 		if (message != null) {
 			Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
@@ -343,16 +329,12 @@ public boolean onPrepareOptionsMenu(Menu menu) {
 	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
 		int id = item.getItemId();
-		switch (id) {
-			case (R.id.page_back):
-				webview_main.goBack();
-				break;
-			case (R.id.page_forward):
-				webview_main.goForward();
-				break;
-			case (R.id.page_refresh):
-				webview_main.reload();
-				break;
+		if (id == R.id.page_back) {
+			webview_main.goBack();
+		} else if (id == R.id.page_forward) {
+			webview_main.goForward();
+		} else if (id == R.id.page_refresh) {
+			webview_main.reload();
 		}
 		return true;
 	}
@@ -371,4 +353,17 @@ protected void onStop() {
 		super.onStop();
 		unregisterReceiver(NetworkConnectivityReceiver);
 	}
+
+	@Override
+	protected void onDestroy() {
+		super.onDestroy();
+		if (webview_main != null) {
+			webview_main.setWebViewClient(null);
+			webview_main.setWebChromeClient(null);
+			webview_main.clearHistory();
+			webview_main.clearCache(true);
+			webview_main.destroy();
+			webview_main = null;
+		}
+	}
 }
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsActivity.java
new file mode 100644
index 0000000..51160b5
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsActivity.java
@@ -0,0 +1,60 @@
+package com.truemlgpro.wifiinfo.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+
+public class SettingsActivity extends AppCompatActivity {
+	@Override
+	protected void onCreate(Bundle savedInstanceState)
+	{
+		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
+
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.settings_activity);
+
+		Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+
+		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		FontManager.init(this, getApplicationContext(), true);
+
+		setSupportActionBar(toolbar);
+		final ActionBar actionbar = getSupportActionBar();
+		actionbar.setDisplayHomeAsUpEnabled(true);
+		actionbar.setDisplayShowHomeEnabled(true);
+		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.settings));
+
+		toolbar.setNavigationOnClickListener(v -> {
+			// Back button pressed
+			restartActivity();
+		});
+
+		getSupportFragmentManager().beginTransaction()
+			.replace(R.id.content_frame, new SettingsFragment())
+			.commit();
+	}
+
+	private void restartActivity() {
+		Intent mainActivityIntent = new Intent(SettingsActivity.this, MainActivity.class);
+		mainActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+		overridePendingTransition(0, 0);
+		startActivity(mainActivityIntent);
+	}
+
+	@Override
+	public void onBackPressed() {
+		super.onBackPressed();
+		restartActivity();
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsFragment.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsFragment.java
new file mode 100644
index 0000000..ff9e184
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SettingsFragment.java
@@ -0,0 +1,156 @@
+package com.truemlgpro.wifiinfo.ui;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationManagerCompat;
+import androidx.core.content.ContextCompat;
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.SwitchPreference;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+import com.truemlgpro.wifiinfo.utils.AppClipboardManager;
+import com.truemlgpro.wifiinfo.utils.SharedPreferencesManager;
+
+public class SettingsFragment extends PreferenceFragmentCompat {
+	private PackageInfo pi;
+	private SwitchPreference ntfcSwitchPreference;
+	private ActivityResultLauncher notificationSettingsLauncher;
+	private ActivityResultLauncher requestPermissionLauncher;
+
+	@Override
+	public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+		setPreferencesFromResource(R.xml.preferences, rootKey);
+
+		boolean notificationPreferenceState = new SharedPreferencesManager(requireActivity().getApplicationContext())
+				.retrieveBoolean(PreferenceKeys.KEY_PREF_SHOW_NTFC, PreferenceDefaults.SHOW_NTFC);
+		boolean areNotificationsEnabled = NotificationManagerCompat.from(requireActivity()).areNotificationsEnabled();
+
+		ntfcSwitchPreference = findPreference(PreferenceKeys.KEY_PREF_SHOW_NTFC);
+		assert ntfcSwitchPreference != null;
+
+		ntfcSwitchPreference.setChecked(areNotificationsEnabled && notificationPreferenceState);
+
+		ntfcSwitchPreference.setOnPreferenceChangeListener((preference, newValue) -> {
+			boolean isNtfcSwitchEnabled = (boolean) newValue;
+			if (isNtfcSwitchEnabled) {
+				if (Build.VERSION.SDK_INT >= 33) {
+					if (!isNotificationPermissionGranted()) {
+						if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+							requestNotificationPermission();
+						} else {
+							openAppSettings();
+						}
+					}
+				} else {
+					if (!areNotificationsEnabled) {
+						openAppSettings();
+					}
+				}
+			}
+			return true;
+		});
+
+		notificationSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+				result -> ntfcSwitchPreference.setChecked(areNotificationsEnabled));
+
+		if (Build.VERSION.SDK_INT >= 33) {
+			requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
+				if (isGranted) {
+					if (!ntfcSwitchPreference.isChecked()) {
+						ntfcSwitchPreference.setChecked(true);
+					}
+				} else {
+					if (ntfcSwitchPreference.isChecked()) {
+						ntfcSwitchPreference.setChecked(false);
+					}
+				}
+			});
+		}
+
+		if (Build.VERSION.SDK_INT >= 26) {
+			CheckBoxPreference colorizeNtfcPref = findPreference(PreferenceKeys.KEY_PREF_COLORIZE_NTFC);
+			assert colorizeNtfcPref != null;
+			colorizeNtfcPref.setVisible(true);
+			colorizeNtfcPref.setIcon(R.drawable.format_color_fill_24px);
+		}
+
+		try {
+			pi = requireActivity().getPackageManager().getPackageInfo(requireActivity().getPackageName(), 0);
+		} catch (PackageManager.NameNotFoundException e) {
+			e.printStackTrace();
+		}
+
+		setPreferenceSummary("app_version_pref", pi.versionName);
+		setPreferenceSummary("android_version_pref", Build.VERSION.RELEASE);
+		setPreferenceSummary("sdk_version_code_pref", String.valueOf(Build.VERSION.SDK_INT));
+		setPreferenceSummary("device_model_pref", Build.MODEL);
+		setPreferenceSummary("product_name_pref", Build.PRODUCT);
+
+		setCopyOnPreferenceClickListener("app_version_pref");
+		setCopyOnPreferenceClickListener("android_version_pref");
+		setCopyOnPreferenceClickListener("sdk_version_code_pref");
+		setCopyOnPreferenceClickListener("device_model_pref");
+		setCopyOnPreferenceClickListener("product_name_pref");
+	}
+
+	private void openAppSettings() {
+		Intent intentNtfcSettings = new Intent();
+		intentNtfcSettings.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
+		if (Build.VERSION.SDK_INT >= 26) {
+			intentNtfcSettings.putExtra(Settings.EXTRA_APP_PACKAGE, requireActivity().getPackageName());
+		} else {
+			intentNtfcSettings.putExtra("app_package", requireActivity().getPackageName());
+			intentNtfcSettings.putExtra("app_uid", requireActivity().getApplicationInfo().uid);
+		}
+		notificationSettingsLauncher.launch(intentNtfcSettings);
+	}
+
+	private boolean hasPermissions(Context context, String... permissions) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
+			for (String permission : permissions) {
+				if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	private void setPreferenceSummary(String key, CharSequence summary) {
+		findPreference(key).setSummary(summary);
+	}
+
+	private void setCopyOnPreferenceClickListener(String key) {
+		findPreference(key).setOnPreferenceClickListener(preference -> {
+			AppClipboardManager.copyToClipboard(requireContext(), (String) preference.getTitle(), (String) preference.getSummary());
+			return true;
+		});
+	}
+
+	@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+	private boolean isNotificationPermissionGranted() {
+		boolean permissionGranted = true;
+		if (Build.VERSION.SDK_INT >= 33) {
+			permissionGranted = hasPermissions(requireActivity(), Manifest.permission.POST_NOTIFICATIONS);
+		}
+		return permissionGranted;
+	}
+
+	@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+	private void requestNotificationPermission() {
+		requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/SubnetScannerActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SubnetScannerActivity.java
similarity index 50%
rename from app/src/main/java/com/truemlgpro/wifiinfo/SubnetScannerActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/SubnetScannerActivity.java
index 7ad7499..1bffc3b 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/SubnetScannerActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SubnetScannerActivity.java
@@ -1,5 +1,7 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
+import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -10,41 +12,55 @@
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.os.Bundle;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
-import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.EditText;
-import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
 import com.google.android.material.textfield.TextInputLayout;
 import com.stealthcopter.networktools.SubnetDevices;
 import com.stealthcopter.networktools.subnet.Device;
-
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.adapters.SubnetScannerAdapter;
+import com.truemlgpro.wifiinfo.models.SubnetDevice;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.OUIDatabaseHelper;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+
+import java.lang.ref.WeakReference;
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.net.UnknownHostException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
-import java.util.Scanner;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
+import jcifs.CIFSContext;
+import jcifs.CIFSException;
+import jcifs.NetbiosAddress;
+import jcifs.config.PropertyConfiguration;
+import jcifs.context.BaseContext;
 
 public class SubnetScannerActivity extends AppCompatActivity {
 	private TextView textview_nonetworkconn;
@@ -57,28 +73,30 @@ public class SubnetScannerActivity extends AppCompatActivity {
 	private TextInputLayout text_input_layout_threads;
 	private EditText edittext_timeout;
 	private EditText edittext_threads;
-	private ListView listview_subnet_devices;
+	private RecyclerView recyclerview_subnet_devices;
 
-	private ArrayList devicesArrayList;
-	private ArrayAdapter adapter;
+	private ArrayList devicesArrayList;
+	private static SubnetScannerAdapter recyclerAdapter;
 
 	private SubnetDevices subnetScanner;
+	private OUIDatabaseHelper ouiDbHelper;
 
 	private BroadcastReceiver NetworkConnectivityReceiver;
 	private ConnectivityManager connectivityManager;
 	private NetworkInfo wifiCheck;
-
-	private static Scanner sc;
+	private NetworkInfo cellularCheck;
 
 	private static Boolean wifi_connected;
+	private static Boolean cellular_connected;
 
-	private int threads = 100;
+	private int threads = 256;
 	private int timeout = 3000;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.subnet_scanner_activity);
@@ -94,23 +112,27 @@ protected void onCreate(Bundle savedInstanceState)
 		text_input_layout_threads = (TextInputLayout) findViewById(R.id.input_layout_threads_subnet_scanner);
 		edittext_timeout = (EditText) findViewById(R.id.edittext_timeout_subnet_scanner);
 		edittext_threads = (EditText) findViewById(R.id.edittext_threads_subnet_scanner);
-		listview_subnet_devices = (ListView) findViewById(R.id.listview_subnet_devices);
+		recyclerview_subnet_devices = (RecyclerView) findViewById(R.id.recyclerview_subnet_devices);
 
 		devicesArrayList = new ArrayList<>();
-		adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, devicesArrayList);
-		listview_subnet_devices.setAdapter(adapter);
+		LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
+		DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerview_subnet_devices.getContext(), linearLayoutManager.getOrientation());
+		recyclerview_subnet_devices.addItemDecoration(dividerItemDecoration);
+		recyclerview_subnet_devices.setLayoutManager(linearLayoutManager);
+		recyclerAdapter = new SubnetScannerAdapter(devicesArrayList, this);
+		recyclerview_subnet_devices.setAdapter(recyclerAdapter);
 
-		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		ouiDbHelper = new OUIDatabaseHelper(this);
 
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.subnet_scanner));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -120,7 +142,7 @@ protected void onCreate(Bundle savedInstanceState)
 		subnet_scan_button.setOnClickListener(v -> {
 			startSubnetScanner();
 			devices_found_text.setText(getString(R.string.devices_found_na));
-			adapter.clear();
+			recyclerAdapter.clear();
 		});
 
 		subnet_scan_cancel_button.setOnClickListener(v -> {
@@ -138,6 +160,24 @@ private String getWiFiLocalIPv4Address() {
 			for (NetworkInterface networkInterface : allNetworkInterfaces) {
 				if (!networkInterface.getName().equalsIgnoreCase("wlan0"))
 					continue;
+
+				List allInetAddresses = Collections.list(networkInterface.getInetAddresses());
+				for (InetAddress inetAddr : allInetAddresses) {
+					if (!inetAddr.isLoopbackAddress() && inetAddr instanceof Inet4Address) {
+						return inetAddr.getHostAddress();
+					}
+				}
+			}
+		} catch (SocketException e) {
+			Log.e("getWiFiLocalIPv4", e.toString());
+		}
+		return null;
+	}
+
+	private String getCellularLocalIPv4Address() {
+		try {
+			List allNetworkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+			for (NetworkInterface networkInterface : allNetworkInterfaces) {
 				List allInetAddresses = Collections.list(networkInterface.getInetAddresses());
 				for (InetAddress inetAddr : allInetAddresses) {
 					if (!inetAddr.isLoopbackAddress() && inetAddr instanceof Inet4Address) {
@@ -145,8 +185,8 @@ private String getWiFiLocalIPv4Address() {
 					}
 				}
 			}
-		} catch (SocketException ex) {
-			Log.e("getWiFiLocalIPv4", ex.toString());
+		} catch (SocketException e) {
+			Log.e("getCellLocalIPv4", e.toString());
 		}
 		return null;
 	}
@@ -164,7 +204,7 @@ private String getMACAddress() {
 				}
 
 				StringBuilder macAddressStringBuilder = new StringBuilder();
-				for (byte b : macBytes) {  
+				for (byte b : macBytes) {
 					macAddressStringBuilder.append(String.format("%02X:", b));
 				}
 
@@ -174,7 +214,7 @@ private String getMACAddress() {
 
 				return macAddressStringBuilder.toString();
 			}
-		} catch (Exception e) {
+		} catch (SocketException e) {
 			e.printStackTrace();
 		}
 		return null;
@@ -190,93 +230,207 @@ private String getGateway() {
 		return String.format("%d.%d.%d.%d", (ip & 0xff), (ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff));
 	}
 
-	private void setEnabled(final View view, final boolean enabled) {
-		runOnUiThread(() -> {
-			if (view != null) {
-				view.setEnabled(enabled);
+	private static class NetBiosScanner {
+		private final WeakReference contextRef;
+
+		public NetBiosScanner(Context context) {
+			this.contextRef = new WeakReference<>(context);
+		}
+
+		public void performThreadedNetBiosLookup(String ipAddress) {
+			ExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+			scheduler.submit(() -> {
+				try {
+					String netBiosName = getNetBiosName(ipAddress);
+					if (netBiosName != null && !netBiosName.isEmpty()) {
+						Activity activity = (Activity) contextRef.get();
+						if (activity != null) {
+							activity.runOnUiThread(() -> recyclerAdapter.updateNetBiosName(ipAddress, netBiosName));
+						}
+					}
+				} finally {
+					scheduler.shutdownNow();
+				}
+			});
+		}
+
+		// Should run in a non-UI thread
+		@WorkerThread
+		protected String getNetBiosName(String ipAddress) {
+			String deviceNamePrimary = "";
+			try {
+				CIFSContext cifsContext = new BaseContext(new PropertyConfiguration(System.getProperties())).withGuestCrendentials();
+				NetbiosAddress[] netBiosDeviceAddresses = cifsContext.getNameServiceClient().getNbtAllByAddress(ipAddress);
+				for (NetbiosAddress addr : netBiosDeviceAddresses) {
+					if (!addr.isGroupAddress(cifsContext)) {
+						deviceNamePrimary = String.valueOf(addr.getName())
+								.replaceAll("<[0-9A-Fa-f]+>", "");
+						break;
+					}
+				}
+			} catch (UnknownHostException | CIFSException e) {
+				deviceNamePrimary = "";
 			}
-		});
+			return deviceNamePrimary;
+		}
 	}
 
-	private void addDevicesToList(final String text) {
-		Comparator ipComparator = (ip1, ip2) -> convertDiscoveredIPToLong(ip1).compareTo(convertDiscoveredIPToLong(ip2));
-		int index = Collections.binarySearch(devicesArrayList, text, ipComparator);
+	private void addDevicesToList(
+			final String ip, final String mac, final String vendor,
+			final String deviceName, final String deviceType, final String devicePing) {
+		SubnetDevice subnetDevice = new SubnetDevice(ip, mac, vendor, deviceName, deviceType, devicePing);
+		Comparator ipComparator = (itemOne, itemNext) -> convertDiscoveredIPToLong(itemOne.getIP()).compareTo(convertDiscoveredIPToLong(itemNext.getIP()));
+		int index = Collections.binarySearch(devicesArrayList, subnetDevice, ipComparator);
+		int insertedItemPosition = (index < 0) ? (-index - 1) : index;
 
 		runOnUiThread(() -> {
-			devicesArrayList.add((index < 0) ? (-index - 1) : index, text);
-			adapter.notifyDataSetChanged();
+			devicesArrayList.add(insertedItemPosition, subnetDevice);
+			recyclerAdapter.notifyItemInserted(insertedItemPosition);
+			recyclerview_subnet_devices.smoothScrollToPosition(devicesArrayList.size() - 1);
 		});
 	}
 
 	@NonNull
 	private static Long convertDiscoveredIPToLong(String ip) {
-		sc = new Scanner(ip).useDelimiter("\\.|\\W\\|(|<=\\|).*$");
+		String[] octets = ip.split("\\.");
+
+		long octet1 = Long.parseLong(octets[0]);
+		long octet2 = Long.parseLong(octets[1]);
+		long octet3 = Long.parseLong(octets[2]);
+		long octet4 = Long.parseLong(octets[3]);
 
-		return (sc.nextLong() << 24) + (sc.nextLong() << 16) + 
-			(sc.nextLong() << 8) + (sc.nextLong());
+		return (octet1 << 24) + (octet2 << 16) +
+			(octet3 << 8) + (octet4);
 	}
 
 	private void sortListByIP() {
-		Collections.sort(devicesArrayList, (ip1, ip2) -> convertDiscoveredIPToLong(ip1).compareTo(convertDiscoveredIPToLong(ip2)));
+		Collections.sort(devicesArrayList, (itemOne, itemNext) -> convertDiscoveredIPToLong(itemOne.getIP()).compareTo(convertDiscoveredIPToLong(itemNext.getIP())));
 	}
 
 	private void startSubnetScanner() {
-		setEnabled(subnet_scan_button, false);
-		setEnabled(subnet_scan_cancel_button, true);
+		String threads_string = edittext_threads.getText().toString();
+		String timeout_string = edittext_timeout.getText().toString();
 
-		if (TextUtils.isEmpty(edittext_threads.getText().toString()) || !isStringInt(edittext_threads.getText().toString()) || threads <= 0) {
-			edittext_threads.setText("256");
-		}
+		disableViews();
 
-		if (TextUtils.isEmpty(edittext_timeout.getText().toString()) || !isStringInt(edittext_timeout.getText().toString()) || timeout <= 0) {
-			edittext_timeout.setText("3000");
+		if (TextUtils.isEmpty(threads_string) || !isStringInt(threads_string) || threads <= 0) {
+			threads_string = "256";
+			edittext_threads.setText(threads_string);
 		}
+		threads = Integer.parseInt(threads_string);
 
-		threads = Integer.parseInt(edittext_threads.getText().toString());
-		timeout = Integer.parseInt(edittext_timeout.getText().toString());
+		if (TextUtils.isEmpty(timeout_string) || !isStringInt(timeout_string) || timeout <= 0) {
+			timeout_string = "3000";
+			edittext_timeout.setText(timeout_string);
+		}
+		timeout = Integer.parseInt(timeout_string);
 
 		runOnUiThread(() -> subnet_scanner_progress_bar.setVisibility(View.VISIBLE));
 
 		subnetScanner = SubnetDevices.Companion.setDisableProcNetMethod(Build.VERSION.SDK_INT > 29).fromLocalAddress().setNoThreads(threads).setTimeOutMillis(timeout).findDevices(new SubnetDevices.OnSubnetDeviceFound() {
 			@Override
 			public void onDeviceFound(Device device) {
+				String devicePing = device.time + "ms";
 				if (wifi_connected) {
 					if (device.ip.equals(getWiFiLocalIPv4Address()) && !device.ip.equals(getGateway())) {
 						if (getMACAddress() == null || Build.VERSION.SDK_INT > 29) {
-							addDevicesToList(device.ip + " | " + getString(R.string.mac_na) + " " + getString(R.string.your_device));
+							addDevicesToList(
+									device.ip,
+									getString(R.string.na),
+									"",
+									Build.MANUFACTURER + " " + Build.MODEL,
+									getString(R.string.your_device),
+									devicePing);
 						} else {
-							addDevicesToList(device.ip + " | " + String.format(getString(R.string.mac), getMACAddress() + " " + getString(R.string.your_device)));
+							addDevicesToList(
+									device.ip,
+									getMACAddress(),
+									ouiDbHelper.getVendorFromMac(getMACAddress()),
+									Build.MANUFACTURER + " " + Build.MODEL,
+									getString(R.string.your_device),
+									devicePing);
 						}
 					} else if (!device.ip.equals(getWiFiLocalIPv4Address()) && !device.ip.equals(getGateway())) {
 						if (device.mac == null) {
-							addDevicesToList(device.ip + " | " + getString(R.string.mac_na));
+							addDevicesToList(
+									device.ip,
+									getString(R.string.na),
+									"",
+									"",
+									"",
+									devicePing);
 						} else {
-							addDevicesToList(device.ip + " | " + String.format(getString(R.string.mac), device.mac.toUpperCase()));
+							addDevicesToList(
+									device.ip,
+									device.mac,
+									ouiDbHelper.getVendorFromMac(device.mac),
+									"",
+									"",
+									devicePing);
 						}
 					} else if (device.ip.equals(getGateway())) {
 						if (device.mac == null) {
-							addDevicesToList(device.ip + " | " + getString(R.string.mac_na) + " " + getString(R.string.gateway));
+							addDevicesToList(
+									device.ip,
+									getString(R.string.na),
+									"",
+									"",
+									getString(R.string.gateway),
+									devicePing);
 						} else {
-							addDevicesToList(device.ip + " | " + String.format(getString(R.string.mac), device.mac.toUpperCase()) + " " + getString(R.string.gateway));
+							addDevicesToList(
+									device.ip,
+									device.mac,
+									ouiDbHelper.getVendorFromMac(device.mac),
+									"",
+									getString(R.string.gateway),
+									devicePing);
 						}
 					}
 				}
 
-				runOnUiThread(() -> {
-					devices_found_text.setText(String.format(getString(R.string.devices_found), devicesArrayList.size()));
-				});
+				if (cellular_connected) {
+					if (device.ip.equals(getCellularLocalIPv4Address())) {
+						addDevicesToList(
+								device.ip,
+								getString(R.string.na),
+								"",
+								Build.MANUFACTURER + " " + Build.MODEL,
+								getString(R.string.your_device),
+								devicePing);
+					} else {
+						addDevicesToList(
+								device.ip,
+								getString(R.string.na),
+								"",
+								"",
+								"",
+								devicePing);
+					}
+				}
+
+				runOnUiThread(() -> devices_found_text.setText(String.format(getString(R.string.devices_found), devicesArrayList.size())));
 			}
 
+			@SuppressLint("NotifyDataSetChanged")
 			@Override
 			public void onFinished(final ArrayList devicesFound) {
+				enableViews();
+
+				if (wifi_connected && !cellular_connected) {
+					for (Device device : devicesFound) {
+						if (!device.ip.equals(getWiFiLocalIPv4Address())) {
+							new NetBiosScanner(SubnetScannerActivity.this).performThreadedNetBiosLookup(device.ip);
+						}
+					}
+				}
+
 				runOnUiThread(() -> {
 					devices_found_text.setText(String.format(getString(R.string.devices_found), devicesFound.size()));
 					sortListByIP();
-					adapter.notifyDataSetChanged();
+					recyclerAdapter.notifyDataSetChanged();
 					subnet_scanner_progress_bar.setVisibility(View.INVISIBLE);
 				});
-				setEnabled(subnet_scan_button, true);
-				setEnabled(subnet_scan_cancel_button, false);
 			}
 		});
 	}
@@ -288,20 +442,33 @@ public void onReceive(Context context, Intent intent) {
 		}
 	}
 
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
+	}
+
 	private void checkWiFiConnectivity() {
 		connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
 		wifiCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+		cellularCheck = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
 		if (wifiCheck.isConnected()) { // Wi-Fi Connectivity Check
 			showWidgets();
 			local_ip_text.setText(String.format(getString(R.string.your_ip), getWiFiLocalIPv4Address()));
 			wifi_connected = true;
+			cellular_connected = false;
+		} else if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) { // Cellular Connectivity Check
+			showWidgets();
+			local_ip_text.setText(String.format(getString(R.string.your_ip), getCellularLocalIPv4Address()));
+			wifi_connected = false;
+			cellular_connected = true;
 		} else {
 			local_ip_text.setText(getString(R.string.your_ip_na));
 			devices_found_text.setText(getString(R.string.devices_found_na));
-			adapter.clear();
+			recyclerAdapter.clear();
 			hideWidgets();
 			wifi_connected = false;
+			cellular_connected = false;
 		}
 	}
 
@@ -321,7 +488,7 @@ private void showWidgets() {
 		subnet_scan_cancel_button.setVisibility(View.VISIBLE);
 		text_input_layout_timeout.setVisibility(View.VISIBLE);
 		text_input_layout_threads.setVisibility(View.VISIBLE);
-		listview_subnet_devices.setVisibility(View.VISIBLE);
+		recyclerview_subnet_devices.setVisibility(View.VISIBLE);
 		subnet_scanner_progress_bar.setVisibility(View.INVISIBLE);
 		textview_nonetworkconn.setVisibility(View.GONE);
 	}
@@ -333,11 +500,29 @@ private void hideWidgets() {
 		subnet_scan_cancel_button.setVisibility(View.GONE);
 		text_input_layout_timeout.setVisibility(View.GONE);
 		text_input_layout_threads.setVisibility(View.GONE);
-		listview_subnet_devices.setVisibility(View.GONE);
+		recyclerview_subnet_devices.setVisibility(View.GONE);
 		subnet_scanner_progress_bar.setVisibility(View.GONE);
 		textview_nonetworkconn.setVisibility(View.VISIBLE);
 	}
 
+	private void setEnabled(final View view, final boolean enabled) {
+		runOnUiThread(() -> {
+			if (view != null) {
+				view.setEnabled(enabled);
+			}
+		});
+	}
+
+	private void enableViews() {
+		setEnabled(subnet_scan_button, true);
+		setEnabled(subnet_scan_cancel_button, false);
+	}
+
+	private void disableViews() {
+		setEnabled(subnet_scan_button, false);
+		setEnabled(subnet_scan_cancel_button, true);
+	}
+
 	@Override
 	protected void onStart() {
 		super.onStart();
@@ -355,4 +540,10 @@ protected void onStop() {
 		}
 		unregisterReceiver(NetworkConnectivityReceiver);
 	}
+
+	@Override
+	protected void onDestroy() {
+		super.onDestroy();
+		recyclerview_subnet_devices.setAdapter(null);
+	}
 }
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/SupportersActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SupportersActivity.java
similarity index 83%
rename from app/src/main/java/com/truemlgpro/wifiinfo/SupportersActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/SupportersActivity.java
index 8fde784..d7a42e7 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/SupportersActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/SupportersActivity.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
@@ -10,7 +10,11 @@
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
 
 public class SupportersActivity extends AppCompatActivity {
 	private TextView pab_text;
@@ -23,18 +27,18 @@ public class SupportersActivity extends AppCompatActivity {
 	private TextView artem_text;
 	private TextView terrin_text;
 	private TextView torneix_text;
+	private TextView ognjen28a_text;
 	private TextView killbayne_text;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.supporters_activity);
 
-		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
 		Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 		pab_text = (TextView) findViewById(R.id.pab_text);
 		anyx_text = (TextView) findViewById(R.id.anyx_text);
@@ -46,17 +50,18 @@ protected void onCreate(Bundle savedInstanceState)
 		artem_text = (TextView) findViewById(R.id.artem_text);
 		terrin_text = (TextView) findViewById(R.id.terrin_text);
 		torneix_text = (TextView) findViewById(R.id.torneix_text);
+		ognjen28a_text = (TextView) findViewById(R.id.ognjen28a_text);
 		killbayne_text = (TextView) findViewById(R.id.killbayne_text);
 
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		KeepScreenOnManager.init(getWindow(), getApplicationContext());
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.supporters));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -77,6 +82,7 @@ public void initializeOnClickListeners() {
 		artem_text.setOnClickListener(v -> copyToClipboard(getString(R.string.supporter_artem)));
 		terrin_text.setOnClickListener(v -> copyToClipboard(getString(R.string.supporter_terrin_tin)));
 		torneix_text.setOnClickListener(v -> copyToClipboard(getString(R.string.supporter_torneix)));
+		ognjen28a_text.setOnClickListener(v -> copyToClipboard(getString(R.string.supporter_ognjen28a)));
 		killbayne_text.setOnClickListener(v -> copyToClipboard(getString(R.string.supporter_killbayne)));
 	}
 
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/ToolsActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/ToolsActivity.java
similarity index 85%
rename from app/src/main/java/com/truemlgpro/wifiinfo/ToolsActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/ToolsActivity.java
index bb39662..11e43cf 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/ToolsActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/ToolsActivity.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.content.Intent;
 import android.os.Bundle;
@@ -8,13 +8,18 @@
 import androidx.appcompat.widget.Toolbar;
 import androidx.cardview.widget.CardView;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
 
 public class ToolsActivity extends AppCompatActivity {
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.tools_activity);
@@ -29,16 +34,14 @@ protected void onCreate(Bundle savedInstanceState)
 		CardView cardview_dns_lookup = (CardView) findViewById(R.id.cardview_dns_lookup);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.tools));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/WhoIsToolActivity.java b/app/src/main/java/com/truemlgpro/wifiinfo/ui/WhoIsToolActivity.java
similarity index 74%
rename from app/src/main/java/com/truemlgpro/wifiinfo/WhoIsToolActivity.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/ui/WhoIsToolActivity.java
index 6c1d95d..a122412 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/WhoIsToolActivity.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/ui/WhoIsToolActivity.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.ui;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -10,6 +10,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -24,12 +26,17 @@
 import androidx.appcompat.widget.Toolbar;
 
 import com.google.android.material.textfield.TextInputLayout;
-
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.utils.FontManager;
+import com.truemlgpro.wifiinfo.utils.KeepScreenOnManager;
+import com.truemlgpro.wifiinfo.utils.LocaleManager;
+import com.truemlgpro.wifiinfo.utils.ThemeManager;
+import com.truemlgpro.wifiinfo.utils.URLandIPConverter;
+
+import java.util.Objects;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicReference;
 
-import me.anwarshahriar.calligrapher.Calligrapher;
 import thecollectiveweb.com.tcwhois.TCWHOIS;
 
 public class WhoIsToolActivity extends AppCompatActivity {
@@ -41,9 +48,6 @@ public class WhoIsToolActivity extends AppCompatActivity {
 	private Button fetch_whois_info_button;
 	private ScrollView who_is_scroll;
 
-	public Boolean wifi_connected;
-	public Boolean cellular_connected;
-
 	private Menu toolbarWhoisMenu;
 
 	private Bundle whoIsBundle = new Bundle();
@@ -56,15 +60,13 @@ public class WhoIsToolActivity extends AppCompatActivity {
 	private static final int STATE_RUNNABLE_STARTED = 11;
 	private static final int STATE_RUNNABLE_FINISHED = 12;
 
-	private static final int MIN_TEXT_LENGTH = 4;
-	private static final String EMPTY_STRING = "";
-
 	private BroadcastReceiver NetworkConnectivityReceiver;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState)
 	{
 		ThemeManager.initializeThemes(this, getApplicationContext());
+		LocaleManager.initializeLocale(getApplicationContext());
 
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.who_is_tool_activity);
@@ -79,16 +81,14 @@ protected void onCreate(Bundle savedInstanceState)
 		who_is_scroll = (ScrollView) findViewById(R.id.who_is_scroll);
 
 		KeepScreenOnManager.init(getWindow(), getApplicationContext());
-
-		Calligrapher calligrapher = new Calligrapher(this);
-		String font = new SharedPreferencesManager(getApplicationContext()).retrieveString(SettingsActivity.KEY_PREF_APP_FONT, MainActivity.appFont);
-		calligrapher.setFont(this, font, true);
+		FontManager.init(this, getApplicationContext(), true);
 
 		setSupportActionBar(toolbar);
 		final ActionBar actionbar = getSupportActionBar();
 		actionbar.setDisplayHomeAsUpEnabled(true);
 		actionbar.setDisplayShowHomeEnabled(true);
 		actionbar.setElevation(20);
+		actionbar.setTitle(getResources().getString(R.string.whois_tool));
 
 		toolbar.setNavigationOnClickListener(v -> {
 			// Back button pressed
@@ -96,51 +96,36 @@ protected void onCreate(Bundle savedInstanceState)
 		});
 
 		fetch_whois_info_button.setOnClickListener(v -> {
-			if (!shouldShowError()) {
-				startWhoIsThread();
-				hideError();
-			} else {
-				showError();
+			String whois_url_ip = who_is_edit_text.getText().toString();
+			if (TextUtils.isEmpty(whois_url_ip)) {
+				whois_url_ip = "google.com";
+				who_is_edit_text.setText(whois_url_ip);
 			}
+			startWhoIsThread();
 		});
 	}
 
-	private boolean shouldShowError() {
-		int textLength = who_is_edit_text.getText().length();
-		return textLength >= 0 && textLength < MIN_TEXT_LENGTH;
-	}
-
-	private void showError() {
-		who_is_input_layout.setError(getString(R.string.field_too_short));
-	}
-
-	private void hideError() {
-		who_is_input_layout.setError(EMPTY_STRING);
-	}
-
 	private final Handler msgHandler = new Handler(Looper.myLooper()) {
 		@Override
 		public void handleMessage(Message msg) {
 			switch (msg.what) {
-				case STATE_SUCCESS:
+				case STATE_SUCCESS -> {
 					whoIsBundle = msg.getData();
 					String whoIsQuery = whoIsBundle.getString(MSG_KEY);
 					appendResultsText(whoIsQuery);
-					break;
-				case STATE_ERROR_MALFORMED_URL:
-					appendResultsText(getString(R.string.error_malformed_url));
-					break;
-				case STATE_ERROR_UNKNOWN_HOST:
-					appendResultsText(getString(R.string.error_unknown_host));
-					break;
-				case STATE_RUNNABLE_STARTED:
+				}
+				case STATE_ERROR_MALFORMED_URL ->
+						appendResultsText(getString(R.string.error_malformed_url));
+				case STATE_ERROR_UNKNOWN_HOST ->
+						appendResultsText(getString(R.string.error_unknown_host));
+				case STATE_RUNNABLE_STARTED -> {
 					runOnUiThread(() -> who_is_progress_bar.setVisibility(View.VISIBLE));
 					setEnabled(fetch_whois_info_button, false);
-					break;
-				case STATE_RUNNABLE_FINISHED:
+				}
+				case STATE_RUNNABLE_FINISHED -> {
 					runOnUiThread(() -> who_is_progress_bar.setVisibility(View.INVISIBLE));
 					setEnabled(fetch_whois_info_button, true);
-					break;
+				}
 			}
 		}
 	};
@@ -149,23 +134,19 @@ public void handleMessage(Message msg) {
 		@Override
 		public void run() {
 			msgHandler.sendEmptyMessage(STATE_RUNNABLE_STARTED);
-			try {
-				String url = who_is_edit_text.getText().toString();
-				String ip = URLandIPConverter.convertUrl("https://" + url);
-				String fetched_whois_data = getWhoIsInfo(url);
-				String lineSeparator = "\n----------------------------\n";
-				String output = String.format(getString(R.string.whois_result_output), url, ip, fetched_whois_data, lineSeparator);
-				Message msg = msgHandler.obtainMessage(STATE_SUCCESS);
-				whoIsBundle.putString(MSG_KEY, output);
-				msg.setData(whoIsBundle);
-				msgHandler.sendMessage(msg);
-			} catch (MalformedURLException e) {
-				e.printStackTrace();
-				msgHandler.sendEmptyMessage(STATE_ERROR_MALFORMED_URL);
-			} catch (UnknownHostException e) {
-				e.printStackTrace();
-				msgHandler.sendEmptyMessage(STATE_ERROR_UNKNOWN_HOST);
-			}
+
+			String url = who_is_edit_text.getText().toString();
+			AtomicReference ip = new AtomicReference<>("");
+			URLandIPConverter.convertUrlToIp(url, ip::set);
+			String fetched_whois_data = getWhoIsInfo(url);
+
+			String lineSeparator = "\n----------------------------\n";
+			String output = String.format(getString(R.string.whois_result_output), url, ip, fetched_whois_data, lineSeparator);
+
+			Message msg = msgHandler.obtainMessage(STATE_SUCCESS);
+			whoIsBundle.putString(MSG_KEY, output);
+			msg.setData(whoIsBundle);
+			msgHandler.sendMessage(msg);
 			msgHandler.sendEmptyMessage(STATE_RUNNABLE_FINISHED);
 		}
 	};
@@ -189,14 +170,6 @@ public String getWhoIsInfo(String url) {
 		return whoisData;
 	}
 
-	private void setEnabled(final View view, final boolean enabled) {
-		runOnUiThread(() -> {
-			if (view != null) {
-				view.setEnabled(enabled);
-			}
-		});
-	}
-
 	class NetworkConnectivityReceiver extends BroadcastReceiver {
 		@Override
 		public void onReceive(Context context, Intent intent) {
@@ -204,30 +177,31 @@ public void onReceive(Context context, Intent intent) {
 		}
 	}
 
+	private boolean isSimCardPresent(Context context) {
+		TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+		return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
+	}
+
 	public void checkNetworkConnectivity(Boolean shouldClearLog) {
 		ConnectivityManager CM = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
-		NetworkInfo WiFiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
-		NetworkInfo CellularCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+		NetworkInfo wifiCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+		NetworkInfo cellularCheck = CM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
 
-		if (WiFiCheck.isConnected() && !CellularCheck.isConnected()) { // Wi-Fi Connectivity Check
+		if (wifiCheck.isConnected()) { // Wi-Fi Connectivity Check
 			showWidgets();
 			if (toolbarWhoisMenu != null) {
 				if (!toolbarWhoisMenu.findItem(R.id.clear_whois_log).isEnabled()) {
 					setToolbarItemEnabled(R.id.clear_whois_log, true);
 				}
 			}
-			wifi_connected = true;
-			cellular_connected = false;
-		} else if (CellularCheck.isConnected() && !WiFiCheck.isConnected()) { // Cellular Connectivity Check
+		} else if (isSimCardPresent(this) && Objects.nonNull(cellularCheck) && cellularCheck.isConnected()) { // Cellular Connectivity Check
 			showWidgets();
 			if (toolbarWhoisMenu != null) {
 				if (!toolbarWhoisMenu.findItem(R.id.clear_whois_log).isEnabled()) {
 					setToolbarItemEnabled(R.id.clear_whois_log, true);
 				}
 			}
-			wifi_connected = false;
-			cellular_connected = true;
-		} else if (!WiFiCheck.isConnected() && !CellularCheck.isConnected()) {
+		} else {
 			if (shouldClearLog) { textview_who_is_results.setText(""); }
 			if (toolbarWhoisMenu != null) {
 				if (toolbarWhoisMenu.findItem(R.id.clear_whois_log).isEnabled()) {
@@ -235,8 +209,6 @@ public void checkNetworkConnectivity(Boolean shouldClearLog) {
 				}
 			}
 			hideWidgets();
-			wifi_connected = false;
-			cellular_connected = false;
 		}
 	}
 
@@ -260,6 +232,14 @@ public void hideWidgets() {
 		textview_nonetworkconn.setVisibility(View.VISIBLE);
 	}
 
+	private void setEnabled(final View view, final boolean enabled) {
+		runOnUiThread(() -> {
+			if (view != null) {
+				view.setEnabled(enabled);
+			}
+		});
+	}
+
 	private void appendResultsText(final String text) {
 		runOnUiThread(() -> {
 			textview_who_is_results.append(text + "\n");
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/AppClipboardManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/AppClipboardManager.java
new file mode 100644
index 0000000..7e83bab
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/AppClipboardManager.java
@@ -0,0 +1,26 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.widget.Toast;
+
+import com.truemlgpro.wifiinfo.R;
+
+/**
+ * A helper class for managing the clipboard functionality
+ */
+public class AppClipboardManager {
+	/**
+	 * Copies passed text to the clipboard
+	 * @param appContext a context to pass, has to be an App Context
+	 * @param label a user-visible label for the clip data
+	 * @param text the text to be copied to the clip
+	 */
+	public static void copyToClipboard(Context appContext, String label, String text) {
+		ClipboardManager cbm = (ClipboardManager) appContext.getSystemService(Context.CLIPBOARD_SERVICE);
+		ClipData clip = ClipData.newPlainText(label, text);
+		cbm.setPrimaryClip(clip);
+		Toast.makeText(appContext, appContext.getString(R.string.copied_to_clipboard) + ": " + text, Toast.LENGTH_SHORT).show();
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/FontManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/FontManager.java
new file mode 100644
index 0000000..baf9bfc
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/FontManager.java
@@ -0,0 +1,26 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+
+import me.anwarshahriar.calligrapher.Calligrapher;
+
+/**
+ * A helper class for managing fonts
+ */
+public class FontManager {
+	/**
+	 * Sets a font for the Activity depending on the SharedPreference value
+	 * @param activity an Activity to pass to Calligrapher, used for changing the font for all TextViews
+	 * @param appContext a context to pass, has to be an App Context
+	 * @param includeActionBar defines if Calligrapher should change the font for the ActionBar
+	 */
+	public static void init(Activity activity, Context appContext, boolean includeActionBar) {
+		Calligrapher calligrapher = new Calligrapher(activity);
+		String font = new SharedPreferencesManager(appContext).retrieveString(PreferenceKeys.KEY_PREF_APP_FONT, PreferenceDefaults.APP_FONT);
+		calligrapher.setFont(activity, font, includeActionBar);
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/IANADatabaseHelper.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/IANADatabaseHelper.java
new file mode 100644
index 0000000..6d0bc67
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/IANADatabaseHelper.java
@@ -0,0 +1,70 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
+
+public class IANADatabaseHelper extends SQLiteAssetHelper {
+	private static final String DATABASE_NAME = "iana_ports.db";
+	private static final String TABLE_NAME = "ports";
+	private static final String COLUMN_NAME = "name";
+	private static final String COLUMN_NUMBER = "number";
+	private static final String COLUMN_DESCRIPTION = "description";
+	private static final String COLUMN_PROTOCOL = "protocol";
+
+	public IANADatabaseHelper(Context context) {
+		super(context, DATABASE_NAME, null, 1);
+	}
+
+	@SuppressLint("Range")
+	public String getServiceName(int portNumber, String protocol) {
+		SQLiteDatabase db = this.getReadableDatabase();
+		String query = "SELECT " + COLUMN_NAME + " FROM " + TABLE_NAME +
+				" WHERE (" + COLUMN_NUMBER + " = ? AND " + COLUMN_PROTOCOL + " = '')" +
+				" OR (" + COLUMN_NUMBER + " = ? AND " + COLUMN_PROTOCOL + " = ?)";
+
+		String[] selectionArgs = new String[3];
+		selectionArgs[0] = Integer.toString(portNumber);
+		selectionArgs[1] = Integer.toString(portNumber);
+		selectionArgs[2] = protocol;
+
+		Cursor cursor = db.rawQuery(query, selectionArgs);
+		String serviceName = null;
+		if (cursor.moveToFirst()) {
+			serviceName = cursor.getString(cursor.getColumnIndex(COLUMN_NAME));
+			if (serviceName == null || serviceName.isEmpty()) {
+				serviceName = "-";
+			}
+		}
+		cursor.close();
+		db.close();
+
+		return serviceName;
+	}
+
+	@SuppressLint("Range")
+	public String getServiceDescription(int portNumber, String protocol) {
+		SQLiteDatabase db = this.getReadableDatabase();
+		String query = "SELECT " + COLUMN_DESCRIPTION + " FROM " + TABLE_NAME +
+				" WHERE (" + COLUMN_NUMBER + " = ? AND " + COLUMN_PROTOCOL + " = '')" +
+				" OR (" + COLUMN_NUMBER + " = ? AND " + COLUMN_PROTOCOL + " = ?)";
+
+		String[] selectionArgs = new String[3];
+		selectionArgs[0] = Integer.toString(portNumber);
+		selectionArgs[1] = Integer.toString(portNumber);
+		selectionArgs[2] = protocol;
+
+		Cursor cursor = db.rawQuery(query, selectionArgs);
+		String serviceDescription = null;
+		if (cursor.moveToFirst()) {
+			serviceDescription = cursor.getString(cursor.getColumnIndex(COLUMN_DESCRIPTION));
+		}
+		cursor.close();
+		db.close();
+
+		return serviceDescription;
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/KeepScreenOnManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/KeepScreenOnManager.java
similarity index 70%
rename from app/src/main/java/com/truemlgpro/wifiinfo/KeepScreenOnManager.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/utils/KeepScreenOnManager.java
index dfd8380..d957cf3 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/KeepScreenOnManager.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/KeepScreenOnManager.java
@@ -1,19 +1,22 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.utils;
 
 import android.content.Context;
 import android.view.Window;
 
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+
 /**
  * A helper class for managing KEEP_SCREEN_ON flags
  */
 public class KeepScreenOnManager {
 	/**
-	 * Sets or resets the activity window FLAG_KEEP_SCREEN_ON flag depending on SharedPreference value
+	 * Sets or resets the activity window FLAG_KEEP_SCREEN_ON flag depending on the SharedPreference value
 	 * @param activityWindow a window to pass using getWindow() on Activity
-	 * @param appContext a context to pass, has to be an Application Context
+	 * @param appContext a context to pass, has to be an App Context
 	 */
 	public static void init(Window activityWindow, Context appContext) {
-		boolean keyKeepScreenOn = new SharedPreferencesManager(appContext).retrieveBoolean(SettingsActivity.KEY_PREF_KEEP_SCREEN_ON_SWITCH, MainActivity.keepScreenOn);
+		boolean keyKeepScreenOn = new SharedPreferencesManager(appContext).retrieveBoolean(PreferenceKeys.KEY_PREF_KEEP_SCREEN_ON, PreferenceDefaults.KEEP_SCREEN_ON);
 
 		if (keyKeepScreenOn) {
 			keepScreenOn(activityWindow);
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/LocaleManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/LocaleManager.java
similarity index 50%
rename from app/src/main/java/com/truemlgpro/wifiinfo/LocaleManager.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/utils/LocaleManager.java
index 507ac84..65d1f31 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/LocaleManager.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/LocaleManager.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.utils;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -7,21 +7,21 @@
 import androidx.core.os.ConfigurationCompat;
 import androidx.core.os.LocaleListCompat;
 
-import java.util.Locale;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
 
 /**
  * A helper class for managing locale settings
  */
 public class LocaleManager {
 	/**
-	 * Initializes locales for Activities based on selected Locale preference
-	 * @param appContext a context to pass, has to be an Application Context
+	 * Initializes locales for Activities based on the selected Locale preference
+	 * @param appContext a context to pass, has to be an App Context
 	 */
 	public static void initializeLocale(Context appContext) {
-		String appLocalePref = new SharedPreferencesManager(appContext).retrieveString(SettingsActivity.KEY_PREF_APP_LANGUAGE, MainActivity.appLang);
-		if (appLocalePref.equals("default_lang")) {
-			String defaultSystemLocale = String.valueOf(getDefaultSystemLocale());
-			setLocale(defaultSystemLocale);
+		String appLocalePref = new SharedPreferencesManager(appContext).retrieveString(PreferenceKeys.KEY_PREF_APP_LANGUAGE, PreferenceDefaults.APP_LANG);
+		if (PreferenceDefaults.APP_LANG.equals(appLocalePref)) {
+			setLocale(getDefaultSystemLocale());
 		} else {
 			setLocale(appLocalePref);
 		}
@@ -32,7 +32,7 @@ private static void setLocale(String locale) {
 		AppCompatDelegate.setApplicationLocales(appLocale);
 	}
 
-	private static Locale getDefaultSystemLocale() {
-		return ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(0);
+	public static String getDefaultSystemLocale() {
+		return String.valueOf(ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(1));
 	}
 }
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/OUIDatabaseHelper.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/OUIDatabaseHelper.java
new file mode 100644
index 0000000..57889b4
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/OUIDatabaseHelper.java
@@ -0,0 +1,41 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
+
+public class OUIDatabaseHelper extends SQLiteAssetHelper {
+	private static final String DATABASE_NAME = "oui.db";
+	private static final String TABLE_NAME = "oui";
+	private static final String COLUMN_MAC = "mac";
+	private static final String COLUMN_VENDOR = "vendor";
+
+	public OUIDatabaseHelper(Context context) {
+		super(context, DATABASE_NAME, null, 1);
+	}
+
+	@SuppressLint("Range")
+	public String getVendorFromMac(String macAddress) {
+		SQLiteDatabase db = this.getReadableDatabase();
+
+		// Remove the last 3 octets from the input MAC address
+		String formattedMacAddress = macAddress.replaceAll("[:-]", "").toUpperCase();
+		formattedMacAddress = formattedMacAddress.substring(0, 6);
+
+		String query = "SELECT " + COLUMN_VENDOR + " FROM " + TABLE_NAME +
+				" WHERE " + COLUMN_MAC + " = '" + formattedMacAddress + "'";
+
+		Cursor cursor = db.rawQuery(query, null);
+		String vendor = null;
+		if (cursor.moveToFirst()) {
+			vendor = cursor.getString(cursor.getColumnIndex(COLUMN_VENDOR));
+		}
+		cursor.close();
+		db.close();
+
+		return vendor;
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/SharedPreferencesManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/SharedPreferencesManager.java
similarity index 98%
rename from app/src/main/java/com/truemlgpro/wifiinfo/SharedPreferencesManager.java
rename to app/src/main/java/com/truemlgpro/wifiinfo/utils/SharedPreferencesManager.java
index b6364be..68bb93d 100644
--- a/app/src/main/java/com/truemlgpro/wifiinfo/SharedPreferencesManager.java
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/SharedPreferencesManager.java
@@ -1,4 +1,4 @@
-package com.truemlgpro.wifiinfo;
+package com.truemlgpro.wifiinfo.utils;
 
 import android.content.Context;
 import android.content.SharedPreferences;
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/ThemeManager.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/ThemeManager.java
new file mode 100644
index 0000000..8f5be35
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/ThemeManager.java
@@ -0,0 +1,43 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.truemlgpro.wifiinfo.R;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceDefaults;
+import com.truemlgpro.wifiinfo.interfaces.PreferenceKeys;
+
+/**
+ * A helper class for managing themes
+ */
+public class ThemeManager {
+	/**
+	 * Initializes themes for Activities based on selected Theme preference
+	 * @param activity an activity to apply a theme to
+	 * @param appContext a context to pass, has to be an Application Context
+	 */
+	public static void initializeThemes(Activity activity, Context appContext) {
+		boolean keyDarkTheme = new SharedPreferencesManager(appContext).retrieveBoolean(PreferenceKeys.KEY_PREF_DARK_MODE, PreferenceDefaults.DARK_MODE);
+		boolean keyAmoledTheme = new SharedPreferencesManager(appContext).retrieveBoolean(PreferenceKeys.KEY_PREF_AMOLED_MODE, PreferenceDefaults.AMOLED_MODE);
+
+		if (keyAmoledTheme) {
+			if (keyDarkTheme) {
+				activity.setTheme(R.style.AmoledDarkTheme);
+			}
+		} else {
+			activity.setTheme(R.style.DarkTheme);
+		}
+
+		if (!keyDarkTheme) {
+			activity.setTheme(R.style.LightTheme);
+		}
+	}
+
+	/**
+	 * Checks if current theme is a dark theme or not
+	 * @param appContext a context to pass, has to be an Application Context
+	 */
+	public static boolean isDarkTheme(Context appContext) {
+		return new SharedPreferencesManager(appContext).retrieveBoolean(PreferenceKeys.KEY_PREF_DARK_MODE, PreferenceDefaults.DARK_MODE);
+	}
+}
diff --git a/app/src/main/java/com/truemlgpro/wifiinfo/utils/URLandIPConverter.java b/app/src/main/java/com/truemlgpro/wifiinfo/utils/URLandIPConverter.java
new file mode 100644
index 0000000..29ad834
--- /dev/null
+++ b/app/src/main/java/com/truemlgpro/wifiinfo/utils/URLandIPConverter.java
@@ -0,0 +1,101 @@
+package com.truemlgpro.wifiinfo.utils;
+
+import android.os.AsyncTask;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class URLandIPConverter {
+	public interface ConversionCallback {
+		void onConversionResult(String result);
+	}
+
+	public static void convertUrlToIp(String url, ConversionCallback callback) {
+		if (!url.startsWith("http://") && !url.startsWith("https://")) {
+			// If neither is present, default to "http://"
+			url = "http://" + url;
+		}
+		new ConvertUrlToIpTask(callback).execute(url);
+	}
+
+	public static void convertIpToUrl(String ip, ConversionCallback callback) {
+		new ConvertIpToUrlTask(callback).execute(ip);
+	}
+
+	private static String extractDomainFromUrl(String url) {
+		// Define the regex pattern to extract the domain.
+		Pattern pattern = Pattern.compile("^(https?)(:)(\\/\\/\\/?)"
+				+ "([\\w]*(?::[\\w]*)?@)?([\\d\\w\\.-]+)(?::(\\d+))?([\\/\\\\\\w\\.()-]*)?"
+				+ "(?:([?][^#]*)?(#.*)?)*");
+
+		Matcher matcher = pattern.matcher(url);
+
+		if (matcher.find()) {
+			// Group 5 contains the domain.
+			return matcher.group(5);
+		}
+
+		return null; // Invalid URL format.
+	}
+
+	private static class ConvertUrlToIpTask extends AsyncTask {
+		private final ConversionCallback callback;
+
+		ConvertUrlToIpTask(ConversionCallback callback) {
+			this.callback = callback;
+		}
+
+		@Override
+		protected String doInBackground(String... params) {
+			String url = params[0];
+			String domain = extractDomainFromUrl(url);
+			if (domain != null) {
+				try {
+					InetAddress address = InetAddress.getByName(domain);
+					return address.getHostAddress();
+				} catch (UnknownHostException e) {
+					e.printStackTrace();
+					return "Error resolving IP address.";
+				}
+			} else {
+				return "Invalid URL format.";
+			}
+		}
+
+		@Override
+		protected void onPostExecute(String result) {
+			if (callback != null) {
+				callback.onConversionResult(result);
+			}
+		}
+	}
+
+	private static class ConvertIpToUrlTask extends AsyncTask {
+		private final ConversionCallback callback;
+
+		ConvertIpToUrlTask(ConversionCallback callback) {
+			this.callback = callback;
+		}
+
+		@Override
+		protected String doInBackground(String... params) {
+			String ip = params[0];
+			try {
+				InetAddress inetAddress = InetAddress.getByName(ip);
+				return inetAddress.getHostName();
+			} catch (UnknownHostException e) {
+				e.printStackTrace();
+				return "Error resolving hostname.";
+			}
+		}
+
+		@Override
+		protected void onPostExecute(String result) {
+			if (callback != null) {
+				callback.onConversionResult(result);
+			}
+		}
+	}
+}
diff --git a/app/src/main/res/color/button_state_colors.xml b/app/src/main/res/color/button_state_colors.xml
new file mode 100644
index 0000000..6b36729
--- /dev/null
+++ b/app/src/main/res/color/button_state_colors.xml
@@ -0,0 +1,5 @@
+
+
+	
+	
+
\ No newline at end of file
diff --git a/app/src/main/res/color/button_state_text_colors.xml b/app/src/main/res/color/button_state_text_colors.xml
new file mode 100644
index 0000000..5b3fd92
--- /dev/null
+++ b/app/src/main/res/color/button_state_text_colors.xml
@@ -0,0 +1,5 @@
+
+
+	
+	
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/akebi.png b/app/src/main/res/drawable/akebi.png
index 487f3fd..ac25d75 100644
Binary files a/app/src/main/res/drawable/akebi.png and b/app/src/main/res/drawable/akebi.png differ
diff --git a/app/src/main/res/drawable/asfi.png b/app/src/main/res/drawable/asfi.png
new file mode 100644
index 0000000..3f5d3cd
Binary files /dev/null and b/app/src/main/res/drawable/asfi.png differ
diff --git a/app/src/main/res/drawable/asfi_rouge.png b/app/src/main/res/drawable/asfi_rouge.png
deleted file mode 100644
index d7827e3..0000000
Binary files a/app/src/main/res/drawable/asfi_rouge.png and /dev/null differ
diff --git a/app/src/main/res/drawable/killbayne.png b/app/src/main/res/drawable/killbayne.png
index c8ca34f..cb6300e 100644
Binary files a/app/src/main/res/drawable/killbayne.png and b/app/src/main/res/drawable/killbayne.png differ
diff --git a/app/src/main/res/drawable/ognjen28a.png b/app/src/main/res/drawable/ognjen28a.png
new file mode 100644
index 0000000..693bcae
Binary files /dev/null and b/app/src/main/res/drawable/ognjen28a.png differ
diff --git a/app/src/main/res/drawable/rouge.png b/app/src/main/res/drawable/rouge.png
new file mode 100644
index 0000000..a569ade
Binary files /dev/null and b/app/src/main/res/drawable/rouge.png differ
diff --git a/app/src/main/res/drawable/torneix.png b/app/src/main/res/drawable/torneix.png
index f1f9fe2..137f208 100644
Binary files a/app/src/main/res/drawable/torneix.png and b/app/src/main/res/drawable/torneix.png differ
diff --git a/app/src/main/res/layout/dns_lookup_activity.xml b/app/src/main/res/layout/dns_lookup_activity.xml
index 5eaac70..1d51866 100644
--- a/app/src/main/res/layout/dns_lookup_activity.xml
+++ b/app/src/main/res/layout/dns_lookup_activity.xml
@@ -91,7 +91,7 @@
 				android:layout_marginEnd="2dp"
 				android:layout_weight="1"
 				android:text="@string/start"
-				android:textColor="#FFFFFF" />
+				android:textColor="?attr/ButtonTextColor" />
 
 			
 
-						
-
-							
-
-							
-
-						
-
 						
 
 							
+								android:text="@string/mac_address" />
 
 							
 
 							
+								android:text="@string/network_interface" />
 
 							
 
 							
+								android:text="@string/network_id" />
 
 							
 
@@ -161,7 +161,7 @@
 					android:layout_width="0dp"
 					android:layout_height="wrap_content"
 					android:text="@android:string/cancel"
-					android:textColor="#FFFFFF"
+					android:textColor="?attr/ButtonTextColor"
 					android:enabled="false"
 					android:layout_marginStart="2dp"
 					android:layout_weight="1" />
diff --git a/app/src/main/res/layout/port_scanner_activity.xml b/app/src/main/res/layout/port_scanner_activity.xml
index ae49308..e1bf3e2 100644
--- a/app/src/main/res/layout/port_scanner_activity.xml
+++ b/app/src/main/res/layout/port_scanner_activity.xml
@@ -197,7 +197,7 @@
 				android:layout_height="wrap_content"
 				android:layout_weight="1"
 				android:text="@string/scan"
-				android:textColor="#FFFFFF"
+				android:textColor="?attr/ButtonTextColor"
 				style="@style/Widget.AppCompat.Button.Colored" />