Glide FileNotFoundException - image

سلام عليكم
I want to load image with Glide but I got this error :
java.io.FileNotFoundException: https://via.placeholder.com/600/1e5390
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:238)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java)
at com.bumptech.glide.load.data.HttpUrlFetcher.loadDataWithRedirects(HttpUrlFetcher.java:102)
at com.bumptech.glide.load.data.HttpUrlFetcher.loadData(HttpUrlFetcher.java:56)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:276)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
This is the image url:
https://via.placeholder.com/600/1e5390
And this is the Glide code:
#BindingAdapter("imageUrl")
fun ImageView.bindImage( imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(this.context)
.load(imgUri)
.apply(
RequestOptions()
.error(R.drawable.ic_broken_image)
)
.into(this)
}
}
And use it here :
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:imageUrl="#{photoObject.photoUrl}"
app:srcCompat="#drawable/ic_broken_image" />
Thank you :))

This solve my problem:
val theImage = GlideUrl(
imgUrl, LazyHeaders.Builder()
.addHeader("User-Agent", "5")
.build()
)
Add user agent header to Glide
Full code :
#BindingAdapter("imageUrl")
fun ImageView.bindImage( imgUrl: String?) {
val theImage = GlideUrl(
imgUrl, LazyHeaders.Builder()
.addHeader("User-Agent", "5")
.build()
)
theImage.let {
Glide.with(this.context)
.load(theImage)
.apply(
RequestOptions()
.error(R.drawable.ic_broken_image)
)
.into(object : CustomViewTarget<ImageView, Drawable>(this) {
override fun onLoadFailed(errorDrawable: Drawable?) {}
override fun onResourceCleared(placeholder: Drawable?) {}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
this#bindImage.setImageDrawable(resource)
}
}) }
}

#BindingAdapter("app:setPhoto")
fun ImageView.setPhoto(purl: String?) {
val theImage = GlideUrl(
purl,LazyHeaders.Builder()
.addHeader("User-Agent", "5")
.build()
)
theImage.let {
Glide.with(this.context)
.load(theImage)
.into(this)
}
}

My solution, get agent:
val agent = WebView(this.context).settings.userAgentString
val glideUrl = GlideUrl(
"url_here",
LazyHeaders.Builder().addHeader("User-Agent", agent).build()
)
Glide.with(context)
.asBitmap()
.load(glideUrl)
.into(this)

Related

Handling commands from the viewmodel to the UI

