Explorar el Código

working on serverList, Subscription. etc...

Khubaib hace 1 año
padre
commit
6537281730

+ 3 - 0
app/build.gradle.kts

@@ -87,6 +87,9 @@ dependencies {
     // Ping
     implementation("com.github.stealthcopter:AndroidNetworkTools:0.4.5.3")
 
+    // Billing
+    implementation("com.android.billingclient:billing:6.1.0")
+
 
     testImplementation("junit:junit:4.13.2")
     androidTestImplementation("androidx.test.ext:junit:1.1.5")

+ 2 - 1
app/src/main/java/com/vpn/fastestvpnservice/beans/Server.kt

@@ -31,5 +31,6 @@ class Server(
     var countryServers: List<Server>? = null,
     var totalServers: Int = 1,
     var enableServers: Int = 1,
-    var distance: Float = 0.0f
+    var distance: Float = 0.0f,
+    var ping: Int = 0
 )

+ 41 - 23
app/src/main/java/com/vpn/fastestvpnservice/customItems/CountryItem.kt

@@ -26,6 +26,7 @@ import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.runtime.mutableIntStateOf
@@ -41,6 +42,7 @@ import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -112,7 +114,7 @@ fun CountryItem(server: Server, category: String) {
                                         Toast
                                             .makeText(
                                                 context,
-                                                server.country,
+                                                server.country + " " + server.ping,
                                                 Toast.LENGTH_SHORT
                                             )
                                             .show()
@@ -169,21 +171,25 @@ fun CountryItem(server: Server, category: String) {
 
                             /* Country Expandable's Row */
                             if (isServerExpanded) {
+
+
                                 ExpandableRow(server = server)
                             }
                         }
                     }
                     else {
+                        Log.d("test_return_ping","C[1]=> " + server.server_name + server.ping)
 
 //                    Log.d("test_servers_count", "${server.server_name} ${server.countryServers?.size}")
                         /* Country Location's Row -> 1 Location() */
 
-                        var ping by remember { mutableIntStateOf(0) }
+//                        var ping by remember { mutableIntStateOf(0) }
                         Ping.onAddress(server.ip as String).setTimeOutMillis(1000).doPing(
                             object : Ping.PingListener{
                                 override fun onResult(pingResult: PingResult?) {
                                     android.os.Handler(Looper.getMainLooper()).post {
-                                        ping = pingResult?.timeTaken?.toInt()!!
+                                        val ping = pingResult?.timeTaken?.toInt()!!
+                                        server.ping = ping
                                         Log.d("test_ping", "ping = $ping")
                                     }
                                 }
@@ -206,7 +212,7 @@ fun CountryItem(server: Server, category: String) {
                                     Toast
                                         .makeText(
                                             context,
-                                            server.country,
+                                            server.country + " " + server.ping,
                                             Toast.LENGTH_SHORT
                                         )
                                         .show()
@@ -222,7 +228,7 @@ fun CountryItem(server: Server, category: String) {
                                     .size(24.dp)
                             )
                             val serverTitle = if (category.lowercase().toString() == "servers") server.country else server.server_name
-                            Text(text = serverTitle!!,
+                            Text(text = serverTitle!! + server.ping,
                                 style = TextStyle(
                                     fontSize = 16.sp,
                                     color = MaterialTheme.colorScheme.primary
@@ -232,8 +238,9 @@ fun CountryItem(server: Server, category: String) {
                                     .align(Alignment.CenterVertically)
                             )
                             Spacer(modifier = Modifier.weight(1F))
+                            val pings = server.ping
                             Text(
-                                text = "$ping ms",
+                                text = "${server.ping} ms",
                                 style = TextStyle(
                                     fontSize = 16.sp,
                                     color = colorResource(id = R.color.blue_text)
@@ -314,23 +321,12 @@ fun ColumnScope.ExpandableRow(server: Server) {
             .background(Color.Transparent)
     ) {
 
-        var ping by remember { mutableIntStateOf(0) } 
-        Ping.onAddress(server.ip as String).setTimeOutMillis(1000).doPing(
-            object : Ping.PingListener{
-                override fun onResult(pingResult: PingResult?) {
-                    android.os.Handler(Looper.getMainLooper()).post {
-                        ping = pingResult?.timeTaken?.toInt()!!
-                    }
-                }
+//        var ping: Int = 0
 
-                override fun onError(e: Exception?) {}
-                override fun onFinished(pingStats: PingStats?) {}
-            }
-        )
 
         // Expandable Row
 
-            server.countryServers?.let { serverData ->
+        server.countryServers?.let { serverData ->
                 serverData.forEachIndexed { index, serverInfo ->
                     Row(
                         verticalAlignment = Alignment.CenterVertically,
@@ -348,7 +344,7 @@ fun ColumnScope.ExpandableRow(server: Server) {
                                 Toast
                                     .makeText(
                                         context,
-                                        serverInfo.server_name,
+                                        serverInfo.server_name + " " + serverInfo.ping,
                                         Toast.LENGTH_SHORT
                                     )
                                     .show()
@@ -357,18 +353,23 @@ fun ColumnScope.ExpandableRow(server: Server) {
                         Text(text = serverInfo.server_name!!,
                             style = TextStyle(
                                 fontSize = 16.sp,
-                                color = MaterialTheme.colorScheme.primary
+                                color = MaterialTheme.colorScheme.onSecondary
                             ),
                             modifier = Modifier
                                 .padding(start = 16.dp, bottom = 0.dp)
                                 .align(Alignment.CenterVertically)
+                                .alpha(0.4F)
                         )
                         Spacer(modifier = Modifier.weight(1F))
 
-                        Text(text = "$ping ms",
+//                        serverListViewModel.calculatePing(server)
+
+                        Text(text = "${serverInfo.ping} ms",
                             style = TextStyle(
                                 fontSize = 16.sp,
-                                color = colorResource(id = R.color.blue_text)
+                                color = colorResource(id = R.color.blue_text),
+                                lineHeight = 30.sp,
+                                textAlign = TextAlign.Right
                             ),
                             modifier = Modifier
                                 .padding(start = 0.dp, end = 30.dp)
@@ -426,6 +427,23 @@ fun ColumnScope.ExpandableRow(server: Server) {
     }
 }
 
+fun calculatePing(server: Server, onPingResult: (Int) -> Unit) {
+    Ping.onAddress(server.ip as String).setTimeOutMillis(1000).doPing(
+        object : Ping.PingListener{
+            override fun onResult(pingResult: PingResult?) {
+                android.os.Handler(Looper.getMainLooper()).post {
+                    val ping = pingResult?.timeTaken?.toInt()!!
+                    onPingResult(ping)
+                    Log.d("test_ping", "ping[0] = $ping")
+                }
+            }
+
+            override fun onError(e: java.lang.Exception?) {}
+            override fun onFinished(pingStats: PingStats?) {}
+        }
+    )
+}
+
 @Preview
 @Composable
 fun CountryItemPreview() {

+ 3 - 3
app/src/main/java/com/vpn/fastestvpnservice/customItems/ServerItem.kt

@@ -132,7 +132,7 @@ fun ServerItem(server: Server) {
                     .align(Alignment.CenterVertically)
                 )
             Spacer(modifier = Modifier.weight(1F))
-            Text(text = "$ping ms",
+            Text(text = "${server.ping} ms",
                 style = TextStyle(
                     fontSize = 16.sp,
                     color = colorResource(id = R.color.blue_text)
@@ -288,7 +288,7 @@ fun FavoriteServerItem(server: Server) {
                     .align(Alignment.CenterVertically)
             )
             Spacer(modifier = Modifier.weight(1F))
-            Text(text = "$ping ms",
+            Text(text = "${server.ping} ms",
                 style = TextStyle(
                     fontSize = 16.sp,
                     color = colorResource(id = R.color.blue_text)
@@ -419,7 +419,7 @@ fun ServerSearchItem(server: Server) {
                     .align(Alignment.CenterVertically)
             )
             Spacer(modifier = Modifier.weight(1F))
-            Text(text = "$ping ms",
+            Text(text = "${server.ping} ms",
                 style = TextStyle(
                     fontSize = 16.sp,
                     color = colorResource(id = R.color.blue_text)

+ 60 - 0
app/src/main/java/com/vpn/fastestvpnservice/fragments/BlankFragment.kt

@@ -0,0 +1,60 @@
+package com.vpn.fastestvpnservice.fragments
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.vpn.fastestvpnservice.R
+
+// TODO: Rename parameter arguments, choose names that match
+// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
+private const val ARG_PARAM1 = "param1"
+private const val ARG_PARAM2 = "param2"
+
+/**
+ * A simple [Fragment] subclass.
+ * Use the [BlankFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class BlankFragment : Fragment() {
+    // TODO: Rename and change types of parameters
+    private var param1: String? = null
+    private var param2: String? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        arguments?.let {
+            param1 = it.getString(ARG_PARAM1)
+            param2 = it.getString(ARG_PARAM2)
+        }
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.fragment_blank, container, false)
+    }
+
+    companion object {
+        /**
+         * Use this factory method to create a new instance of
+         * this fragment using the provided parameters.
+         *
+         * @param param1 Parameter 1.
+         * @param param2 Parameter 2.
+         * @return A new instance of fragment BlankFragment.
+         */
+        // TODO: Rename and change types and number of parameters
+        @JvmStatic
+        fun newInstance(param1: String, param2: String) =
+            BlankFragment().apply {
+                arguments = Bundle().apply {
+                    putString(ARG_PARAM1, param1)
+                    putString(ARG_PARAM2, param2)
+                }
+            }
+    }
+}

+ 3 - 1
app/src/main/java/com/vpn/fastestvpnservice/navigation/BottomBarNavGraph.kt

@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
 import androidx.navigation.NavHostController
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
+import com.vpn.fastestvpnservice.fragments.BlankFragment
 import com.vpn.fastestvpnservice.screens.Login
 import com.vpn.fastestvpnservice.screens.ServerList
 import com.vpn.fastestvpnservice.screens.accountScreensAll.ChangePassword
@@ -69,7 +70,8 @@ fun BottomBarNavGraph(navHostController: NavHostController, settingsNavHostContr
             FavoriteServers(navHostController = navHostController)
         }
         composable(route = Screen.Subscription.route) {
-            SubscriptionScreen(navHostController = navHostController)
+//            SubscriptionScreenFragment(navHostController = navHostController)
+            SubscriptionScreen(navHostController)
         }
         composable(route = Screen.CustomerSupport.route) {
             CustomerSupport(navHostController = navHostController)

+ 86 - 61
app/src/main/java/com/vpn/fastestvpnservice/screens/ServerListScreen.kt

@@ -5,6 +5,7 @@ import android.location.Location
 import android.util.Log
 import android.view.animation.Animation
 import android.view.animation.AnimationUtils
+import android.widget.Toast
 import androidx.compose.animation.Animatable
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.LocalOverscrollConfiguration
@@ -526,19 +527,19 @@ fun ColumnScope.ShowAllLocationsList(
     serverListViewModel: ServerListViewModel,
     basePreferenceHelper: BasePreferenceHelper
 ) {
-    val allLocationsTabItems = listOf(
-        "Countries", "Streaming", "D-VPN", "P2P"
-    )
     val serverData = basePreferenceHelper.getServerData()
-    val filterData = serverData.get(0).servers?.let {
-        serverListViewModel.filterServersByISO(
-            it
-        )
-    }
+//    val filterData = serverData.get(0).servers?.let {
+//        serverListViewModel.filterServersByISO(
+//            it
+//        )
+//    }
 
     val pagerState = rememberPagerState(pageCount = { serverData.size })
     val selectedIndex by remember { derivedStateOf { pagerState.currentPage } }
     val scope = rememberCoroutineScope()
+    val data = basePreferenceHelper.getServerData()
+    val context = LocalContext.current
+    var filterServersList: ArrayList<Server> = ArrayList<Server>()
 
 //    serverData.forEachIndexed { index, server ->
 //        Log.d("test_data", "${server.name} ${server.servers?.size} -> ${serverData.size}")
@@ -577,8 +578,32 @@ fun ColumnScope.ShowAllLocationsList(
             Tab(
                 selected = selectedIndex == index1,
                 onClick = {
+                    Toast.makeText(
+                        context, "$selectedIndex :: $index1", Toast.LENGTH_SHORT
+                    ).show()
                     scope.launch {
                         pagerState.animateScrollToPage(index1)
+
+                        val filterServersData =
+                            if (data[index1].name?.lowercase().toString() == "servers") {
+                                data.get(index1).servers?.let {
+                                    serverListViewModel.filterServersByISO(
+                                        it
+                                    )
+                                }
+                            }
+                            else {
+                                data.get(index1).servers?.let {
+                                    serverListViewModel.filterServersByStreamingServers(
+                                        it
+                                    )
+                                }
+                            }
+
+                        serverListViewModel._mutableLiveDataGetServers.value = filterServersData
+                        if (filterServersData != null) {
+                            filterServersList = filterServersData
+                        }
                     }
                 },
                 selectedContentColor = Color.White,
@@ -607,21 +632,8 @@ fun ColumnScope.ShowAllLocationsList(
         }
     }
 
-    val data = basePreferenceHelper.getServerData()
-
-
     var isServersListExpanded by rememberSaveable { mutableStateOf(List(data.size) {true}) }
 
-//    var isServersListExpanded = List(data.size) { true }
-
-    var isStreamingExpanded by rememberSaveable { mutableStateOf(true) }
-    var isDvpnExpanded by rememberSaveable { mutableStateOf(true) }
-    var isP2PExpanded by rememberSaveable { mutableStateOf(true) }
-
-    var isServersShown by rememberSaveable { mutableStateOf(true) }
-
-    val isServersTabExpanded by rememberSaveable { mutableStateOf(listOf(true)) }
-
 //    val rotationState by animateFloatAsState(
 //        targetValue = if (isCountriesExpanded) 180f else 0f,
 //        label = if (isCountriesExpanded) "Show Less" else "Show More"
@@ -644,34 +656,6 @@ fun ColumnScope.ShowAllLocationsList(
                 var serverTitle by rememberSaveable { mutableStateOf("") }
                 var icon by rememberSaveable { mutableStateOf(0) }
 
-//                when (serverTabPager) {
-//                    0 -> {
-//                        serverTitle = serverData[0].name!!
-//                        icon = if (isCountriesExpanded) R.drawable.dragarrow3x
-//                        else R.drawable.downarrow3x
-//                    }
-//                    1 -> {
-//                        serverTitle = serverData[1].name!!
-//                        icon = if (isStreamingExpanded) R.drawable.dragarrow3x
-//                        else R.drawable.downarrow3x
-//                    }
-//                    2 -> {
-//                        serverTitle = serverData[2].name!!
-//                        icon = if (isDvpnExpanded) R.drawable.dragarrow3x
-//                        else R.drawable.downarrow3x
-//                    }
-//                    3 -> {
-//                        serverTitle = serverData[3].name!!
-//                        icon = if (isP2PExpanded) R.drawable.dragarrow3x
-//                        else R.drawable.downarrow3x
-//                    }
-//                    4 -> {
-//                        serverTitle = serverData[4].name!!
-//                        icon = if (isP2PExpanded) R.drawable.dragarrow3x
-//                        else R.drawable.downarrow3x
-//                    }
-//                }
-
                 serverTitle = serverData[serverTabPager].name!!
                 icon = if (isServersListExpanded[serverTabPager]) R.drawable.dragarrow3x
                 else R.drawable.downarrow3x
@@ -702,26 +686,29 @@ fun ColumnScope.ShowAllLocationsList(
                     CompositionLocalProvider(
                         LocalOverscrollConfiguration provides null
                     ) {
-                        val filterServersData = if (data[serverTabPager].name?.lowercase().toString() == "servers") {
-                            data.get(serverTabPager).servers?.let {
+                            if (data[serverTabPager].name?.lowercase().toString() == "servers") {
+                            filterServersList = data.get(serverTabPager).servers?.let {
                                 serverListViewModel.filterServersByISO(
                                     it
                                 )
-                            }
+                            }!!
                         }
                         else {
-                            data.get(serverTabPager).servers?.let {
+                            filterServersList = data.get(serverTabPager).servers?.let {
                                 serverListViewModel.filterServersByStreamingServers(
                                     it
                                 )
-                            }
+                            }!!
                         }
 
-                        Log.d("streaming_servers",data[serverTabPager].name?.toString() + filterServersData?.size.toString())
-
-
+//                        filterServersList = serverListViewModel.liveDataGetServers.observeAsState().value!!
+//
+//                        filterServersList.let {
+//                            serverListViewModel._mutableLiveDataGetServers.value = null
+//                        }
                         LazyColumn() {
-                            items(items = filterServersData!!) { country ->
+//                            Log.d("test_servers_size","size " + filterServersList?.size.toString())
+                            items(items = filterServersList) { country ->
                                 CountryItem(server = country, data[serverTabPager].name!!)
                             }
                         }
@@ -835,18 +822,25 @@ fun ColumnScope.ShowSearchBar(
     val sheetState = rememberModalBottomSheetState()
     val filterList = listOf("Popularity", "Alphabetically")
     var selectedFilterList by remember { mutableStateOf(filterList[0]) }
+    val scope = rememberCoroutineScope()
 
     SearchBar(
         query = searchText!!,
         onQueryChange = {
-            searchListViewModel.searchTextChange(it)
+            scope.launch {
+                searchListViewModel.searchTextChange(it)
+            }
         },
         onSearch = {
-            searchListViewModel.isActiveChange(false)
+            scope.launch {
+                searchListViewModel.isActiveChange(false)
+            }
         },
         active = isActive!!,
         onActiveChange = {
-            searchListViewModel.isActiveChange(it)
+            scope.launch {
+                searchListViewModel.isActiveChange(it)
+            }
         },
         placeholder = {
             Text(
@@ -1070,4 +1064,35 @@ fun ServerListPreview() {
 //                            }
 //                        }
 //                    }
+//                }
+
+
+
+
+//                when (serverTabPager) {
+//                    0 -> {
+//                        serverTitle = serverData[0].name!!
+//                        icon = if (isCountriesExpanded) R.drawable.dragarrow3x
+//                        else R.drawable.downarrow3x
+//                    }
+//                    1 -> {
+//                        serverTitle = serverData[1].name!!
+//                        icon = if (isStreamingExpanded) R.drawable.dragarrow3x
+//                        else R.drawable.downarrow3x
+//                    }
+//                    2 -> {
+//                        serverTitle = serverData[2].name!!
+//                        icon = if (isDvpnExpanded) R.drawable.dragarrow3x
+//                        else R.drawable.downarrow3x
+//                    }
+//                    3 -> {
+//                        serverTitle = serverData[3].name!!
+//                        icon = if (isP2PExpanded) R.drawable.dragarrow3x
+//                        else R.drawable.downarrow3x
+//                    }
+//                    4 -> {
+//                        serverTitle = serverData[4].name!!
+//                        icon = if (isP2PExpanded) R.drawable.dragarrow3x
+//                        else R.drawable.downarrow3x
+//                    }
 //                }

+ 55 - 1
app/src/main/java/com/vpn/fastestvpnservice/screens/accountScreensAll/SubscriptionScreen.kt

@@ -1,8 +1,13 @@
 package com.vpn.fastestvpnservice.screens.accountScreensAll
 
+import android.content.Context
 import android.net.VpnService
 import android.os.Build
+import android.os.Bundle
 import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
 import android.widget.Toast
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.LocalOverscrollConfiguration
@@ -38,6 +43,7 @@ import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
@@ -49,12 +55,20 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import androidx.core.app.ActivityCompat
+import androidx.core.app.ComponentActivity
+import androidx.fragment.app.Fragment
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.viewmodel.compose.viewModel
 import androidx.navigation.NavHostController
 import androidx.navigation.compose.rememberNavController
+import com.android.billingclient.api.BillingClient
+import com.android.billingclient.api.BillingClientStateListener
+import com.android.billingclient.api.BillingResult
+import com.android.billingclient.api.PurchasesUpdatedListener
+import com.android.billingclient.api.SkuDetails
 import com.vpn.fastestvpnservice.R
 import com.vpn.fastestvpnservice.beans.DataResponse
 import com.vpn.fastestvpnservice.beans.ProductFeatures
@@ -66,18 +80,57 @@ import com.vpn.fastestvpnservice.customItems.SubscriptionPackageItem
 import com.vpn.fastestvpnservice.customItems.getSelectedPosition
 import com.vpn.fastestvpnservice.screens.bottomNavBarScreens.OnLifecycleEvent
 import com.vpn.fastestvpnservice.sealedClass.Screen
+import com.vpn.fastestvpnservice.ui.theme.FastestVPNTheme
 import com.vpn.fastestvpnservice.utils.Utils
 import com.vpn.fastestvpnservice.viewmodels.SubscriptionViewModel
 
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-fun SubscriptionScreen(navHostController: NavHostController) {
+fun SubscriptionScreen(navHostController: NavHostController)
+{
+
+//    class SubscriptionScreenFragment(navHostController: NavHostController): Fragment() {
+//        var navHostController: NavHostController
+//
+//        init {
+//            this.navHostController = navHostController
+//        }
+//        override fun onCreateView(
+//            inflater: LayoutInflater,
+//            container: ViewGroup?,
+//            savedInstanceState: Bundle?
+//        ): View? {
+//            return ComposeView(requireContext()).apply {
+//                Log.d("test_compose_fragment", "SubscriptionScreenFragment")
+//                setContent {
+//                    FastestVPNTheme {
+//                        Box(
+//                            modifier = Modifier
+//                                .fillMaxSize()
+//                                .fillMaxWidth()
+//                                .fillMaxHeight()
+//                                .background(Color.Gray)
+//                        ) {
+//
+//                            Text(text = "Test")
+////                        SubscriptionScreen(navHostController = navHostController)
+//
+//                        }
+//                    }
+//
+//                }
+//            }
+//        }
+//    }
 
     val context = LocalContext.current
     val subscriptionViewModel: SubscriptionViewModel = viewModel{
         SubscriptionViewModel(context = context)
     }
     var selectedPosition: Int = 0
+    var skuDetailsList: List<SkuDetails>? = null
+
 
     Box(
         modifier = Modifier
@@ -184,6 +237,7 @@ fun SubscriptionScreen(navHostController: NavHostController) {
     }
 }
 
+
 @Composable
 fun OnLifecycleEventSS(onEvent: (
     owner: LifecycleOwner,

+ 10 - 2
app/src/main/java/com/vpn/fastestvpnservice/screens/bottomNavBarScreens/HomeScreen.kt

@@ -101,7 +101,7 @@ fun Home(navHostController: NavHostController) {
     val homeViewModel: HomeViewModel = viewModel{
         HomeViewModel(context)
     }
-    val serverViewModel: ServerListViewModel = viewModel{
+    val serverListViewModel: ServerListViewModel = viewModel{
         ServerListViewModel(context)
     }
     val splashViewModel: SplashViewModel = viewModel{
@@ -128,7 +128,7 @@ fun Home(navHostController: NavHostController) {
                         }
 
                         if (temp.isNotEmpty()) {
-                            filterServerByConnectionCount = serverViewModel.filterServerByConnectionCount(
+                            filterServerByConnectionCount = serverListViewModel.filterServerByConnectionCount(
                                 it
                             )
                         }
@@ -136,6 +136,14 @@ fun Home(navHostController: NavHostController) {
                 }
                 prefHelper.setServerObject(filterServerByConnectionCount)
 
+//                val data = prefHelper.getServerData()
+//                data.get(0).servers?.let {
+//                    serverListViewModel.filterServersByISO(
+//                        it
+//                    )
+//                }
+
+
                 server = filterServerByConnectionCount
 
 //                splashViewModel.serverDataApi()

+ 6 - 0
app/src/main/java/com/vpn/fastestvpnservice/ui/theme/Theme.kt

@@ -28,6 +28,9 @@ private val DarkColorScheme = darkColorScheme(
     primary = Color.White,
     onPrimary = Color.Black,
     secondary = Color.White,
+    onSecondary = Color.White,
+    tertiary = Color.White,
+
     background = Color.Black
 )
 
@@ -36,6 +39,9 @@ private val LightColorScheme = lightColorScheme(
     primary = Color(0xFF0d1a2a),    // darkBlueGrey
     onPrimary = Color.White,
     secondary = Color.Black,
+    onSecondary = Color(0xFF0d1b2a),
+    tertiary = Color(0xFF2952c3),
+
     background = Color(0xFFf3f3f3)
 
     /* Other default colors to override

+ 188 - 0
app/src/main/java/com/vpn/fastestvpnservice/viewmodels/BillingViewModel.kt

@@ -0,0 +1,188 @@
+package com.vpn.fastestvpnservice.viewmodels
+
+import android.util.Log
+import androidx.lifecycle.AndroidViewModel
+import com.android.billingclient.api.BillingClient
+import com.android.billingclient.api.BillingClientStateListener
+import com.android.billingclient.api.BillingResult
+import com.android.billingclient.api.Purchase
+import com.android.billingclient.api.PurchasesUpdatedListener
+import com.android.billingclient.api.SkuDetails
+import com.android.billingclient.api.SkuDetailsParams
+import com.vpn.fastestvpnservice.application.App
+import com.vpn.fastestvpnservice.application.BaseApplication
+import com.vpn.fastestvpnservice.beans.ProductFeatures
+import com.vpn.fastestvpnservice.beans.UpgradePriceList
+import com.vpn.fastestvpnservice.helpers.BasePreferenceHelper
+
+class BillingViewModel(application: App): AndroidViewModel(application),
+    PurchasesUpdatedListener
+{
+    val tag = "BillingViewModel"
+    val prefHelper = BasePreferenceHelper(application.applicationContext)
+    private var skuDetailsList: List<SkuDetails>? = null
+
+    private val billingClient: BillingClient by lazy {
+        BillingClient.newBuilder(application.applicationContext)
+            .setListener(this)
+            .enablePendingPurchases()
+            .build()
+    }
+
+    init {
+        startConnection()
+    }
+
+    private fun startConnection() {
+        billingClient.startConnection(object : BillingClientStateListener {
+
+            override fun onBillingSetupFinished(billingResult: BillingResult) {
+                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
+                    Log.d(tag, "onBillingSetupFinished")
+                    Log.d(tag, "BILLING | startConnection | RESULT OK")
+                    onLoadProductsClicked()
+                } else {
+                    Log.d(tag, "BILLING | startConnection | RESULT: ${billingResult.responseCode}")
+                }
+            }
+
+            override fun onBillingServiceDisconnected() {
+                Log.d(tag, "BILLING | onBillingServiceDisconnected | DISCONNECTED")
+            }
+
+        })
+    }
+
+    private fun onLoadProductsClicked() {
+//        val skuList = listOf("com.fastestvpn.vpn.one.month", "com.fastestvpn.vpn.one.year")
+        val skuList: ArrayList<String> = ArrayList<String>()
+
+        val identifierProduct = prefHelper.getFeaturesData()
+
+        identifierProduct.forEachIndexed { index, productFeatures ->
+            productFeatures.identifier?.let { skuList.add(it) }
+        }
+
+        skuList.forEachIndexed { index, s ->
+            Log.d("skulist identidiers", s)
+        }
+
+        val skuDetailsListsSubs: MutableList<SkuDetails> = ArrayList<SkuDetails>()
+        val params = SkuDetailsParams.newBuilder()
+        params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
+
+        billingClient.querySkuDetailsAsync(params.build())
+        { billingResult, skuDetailsList ->
+            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK
+                && skuDetailsList != null) {
+                for (skuDetails in skuDetailsList) {
+                    skuDetailsListsSubs.add(skuDetails)
+                }
+
+                params.setType(BillingClient.SkuType.SUBS)
+                billingClient.querySkuDetailsAsync(params.build())
+                { billingResults, skuDetailsLists ->
+                    if (billingResults.responseCode == BillingClient.BillingResponseCode.OK
+                        && skuDetailsLists != null) {
+                        for (skuDetails in skuDetailsLists) {
+                            skuDetailsListsSubs.add(skuDetails)
+                        }
+//                        initProductList(skuDetailsListsSubs)
+                    }
+                }
+            }
+        }
+    }
+
+//    private fun initProductList(skuDetailsList: List<SkuDetails>) {
+//
+//        this.skuDetailsList = skuDetailsList
+//        if (this.skuDetailsList?.size!! > 0) {
+//
+//            var boolValue: Boolean
+//            var freePeriod: String
+//            var title: String
+//            var selectFeatureListSorted = ArrayList<ProductFeatures>()
+//
+//            for ((index, value) in skuDetailsList.withIndex()){
+//                for ((indexed, values) in featuresList.withIndex()){
+//
+//                    if (value.sku == values.identifier)
+//                    {
+//                        Log.d("value of sku", "Equal => $index + $indexed")
+//                        featuresListSorted.add(featuresList.get(indexed))
+//                    }
+//                }
+//            }
+//
+//            featuresListSorted.forEachIndexed { index, productFeatures ->
+//                Log.d("featuresListSorted", "${productFeatures.title} + ${productFeatures.identifier} + ${productFeatures.price}")
+//            }
+//
+//            for ((index, value) in skuDetailsList.withIndex()){
+//
+//                Log.d("value of 1", value.title + " ," + value.freeTrialPeriod + "=>" + featuresList.get(index).identifier)
+//
+//                if (skuDetailsList.size > 1)
+//                {
+//                    boolValue = index == 1
+//                }
+//                else{
+//                    boolValue = index == 0
+//                }
+//
+//                if (value.freeTrialPeriod == "P3D")
+//                {
+//                    freePeriod = "Free for 3 days"
+//                } else if (value.freeTrialPeriod == "P1W")
+//                {
+//                    freePeriod = "Free for 7 days"
+//                }
+//                else
+//                {
+//                    freePeriod = ""
+//                }
+//
+//
+//                selectPriceList.add(
+//                    UpgradePriceList(value.title, value.description, value.price, freePeriod,
+//                    boolValue, index)
+//                )
+//            }
+//
+//            selectPriceList.forEachIndexed { index, upgradePriceList ->
+//
+//                if (upgradePriceList.isEnabled) {
+//                    selectedPosition = index
+//                }
+//            }
+//
+//            requireActivity().runOnUiThread(Runnable {
+//                if (isVisible) mainActivity.hideLoading()
+//
+//                upgradeListAdapter.data.clear()
+//                if (featuresList.size > 1)
+//                {
+//                    upgradeListAdapter.setNewData(featuresListSorted.get(1).features)
+//                }
+//                else if (featuresList.size > 0)
+//                {
+//                    upgradeListAdapter.setNewData(featuresListSorted.get(0).features)
+//                }
+//
+//                selectPriceListAdapter.data.clear()
+//                selectPriceListAdapter.setNewData(selectPriceList)
+//                selectPriceListAdapter.notifyDataSetChanged()
+//                mDialogBoxes?.hideProgress()
+//
+//            })
+//        }
+//    }
+
+
+    override fun onPurchasesUpdated(p0: BillingResult, p1: MutableList<Purchase>?) {
+        TODO("Not yet implemented")
+    }
+
+
+}

+ 45 - 8
app/src/main/java/com/vpn/fastestvpnservice/viewmodels/SearchListViewModel.kt

@@ -3,6 +3,7 @@ package com.vpn.fastestvpnservice.viewmodels
 import android.content.Context
 import android.util.Log
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.platform.LocalContext
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
@@ -16,6 +17,11 @@ import com.vpn.fastestvpnservice.beans.ServerResponse
 import com.vpn.fastestvpnservice.helpers.BasePreferenceHelper
 import com.vpn.fastestvpnservice.retrofit.RetrofitNetworkHandling
 import com.vpn.fastestvpnservice.retrofit.WebServiceFactory
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 import retrofit2.Call
 
 class SearchListViewModel constructor(
@@ -46,17 +52,40 @@ class SearchListViewModel constructor(
     private var _countriesList = MutableLiveData<ArrayList<Server>>(getCountries())
     var countriesList: LiveData<ArrayList<Server>> = _countriesList
 
-    fun searchTextChange(text: String) {
+     suspend fun searchTextChange(text: String) {
+//        Log.d("test_search_logic", "B:=> ${_searchText.value} & $text")
         _searchText.value = text
 
-        _countriesList.value = getCountries()
+//        Log.d("test_search_logic", "A:=> ${_searchText.value} & $text")
+
+//         delay(100)
+
+         if (_searchText.value == text) {
+            Log.d("test_search_logic", "Same:: => ${_searchText.value} & $text")
+            _countriesList.value = getCountries()
+        }
     }
 
-    fun isActiveChange(state: Boolean) {
+    suspend fun isActiveChange(state: Boolean) {
         _isActive.value = state
 
         if (_isActive.value == false) {
             searchTextChange("")
+//            _searchText.value = ""
+//            _countriesList.value = getCountries()
+        }
+    }
+
+    private var debounceJob: Job? = null
+
+    fun debounceSearch(text: String, scope: CoroutineScope = CoroutineScope(IO)) {
+        debounceJob?.cancel() // Cancel any previous debounce job
+        debounceJob = scope.launch {
+            delay(500) // Adjust delay as needed
+            if (_searchText.value == text) {
+                Log.d("test_search_logic", "Same:: => ${_searchText.value} & $text")
+                getCountries()
+            }
         }
     }
 
@@ -65,6 +94,8 @@ class SearchListViewModel constructor(
         val allServers: ArrayList<Server> = ArrayList<Server>()
         val serversArray: ArrayList<Server> = ArrayList<Server>()
         var filtersArray: ArrayList<Server> = ArrayList<Server>()
+        var emptyArray: ArrayList<Server> = ArrayList<Server>()
+
 
         serversData.forEachIndexed { index, serverData ->
             serverData.servers?.forEachIndexed { indexes, server ->
@@ -83,14 +114,20 @@ class SearchListViewModel constructor(
             }
         }
 
-        filtersArray = if (serversArray.size > 0) {
-            serversArray
-        } else {
+        filtersArray = if (_searchText.value == ""){
             searchServersList
+        } else {
+            if (serversArray.size > 0) {
+                serversArray
+            } else {
+                emptyArray
+            }
         }
-        Log.d("test_search", searchServersList.size.toString())
-        return filtersArray
 
+
+
+        Log.d("test_search", searchServersList.size.toString())
+            return filtersArray
     }
 
     fun updateFavUnFavServerState(server: Server) {

+ 62 - 3
app/src/main/java/com/vpn/fastestvpnservice/viewmodels/ServerListViewModel.kt

@@ -1,13 +1,19 @@
 package com.vpn.fastestvpnservice.viewmodels
 
 import android.content.Context
+import android.os.Looper
 import android.util.Log
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import com.google.gson.Gson
 import com.google.gson.reflect.TypeToken
+import com.stealthcopter.networktools.Ping
+import com.stealthcopter.networktools.ping.PingResult
+import com.stealthcopter.networktools.ping.PingStats
 import com.vpn.fastestvpnservice.beans.Server
 import com.vpn.fastestvpnservice.beans.ServerData
 import com.vpn.fastestvpnservice.beans.ServerResponse
@@ -25,11 +31,18 @@ class ServerListViewModel(context: Context): ViewModel() {
     val mutableLiveDataFavourite = MutableLiveData<List<Server>>()
     val mutableLiveDataFavUnFav = MutableLiveData<ServerResponse?>()
 
-    var mutableLiveDataCountrySize = MutableLiveData<Int>()
+    var _mutableLiveDataPing = MutableLiveData<Int?>(0)
+    var liveDataPing: LiveData<Int?> = _mutableLiveDataPing
+
+    var _mutableLiveDataPagerIndex = MutableLiveData<Int>(0)
+    var liveDataPagerIndex: LiveData<Int> = _mutableLiveDataPagerIndex
 
     var _mutableLiveDataGetFavList = MutableLiveData<ArrayList<Server>>(getFavList())
     var liveDataGetFavList: LiveData<ArrayList<Server>> = _mutableLiveDataGetFavList
 
+    var _mutableLiveDataGetServers = MutableLiveData<ArrayList<Server>>()
+    var liveDataGetServers: LiveData<ArrayList<Server>> = _mutableLiveDataGetServers
+
     var _mutableLiveDataGetServerData = MutableLiveData<ArrayList<ServerData>>(getServerData())
     var liveDataGetServerData: LiveData<ArrayList<ServerData>> = _mutableLiveDataGetServerData
 
@@ -37,9 +50,25 @@ class ServerListViewModel(context: Context): ViewModel() {
 //        preferencesHelper = BasePreferenceHelper(context)
 //    }
 
+    fun setPagerIndex(index: Int) {
+        _mutableLiveDataPagerIndex.value = index
+    }
+
+    fun calculatePing(server: Server, onPingResult: (Int) -> Unit) {
+        Ping.onAddress(server.ip as String).setTimeOutMillis(1000).doPing(
+            object : Ping.PingListener{
+                override fun onResult(pingResult: PingResult?) {
+                    android.os.Handler(Looper.getMainLooper()).post {
+                        val ping = pingResult?.timeTaken?.toInt()!!
+                        onPingResult(ping)
+                        Log.d("test_ping", "ping[0] = $ping")
+                    }
+                }
 
-    fun setCountrySize(server: Server) {
-        mutableLiveDataCountrySize.value = server.totalServers
+                override fun onError(e: java.lang.Exception?) {}
+                override fun onFinished(pingStats: PingStats?) {}
+            }
+        )
     }
 
     fun filterServersByISO(serverlist: ArrayList<Server>): ArrayList<Server> {
@@ -92,6 +121,13 @@ class ServerListViewModel(context: Context): ViewModel() {
 
                     distinctBy.get(index).countryServers = entry.value
 
+                    value.countryServers?.forEachIndexed { indexs1, serverInfo ->
+                        calculatePing(serverInfo) {
+                            serverInfo.ping = it
+                            Log.d("test_return_ping","ISO=> " + serverInfo.server_name + serverInfo.ping)
+                        }
+                    }
+
                     if (total_enables >= 1) {
                         distinctBy.get(index).enableServers = 1
                     } else {
@@ -193,6 +229,12 @@ class ServerListViewModel(context: Context): ViewModel() {
         data.sortBy {
             it.server_name
         }
+
+        data.forEachIndexed { index, server ->
+            calculatePing(server) {
+                server.ping = it
+            }
+        }
 //        serverProtocol.groupBy(Server::country).mapValues { entry ->
 //            for ((index, value) in distinctBy.withIndex()) {
 //                if (entry.key == value.country) {
@@ -258,6 +300,17 @@ class ServerListViewModel(context: Context): ViewModel() {
 
                     distinctBy.get(index).countryServers = entry.value
 
+//                    calculatePing(value) {
+//                        distinctBy.get(index).ping = it
+//                        Log.d("test_return_ping", "value => ${value.server_name} ${value.ping}")
+//                    }
+
+                    value.countryServers?.forEachIndexed { indexs1, serverInfo ->
+                        calculatePing(serverInfo) {
+                            serverInfo.ping = it
+                            Log.d("test_return_ping", "SS=> " +serverInfo.server_name + serverInfo.ping)
+                        }
+                    }
                 }
             }
         }
@@ -412,6 +465,12 @@ class ServerListViewModel(context: Context): ViewModel() {
             }
         }
 
+        tempList.forEachIndexed { index, server ->
+            calculatePing(server) {
+                server.ping = it
+            }
+        }
+
         Log.d("test_server favList", "size of favlist = ${tempList.size}")
         return tempList
     }

+ 19 - 0
app/src/main/res/layout/fragment_blank.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".fragments.BlankFragment">
+
+    <!-- TODO: Update blank fragment layout -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/hello_blank_fragment" />
+
+    <androidx.compose.ui.platform.ComposeView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        />
+
+</FrameLayout>

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -1,4 +1,6 @@
 <resources>
     <string name="app_name">FastestVPN</string>
     <string name="chk_internet_connectivity">Please check your internet connection</string>
+    <!-- TODO: Remove or change this placeholder text -->
+    <string name="hello_blank_fragment">Hello blank fragment</string>
 </resources>