@@ -35,6 +35,8 @@ class MainRecyclerAdapter(
3535 }
3636
3737 private var data: MutableList <ServersCache > = mutableListOf ()
38+
39+ private var isRunningObserver: androidx.lifecycle.Observer <Boolean >? = null
3840
3941 @SuppressLint(" NotifyDataSetChanged" )
4042 fun setData (newData : MutableList <ServersCache >? , position : Int = -1) {
@@ -47,6 +49,28 @@ class MainRecyclerAdapter(
4749 }
4850 }
4951
52+ override fun onAttachedToRecyclerView (recyclerView : RecyclerView ) {
53+ super .onAttachedToRecyclerView(recyclerView)
54+ val lifecycleOwner = recyclerView.context as ? androidx.lifecycle.LifecycleOwner
55+ if (lifecycleOwner != null ) {
56+ isRunningObserver = androidx.lifecycle.Observer { _ ->
57+ val selectedGuid = MmkvManager .getSelectServer()
58+ val position = data.indexOfFirst { it.guid == selectedGuid }
59+ if (position >= 0 ) {
60+ notifyItemChanged(position)
61+ }
62+ }
63+ mainViewModel.isRunning.observe(lifecycleOwner, isRunningObserver!! )
64+ }
65+ }
66+
67+ override fun onDetachedFromRecyclerView (recyclerView : RecyclerView ) {
68+ super .onDetachedFromRecyclerView(recyclerView)
69+ isRunningObserver?.let {
70+ mainViewModel.isRunning.removeObserver(it)
71+ }
72+ }
73+
5074 override fun getItemCount () = data.size + 1
5175
5276 override fun onBindViewHolder (holder : BaseViewHolder , position : Int ) {
@@ -62,7 +86,7 @@ class MainRecyclerAdapter(
6286 holder.itemMainBinding.tvStatistics.text = getAddress(profile)
6387 holder.itemMainBinding.tvType.text = getProtocolName(profile)
6488
65- // Network & security icon+text di bawah alamat
89+ // Network & security icon+text (TCP Fix)
6690 val isNetSecEnabled = MmkvManager .decodeSettingsBool(AppConfig .PREF_NETWORK_SECURITY_ENABLED ) == true
6791 bindNetworkSecurity(holder, profile, isNetSecEnabled)
6892
@@ -85,8 +109,32 @@ class MainRecyclerAdapter(
85109 holder.itemMainBinding.tvTraffic.visibility = View .GONE
86110 }
87111
88- // layoutIndicator & Card Background (Transparent when selected)
89- if (guid == MmkvManager .getSelectServer()) {
112+ val isSelectedServer = (guid == MmkvManager .getSelectServer())
113+ val isVpnConnected = mainViewModel.isRunning.value == true
114+
115+ if (isSelectedServer && isVpnConnected) {
116+ holder.itemMainBinding.vStatusDot.setBackgroundResource(R .drawable.blink_color)
117+ val blinkAnimDrawable = holder.itemMainBinding.vStatusDot.background
118+
119+ if (blinkAnimDrawable is android.graphics.drawable.AnimationDrawable ) {
120+ holder.itemMainBinding.vStatusDot.visibility = View .VISIBLE
121+ holder.itemMainBinding.vStatusDot.post {
122+ if (! blinkAnimDrawable.isRunning) {
123+ blinkAnimDrawable.start()
124+ }
125+ }
126+ }
127+ } else {
128+ val blinkAnimDrawable = holder.itemMainBinding.vStatusDot.background
129+ if (blinkAnimDrawable is android.graphics.drawable.AnimationDrawable ) {
130+ blinkAnimDrawable.stop()
131+ }
132+ holder.itemMainBinding.vStatusDot.visibility = View .GONE
133+ holder.itemMainBinding.vStatusDot.background = null
134+ }
135+
136+ // layoutIndicator & Card Background
137+ if (isSelectedServer) {
90138 val styleName = MmkvManager .decodeSettingsString(
91139 AppConfig .PREF_INDICATOR_STYLE ,
92140 IndicatorStyle .STYLE_0 .name
@@ -158,11 +206,10 @@ class MainRecyclerAdapter(
158206 enabled : Boolean
159207 ) {
160208 val context = holder.itemView.context
161-
162209 val iconSize = (14 * context.resources.displayMetrics.density).toInt()
163210
164211 fun makeIcon (drawableRes : Int ): android.graphics.drawable.Drawable ? {
165- val d = androidx.core.content. ContextCompat .getDrawable(context, drawableRes) ? : return null
212+ val d = ContextCompat .getDrawable(context, drawableRes) ? : return null
166213 val wrapped = androidx.core.graphics.drawable.DrawableCompat .wrap(d.mutate())
167214 androidx.core.graphics.drawable.DrawableCompat .setTint(
168215 wrapped,
@@ -176,7 +223,7 @@ class MainRecyclerAdapter(
176223 }
177224
178225 val isComplex = profile.configType.isComplexType()
179- val network = profile.network?.takeIf { it.isNotBlank() && ! it.equals( " tcp " , ignoreCase = true ) }
226+ val network = profile.network?.takeIf { it.isNotBlank() }
180227
181228 val security = profile.security?.takeIf { it.isNotBlank() }?.let { sec ->
182229 if (profile.insecure == true && sec.equals(" tls" , ignoreCase = true )) {
@@ -190,7 +237,6 @@ class MainRecyclerAdapter(
190237 holder.itemMainBinding.layoutNetworkSecurity.visibility =
191238 if (showAny) View .VISIBLE else View .GONE
192239
193- // tv_network
194240 if (enabled && ! isComplex && network != null ) {
195241 holder.itemMainBinding.tvNetwork.text = network
196242 holder.itemMainBinding.tvNetwork.setCompoundDrawables(makeIcon(R .drawable.ic_thumb_up_outline), null , null , null )
@@ -199,16 +245,9 @@ class MainRecyclerAdapter(
199245 holder.itemMainBinding.tvNetwork.visibility = View .GONE
200246 }
201247
202- // tv_security
203248 if (enabled && ! isComplex && security != null ) {
204249 holder.itemMainBinding.tvSecurity.text = security
205-
206- val iconRes = if (security == " none" ) {
207- R .drawable.ic_unlock_24dp
208- } else {
209- R .drawable.ic_lock_24dp
210- }
211-
250+ val iconRes = if (security == " none" ) R .drawable.ic_unlock_24dp else R .drawable.ic_lock_24dp
212251 holder.itemMainBinding.tvSecurity.setCompoundDrawables(makeIcon(iconRes), null , null , null )
213252 holder.itemMainBinding.tvSecurity.visibility = View .VISIBLE
214253 } else {
@@ -234,18 +273,13 @@ class MainRecyclerAdapter(
234273 return when (viewType) {
235274 VIEW_TYPE_ITEM ->
236275 MainViewHolder (ItemRecyclerMainBinding .inflate(LayoutInflater .from(parent.context), parent, false ))
237-
238276 else ->
239277 FooterViewHolder (ItemRecyclerFooterBinding .inflate(LayoutInflater .from(parent.context), parent, false ))
240278 }
241279 }
242280
243281 override fun getItemViewType (position : Int ): Int {
244- return if (position == data.size) {
245- VIEW_TYPE_FOOTER
246- } else {
247- VIEW_TYPE_ITEM
248- }
282+ return if (position == data.size) VIEW_TYPE_FOOTER else VIEW_TYPE_ITEM
249283 }
250284
251285 open class BaseViewHolder (itemView : View ) : RecyclerView.ViewHolder(itemView), ItemTouchHelperViewHolder {
@@ -255,14 +289,12 @@ class MainRecyclerAdapter(
255289
256290 class MainViewHolder (val itemMainBinding : ItemRecyclerMainBinding ) :
257291 BaseViewHolder (itemMainBinding.root) {
258-
259292 override fun onItemSelected () {
260293 val context = itemView.context
261294 val typedValue = TypedValue ()
262295 context.theme.resolveAttribute(com.google.android.material.R .attr.colorSurfaceVariant, typedValue, true )
263296 itemMainBinding.layoutCard.setCardBackgroundColor(typedValue.data)
264297 }
265-
266298 override fun onItemClear () {
267299 val context = itemView.context
268300 val typedValue = TypedValue ()
@@ -283,10 +315,6 @@ class MainRecyclerAdapter(
283315 return true
284316 }
285317
286- override fun onItemMoveCompleted () {
287- // do nothing
288- }
289-
290- override fun onItemDismiss (position : Int ) {
291- }
318+ override fun onItemMoveCompleted () {}
319+ override fun onItemDismiss (position : Int ) {}
292320}
0 commit comments