Accessing Resources IDs using Kotlin & Anko - kotlin

I am new to Android/Kotlin/Anko and I have a question regarding the way to access color (and probably other) resources from within Anko.
I know that there are helpers like textResource where you simply pass the R.string.my_color to simplify the process of setting resource strings but how about accessing colors using the Resources instance from the View class ?
Let’s say you have a subclass of Button and want to change the text color. If you use the textResource it will change the text string not the color, and if you use textColor then you must specify the real resource ID by using resources.getColor(R.color.my_color, null) which wouldn't be so annoying if you didn't have to pass the optional theme parameter (null here)
Is creating an extension on Resources useful here ?
fun Int.fromResources(resources: Resources): Int {
return resources.getColor(this, null)
}
What is the recommended way ?
EDIT
I changed the textColor value extension to do just that, which I found the cleanest thing to do except I have no idea if this is really Android friendly
var android.widget.TextView.textColor: Int
get() = throw AnkoException("'android.widget.TextView.textColor' property does not have a getter")
set(v) = setTextColor(resources.getColor(v, null))

I think you can use a property extension like this one instead of the one you suggested:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(resources.getColor(v, null))
Or use ContextCompat as suggested by Damian Petla:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(ContextCompat.getColor(context, v))
You should keep Anko's textColor:
Allows you to set a color directly without taking it from XML, if needed at some point
Prevents you from importing the wrong textColor (Anko's one or yours), same property names with different behaviour is not a good idea.

Related

Android Studio: Timber: automatically add the function name

Does anyone know a trick to include the function name in a Timber text/tag without actually having to type it? Here's what I do and I figured I might as well ask... (initRecognizer is the function's name; ideally I enter something like $xyz)
Thank you
Timber.d("SR: initRecognizer psid=$psid")
By default, Timber uses className as the tag. You can provide your own tag by overriding createStackElementTag function while planting the tree. Something like:
Timber.plant(
object : Timber.DebugTree() {
override fun createStackElementTag(element: StackTraceElement): String {
val className = super.createStackElementTag(element)
return "TAG $className ${element.methodName}"
}
}
)
I usually use a "TAG" prefix in my tags to quickly filter my logs from logcat.
Now you simply call Timber.d("your debug msg") and the function name will automatically be added in the tag in the logcat.

Micronaut declarative client with base url per environment

I'd like to be able to use Micronaut's declarative client to hit an a different endpoint based on whether I'm in a local development environment vs a production environment.
I'm setting my client's base uri in application.dev.yml:
myserviceclient:
baseUri: http://localhost:1080/endpoint
Reading the docs from Micronaut, they have the developer jumping through quite a few hoops to get a dynamic value piped into the actual client. They're actually quite confusing. So I've created a configuration like this:
#ConfigurationProperties(PREFIX)
class MyServiceClientConfig {
companion object {
const val PREFIX = "myserviceclient"
const val BASE_URL = "http://localhost:1080/endpoint"
}
var baseUri: String? = null
fun toMap(): MutableMap<String, Any> {
val m = HashMap<String, Any>()
if (baseUri != null) {
m["baseUri"] = baseUri!!
}
return m
}
}
But as you can see, that's not actually reading any values from application.yml, it's simply setting a const value as a static on the class. I'd like that BASE_URL value to be dynamic based on which environment I'm in.
To use this class, I've created a declarative client like this:
#Client(MyServiceClientConfig.BASE_URL)
interface MyServiceClient {
#Post("/user/kfc")
#Produces("application/json")
fun sendUserKfc(transactionDto: TransactionDto)
}
The docs show an example where they're interpolating values from the config map that's built like this:
#Get("/api/\${bintray.apiversion}/repos/\${bintray.organization}/\${bintray.repository}/packages")
But how would I make this work in the #Client() annotation?
Nowhere in that example do they show how bintray is getting defined/injected/etc. This appears to be the same syntax that's used with the #Value() annotation. I've tried using that as well, but every value I try to use ends up being null.
This is very frustrating, but I'm sure I'm missing a key piece that will make this all work.
I'm setting my client's base uri in application.dev.yml
You probably want application-dev.yml.
But how would I make this work in the #Client() annotation?
You can put a config key in the #Client value using something like #Client("${myserviceclient.baseUri}").
If you want the url somewhere in your code use this:
#Value("${micronaut.http.services.occupancy.urls}")
private String occupancyUrl;

custom boolean attributes not binding

