why we use #DrawableRes in android - android

fun loadIcon(context: Context, url:String, #DrawableRes placeHolder:Int): Bitmap
what is the difference here while we use #DrawableRes

Related

How to parcelize a function in kotlin

I want to make a parcelable data class to put bundle. Also this data class containing a function that I will invoke. But when I put the app back , I am getting exception with parcelize. Here is my data class that parcelable,
#Parcelize
data class TransactionConfirmData(
val title: String,
#StringRes val cancelBtnLabel: Int
#StringRes val confirmBtnLabel: Int
val confirmElements: Map<String, String>,
val customConfirmDataList: List<CustomTransactionConfirmData> = emptyList(),
val requestMethodId: Int,
#NavigationRes val cancelDestination: Int = 0,
val contentStructure:
TransactionBodyContentCaseTransactionBodyContentCase.BASE_CONFIRM,
#IgnoredOnParcel val onPressFunction: Serializable
) : Parcelable {
fun onPressOkey() = onPressFunction as () -> Unit
companion object {
fun create(
title: String,
#StringRes cancelBtnLabel: Int = R.string.bottom_sheet_behavior
#StringRes confirmBtnLabel: Int = R.string.bottom_sheet_behavior
confirmElements: Map<String, String>,
customConfirmDataList: List<CustomTransactionConfirmData> = emptyList(),
requestMethodId: Int,
#NavigationRes cancelDestination: Int = 0,
contentStructure: TransactionBodyContentCase =
TransactionBodyContentCase.BASE_CONFIRM,
onPressFunction: ()->Unit = {}
): TransactionConfirmData = TransactionConfirmData(
title,
cancelBtnLabel,
confirmBtnLabel,
confirmElements,
customConfirmDataList,
requestMethodId,
cancelDestination,
contentStructure,
onPressFunction as Serializable
)
}
}
But this is not working.
I solved that with a listener class like below;
class Listener(val clickAction:()->Unit):Parcelable{
fun onClick() = clickAction()
}
then you can create the field like this in your data class;
val onPressOkey:Listener = Listener {}
Also you can call this onPressOkey ;
yourDataClass.onPressOkey.onClick()
The key is the annotation #IgnoredOnParcel.

Order List in Textview Android Kotlin

Hey I want to show text in order list in textview in android Kotlin. I tried Ordered List inside an Android Textview. I tried heres code it works, but its miss match the height of number and text. I will share the code what i done so far.
NumberIndentSpan.kt
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.style.LeadingMarginSpan
class NumberIndentSpan(
private val leadWidth: Int,
private val gapWidth: Int,
private val index: Int
) : LeadingMarginSpan {
override fun getLeadingMargin(first: Boolean): Int = leadWidth + gapWidth
override fun drawLeadingMargin(
c: Canvas?,
p: Paint?,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence?,
start: Int,
end: Int,
first: Boolean,
layout: Layout?
) {
if (first) {
val orgStyle = p!!.style
p.style = Paint.Style.FILL
val width = p.measureText("4.")
c!!.drawText("$index.", (leadWidth + x - width / 2) * dir, bottom - p.descent(), p)
p.style = orgStyle
}
}
}
MainActivity.kt
val list = mutableListOf(
"Log in Google account\n",
"Scroll to the page\n",
"Tap disconnect from account to logout"
)
var number = 1
val builder = SpannableStringBuilder()
for (text in list) {
val contentStart: Int = builder.length
builder.append(text)
builder.setSpan(NumberIndentSpan(15, 15, number), contentStart, builder.length, 0)
number++
}
textView.text = builder
I want to place number and text at equal height. As images show what i getting from this code. Also i am getting format issue like as 1st point and 2nd point text starting is different, i want in sequence. How can i fix all these things. Thanks in advance
I had the same issue. That's because you have applied a custom font and most likely a line spacing extra that you are not contemplating during the Y positioning.
This is my working solution: https://gist.github.com/marcelpallares/ae6285ee0dcb3f04493991dcb18d01bd
class NumberIndentSpan(
private val number: Int,
private val leadWidth: Int = 15,
private val gapWidth: Int = 30,
) : LeadingMarginSpan {
override fun getLeadingMargin(first: Boolean): Int {
return leadWidth + gapWidth
}
override fun drawLeadingMargin(
canvas: Canvas,
paint: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
t: CharSequence?,
start: Int,
end: Int,
first: Boolean,
layout: Layout?
) {
if (first) {
val text = "$number."
val width = paint.measureText(text)
val yPosition = baseline.toFloat()
val xPosition = (leadWidth + x - width / 2) * dir
canvas.drawText(text, xPosition, yPosition, paint)
}
}
}

How to update ImageView contentDescription with Glide in a Widget

I have an Android widget for the home screen, whose layout is basically an ImageView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rl_ultraviolet_widget_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:gravity="center">
<ImageView
android:id="#+id/iv_ultraviolet_widget_image"
android:layout_width="#dimen/widget_size"
android:layout_height="#dimen/widget_size"
android:src="#drawable/ic_uvi_loading" />
</RelativeLayout>
The ImageView will be updated over the time with the "current" value, which is represented with an image (a drawable) using this code:
/**
* Updates ultraviolet index widget view.
*/
#JvmStatic
private fun updateWidgetViews(context: Context, appWidgetId: Int, #DrawableRes drawableId: Int) {
val widgetView = RemoteViews(context.packageName, R.layout.ultraviolet_widget)
// Set an Intent to launch MainActivity when clicking on the widget
val intent = Intent(context, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
widgetView.setOnClickPendingIntent(R.id.rl_ultraviolet_widget_layout, pendingIntent)
val appWidgetTarget = AppWidgetTarget(context, R.id.iv_ultraviolet_widget_image, widgetView, appWidgetId)
Glide
.with(context)
.asBitmap() // This is needed because AppWidgetTarget is a Bitmap target. See: https://github.com/bumptech/glide/issues/2717#issuecomment-351791721
.transition(BitmapTransitionOptions.withCrossFade())
.load(drawableId)
.into(appWidgetTarget)
pushWidgetUpdate(context, appWidgetId, widgetView)
}
/**
* Pushes update for a given widget.
*/
#JvmStatic
fun pushWidgetUpdate(context: Context, appWidgetId: Int, remoteViews: RemoteViews) {
val manager = AppWidgetManager.getInstance(context)
// Remember to check always for null in platform types (types ended in !).
// See: https://stackoverflow.com/a/43826700/5189200
manager?.let {
manager.updateAppWidget(appWidgetId, remoteViews)
Timber.d("Widget %d was updated", appWidgetId)
}
}
My question is how to also update the contentDescription property in the widget's ImageView with a description on every update and using Glide, so that my widget is more accessible and it can be used with TalkBack. I have searched, but I haven't found any example with Glide and a remote view.
Thanks in advance,
Xavi
You can add listener to Glide when load an image. So, when load image success, you update the contentDescription of imageView.
You can read how to add listener to Gilde in this: https://github.com/codepath/android_guides/wiki/Displaying-Images-with-the-Glide-Library#loading-errors
GlideApp.with(context)
.load("http://via.placeholder.com/300.png")
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
.listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
// log exception
Log.e("TAG", "Error loading image", e);
return false; // important to return false so the error placeholder can be placed
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
// load image success, update the contentDescription here
return false;
}
})
.into(ivImg);
Thanks to the help of #Nguyễn Quang Thọ, I found the solution. First, I created a data class (a tuple) in Kotlin for holding drawable and its associated string:
data class UltravioletImageContents(#DrawableRes val drawableId: Int, val contentDescription: String)
Second, I used the Glide result listener to add the contentDescription update and push the changes to the widget in my updateWidgetViews function.
#JvmStatic
private fun updateWidgetViews(context: Context, appWidgetId: Int, ultravioletImageContents: UltravioletImageContents) {
val widgetView = RemoteViews(context.packageName, R.layout.ultraviolet_widget)
// Set an Intent to launch MainActivity when clicking on the widget
val intent = Intent(context, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
widgetView.setOnClickPendingIntent(R.id.rl_ultraviolet_widget_layout, pendingIntent)
// Set image
val appWidgetTarget = AppWidgetTarget(context, R.id.iv_ultraviolet_widget_image, widgetView, appWidgetId)
Glide
.with(context)
.asBitmap() // This is needed because AppWidgetTarget is a Bitmap target. See: https://github.com/bumptech/glide/issues/2717#issuecomment-351791721
.transition(BitmapTransitionOptions.withCrossFade())
.load(ultravioletImageContents.drawableId)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, isFirstResource: Boolean): Boolean {
Timber.e(e, "Drawable cannot be loaded and set in widget image view")
return false // Important to return false so the error placeholder can be placed
}
override fun onResourceReady(resource: Bitmap?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
// Set contentDescription in order to enable accessibility and be usable with TalkBack
widgetView.setContentDescription(R.id.iv_ultraviolet_widget_image, ultravioletImageContents.contentDescription)
pushWidgetUpdate(context, appWidgetId, widgetView)
return false
}
})
.into(appWidgetTarget)
}
Lastly, I would like to remark that pushWidgetUpdate was moved inside onResourceReady, since I think it doesn't have sense to update my widget without the image (but, maybe, I am missing something).
Here is a sample in Kotlin
Glide.with(context)
.load(data.imageUrl) // URL Of Image
.placeholder(R.drawable.placeholder)
.error(R.drawable.image_not_found)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
return false // important to return false so the error placeholder can be placed
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
myImageView.contentDescription = data.accessibilityText
return false
}
})
.into(myImageView)

