Access strings.xml values in library - android

I've written a networking library for my android app, and I want to be able to access a url value that I defined in my strings.xml file from that library. The problem is that the library isn't an Activity or Fragment or anything like that, and so it doesn't have access to the normal Context or other things that being a first-class Android citizen gets you. Is there any way for me to access the value without having a Context (or without having one passed in during function calls from Activities and Fragments)?
I know that I can access the R class easily, but all that gives me is byte offsets - I need the getString() method to turn that offset into something meaningful unless I want to do lots of fun romping in raw-bytes land (which I'd like to avoid).
Thanks!

No, there isn't, and manually parsing low level data is definitely not the solution here. The whole point of resources is to be retrieved according to the Context qualification (hence the powerful qualifiers system), and that's why I made that suggestion in the other question you just asked. :-)
Unless you provide a way (method parameter, for example), you won't be able to retrieve the resource. It comes to a point where you should rethink the architecture of your library: why do you need the URI to be qualified? If you're on a library, the client app will have its own Context, and can safely supply one to your library, thus retrieving the resource. If that's not the point (I.e., access the URI in code), then you shouldn't be using a resource, but a final static variable instead, like I suggested.
There are a few instances where you can benefit from the qualifiers capability when dealing with URIs, but, as I said, if you're using on your library only, you probably want a variable defined in code instead.

Related

Should one not use .class.getSimpleName() for TAGs?

I've read that one should not use TAGs like
private static final String TAG = ThisClassName.class.getSimpleName();
but rather
private static final String TAG = "MCLSN";
because ProGuard will create bugs and/or not obfuscate code correctly. Are these valid reasons?
References:
http://www.drewhannay.com/2016/02/android-logcat-tag-best-practices.html
https://blog.mindorks.com/applying-proguard-in-an-android-application
This can certainly be a problem. I'm working with code currently which uses getSimpleName() for TAGs and, as mentioned already, with proguard these can be obfuscated from say "FragmentA" to "a".
If these tags are used for identification, there can easily be bugs introduced in this way as there may be multiple names obfuscated to "a".
We have an extra problem in that these obfuscated TAGs are used in database ids and fields. So fixing this issue will break database behaviour for customers.
Bad stuff, best avoided early if possible.
Honestly, it's a matter of preference, but I prefer the simple String option.
The only benefit of using the getSimpleName() option is because it has support for refactoring. So it's only helpful if you're changing the class's name (which should rarely happen), or if you're copy/pasting a file to use as a template.
So if you're doing that often, I could see why you would prefer the first option. It just makes it easier to not have to manually change the TAG.
I also originally used getSimpleName() for TAGs until recently when I had experienced a minor issue.
When using MVVM architecture, there's really only one ViewModel per View, so I've created a ViewModelFactory that creates and returns the proper ViewModel for the View. Since it's meant to be unique, I decided to use the TAG as the identifier for the ViewModel I need.
Therefore, with a simple Switch-case statement on the TAG, I should be able to get the proper ViewModel. However, you simply can't use TAG as a case expression when you use getSimpleName(). I received the error: Constant Expression Required.
So if you only plan to use your TAG for certain situations like Logcat, you're fine with just using getSimpleName(). But if you plan to use them as a unique class identifier for a specific group of classes, then you'll need the pure String option.

Need to share drawables between different apps from different programmers