Custom boolean attributes do not bind like built in. checked.bind='1===2' will not include the checked attribute in the tag. myboolatt.bind='1===2' WILL include the myboolatt in the tag. I did myboolatt.bind='1===2 | boolconverter' to log out the value and it says false.
So what am I doing wrong? It seems Aurelia is inconsistent on binding expressions. Another instance is I can do this title=${$index<12} inside a repeat and I get what is expected. So I thought this would work - myboolatt.bind=${$index<12}. That doesn't even compile.
Believe me I have read the all the doc (doesn't mean I didn't miss something) and many posts including the long running discussions between the Aurelia team concerning boolean attributes.
I have wrapped expressions in "" and in ${} and in both but just can't get it to work.
It feels like I am missing 1 vital piece of information that will magically explain these inconsistencies and slay my frustration. One of the reasons I like Aurelia so much (on top of convention based) is that I have actually just guessed at a few things - thinking this is how I would do it - and ding-dang if they didn't just work.
I really feel like this should just work. So again I ask - what am I doing wrong?
Thanks
If you are using .bind, you bind it to the att variable in your js/ts file, and you should not use any dollar signs or brackets.
For example, myboolatt.bind=${$index<12} should be myboolatt.bind="$index<12". The use of the dollar sign in index has nothing to do with the bindings. This is just a variable provided by Aurelia.
When you want to use variables without bind you use ${variable}.
The checked attribute not being present, I in the tag I'm guessing is because it's evaluated to false, and the checked attribute is not a true/false attribute. A input that's checked will look like <input type="checkbox" checked/>
It was not Aurelia binding. I initially created bool attributes but did not have a need to bind. They were just empty classes. Then I needed binding so I added the necessary methods. What ended up causing the confusion was using myboolatt sends no argument into valueChanged where myboolatt.bind sends T or F. Not being a regular js'er this threw me a bit and I see there are a number of ways to handle. Anyway here are the attributes and usage. Hope it helps someone else.
Thanks
function private_ValueChanged(self, att, value) {
if (value === false) self.element.removeAttribute(att);
else self.element.setAttribute(att, '');
}
export class toggleCustomAttribute {
static inject = [Element];
constructor(element) { this.element = element; }
valueChanged(newValue, oldValue) { private_ValueChanged(this, 'toggle', newValue);
}
export class radioCustomAttribute {
static inject = [Element];
constructor(element) { this.element = element; }
valueChanged(newValue, oldValue) { private_ValueChanged(this, 'radio', newValue); }
}
<ff-button repeat.for="keymen of keyChangeMenu" radio.bind="$index<12" class='ff-button' click.delegate="keyChangeClick($event.target)" id.bind="$index+'_key'" >
${keymen[0]}<sup>${keymen[1]}</sup>
</ff-button>

Get unique css selector from WebElement

is it possible to get a css selector of an WebElement?
eg
var optionSelectors = mutableListOf<String>()
val options = selectWebElement?.findElements(By.cssSelector("option")).orEmpty()
for(option in options){
var optionSelector = option.getSelector()
optionSelectors.add(optionSelector)
}
return toJson(optionSelectors)
Thank you in advance
You can always use Reflection to get foundBy property value like:
Field field = element.getClass().getDeclaredField("foundBy");
field.setAccessible(true);
String foundBy = field.get(element).toString();
However the nature of your question is a little bit weird, given you found the element already you should know its selector, shouldn't you? If you want to interact with the Select option values you can go for the relevant Select class which has getOptions() function.
Also consider going for Page Object Model design pattern, it is one of best practices to keep your test logic separate from UI layer

Getting parent in XAML

I have following XAML code :
<maps:Map Name="PlaygroundMap" Credentials="YOUR_BING_MAPS_KEY">
<maps:MapItemsControl
x:Name="PlaygroundMapItems"
my:MapExt.ClusteredItemsSource="{Binding Locations}"
ItemTemplate="{StaticResource PinTemplate}"/>
</maps:Map>
Also ClusteredItemsSource is my own attached property with the following code (only important part is shown) :
private static void OnItemsReceived(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var mapItemsControl = d as MapItemsControl;
if(mapItemsControl == null) return;
var map = VisualTreeHelper.GetParent(mapItemsControl); // this is always NULL
var map2 = mapItemsControl.Parent; // this is always NULL
var items = e.NewValue as IEnumerable<Location>;
mapItemsControl.ItemsSource = items;
}
So my requirement is to have MAP reference in OnItemsReceived method. I thought that this will be easy because if you check XAML again you will see that mapItemsControl is child of Map (or map is parent of mapItemsControl).
But somehow variable map & map2 are always NULL. Is this normal behaviour or?
Also, if this "parent" approach will not work, can you suggest me what are my other options to get Map reference in MapItemsControl (to be exact, to get map reference in my custom attached property).
Btw, I'm using Bing Maps SDK for WinRT.
VisualTreeHelper won't help you if your controls are not part of the visual tree - either because they were not added to one yet (Loaded event wasn't raised yet) or maybe they don't use the standard parent/child control relationship triggers (by one being logically a child of a Panel, ContentControl etc.)
The best approach here really is to forget about the visual tree and use bindings and view models instead.