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.
Related
So, I'm trying to use a running application and have it access code of a not yet installed application through its .apk file. I need to access things such as the secondary applications packagename, provider, bundles, data, etc all without installing it onto the phone.
So far from what I understand I can use either one of two things.
1) dexClassLoader. With this option I need to be able to access the .apk file from some sort of storage such as an SD card.
2) pathClassLoader. Seems like the correct option here. Can use JAR/ZIP/APK files with this option.
Essentially I'm trying to load the second application into the first one by binding its code to a running process, I'm just having trouble accessing its code without decompiling it. I guess my question is should I be using/trying the first or second option here, or is a third option available?
While you can dynamically load code from the second application using dexClassLoader, etc., you cannot dynamically add new components to your application. Any component must be statically declared in the AndroidManifest.xml.
So you cannot, for example, "import" an activity from the second apk into your main application. You can access the class for that Activity, but you can't directly start an activity using it.
Depending on your requirements, it might be possible to create a "placeholder" activity in your main application, which creates an instance of the Activity class from the secondary apk and forwards all method calls to that instance. In general, I would not actually suggest this. I suspect it would be very difficult (if not impossible in some cases) to actually do correctly.
Let me start by describing my problem. I am working on a large Android application that has a large number of layouts and strings (all in strings.xml). A new requirement has been made that requires some of the strings to be different depending on a condition at startup (think all instances of "cat" being replaced with "dog"). This condition does NOT depend on anything with the device (locale, size, resolution, etc.): it only depends on something within the business logic.
The simplest approach would be to create new copies of all the layout files that use the strings that have "cat", and in those new layout copies instead use strings that have "dog". However, this is not entirely feasible due to the number of layouts, as well as future maintenance concerns of basically duplicated layouts.
I know this isn't a discussion forum, so I have an approach I'd like to focus on. It seems to me the problem is closely related to normal resource configuration/locale, where different resources are provided due to attributes of the device. Is there a way to get this working for my situation? The differences are the desired configuration is only known at runtime and there isn't an existing resource qualifier that makes sense (we might translate into French, for example, so I don't want to piggyback on that). I can't imagine my problem is unique, though.
If there's another approach I should use let me know, although I don't want to go too far in the discussion route because SO frowns upon it. Frankly it's probably risky even asking this here, but I don't know where else to ask it.
I can think on several solutions:
You can set the string on runtime like yourTextView.setText(R.string.any_string) with:
1) A To have some resources strings for you business logic.
2) A placeholder string,
getResources().getString(R.string.tempale, "your value")
3) Get the string from web service if that posible
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.
I'm just wondering how the following scenario can be solved:
I want to write a very simple app for my daughter. The app displays 4 colored fields on the activity and through speech output says something like "tap green". She should then tap the green field to get positive feedback. So far, so easy.
I then thought it might be nice if the app would also "grow" as she grows older. Maybe later I want to have a game mode where I display animals, numbers, vehicles, etc., so it would be nice if I could have something like "Shape sets" - basically a set of images along with a description of what the app should say for each image. Also easy enough - all you need is a set of images and an XML file describing the images.
BUT
I'd like to be able to install these "Shape sets" as additional APKs later on, so that I don't have to modify the app every time. I'd like to install the APKs, so that the contents are added to a specific sub folder on the SD card, into which my app looks to enumerate available "Shape sets".
If I ever published the app to the Play Store, other people should also be able to download the "Shape sets" I create (no need for user contributions, though).
Is that possible? If so, what would I have to do to have the Android OS "copy" the contents of an APK to a specific folder (lets say "/TapGame/Shape Sets/Animals") on the SD card? Or is there even another way of achieving what I want that I didn't think of?
The term "plugin architecture" just came to my mind as I wrote the question. Searching using that term I found this question: Extend my android app in different APK
It seems to provide a solution to my problem - I'll investigate this further, but please feel free to suggest other possible solutions!
Is that possible?
Um, sure.
If so, what would I have to do to have the Android OS "copy" the contents of an APK to a specific folder (lets say "/TapGame/Shape Sets/Animals") on the SD card?
Android won't do any of that. You have to do that. You would have to detect that a "shape set" APK was installed (either watching for package-installed broadcasts, scanning all installed apps for ones that seem to be a "shape set", etc.). Then you would have to arrange to copy whatever you wanted to wherever you wanted it, either by:
Asking the "shape set" app to do it (e.g., send a command to some IntentService), or
Using createPackageContext() and trying to do the copying from your main app
Or is there even another way of achieving what I want that I didn't think of?
Um, just use ZIP files that your app downloads itself from a well-known location. That corresponds to Dave Smith's final paragraph of his answer on the question you just linked to in your edit.
Or, just update the main app. I'm not quite certain what effort you think that you are saving otherwise.
Or, just keep the content online, using a Web service to indicate the available "shape sets" and downloading them as needed (with optional caching).
Fascinating question. If you really want to go for plug-ins then OSGi would probably be the way to go, but it's a lot of work to get to know and to use and seems like overkill in this case.
I don't know how your shapes are defined, but they are probably each defined in a separate file-set, providing the shape (maybe a png or jpg?) as well as the audio-file that will be used as a command for this shape. If the folder in which these file-sets are stored is fixed (TapGame/Shape Sets/...) the app could scan the folder each startup and the views could be generated accordingly (in this case, the activity cannot be build entirely in the XML-File, but must be partially done programmatically).
The Plugin-Aps would be rather easy. They are an apk which includes the file sets (jpg and mp3 or whatever). Started once they deposit all these files into the specified folder (they probably check before if these files exist) and then the apk can shut down again and be uninstalled.
on the next startup the Tap Game App would find the new symbols and include them into the game.
This seems rather straight forward to me. Another way would be to actually store the shapes and audio files on the internet and with each start of the App check if the number of shapes and audio-files has changed and create local copies of new ones. This would mean no downloads of apks... probably a more usual approach to the issue.
I am very interested to hear what you make of it, seems like a different approach then the norm, which is always cool to see.
I have released an android app downloadable in the UK, but I wish to release a (very slightly different) version of the app for the U.S. Currently I believe I need to do the following for the new version of the app ...
1. Change the 'package' attribute in the manifest file to something different.
2. Ensure that on Google Play only the US can download the app.
Is there anything else I am yet to discover I need to do, or any other problems I am yet to consider?
Edit 19th July 13:19 GMT - I am currently warming towards using TelephonyManager.getNetworkCountryIso() as an initial country guess, and falling back on the locale if this fails (because of no SIM card). What do people think? Using GPS is also an option though, but is that overkill?
Since you say you're just changing a couple of strings (we'll think of the URL as just a string, too), I would suggest the following:
Create the resource directories res/values and res/values-en-rUS.
In each of these, create a strings.xml resource file
Define your default (UK specific) string values in the res/values directory, and your US specific string values in the res/values-en-rUS folder.
Then, to refer to these strings, simply use #string/my_string_name when referring to them from an XML document, or getResources().getString(R.string.my_string_name) when referring to them from code.
For more details on the types of resource qualifiers, check out this page, also for the list of qualified country codes you can use, see this page.
I'm sure anyone determined enough could change their region to US -- I'm honestly not sure offhand how the region is determined -- but for all intents and purposes this should do the trick without having to maintain two separate applications. I would just evaluate how critical it is that UK users be unable to access the US specific functions of the application, and with that in mind determine whether it is worth the maintenance of two applications.
EDIT Some more additional searching leads me to believe the region is locked into the build.prop file, which is not editable outside of rooting your device (i.e. it is not going to happen accidentally). That said, if it's still imperative that they have the correct option, I might suggest a popup dialog only on the first run of the application that confirms the locale with the user, saves it as a SharedPreference, and then choose your Strings programmatically based on that preference. That way you're still only maintaining one application, but you still have the flexibility of handling the two regions.
This is correct. The Play Store goes off of two things for who can download and if they can update. The first is the packagename com.andya.myukapp -> com.andya.myusapp should work, as long as none of your existing customers are expecting to switch freely between the two (assuming it's a paid app)
I eventually decided that the best policy was to use TelephonyManager to check the country of the Network.
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String networkCountryCode = tm.getNetworkCountryIso();
If that failed, I got the locale.
String locale = context.getResources().getConfiguration().locale.getCountry();
This would happen once at the start of the app. The results of this would then be saved and be configurable in the settings screen.