The peculiarity of this application is that every time a user does something (except common things like typing) the application must check with an authority that they are indeed allowed to perform that action.
For example, let us say that the user wishes to see their profile (which is on the top bar)
the Composable screen looks something like this:
#Composable
fun HomeScreen(
navController: NavController,
vm: HomeViewModel = hiltViewModel()
) {
val state = vm.state.value
val scaffoldState = rememberScaffoldState()
HomeScreen(state, scaffoldState, vm::process)
}
#Composable
fun HomeScreen(state: HomeState, scaffoldState: ScaffoldState, event: (HomeEvent) -> Unit) {
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = {
Text("Hello world")
},
actions = {
IconButton(onClick = {
event.invoke(HomeEvent.ShowProfile)
}) {
Icon(
painter = painterResource(id = R.drawable.ic_person),
contentDescription = stringResource(id = R.string.profile)
)
}
}
)
}
) {
}
}
the view model receives it like so:
#HiltViewModel
class HomeViewModel #Inject constructor(app: Application, private val checkAllowed: CheckAllowed): AndroidViewmodel(app) {
val state = mutableStateOf(HomeState.Idle)
fun process(event:HomeEvent) {
when(event) {
HomeEvent.ShowProfile -> {
state.value = HomeState.Loading
viewModelScope.launch {
try {
val allowed = checkAllowed(Permission.SeeProfile) //use case that checks if the action is allowed
if (allowed) {
} else {
}
} finally {
state.value = HomeState.Idle
}
}
}
}
}
}
I now have to send a command to the ui, to either show a snackbar with the error or navigate to the profile page.
I have read a number of articles saying that compose should have a state, and the correct way to do this is make a new state value, containing the response, and when the HomeScreen receives it , it will act appropriately and send a message back that it is ok
I assume something like this :
in the viewmodel
val command = mutableStateOf<HomeCommand>(HomeCommand.Idle)
fun commandExecuted() {
command.value = HomeCommand.Idle
}
and inside the HomeScreen
val command = vm.command.value
try {
when (command) {
is HomeCommand.ShowProfile -> navController.navigate("profile_screen")
is HomeCommand.ShowSnackbar -> scaffoldState.snackbarHostState.showSnackbar(command.message, "Dismiss", SnackbarDuration.Indefinite)
}
}finally {
vm.commandExecuted()
}
but the way I did it is using flows like so:
inside the viewmodel:
private val _commands = MutableSharedFlow<HomeCommand>(0, 10, BufferOverflow.DROP_LATEST)
val commands: Flow<HomeCommand> = _commands
and inside the HomeScreen:
LaunchedEffect(key1 = vm) {
this#ExecuteCommands.commands.collectLatest { command ->
when (command) {
is HomeCommand.ShowProfile -> navController.navigate("profile_screen")
is HomeCommand.ShowSnackbar -> scaffoldState.snackbarHostState.showSnackbar(command.message, "Dismiss", SnackbarDuration.Indefinite)
}
}
This seems to work, but I am afraid there may be a memory leak or something I'm missing that could cause problems
Is my approach correct? Should I change it to state as in the first example? can I make it better somehow?

How do I make a basic animation on Skiko (Kotlin MPP bindings to Skia)?

Using Skiko and Kotlin I want to make a basic animation: A 0 to 100 counter that automatically updates the text each second.
I managed to do it, but it has a problem, it is blinking each time the window repaints.
Here is the code:
import kotlinx.coroutines.*
import org.jetbrains.skija.*
import org.jetbrains.skiko.*
import javax.swing.*
public fun main() {
val window = SkiaWindow().apply {
layer.renderer = CounterRenderer()
setSize(400, 175)
isVisible = true
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
}
GlobalScope.launch {
for (i in 0..100) {
delay(1000)
window.layer.repaint()
}
}
}
public class CounterRenderer : SkiaRenderer {
private lateinit var canvas: Canvas
private var counter = 0
override fun onInit() {
}
override fun onDispose() {
}
override fun onReshape(width: Int, height: Int) {
}
override fun onRender(canvas: Canvas, width: Int, height: Int) {
this.canvas = canvas
val typeface = Typeface.makeFromName("Roboto", FontStyle.NORMAL)
val fontSize = 30F
val font = Font(typeface, fontSize)
val paint = Paint().setColor(0XFF000000.toInt())
canvas.drawString("Counter: ${counter++}", 10F, 50F, font, paint)
}
}
I have tried to search for examples of animations with skija or skiko without success.
I would really appreciate if you could give me some examples.
After navigating the Android Compose code, especially this class:
ComposeLayer.
I finally got it to work with this code:
import kotlinx.coroutines.*
import org.jetbrains.skija.*
import org.jetbrains.skiko.*
import javax.swing.*
public fun main() {
val window = SkiaWindow().apply {
layer.renderer = CounterRenderer()
setSize(400, 175)
isVisible = true
defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
}
GlobalScope.launch(Dispatchers.Main) {
for (i in 0..100) {
delay(500)
window.layer.redrawLayer()
}
}
}
public class CounterRenderer : SkiaRenderer {
private var counter = 0
private val typeface = Typeface.makeFromName("Roboto", FontStyle.NORMAL)
private val fontSize = 30F
private val font = Font(typeface, fontSize)
private val paint = Paint().setColor(0XFF000000.toInt())
override fun onInit() {
}
override fun onDispose() {
}
override fun onReshape(width: Int, height: Int) {
}
override fun onRender(canvas: Canvas, width: Int, height: Int) {
canvas.drawString("Counter: ${counter++}", 10F, 50F, font, paint)
}
}
To run this code you need to install the specific Main Dispatcher, in this case it is by adding this to the gradle configuration:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.3.9")