I am attempting to create a free, configurable version of my EGMaps Android app which anyone can use to easily create their own map-based apps. The goal is to provide a framework so people with little programming knowledge can just fill in blanks, provide their data, and have it work.
There are two apps involved:
App #1 (EGMaps) does pretty much all the work. It needs access to data provided, or pointed to, by other App #2. I'm the only one working on this one.
App #2 will be created by multiple, maybe lots of other people, all with different app signatures. This is a very small, simple app which does very little other than passing data to EGMaps. I'll be providing source code and instructions on what to fill in. The other programmers can either use it directly, or modify it however they want for their app, which will then eventually call EGMaps.
App #2 needs to pass a lot of data like GPS coordinates, GPS tracks, marker locations, etc, which it's already doing. It also needs to pass an unknown, but potentially large number of small drawables. Due to space considerations, I'd prefer to use the drawables directly from the calling app, rather than copying them over or downloading and storing them inside of EGMaps. These drawables will eventually be Google Maps Marker icons.
Since the apps are written by different programmers, the app signatures are different, so setting the same user ID doesn't help.
This is as close as I've come:
iconString=callingPackage+":drawable/"+iconName;
iconValue=getResources().getIdentifier(iconString, "drawable", null);
callingPackage = name of calling app (ie: App #2). I have verified this is correct.
iconName = name of icon, as found in the drawable resources.
Without the callingPackage part, and with the drawables saved directly in the app, this works fine. It's just accessing the external drawable that doesn't work. iconValue always returns 0. I have also tried putting callingPackage into the third parameter of getIdentifier, with and without adding it to iconString, but that didn't make any difference.
Is there any way to directly (or indirectly, I suppose) access these drawables from the calling app without actually copying them from somewhere?
I would have expected getResources().getIdentifier(iconName, "drawable", callingPackage) to have worked, assuming that callingPackage is an actual package name.
You can try createPackageContext() and calling getResources() on it to get a Resources object referencing the other package.

Where to put static URL in android project?

I have an android app that connects to a server at a static URL. I want to figure out where to put the url so that I can access it form my app.
The consensus seems to be that values/strings.xml is the way to go. However, I'm afraid that a single file for all of my static values could get unwieldy. Is there a way to use multiple different files for different types of strings (UI, internals, etc)? If I simply make different files, will android be smart about it and import them all? Is there an accepted canonical way of doing this?
Thanks!
Yes. You can provide many different XML files and name them the way you see fit. It's the resource qualifiers (the folder name) that matters.
Provide your string resource and you will be fine.
However, since your URI is static, I'd put them as a static final variable instead, in code. It will be easier to call it from places where supplying a Context may not be the best approach. And let's face it, there is probably no reason to make it a String resource and have to call it through the overhead of the resource system unless you need it in a XML layout, for example.

Why it is impossible to access resources in a static way?

I know now, that if I need to get a recource in some static function, I have to pass context or recources of the context there somehow - by parameter or through a static variable. But why is it neccessary? The id's of the resources are reachable in static surroundings, for example R.string.some_my_stuff. If I want a system resource, it is also visible there through Resources.getSystem().getString(android.string.some_common_stuff). But why can't I do something similar to get an application resource? The resource files are the usual static part of the sources. Resources are static and belong to application. The classes of application belong to it in the same way and I can access their static parts in a static way.
Why can't I use resources in all the application in same static way, which would be the most natural, but have to access them through instance instead?
I am afraid, I do not understand something very important.
Please, don't repeat that I can't do it. I know it, on my honour. Please, explain why, or show me the way... Only that will cure me from my sadness :-) Thank you.
The resource IDs are unique per application, they are not unique over all application (including the Android system). E.g. there may be two different string in different applications which have the same ID, say 42.
Therefore yon may access only one application statically (every programmer must agree which one that is, its the Android system (there no choice, its the only one always installed)). For all the other application you must be able to tell the system which application's resources you want to access. You do this using the context.

Is there a "proper" way to keep a content provider implementation seperate from it's users?

I have a custom ContentProvider class, which I originally developed in the same project file with the application using it. However since this application is intended to be only one of many users of the ContentProvider, I want to split it in a different project. The code is being developed on the Android PDK, but future clients might be developed on the SDK (on a custom SDK, or SDK plugin, etc).
The problem I'm facing, is about the constants in the ContentProvider class, e.g. CONTENT_URI, column names and as well some constants which are used to interpret the values returned from queries. These of course cannot be accessed from another project. It seems to me I have 3 options at this point:
1) Ignore the problem, and type in the values directly in the user application code. This however makes accessing the ContentProvider uglier. I would have to change some columns, to encode some columns with strings instead of integers, to keep the code maintainable.
2) Put the constants in a separate class, and include a full copy in applications using the ContentProvider. I'm not a fan of duplicating code though. Keeping a duplicate of this code in each target app, will make some things slightly more annoying to maintain.
3) Abuse the fact that I'm developing on the PDK, and expose a platform library, as described in vendor/sample/frameworks/PlatformLibrary. However, platform libraries don't have a manifest file, which if my understanding is correct means that I can't include a ContentProvider. This means I would need one "normal" project for the ContactProvider, and a separate one just to expose the class with the constant values. This feels so much wrong.
The answer at Class structure for a ContentProvider having multiple sub tables seems to imply option (1), which probably looks like the best option right now.
However, maybe I have missed another, neat & tidy, way to do this? Keeping in mind that I am developing on the PDK, I would certainly like my ContentProvider to be usable in the same manner as stock Google providers.
You probably already have at least one class/interface that defines the "contract" of your ContentProvider using static constants for column names, a content URI, etc.
If you put this into its own Android SDK library project (just for the Android classes to be on the build/classpath), you can then use this library from your actual SDK/PDK application's ContentProvider and also distribute it as myapp-api.jar JAR for others to use.
This way you get the best of both worlds: No outdated code (because your ContentProvider depends on it) and other people can use nice constants for URIs and column names.
For an example of a contract class, see ContactsContract.

Categories

Resources