Enums or sealed classes with strings

Can I create an enum or a sealed class in Kotlin that contain string resources?
For instance, I have this class:
private enum class Item(
val id: Int,
#DrawableRes val imageRes: Int,
val title: String
) {
PURSE(1, R.drawable.ic_card, "My balance"),
MESSAGES(2, R.drawable.ic_bell, "Messages")
}
If I add a field val resources: Resources in the constructor, I cannot set a parameter resources, so cannot use string resources from strings.xml. In this case I cannot use localization for enums.
You don't need a Resources instance. You can use a string resource, like you did for the drawable:
private enum class Item(
val id: Int,
#DrawableRes val imageRes: Int,
#StringRes val title: Int
)

Glide ModelLoader that loads either from local or remote

I have a custom Glide model to calculate the center crop for image. The same model is used for getting images from server and also from local storage.
Here's the model interface:
interface CenterCropImageInformation {
fun getCenterCropUri(context: Context, #Px width: Int, #Px height: Int): Uri
}
And here is its ModelLoader that extends from BaseGlideUrlLoader:
class CenterCropImageInformationLoader private constructor(
context: Context,
concreteLoader: ModelLoader<GlideUrl, InputStream>,
modelCache: ModelCache<CenterCropImageInformation, GlideUrl>?
) : BaseGlideUrlLoader<CenterCropImageInformation>(concreteLoader, modelCache) {
private val applicationContext: Context = context.applicationContext
override fun getUrl(
model: CenterCropImageInformation, width: Int,
height: Int, options: Options
): String {
return model.getCenterCropUri(applicationContext, width, height).toString()
}
override fun handles(centerCropImageInformation: CenterCropImageInformation): Boolean {
return true
}
/**
* The default factory for [CenterCropImageInformation]s.
*/
class Factory(
private val applicationContext: Context,
private val modelCache: ModelCache<CenterCropImageInformation, GlideUrl>?
) : ModelLoaderFactory<CenterCropImageInformation, InputStream> {
override fun build(
multiFactory: MultiModelLoaderFactory
): ModelLoader<CenterCropImageInformation, InputStream> {
val modelLoader = multiFactory.build(GlideUrl::class.java, InputStream::class.java)
return CenterCropImageInformationLoader(applicationContext, modelLoader, modelCache)
}
override fun teardown() {}
}
}
This works fine for images with http/https scheme, but it doesn't work for file scheme - the one used for loading images from local device storage.
I had a look into Glide's source code and the closest ModelLoader that sounds like an option is UriLoader, but the issue that that one doesn't support custom models. It only supports Uri.
The optimal solution would be to use a pre-existing ModelLoader that is bundled with Glide, but unless I missed it, I couldn't find any that suits my needs. If that's really the case, how do I implement such ModelLoader?
I figured it out after reading Glide's ModelLoaders tutorial. The key is by delegating the loading to a ModelLoader that knows how to handle file and http/https scheme.
What I had to do is to implement ModelLoader interface directly instead of extending BaseGlideUrlLoader. We already know that Glide's built-in UriLoader can handle both file and http/https scheme so we delegate to it. Now to get an instance of UriLoader we use the MultiModelLoaderFactory that is passed to our factory's build method. The default Glide configuration registers UriLoader for Uri + InputStream pair.
class CenterCropImageInformationLoader(
private val modelLoader: ModelLoader<Uri, InputStream>,
private val modelCache: ModelCache< CenterCropImageInformation, Uri>
) : ModelLoader<CenterCropImageInformation, InputStream> {
override fun buildLoadData(
model: CenterCropImageInformation,
width: Int,
height: Int,
options: Options
): ModelLoader.LoadData<InputStream>? {
val uri: Uri = modelCache.get(model, width, height) ?: model.getUri(model, width, height)
modelCache.put(model, width, height, uri)
return modelLoader.buildLoadData(uri, width, height, options)
}
override fun handles(model: CenterCropImageInformation): Boolean = true
class Factory(
private val applicationContext: Context,
private val modelCache: ModelCache<CenterCropImageInformation, Uri>
) : ModelLoaderFactory<CenterCropImageInformation, InputStream> {
override fun build(
multiFactory: MultiModelLoaderFactory
): ModelLoader<CenterCropImageInformation, InputStream> {
val modelLoader = multiFactory.build(Uri::class.java, InputStream::class.java)
return CenterCropImageInformationLoader(applicationContext, modelLoader, modelCache)
}
override fun teardown() {}
}
}
As we can see we no longer extend BaseGlideUrlLoader. Instead we implement ModelLoader interface and in buildLoadData() implementation we try to get the URI from cache (this is similar to what BaseGlideUrlLoader is doing) and then we call buildLoadData() on the ModelLoader that we passed to the constructor, which happens to be an instance of UriLoader as I mentioned earlier thanks to MultiModelLoaderFactory.
It's really surprising that this type of ModelLoader is not part of Glide built-in model loaders.

Categories

Resources