UIDocumentPickerViewController with Catalyst on MACOS

I have this code for show picker with Catalyst in MacOS:
final class DocumentPicker: NSObject, UIViewControllerRepresentable, ObservableObject {
typealias UIViewControllerType = UIDocumentPickerViewController
#Published var urlsPicked = [URL]()
lazy var viewController:UIDocumentPickerViewController = {
// For picked only folder
let vc = UIDocumentPickerViewController(documentTypes: ["public.folder"], in: .open)
vc.allowsMultipleSelection = false
vc.delegate = self
return vc
}()
........
and:
struct ContentView: View {
#ObservedObject var picker = DocumentPicker()
#State private var urlPick = ""
var body: some View {
HStack {
Text(urlPicked())
.padding()
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.white, lineWidth: 1)
)
TextField("", text: $urlPick)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.system(size: 10))
.disabled(true)
Spacer()
Button(action: {
#if targetEnvironment(macCatalyst)
let viewController = UIApplication.shared.windows[0].rootViewController!
viewController.present(self.picker.viewController, animated: true)
self.picker.objectWillChange.send()
#endif
print("Hai premuto il pulsante per determinare il path della GeoFolder")
}) {
Image(systemName: "square.and.arrow.up")
}
}
.padding()
}
private func urlPicked() -> String {
var urlP = ""
if picker.urlsPicked.count > 0 {
urlP = picker.urlsPicked[0].path
urlPick = picker.urlsPicked[0].path
}
return urlP
}
}
If I run the above code I get the chosen correct path in text, while in textfield nothing and also I have the error in urlPick = picker.urlsPicked[0].path: Modifying state during view update, this will cause undefined behavior.
How can I modify the code to show the correct path chosen also in textfield?
have the error in urlPick = picker.urlsPicked[0].path: Modifying state
during view update, this will cause undefined behavior. How can I
modify the code to show the correct path chosen also in textfield?
Try the following
if picker.urlsPicked.count > 0 {
urlP = picker.urlsPicked[0].path
DispatchQueue.main.async {
urlPick = picker.urlsPicked[0].path
}
}
For anyone seeking to create a MacOS Document Picker with READ-ONLY entitlements, please use the following solution:
import Foundation
import UIKit
extension ViewController: UIDocumentBrowserViewControllerDelegate, UIDocumentPickerDelegate {
#objc func presentDocumentPicker() {
if operatingSystem == .macintosh {
let documentPicker = UIDocumentBrowserViewController(forOpening: [.pdf])
documentPicker.delegate = self
documentPicker.allowsDocumentCreation = false
documentPicker.allowsPickingMultipleItems = false
// Present the document picker.
present(documentPicker, animated: true, completion: nil)
} else {
let documentsPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.pdf])
documentsPicker.delegate = self
documentsPicker.allowsMultipleSelection = false
documentsPicker.modalPresentationStyle = .fullScreen
self.present(documentsPicker, animated: true, completion: nil)
}
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
guard let url = documentURLs.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let url = urls.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
debugPrint("[DocumentPicker] Selected Item with URL : ", url)
controller.dismiss(animated: true)
}
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true)
}
}
Please note that in the event that the entitlements are read-write (i.e. you are also allowing the user to save files to the computer) - then you can simply use a UIDocumentPicker (the non .macintosh example in my snippet).

How to import a helper class in buildSrc/build.gradle.kts, settings.gradle.kts, and build.gradle.kts?

