1616package com.celzero.bravedns.adapter
1717
1818import Logger
19+ import Logger.LOG_TAG_DNS
1920import Logger.LOG_TAG_UI
2021import android.content.Context
22+ import android.graphics.drawable.Drawable
2123import android.os.Bundle
2224import android.view.LayoutInflater
2325import android.view.View
2426import android.view.ViewGroup
27+ import android.widget.ImageView
2528import android.widget.Toast
2629import androidx.appcompat.app.AppCompatActivity
2730import androidx.lifecycle.LifecycleOwner
2831import androidx.paging.PagingDataAdapter
2932import androidx.recyclerview.widget.DiffUtil
3033import androidx.recyclerview.widget.RecyclerView
34+ import com.bumptech.glide.Glide
35+ import com.bumptech.glide.RequestBuilder
36+ import com.bumptech.glide.load.engine.DiskCacheStrategy
37+ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
38+ import com.bumptech.glide.request.target.CustomViewTarget
39+ import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
40+ import com.bumptech.glide.request.transition.Transition
3141import com.celzero.bravedns.R
3242import com.celzero.bravedns.data.AppConnection
3343import com.celzero.bravedns.databinding.ListItemAppDomainDetailsBinding
44+ import com.celzero.bravedns.glide.FavIconDownloader
3445import com.celzero.bravedns.service.DomainRulesManager
3546import com.celzero.bravedns.service.VpnController
3647import com.celzero.bravedns.ui.bottomsheet.AppDomainRulesBottomSheet
@@ -137,14 +148,16 @@ class AppWiseDomainsAdapter(
137148 } else {
138149 b.acdIpAddress.text = conn.appOrDnsName
139150 }
140- b.acdFlag.visibility = View .VISIBLE
141- b.acdFlag.text = conn.flag
151+ // b.acdFlag.visibility = View.VISIBLE
152+ // b.acdFlag.text = conn.flag
153+ showFlagOrIcon(conn.appOrDnsName, conn.flag)
142154 return
143155 }
144156
145157 b.acdCount.text = conn.count.toString()
146158 b.acdDomain.text = conn.appOrDnsName
147- b.acdFlag.text = conn.flag
159+ // b.acdFlag.text = conn.flag
160+ showFlagOrIcon(conn.appOrDnsName, conn.flag)
148161 if (conn.ipAddress.isNotEmpty()) {
149162 b.acdIpAddress.visibility = View .VISIBLE
150163 b.acdIpAddress.text = beautifyIpString(conn.ipAddress)
@@ -154,6 +167,139 @@ class AppWiseDomainsAdapter(
154167 updateStatusUi(conn)
155168 }
156169
170+ private fun showFlagOrIcon (query : String? , flag : String? = null) {
171+ b.acdFlag.text = flag
172+ if (query == null ) {
173+ hideFavIcon()
174+ showFlag()
175+ return
176+ }
177+ val query = query.dropLastWhile { it == ' ,' }
178+
179+ // no need to check in glide cache if the value is available in failed
180+ // cache
181+ if (FavIconDownloader .isUrlAvailableInFailedCache(query) != null ) {
182+ hideFavIcon()
183+ showFlag()
184+ } else {
185+ // Glide will cache the icons against the urls. To extract the fav
186+ // icon from the cache, first verify that the cache is available with
187+ // the next dns url. If it is not available then glide will throw an
188+ // error, do the duckduckgo url check in that case.
189+ displayNextDnsFavIcon(query)
190+ }
191+ }
192+
193+ private fun displayNextDnsFavIcon (query : String ) {
194+ // url to check if the icon is cached from nextdns
195+ val nextDnsUrl = FavIconDownloader .constructFavIcoUrlNextDns(query)
196+ // url to check if the icon is cached from duckduckgo
197+ val duckDuckGoUrl = FavIconDownloader .constructFavUrlDuckDuckGo(query)
198+ // subdomain to check if the icon is cached from duckduckgo
199+ val duckduckgoDomainURL = FavIconDownloader .getDomainUrlFromFdqnDuckduckgo(query)
200+ try {
201+ val factory = DrawableCrossFadeFactory .Builder ().setCrossFadeEnabled(true ).build()
202+ var request = Glide .with (context.applicationContext)
203+ .load(nextDnsUrl)
204+ .onlyRetrieveFromCache(true )
205+ .diskCacheStrategy(DiskCacheStrategy .AUTOMATIC )
206+ .transition(DrawableTransitionOptions .withCrossFade(factory))
207+
208+ val errorRequest = displayDuckduckgoFavIcon(duckDuckGoUrl, duckduckgoDomainURL)
209+ if (errorRequest != null ) {
210+ request = request.error(errorRequest)
211+ }
212+
213+ request.into(
214+ object : CustomViewTarget <ImageView , Drawable >(b.acdIcon) {
215+ override fun onLoadFailed (errorDrawable : Drawable ? ) {
216+ showFlag()
217+ hideFavIcon()
218+ }
219+
220+ override fun onResourceReady (
221+ resource : Drawable ,
222+ transition : Transition <in Drawable >?
223+ ) {
224+ hideFlag()
225+ showFavIcon(resource)
226+ }
227+
228+ override fun onResourceCleared (placeholder : Drawable ? ) {
229+ hideFavIcon()
230+ showFlag()
231+ }
232+ }
233+ )
234+ } catch (_: Exception ) {
235+ Logger .d(LOG_TAG_DNS , " err loading icon, load flag instead" )
236+ displayDuckduckgoFavIcon(duckDuckGoUrl, duckduckgoDomainURL)?.into(
237+ object : CustomViewTarget <ImageView , Drawable >(b.acdIcon) {
238+ override fun onLoadFailed (errorDrawable : Drawable ? ) {
239+ showFlag()
240+ hideFavIcon()
241+ }
242+
243+ override fun onResourceReady (
244+ resource : Drawable ,
245+ transition : Transition <in Drawable >?
246+ ) {
247+ hideFlag()
248+ showFavIcon(resource)
249+ }
250+
251+ override fun onResourceCleared (placeholder : Drawable ? ) {
252+ hideFavIcon()
253+ showFlag()
254+ }
255+ }
256+ )
257+ }
258+ }
259+
260+ /* *
261+ * Loads the fav icons from the cache, the icons are cached by favIconDownloader. On
262+ * failure, will check if there is a icon for top level domain is available in cache. Else,
263+ * will show the Flag.
264+ *
265+ * This method will be executed only when show fav icon setting is turned on.
266+ */
267+ private fun displayDuckduckgoFavIcon (url : String , subDomainURL : String ): RequestBuilder <Drawable >? {
268+ return try {
269+ val factory = DrawableCrossFadeFactory .Builder ().setCrossFadeEnabled(true ).build()
270+ Glide .with (context.applicationContext)
271+ .load(url)
272+ .onlyRetrieveFromCache(true )
273+ .diskCacheStrategy(DiskCacheStrategy .AUTOMATIC )
274+ .error(
275+ Glide .with (context.applicationContext)
276+ .load(subDomainURL)
277+ .onlyRetrieveFromCache(true )
278+ )
279+ .transition(DrawableTransitionOptions .withCrossFade(factory))
280+ } catch (e: Exception ) {
281+ null
282+ }
283+ }
284+
285+ private fun showFavIcon (drawable : Drawable ) {
286+ b.acdIcon.visibility = View .VISIBLE
287+ b.acdIcon.setImageDrawable(drawable)
288+ }
289+
290+ private fun hideFavIcon () {
291+ b.acdIcon.visibility = View .GONE
292+ b.acdIcon.setImageDrawable(null )
293+ }
294+
295+ private fun showFlag () {
296+ b.acdFlag.visibility = View .VISIBLE
297+ }
298+
299+ private fun hideFlag () {
300+ b.acdFlag.visibility = View .GONE
301+ }
302+
157303 private fun setupClickListeners (conn : AppConnection ) {
158304 b.acdContainer.setOnClickListener {
159305 if (isActiveConn) {
0 commit comments