Compare commits
	
		
			21 Commits
		
	
	
		
			v125010201
			...
			v125020581
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a90ccec707 | |||
|  | 2564b19726 | ||
| 61c7bb20cc | |||
| 6a0f5baf0a | |||
| 39f9505c00 | |||
| 6a6d447456 | |||
|  | 0bb4fe6aed | ||
| 7df4c3368c | |||
| c69635b5ae | |||
| 3a829df70e | |||
| 7a0202689f | |||
| b20f6888f5 | |||
| 6b96eb358d | |||
| dfc1bf9fa3 | |||
|  | b173664ff0 | ||
| bc20a421ae | |||
| 794500355a | |||
| 44f9dd53d3 | |||
| 717d6b664c | |||
| e23289a3dc | |||
|  | 2f5ebe2420 | 
| @@ -26,9 +26,10 @@ jobs: | ||||
|       - uses: KengoTODA/actions-setup-docker-compose@v1 | ||||
|         with: | ||||
|           version: "2.23.3" | ||||
|       - name: run selfoss | ||||
|         run: | | ||||
|           docker compose -f .gitea/workflows/assets/docker-compose.yml up -d | ||||
|       #      TESTS ARE RUN LOCALLY | ||||
|       #      - name: run selfoss | ||||
|       #        run: | | ||||
|       #          docker compose -f .gitea/workflows/assets/docker-compose.yml up -d | ||||
|       - name: coverage | ||||
|         run: | | ||||
|           ./gradlew :koverHtmlReport | ||||
| @@ -39,7 +40,8 @@ jobs: | ||||
|           retention-days: 1 | ||||
|           overwrite: true | ||||
|           include-hidden-files: true | ||||
|       - name: Clean | ||||
|         if: always() | ||||
|         run: | | ||||
|           docker compose -f .gitea/workflows/assets/docker-compose.yml stop | ||||
| #      TESTS ARE RUN LOCALLY | ||||
| #      - name: Clean | ||||
| #        if: always() | ||||
| #        run: | | ||||
| #          docker compose -f .gitea/workflows/assets/docker-compose.yml stop | ||||
|   | ||||
| @@ -16,6 +16,7 @@ jobs: | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           ref: master | ||||
|       - name: Config git | ||||
|         run: | | ||||
|           git config --global user.email aminecmi+giteadrone@pm.me | ||||
| @@ -50,7 +51,7 @@ jobs: | ||||
|           followtags: true | ||||
|           ssh_key: ${{ secrets.PRIVATE_KEY }} | ||||
|           tags: true | ||||
|           branch: release | ||||
|           branch: master | ||||
|       - name: copy file via ssh password | ||||
|         uses: appleboy/scp-action@v0.1.7 | ||||
|         with: | ||||
| @@ -124,4 +125,4 @@ jobs: | ||||
|           priority: high | ||||
|           convert_markdown: true | ||||
|           body: Nouveau fichier de mapping pour la version ${{ steps.version.outputs.VERSION }} | ||||
|           attachments: androidApp/build/outputs/mapping/githubConfigRelease/mapping.txt | ||||
|           attachments: androidApp/build/outputs/mapping/githubConfigRelease/mapping.txt | ||||
|   | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -323,4 +323,6 @@ fabric.properties | ||||
| crowdin.properties | ||||
|  | ||||
| .kotlin/ | ||||
| build-cache/ | ||||
| build-cache/ | ||||
|  | ||||
| act | ||||
|   | ||||
							
								
								
									
										40
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,43 @@ | ||||
| **v125020471 | ||||
|  | ||||
| - chore: no more docker-compose. | ||||
| - bump: gradle plugin. | ||||
| - Merge pull request 'fix: check index exists.' (#183) from fix-index into master | ||||
| - fix: check index exists. | ||||
| - Changelog for v125020411 | ||||
|  | ||||
| -------------------------------------------------------------------- | ||||
|  | ||||
| **v125020411 | ||||
|  | ||||
| - Merge pull request 'bump' (#182) from bump into master | ||||
| - chore: non transiant R classes. | ||||
| - Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master | ||||
| - bump | ||||
| - fix: One more missing context. | ||||
|  | ||||
| -------------------------------------------------------------------- | ||||
|  | ||||
| **v125010241 | ||||
|  | ||||
| - Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master | ||||
| - refactor: context fragments issues. | ||||
| - logs: Context issues. | ||||
| - fix: Handle empty url issue, again. | ||||
| - fix: Link not opening. | ||||
| - Changelog for v125010201 | ||||
|  | ||||
| -------------------------------------------------------------------- | ||||
|  | ||||
| **v125010201 | ||||
|  | ||||
| - fix: Handle empty url issue. | ||||
| - Merge pull request 'Removed the floating bar.' (#177) from floating-bar into master | ||||
| - chore: changing actions in reader fragment. | ||||
| - Changelog for v125010131 | ||||
|  | ||||
| -------------------------------------------------------------------- | ||||
|  | ||||
| **v125010131 | ||||
|  | ||||
| - fix: reload the adapter when it's needed. Fixes #128. (#176) | ||||
|   | ||||
| @@ -56,7 +56,7 @@ class HomeActivityTest { | ||||
|     fun testMenuActions() { | ||||
|         onView(withId(R.id.action_search)).perform(click()) | ||||
|         onView( | ||||
|             withId(R.id.search_src_text), | ||||
|             withId(com.google.android.material.R.id.search_src_text), | ||||
|         ).check(matches(isFocused())) | ||||
|         onView(isRoot()).perform(ViewActions.pressBack()) | ||||
|  | ||||
|   | ||||
| @@ -31,7 +31,7 @@ import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity | ||||
| import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowserAsNewTask | ||||
| import bou.amine.apps.readerforselfossv2.model.SelfossModel | ||||
| import bou.amine.apps.readerforselfossv2.repository.Repository | ||||
| import bou.amine.apps.readerforselfossv2.service.AppSettingsService | ||||
| @@ -599,7 +599,7 @@ class HomeActivity : | ||||
|     override fun onOptionsItemSelected(item: MenuItem): Boolean { | ||||
|         when (item.itemId) { | ||||
|             R.id.issue_tracker -> { | ||||
|                 baseContext.openUrlInBrowser(AppSettingsService.BUG_URL) | ||||
|                 baseContext.openUrlInBrowserAsNewTask(AppSettingsService.BUG_URL) | ||||
|                 return true | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -161,12 +161,14 @@ class ReaderActivity : | ||||
|                     override fun onPageSelected(position: Int) { | ||||
|                         super.onPageSelected(position) | ||||
|  | ||||
|                         if (allItems[position].starred) { | ||||
|                             canRemoveFromFavorite() | ||||
|                         } else { | ||||
|                             canFavorite() | ||||
|                         if (!allItems.isNullOrEmpty() && allItems.size >= position) { | ||||
|                             if (allItems[position].starred) { | ||||
|                                 canRemoveFromFavorite() | ||||
|                             } else { | ||||
|                                 canFavorite() | ||||
|                             } | ||||
|                             readItem(allItems[position]) | ||||
|                         } | ||||
|                         readItem(allItems[position]) | ||||
|                     } | ||||
|                 }, | ||||
|             ) | ||||
|   | ||||
| @@ -33,8 +33,9 @@ import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapFitCenter | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.glide.getGlideImageForResource | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowserAsNewTask | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.shareLink | ||||
| import bou.amine.apps.readerforselfossv2.model.MercuryModel | ||||
| import bou.amine.apps.readerforselfossv2.model.SelfossModel | ||||
| @@ -74,7 +75,7 @@ class ArticleFragment : | ||||
|     private var colorSurface: Int = 0 | ||||
|     private var fontSize: Int = DEFAULT_FONT_SIZE | ||||
|     private lateinit var item: SelfossModel.Item | ||||
|     private lateinit var url: String | ||||
|     private var url: String? = null | ||||
|     private lateinit var contentText: String | ||||
|     private lateinit var contentSource: String | ||||
|     private lateinit var contentImage: String | ||||
| @@ -117,8 +118,8 @@ class ArticleFragment : | ||||
|                 e.sendSilentlyWithAcra() | ||||
|             } | ||||
|  | ||||
|             colorOnSurface = requireContext().getColorFromAttr(R.attr.colorOnSurface) | ||||
|             colorSurface = requireContext().getColorFromAttr(R.attr.colorSurface) | ||||
|             colorOnSurface = getColorFromAttr(com.google.android.material.R.attr.colorOnSurface) | ||||
|             colorSurface = getColorFromAttr(com.google.android.material.R.attr.colorSurface) | ||||
|  | ||||
|             contentText = item.content | ||||
|             contentTitle = item.title.getHtmlDecoded() | ||||
| @@ -147,11 +148,11 @@ class ArticleFragment : | ||||
|             handleContent() | ||||
|         } catch (e: InflateException) { | ||||
|             e.sendSilentlyWithAcraWithName("webview not available") | ||||
|             try { | ||||
|             maybeIfContext { | ||||
|                 AlertDialog | ||||
|                     .Builder(requireContext()) | ||||
|                     .setMessage(requireContext().getString(R.string.webview_dialog_issue_message)) | ||||
|                     .setTitle(requireContext().getString(R.string.webview_dialog_issue_title)) | ||||
|                     .Builder(it) | ||||
|                     .setMessage(it.getString(R.string.webview_dialog_issue_message)) | ||||
|                     .setTitle(it.getString(R.string.webview_dialog_issue_title)) | ||||
|                     .setPositiveButton( | ||||
|                         android.R.string.ok, | ||||
|                     ) { _, _ -> | ||||
| @@ -159,8 +160,6 @@ class ArticleFragment : | ||||
|                         requireActivity().finish() | ||||
|                     }.create() | ||||
|                     .show() | ||||
|             } catch (e: IllegalStateException) { | ||||
|                 e.sendSilentlyWithAcraWithName("Context required is null") | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -169,8 +168,8 @@ class ArticleFragment : | ||||
|  | ||||
|     private fun handleContent() { | ||||
|         if (contentText.isEmptyOrNullOrNullString()) { | ||||
|             if (repository.isNetworkAvailable()) { | ||||
|                 getContentFromMercury() | ||||
|             if (repository.isNetworkAvailable() && url.isUrlValid()) { | ||||
|                 getContentFromMercury(url!!) | ||||
|             } | ||||
|         } else { | ||||
|             binding.titleView.text = contentTitle | ||||
| @@ -182,7 +181,7 @@ class ArticleFragment : | ||||
|  | ||||
|             if (!contentImage.isEmptyOrNullOrNullString() && context != null) { | ||||
|                 binding.imageView.visibility = View.VISIBLE | ||||
|                 requireContext().bitmapFitCenter(contentImage, binding.imageView, appSettingsService) | ||||
|                 maybeIfContext { it.bitmapFitCenter(contentImage, binding.imageView, appSettingsService) } | ||||
|             } else { | ||||
|                 binding.imageView.visibility = View.GONE | ||||
|             } | ||||
| @@ -194,39 +193,39 @@ class ArticleFragment : | ||||
|         fab.mainFabClosedIconColor = colorOnSurface | ||||
|         fab.mainFabOpenedIconColor = colorOnSurface | ||||
|  | ||||
|         handleFloatingToolbarActionItems() | ||||
|         maybeIfContext { handleFloatingToolbarActionItems(it) } | ||||
|  | ||||
|         fab.setOnActionSelectedListener { actionItem -> | ||||
|             when (actionItem.id) { | ||||
|                 R.id.share_action -> requireActivity().shareLink(url, contentTitle) | ||||
|                 R.id.open_action -> requireActivity().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item) | ||||
|                 R.id.unread_action -> | ||||
|                     try { | ||||
|                         if (this@ArticleFragment.item.unread) { | ||||
|                             CoroutineScope(Dispatchers.IO).launch { | ||||
|                                 repository.markAsRead(this@ArticleFragment.item) | ||||
|                             } | ||||
|                             this@ArticleFragment.item.unread = false | ||||
|                     if (this@ArticleFragment.item.unread) { | ||||
|                         CoroutineScope(Dispatchers.IO).launch { | ||||
|                             repository.markAsRead(this@ArticleFragment.item) | ||||
|                         } | ||||
|                         this@ArticleFragment.item.unread = false | ||||
|                         maybeIfContext { | ||||
|                             Toast | ||||
|                                 .makeText( | ||||
|                                     requireContext(), | ||||
|                                     it, | ||||
|                                     R.string.marked_as_read, | ||||
|                                     Toast.LENGTH_LONG, | ||||
|                                 ).show() | ||||
|                         } else { | ||||
|                             CoroutineScope(Dispatchers.IO).launch { | ||||
|                                 repository.unmarkAsRead(this@ArticleFragment.item) | ||||
|                             } | ||||
|                             this@ArticleFragment.item.unread = true | ||||
|                         } | ||||
|                     } else { | ||||
|                         CoroutineScope(Dispatchers.IO).launch { | ||||
|                             repository.unmarkAsRead(this@ArticleFragment.item) | ||||
|                         } | ||||
|                         this@ArticleFragment.item.unread = true | ||||
|                         maybeIfContext { | ||||
|                             Toast | ||||
|                                 .makeText( | ||||
|                                     context, | ||||
|                                     it, | ||||
|                                     R.string.marked_as_unread, | ||||
|                                     Toast.LENGTH_LONG, | ||||
|                                 ).show() | ||||
|                         } | ||||
|                     } catch (e: IllegalStateException) { | ||||
|                         e.sendSilentlyWithAcraWithName("Context required is null") | ||||
|                     } | ||||
|  | ||||
|                 else -> Unit | ||||
| @@ -235,14 +234,14 @@ class ArticleFragment : | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun handleFloatingToolbarActionItems() { | ||||
|     private fun handleFloatingToolbarActionItems(c: Context) { | ||||
|         fab.addHomeMadeActionItem( | ||||
|             R.id.share_action, | ||||
|             resources.getDrawable(R.drawable.ic_share_white_24dp), | ||||
|             R.string.reader_action_share, | ||||
|             colorOnSurface, | ||||
|             colorSurface, | ||||
|             requireContext(), | ||||
|             c, | ||||
|         ) | ||||
|         fab.addHomeMadeActionItem( | ||||
|             R.id.open_action, | ||||
| @@ -250,7 +249,7 @@ class ArticleFragment : | ||||
|             R.string.reader_action_open, | ||||
|             colorOnSurface, | ||||
|             colorSurface, | ||||
|             requireContext(), | ||||
|             c, | ||||
|         ) | ||||
|         fab.addHomeMadeActionItem( | ||||
|             R.id.unread_action, | ||||
| @@ -258,7 +257,7 @@ class ArticleFragment : | ||||
|             R.string.unmark, | ||||
|             colorOnSurface, | ||||
|             colorSurface, | ||||
|             requireContext(), | ||||
|             c, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
| @@ -272,7 +271,7 @@ class ArticleFragment : | ||||
|     } | ||||
|  | ||||
|     @Suppress("detekt:SwallowedException") | ||||
|     private fun getContentFromMercury() { | ||||
|     private fun getContentFromMercury(url: String) { | ||||
|         binding.progressBar.visibility = View.VISIBLE | ||||
|  | ||||
|         CoroutineScope(Dispatchers.Main).launch { | ||||
| @@ -311,9 +310,11 @@ class ArticleFragment : | ||||
|     } | ||||
|  | ||||
|     private fun handleLeadImage(leadImageUrl: String?) { | ||||
|         if (!leadImageUrl.isNullOrEmpty() && context != null) { | ||||
|             binding.imageView.visibility = View.VISIBLE | ||||
|             requireContext().bitmapFitCenter(leadImageUrl, binding.imageView, appSettingsService) | ||||
|         if (!leadImageUrl.isNullOrEmpty()) { | ||||
|             maybeIfContext { | ||||
|                 binding.imageView.visibility = View.VISIBLE | ||||
|                 it.bitmapFitCenter(leadImageUrl, binding.imageView, appSettingsService) | ||||
|             } | ||||
|         } else { | ||||
|             binding.imageView.visibility = View.GONE | ||||
|         } | ||||
| @@ -327,11 +328,10 @@ class ArticleFragment : | ||||
|                     view: WebView?, | ||||
|                     url: String, | ||||
|                 ): Boolean = | ||||
|                     if (context != null && | ||||
|                         url.isUrlValid() && | ||||
|                     if (url.isUrlValid() && | ||||
|                         binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE | ||||
|                     ) { | ||||
|                         requireContext().openUrlInBrowser(url) | ||||
|                         maybeIfContext { it.openUrlInBrowserAsNewTask(url) } | ||||
|                         true | ||||
|                     } else { | ||||
|                         false | ||||
| @@ -374,23 +374,14 @@ class ArticleFragment : | ||||
|  | ||||
|     @Suppress("detekt:LongMethod", "detekt:ImplicitDefaultLocale") | ||||
|     private fun htmlToWebview() { | ||||
|         val context: Context | ||||
|         try { | ||||
|             context = requireContext() | ||||
|         } catch (e: IllegalStateException) { | ||||
|             e.sendSilentlyWithAcraWithName("Context required is null") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|         maybeIfContext { | ||||
|             val attrs: IntArray = intArrayOf(android.R.attr.fontFamily) | ||||
|             val a: TypedArray = context.obtainStyledAttributes(resId, attrs) | ||||
|             val a: TypedArray = it.obtainStyledAttributes(resId, attrs) | ||||
|  | ||||
|             binding.webcontent.settings.standardFontFamily = a.getString(0) | ||||
|             binding.webcontent.visibility = View.VISIBLE | ||||
|         } catch (e: IllegalStateException) { | ||||
|             e.sendSilentlyWithAcraWithName("Context issue when setting attributes, but context wasn't null before") | ||||
|             "" | ||||
|         } | ||||
|         binding.webcontent.visibility = View.VISIBLE | ||||
|  | ||||
|         val colorSurfaceString = | ||||
|             String.format( | ||||
| @@ -404,13 +395,12 @@ class ArticleFragment : | ||||
|                 WHITE_COLOR_HEX and (if (colorOnSurface != DATA_NULL_UNDEFINED) colorOnSurface else 0), | ||||
|             ) | ||||
|  | ||||
|         binding.webcontent.settings.useWideViewPort = true | ||||
|         binding.webcontent.settings.loadWithOverviewMode = true | ||||
|         binding.webcontent.settings.javaScriptEnabled = false | ||||
|  | ||||
|         handleImageLoading() | ||||
|         try { | ||||
|             binding.webcontent.settings.useWideViewPort = true | ||||
|             binding.webcontent.settings.loadWithOverviewMode = true | ||||
|             binding.webcontent.settings.javaScriptEnabled = false | ||||
|  | ||||
|             handleImageLoading() | ||||
|  | ||||
|             val gestureDetector = | ||||
|                 GestureDetector( | ||||
|                     activity, | ||||
| @@ -424,49 +414,50 @@ class ArticleFragment : | ||||
|                     event, | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|             binding.webcontent.settings.layoutAlgorithm = | ||||
|                 WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING | ||||
|         } catch (e: IllegalStateException) { | ||||
|             e.sendSilentlyWithAcraWithName("Context is null but wasn't, and that's causing issues with webview config") | ||||
|             e.sendSilentlyWithAcraWithName("Gesture detector issue ?") | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             var baseUrl: String? = null | ||||
|             try { | ||||
|                 val itemUrl = URL(url) | ||||
|                 baseUrl = itemUrl.protocol + "://" + itemUrl.host | ||||
|             } catch (e: MalformedURLException) { | ||||
|                 e.sendSilentlyWithAcraWithName("htmlToWebview > $url") | ||||
|             } | ||||
|         binding.webcontent.settings.layoutAlgorithm = | ||||
|             WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING | ||||
|  | ||||
|             val fontName = | ||||
|         var baseUrl: String? = null | ||||
|         try { | ||||
|             val itemUrl = URL(url.orEmpty()) | ||||
|             baseUrl = itemUrl.protocol + "://" + itemUrl.host | ||||
|         } catch (e: MalformedURLException) { | ||||
|             e.sendSilentlyWithAcraWithName("htmlToWebview > ${url.orEmpty()}") | ||||
|         } | ||||
|  | ||||
|         val fontName: String = | ||||
|             maybeIfContext { | ||||
|                 when (font) { | ||||
|                     getString(R.string.open_sans_font_id) -> "Open Sans" | ||||
|                     getString(R.string.roboto_font_id) -> "Roboto" | ||||
|                     getString(R.string.source_code_pro_font_id) -> "Source Code Pro" | ||||
|                     it.getString(R.string.open_sans_font_id) -> "Open Sans" | ||||
|                     it.getString(R.string.roboto_font_id) -> "Roboto" | ||||
|                     it.getString(R.string.source_code_pro_font_id) -> "Source Code Pro" | ||||
|                     else -> "" | ||||
|                 } | ||||
|             }?.toString().orEmpty() | ||||
|  | ||||
|             val fontLinkAndStyle = | ||||
|                 if (font.isNotEmpty()) { | ||||
|                     """<link href="https://fonts.googleapis.com/css?family=${ | ||||
|                         fontName.replace( | ||||
|                             " ", | ||||
|                             "+", | ||||
|                         ) | ||||
|                     }" rel="stylesheet"> | ||||
|         val fontLinkAndStyle = | ||||
|             if (fontName.isNotEmpty()) { | ||||
|                 """<link href="https://fonts.googleapis.com/css?family=${ | ||||
|                     fontName.replace( | ||||
|                         " ", | ||||
|                         "+", | ||||
|                     ) | ||||
|                 }" rel="stylesheet"> | ||||
|                 |<style> | ||||
|                 |   * { | ||||
|                 |       font-family: '$fontName'; | ||||
|                 |   } | ||||
|                 |</style> | ||||
|                     """.trimMargin() | ||||
|                 } else { | ||||
|                     "" | ||||
|                 } | ||||
|  | ||||
|                 """.trimMargin() | ||||
|             } else { | ||||
|                 "" | ||||
|             } | ||||
|         try { | ||||
|             binding.webcontent.loadDataWithBaseURL( | ||||
|                 baseUrl, | ||||
|                 """<html> | ||||
| @@ -483,7 +474,7 @@ class ArticleFragment : | ||||
|                 |        color: ${ | ||||
|                     String.format( | ||||
|                         "#%06X", | ||||
|                         WHITE_COLOR_HEX and context.resources.getColor(R.color.colorAccent), | ||||
|                         WHITE_COLOR_HEX and (maybeIfContext { it.resources.getColor(R.color.colorAccent) } as Int), | ||||
|                     ) | ||||
|                 } !important; | ||||
|                 |      } | ||||
| @@ -540,10 +531,8 @@ class ArticleFragment : | ||||
|  | ||||
|     private fun openInBrowserAfterFailing() { | ||||
|         binding.progressBar.visibility = View.GONE | ||||
|         try { | ||||
|             requireContext().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item) | ||||
|         } catch (e: IllegalStateException) { | ||||
|             e.sendSilentlyWithAcraWithName("Context required is null") | ||||
|         maybeIfContext { | ||||
|             it.openItemUrlInBrowserAsNewTask(this@ArticleFragment.item) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package bou.amine.apps.readerforselfossv2.android.fragments | ||||
|  | ||||
| import android.content.Context | ||||
| import android.graphics.Color | ||||
| import android.graphics.drawable.Drawable | ||||
| import android.graphics.drawable.GradientDrawable | ||||
| @@ -17,6 +16,7 @@ import bou.amine.apps.readerforselfossv2.android.R | ||||
| import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.glide.imageIntoViewTarget | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext | ||||
| import bou.amine.apps.readerforselfossv2.repository.Repository | ||||
| import bou.amine.apps.readerforselfossv2.service.AppSettingsService | ||||
| import bou.amine.apps.readerforselfossv2.utils.getColorHexCode | ||||
| @@ -60,8 +60,8 @@ class FilterSheetFragment : | ||||
|  | ||||
|         try { | ||||
|             CoroutineScope(Dispatchers.Main).launch { | ||||
|                 handleTagChips(requireContext()) | ||||
|                 handleSourceChips(requireContext()) | ||||
|                 handleTagChips() | ||||
|                 handleSourceChips() | ||||
|  | ||||
|                 binding.progressBar2.visibility = GONE | ||||
|                 binding.filterView.visibility = VISIBLE | ||||
| @@ -79,29 +79,39 @@ class FilterSheetFragment : | ||||
|         return binding.root | ||||
|     } | ||||
|  | ||||
|     private suspend fun handleSourceChips(context: Context) { | ||||
|     private suspend fun handleSourceChips() { | ||||
|         val sourceGroup = binding.sourcesGroup | ||||
|  | ||||
|         repository.getSourcesDetailsOrStats().forEachIndexed { _, source -> | ||||
|             val c = Chip(context) | ||||
|             val c: Chip? = | ||||
|                 maybeIfContext { | ||||
|                     Chip(it) | ||||
|                 } as Chip? | ||||
|  | ||||
|             if (c == null) { | ||||
|                 return | ||||
|             } | ||||
|  | ||||
|             c.ellipsize = TextUtils.TruncateAt.END | ||||
|  | ||||
|             context.imageIntoViewTarget( | ||||
|                 source.getIcon(repository.baseUrl), | ||||
|                 object : ViewTarget<Chip?, Drawable?>(c) { | ||||
|                     override fun onResourceReady( | ||||
|                         resource: Drawable, | ||||
|                         transition: Transition<in Drawable?>?, | ||||
|                     ) { | ||||
|                         try { | ||||
|                             c.chipIcon = resource | ||||
|                         } catch (e: Exception) { | ||||
|                             e.sendSilentlyWithAcraWithName("sources > onResourceReady") | ||||
|             maybeIfContext { | ||||
|                 it.imageIntoViewTarget( | ||||
|                     source.getIcon(repository.baseUrl), | ||||
|                     object : ViewTarget<Chip?, Drawable?>(c) { | ||||
|                         override fun onResourceReady( | ||||
|                             resource: Drawable, | ||||
|                             transition: Transition<in Drawable?>?, | ||||
|                         ) { | ||||
|                             try { | ||||
|                                 c.chipIcon = resource | ||||
|                             } catch (e: Exception) { | ||||
|                                 e.sendSilentlyWithAcraWithName("sources > onResourceReady") | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 appSettingsService, | ||||
|             ) | ||||
|                     }, | ||||
|                     appSettingsService, | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|             c.text = source.title.getHtmlDecoded() | ||||
|  | ||||
| @@ -137,13 +147,17 @@ class FilterSheetFragment : | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private suspend fun handleTagChips(context: Context) { | ||||
|     private suspend fun handleTagChips() { | ||||
|         val tagGroup = binding.tagsGroup | ||||
|  | ||||
|         val tags = repository.getTags() | ||||
|  | ||||
|         tags.forEachIndexed { _, tag -> | ||||
|             val c = Chip(context) | ||||
|             val c: Chip? = maybeIfContext { Chip(it) } as Chip? | ||||
|             if (c == null) { | ||||
|                 return | ||||
|             } | ||||
|  | ||||
|             c.ellipsize = TextUtils.TruncateAt.END | ||||
|             c.text = tag.tag | ||||
|  | ||||
|   | ||||
| @@ -5,39 +5,57 @@ import android.content.Intent | ||||
| import android.util.TypedValue | ||||
| import androidx.annotation.AttrRes | ||||
| import androidx.annotation.ColorInt | ||||
| import androidx.fragment.app.Fragment | ||||
| import bou.amine.apps.readerforselfossv2.android.R | ||||
| import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName | ||||
| import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp | ||||
|  | ||||
| fun Context.shareLink( | ||||
|     itemUrl: String, | ||||
|     itemUrl: String?, | ||||
|     itemTitle: String, | ||||
| ) { | ||||
|     val sendIntent = Intent() | ||||
|     sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | ||||
|     sendIntent.action = Intent.ACTION_SEND | ||||
|     sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp()) | ||||
|     sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle) | ||||
|     sendIntent.type = "text/plain" | ||||
|     startActivity( | ||||
|         Intent | ||||
|             .createChooser( | ||||
|                 sendIntent, | ||||
|                 getString(R.string.share), | ||||
|             ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), | ||||
|     ) | ||||
|     if (itemUrl.isUrlValid()) { | ||||
|         val sendIntent = Intent() | ||||
|         sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | ||||
|         sendIntent.action = Intent.ACTION_SEND | ||||
|         sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl!!.toStringUriWithHttp()) | ||||
|         sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle) | ||||
|         sendIntent.type = "text/plain" | ||||
|         startActivity( | ||||
|             Intent | ||||
|                 .createChooser( | ||||
|                     sendIntent, | ||||
|                     getString(R.string.share), | ||||
|                 ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ColorInt | ||||
| fun Context.getColorFromAttr( | ||||
| fun Fragment.getColorFromAttr( | ||||
|     @AttrRes attrColor: Int, | ||||
|     resolveRefs: Boolean = true, | ||||
| ): Int { | ||||
|     val typedValue = TypedValue() | ||||
|     try { | ||||
|         this.theme.resolveAttribute(attrColor, typedValue, resolveRefs) | ||||
|     } catch (e: Throwable) { | ||||
|         e.sendSilentlyWithAcraWithName("ColorFromAttr") | ||||
|     } | ||||
|     maybeIfContextWithLog { this.requireContext().theme.resolveAttribute(attrColor, typedValue, resolveRefs) } | ||||
|     return typedValue.data | ||||
| } | ||||
|  | ||||
| @Suppress("detekt:SwallowedException") | ||||
| fun Fragment.maybeIfContext(fn: (Context) -> Any): Any? { | ||||
|     try { | ||||
|         return fn(this.requireContext()) | ||||
|     } catch (e: Exception) { | ||||
|         // Do nothing | ||||
|         return null | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Fragment.maybeIfContextWithLog(fn: (Context) -> Any): Any? { | ||||
|     try { | ||||
|         return fn(this.requireContext()) | ||||
|     } catch (e: Exception) { | ||||
|         e.sendSilentlyWithAcraWithName("Fragment context issue...") | ||||
|         return null | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,12 +15,12 @@ import android.widget.Toast | ||||
| import bou.amine.apps.readerforselfossv2.android.R | ||||
| import bou.amine.apps.readerforselfossv2.android.ReaderActivity | ||||
| import bou.amine.apps.readerforselfossv2.model.SelfossModel | ||||
| import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp | ||||
| import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString | ||||
| import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||||
|  | ||||
| fun Context.openItemUrl( | ||||
|     currentItem: Int, | ||||
|     linkDecoded: String, | ||||
|     linkDecoded: String?, | ||||
|     articleViewer: Boolean, | ||||
|     app: Activity, | ||||
| ) { | ||||
| @@ -37,12 +37,13 @@ fun Context.openItemUrl( | ||||
|             intent.putExtra("currentItem", currentItem) | ||||
|             app.startActivity(intent) | ||||
|         } else { | ||||
|             this.openUrlInBrowserAsNewTask(linkDecoded) | ||||
|             this.openUrlInBrowserAsNewTask(linkDecoded!!) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() | ||||
| fun String?.isUrlValid(): Boolean = | ||||
|     !this.isEmptyOrNullOrNullString() && this!!.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() | ||||
|  | ||||
| fun String.isBaseUrlInvalid(): Boolean { | ||||
|     val baseUrl = this.toHttpUrlOrNull() | ||||
| @@ -56,14 +57,16 @@ fun String.isBaseUrlInvalid(): Boolean { | ||||
| } | ||||
|  | ||||
| fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) { | ||||
|     this.openUrlInBrowserAsNewTask(i.getLinkDecoded().toStringUriWithHttp()) | ||||
|     this.openUrlInBrowserAsNewTask(i.getLinkDecoded()) | ||||
| } | ||||
|  | ||||
| fun Context.openUrlInBrowserAsNewTask(url: String) { | ||||
|     val intent = Intent(Intent.ACTION_VIEW) | ||||
|     intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | ||||
|     intent.data = Uri.parse(url) | ||||
|     this.mayBeStartActivity(intent) | ||||
| fun Context.openUrlInBrowserAsNewTask(url: String?) { | ||||
|     if (url.isUrlValid()) { | ||||
|         val intent = Intent(Intent.ACTION_VIEW) | ||||
|         intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | ||||
|         intent.data = Uri.parse(url) | ||||
|         this.mayBeStartActivity(intent) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Context.openUrlInBrowser(url: String) { | ||||
|   | ||||
| @@ -23,10 +23,11 @@ import kotlin.io.encoding.ExperimentalEncodingApi | ||||
|  | ||||
| private const val PRELOAD_IMAGE_TIMEOUT = 10000 | ||||
|  | ||||
| @Suppress("detekt:ReturnCount") | ||||
| @OptIn(ExperimentalEncodingApi::class) | ||||
| fun String.toGlideUrl(appSettingsService: AppSettingsService): GlideUrl { | ||||
| fun String.toGlideUrl(appSettingsService: AppSettingsService): Any { // GlideUrl Or String | ||||
|     if (this.isEmptyOrNullOrNullString()) { | ||||
|         return GlideUrl("") | ||||
|         return "" | ||||
|     } | ||||
|     if (appSettingsService.getBasicUserName().isNotEmpty()) { | ||||
|         val authString = "${appSettingsService.getBasicUserName()}:${appSettingsService.getBasicPassword()}" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| plugins { | ||||
|     //trick: for the same plugin versions in all sub-modules | ||||
|     id("com.android.application").version("8.7.3").apply(false) | ||||
|     id("com.android.library").version("8.7.3").apply(false) | ||||
|     // trick: for the same plugin versions in all sub-modules | ||||
|     id("com.android.application").version("8.8.1").apply(false) | ||||
|     id("com.android.library").version("8.8.1").apply(false) | ||||
|     id("org.jetbrains.kotlin.android").version("2.1.0").apply(false) | ||||
|     kotlin("multiplatform").version("2.1.0").apply(false) | ||||
|     id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false) | ||||
| @@ -16,7 +16,6 @@ allprojects { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| tasks.register("clean", Delete::class) { | ||||
|     delete(layout.buildDirectory) | ||||
| } | ||||
| @@ -24,4 +23,4 @@ tasks.register("clean", Delete::class) { | ||||
| dependencies { | ||||
|     kover(project(":shared")) | ||||
|     kover(project(":androidApp")) | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,6 @@ | ||||
| **v125010201** | ||||
|  | ||||
| - fix: Handle empty url issue. | ||||
| - Merge pull request 'Removed the floating bar.' (#177) from floating-bar into master | ||||
| - chore: changing actions in reader fragment. | ||||
| - Changelog for v125010131 | ||||
| @@ -0,0 +1,8 @@ | ||||
| **v125010241** | ||||
|  | ||||
| - Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master | ||||
| - refactor: context fragments issues. | ||||
| - logs: Context issues. | ||||
| - fix: Handle empty url issue, again. | ||||
| - fix: Link not opening. | ||||
| - Changelog for v125010201 | ||||
| @@ -0,0 +1,7 @@ | ||||
| **v125020411** | ||||
|  | ||||
| - Merge pull request 'bump' (#182) from bump into master | ||||
| - chore: non transiant R classes. | ||||
| - Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master | ||||
| - bump | ||||
| - fix: One more missing context. | ||||
| @@ -0,0 +1,7 @@ | ||||
| **v125020471** | ||||
|  | ||||
| - chore: no more docker-compose. | ||||
| - bump: gradle plugin. | ||||
| - Merge pull request 'fix: check index exists.' (#183) from fix-index into master | ||||
| - fix: check index exists. | ||||
| - Changelog for v125020411 | ||||
| @@ -19,11 +19,11 @@ kotlin.code.style=official | ||||
| android.useAndroidX=true | ||||
| #android.nonTransitiveRClass=true | ||||
| android.enableJetifier=false | ||||
| android.nonTransitiveRClass=false | ||||
| android.nonTransitiveRClass=true | ||||
| #MPP | ||||
| kotlin.mpp.enableCInteropCommonization=true | ||||
| org.gradle.parallel=true | ||||
| org.gradle.caching=true | ||||
| ignoreGitVersion=false | ||||
| kotlin.native.cacheKind.iosX64=none | ||||
| org.gradle.configureondemand=true | ||||
| org.gradle.configureondemand=true | ||||
|   | ||||
							
								
								
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| #Mon Nov 25 22:48:24 CET 2024 | ||||
| #Sun Feb 09 14:44:52 CET 2025 | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
|   | ||||
| @@ -127,8 +127,8 @@ class SelfossModel { | ||||
|         val tags: List<String>, | ||||
|         val author: String? = null, | ||||
|     ) { | ||||
|         fun getLinkDecoded(): String { | ||||
|             var stringUrl: String | ||||
|         fun getLinkDecoded(): String? { | ||||
|             var stringUrl: String? | ||||
|             stringUrl = | ||||
|                 if (link.contains("//news.google.com/news/") && link.contains("&url=")) { | ||||
|                     link.substringAfter("&url=") | ||||
| @@ -146,11 +146,7 @@ class SelfossModel { | ||||
|                 stringUrl = "http:$stringUrl" | ||||
|             } | ||||
|  | ||||
|             if (stringUrl.isEmptyOrNullOrNullString()) { | ||||
|                 throw ModelException("Link $link was translated to $stringUrl, but was empty. Handle this.") | ||||
|             } | ||||
|  | ||||
|             return stringUrl | ||||
|             return if (stringUrl.isEmptyOrNullOrNullString()) null else stringUrl | ||||
|         } | ||||
|  | ||||
|         fun sourceAuthorAndDate(): String { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user