I'd like to create a class to help me loading different types of properties (local.properties, gradle.properties, $GRADLE_HOME/gradle.properties, environment variables, system properties, and custom properties files (maybe in other formats like yml, xml, etc.).
Also, I'd like to use this in my buildSrc/build.gradle.kts, settings.gradle.kts, and build.gradle.kts.
Please consider that we are using Gradle 6.+.
A simple implementation of this class would be (the full implementation would be a lot of more powerful):
plugins/properties/build.gradle.kts:
package com.example
object Properties {
val environmentVariables = System.getenv()
}
How can we successfully import this Properties class in all of those files (buildSrc/build.gradle.kts, settings.gradle.kts, build.gradle.kts) and use it from there? Something like:
println(com.example.Properties.environmentVariables["my.property"])
Can we do that creating this class inside of a plugin and applying it from there? Without pre-compiling and releasing the plugin? Maybe something like:
apply("plugins/properties/build.gradle.kts")
How would it be a minimal implementation for this?
I tried different approaches but I'm not being able to find a way that work with those 3 files altogether.
I'm not completely satisfied with this approach but maybe it can help others.
I wasn't able to reuse a class but a function in all places, like this:
settings.gradle.kts
apply("plugin/properties/build.gradle.kts")
#Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T
val getProperty = uncheckedCast<(key: String) -> String>(extra["getProperty"])
println(getProperty("group"))
buildSrc/build.gradle.kts
apply("../plugin/properties/build.gradle.kts")
#Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T
val getProperty = uncheckedCast<(key: String) -> String>(extra["getProperty"])
println(getProperty("group"))
build.gradle.kts
// Can be used inside of the file
apply("plugin/properties/build.gradle.kts")
#Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T
val getProperty = uncheckedCast<(key: String) -> String>(extra["getProperty"])
println(getProperty("group"))
buildScript {
// Since the other getProperty is not visible here we need to do this again.
apply("plugin/properties/build.gradle.kts")
#Suppress("unchecked_cast", "nothing_to_inline")
inline fun <T> uncheckedCast(target: Any?): T = target as T
val getProperty = uncheckedCast<(key: String) -> String>(extra["getProperty"])
println(getProperty("group"))
}
plugin/properties/build.gradle.kts
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import java.util.Properties as JavaProperties
import org.gradle.api.initialization.ProjectDescriptor
object Properties {
lateinit var rootProjectAbsolutePath : String
val local: JavaProperties by lazy {
loadProperties(JavaProperties(), Paths.get(rootProjectAbsolutePath, "local.properties").toFile())
}
val environment by lazy {
System.getenv()
}
val system: JavaProperties = JavaProperties()
val gradle: JavaProperties by lazy {
loadProperties(JavaProperties(), Paths.get(rootProjectAbsolutePath, "gradle.properties").toFile())
}
val globalGradle: JavaProperties by lazy {
loadProperties(JavaProperties(), Paths.get(System.getProperty("user.home"), ".gradle", "gradle.properties").toFile())
}
fun containsKey(vararg keys: String): Boolean {
if (keys.isNullOrEmpty()) return false
keys.forEach {
when {
local.containsKey(it) -> return true
environment.containsKey(it) -> return true
system.containsKey(it) -> return true
gradle.containsKey(it) -> return true
globalGradle.containsKey(it) -> return true
}
}
return false
}
fun get(vararg keys: String): String {
return this.getAndCast<String>(*keys) ?: throw IllegalArgumentException("Property key(s) ${keys} not found.")
}
fun getOrNull(vararg keys: String): String? {
return getAndCast<String>(*keys)
}
inline fun <reified R> getOrDefault(vararg keys: String, default: R?): R? {
return getAndCast<R>(*keys) ?: default
}
inline fun <reified R> getAndCast(vararg keys: String): R? {
if (keys.isNullOrEmpty()) return null
keys.forEach {
val value = when {
local.containsKey(it) -> local[it]
environment.containsKey(it) -> environment[it]
system.containsKey(it) -> system[it]
gradle.containsKey(it) -> gradle[it]
globalGradle.containsKey(it) -> globalGradle[it]
else -> null
}
// TODO Improve the casting using Jackson
if (value != null) return value as R
}
return null
}
private fun loadProperties(target: JavaProperties, file: File): JavaProperties {
if (file.canRead()) {
file.inputStream().use { target.load(it) }
}
return target
}
}
if (rootProject.name == "buildSrc") {
Properties.rootProjectAbsolutePath = rootDir.parent
} else {
Properties.rootProjectAbsolutePath = rootDir.absolutePath
}
extra["getProperty"] = {key: String -> Properties.get(key)}

OnClick Adapter on Kotlin

My kotlin code brakes when the onclick adapterPosition. I've debug the code but I'm still not sure what is going on and why is not working.
Here is my Category Adapter:
import android.content.Context
import android.content.Intent
import android.support.v7.widget.CardView
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import com.google.android.gms.common.internal.service.Common
import com.letsbuildthatapp.kotlinmessenger.Quizz.Model.Category
import com.letsbuildthatapp.kotlinmessenger.Quizz.Model.Interface.IOnRecyclerViewItemClickListener
import com.letsbuildthatapp.kotlinmessenger.Quizz.Model.Question
import com.letsbuildthatapp.kotlinmessenger.R
class CategoryAdapter(internal var context: Context,
internal var categoryList: List<Category>):
RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
//This is correct
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): MyViewHolder {
val itemView = LayoutInflater.from(context).inflate(R.layout.layout_category_item, parent, false)
return MyViewHolder(itemView)
}
// this is correct
override fun getItemCount(): Int {
return categoryList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.txt_category_name.text = categoryList[position].name
holder.setiOnRecyclerViewItemClickListener(object : IOnRecyclerViewItemClickListener {
override fun onClick(view: View?, position: Int) {
// this is to direct user to the question List
com.letsbuildthatapp.kotlinmessenger.Quizz.Model.Common.Common.selectedCategory = categoryList[position]
val intent = Intent(context, Question::class.java)
context.startActivity(intent)
}
})
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
internal var txt_category_name: TextView
internal var card_category: CardView
internal lateinit var iOnRecyclerViewItemClickListener: IOnRecyclerViewItemClickListener
fun setiOnRecyclerViewItemClickListener(iOnRecyclerViewItemClickListener: IOnRecyclerViewItemClickListener) {
this.iOnRecyclerViewItemClickListener = iOnRecyclerViewItemClickListener
}
init {
txt_category_name = itemView.findViewById(R.id.txt_category_name) as TextView
card_category = itemView.findViewById(R.id.card_category) as CardView
itemView.setOnClickListener(this)
}
override fun onClick(view: View?) {
iOnRecyclerViewItemClickListener.onClick(view, adapterPosition)
}
}
}
Here is the IonRecyclerViewItemClickLister
interface IOnRecyclerViewItemClickListener {
fun onClick(view: View?, position:Int)
}
I've debugged the code and it appears to work well until it reaches to this part:
override fun onClick(view: View?) {
iOnRecyclerViewItemClickListener.onClick(view, adapterPosition)
}
}
}
My question is what am I doing wrong.
Implement your interface in your activity and pass it as parameter to your Adapter. After pass it your view holder.
I'll let you with an example of my project.
My interface
MainActivity - Where I implement the Interface
My adapter
I discovered where the error was
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.txt_category_name.text = categoryList[position].name
holder.setiOnRecyclerViewItemClickListener(object : IOnRecyclerViewItemClickListener {
override fun onClick(view: View?, position: Int) {
// this is to direct user to the question List
com.letsbuildthatapp.kotlinmessenger.Quizz.Model.Common.Common.selectedCategory = categoryList[position]
val intent = Intent(context, Question::class.java)
context.startActivity(intent)
}
})
}
The error was in point to an Activity that doesn't exist.

Resources