parsing an android manifest file with JDOM2 - android

hope you're fine.
I have to parse an android manifest file to extract data like 'the minSdkVersion' used, after several seraches I found a code using JDOM.
Display data related to "uses-sdk" was expected but When running I got a null object.
Need Help
Thanks
Manifest Example
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.rssfeed"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
The code
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
public class ManifestP2 {
static Document document;
static Element root;
static NeededTag tags;//Data receiver
public static void main(String[] args){
SAXBuilder builder = new SAXBuilder();
try{
document = builder.build(new File("AndroidManifest.xml"));
}catch(Exception e){
System.out.println(e.getMessage());
}
root = document.getRootElement();
tags = new NeededTag();
findTag();
System.out.println(tags.usesSdk.toString());
}
static void findTag(){
//get values for "uses-sdk" tag
Element sdk = root.getChild("uses-sdk");
tags.usesSdk.minSdk = sdk.getAttributeValue("android:minSdkVersion");
tags.usesSdk.targetSdk = sdk.getAttributeValue("android:targetSdkVersion");
}
}

The attributes in your document are in the android namespace. Namespaces in XML add a level of complexity, that's true, but they also ensure that you can have special types of documents, like your manifest, where you can store "meta" types of information inside the same document as regular data, while still guaranteeing that there won't be any name collisions.
That's a complicated way of saying that where you see android in your XML document, it refers to a namespace, not the attribute name.
So, for example, where you have:
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="19" />
that really means you have your own element uses-sdk and that element has 2 attributes, one called minSdkVersion, and the other called targetSdkVersion. Those attributes are in the namespace with the URI: http://schemas.android.com/apk/res/android.
To get those attributes off the element in JDOM, you have to ask for them by name, and in the right namespace.
To do that, you have the code:
Namespace ans = Namespace.getNamespace("android", "http://schemas.android.com/apk/res/android");
String min = sdk.getAttributeValue("minSdkVersion", ans);
String target = sdk.getAttributeValue("targetSdkVersion", ans);
You declare the namespace to search for (only the URI is important when searching, the android namespace prefix is there for convenience only)

Related

Launcher icon Xamarin Android not found

I'm doing a Hello World in Xamarin Android, but when I try to change the launcher icon, it throws the following error: "No resource found that matches the given name (at 'icon' with value '#drawable/icon')."
Actually I don't have any "icon" in my resources, the icons' names are ic_launcher:
And my manifest looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="HelloWorldXamarinAndroid.HelloWorldXamarinAndroid" android:versionCode="1" android:versionName="1.0" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="16" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<application android:label="Hi android" android:icon="#drawable/ic_launcher"></application>
</manifest>
I've already cleaned the project and tried to rebuild it, can someone help me?
If I change all the images' names to "Icon.png" and the manifest with "#drawable/Icon", it works, my question is, why Xamarin doesn't work with other names
I found the problem, in Main Activity is set the icon too, so it's necessary to set the new name there too:
[Activity(Label = "HelloWorldXamarinAndroid", MainLauncher = true, Icon = "#drawable/ic_launcher")]
public class MainActivity : Activity
{
...
}
Right click the Droid project and select Android Manifest from the menu list. Under Application Icon you will see which icon the application is referencing.
Below is a picture of where the icon is being references.

Access long key from meta-data from AndroidManifeast.xml

I implemented the following code and want to access the key meta-data value as a Long integer from the MetaActivity.java class.
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.slk.metachecking"
android:versionCode="1"
android:versionName="1.0" >
<application>
<meta-data android:name="key" android:value="488659867867350" />
</application>
MetaActivity.java
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(),
PackageManager.GET_META_DATA);
Bundle metaData = ai.metaData;
metaData.getString("key"); // Returns -36235050
metaData.getLong("key"); // Returns 0
Does Android support long as a type in meta-data tag?
refer to http://developer.android.com/guide/topics/manifest/meta-data-element.html, as you can see only getInt is supported, so if you want to specify long value set it as a string (for example L123456789123456789) and parse it as a substring(1)

Failed to find provider info for 'ContentProvider'

I have a problem that I just cannot figure out. I am using Eclipse to create my own Content Provider but keep getting the following error:
[..] ERROR/ActivityThread(1051): Failed to find provider info for
my.package.provider.countrycontentprovider
Code found here: http://codepad.org/Rx00HjHd
Main parts:
public class CountryContentProvider extends ContentProvider {
public static final String PROVIDER =
"my.package.provider.countrycontentprovider";
public static final Uri CONTENT_URI =
Uri.parse("content://" + PROVIDER + "/country");
// ...
#Override
public boolean onCreate() { return true; }
// ...
}
// from my activity
ContentResolver resolver = getContentResolver();
Cursor c = resolver.query(CountryContentProvider.CONTENT_URI,
null, null, null, null);
// AndroidManifest.xml
<provider
android:name="my.package.provider.CountryContentProvider"
android:authorities="my.package.provider.countrycontentprovider" />
I have added the provider to the manifest and return true from the onCreate function. I use the CountryContentProvider.CONTENT_URI in my activity to get the Content from my provider, but I just keep getting that error message. I have removed and added the code three times (in case of eclipse melt down) to no avail.
I must be missing something. Can someone point me in the right direction?
I was able to reproduce your problem when I moved <provider> out of the <application>...</application> tag. Eclipse didn't say anything like error or warning.
Fortunately this issue is detected by Android Lint starting from ADT 20.
It worked for me only after specifying full path in Authorities tag in manifest file (see SearchableDictionary sample code in SDK).
<provider android:name=".DictionaryProvider"
android:authorities="com.example.android.searchabledict.DictionaryProvider">
Inside your B-app which has the contentresolver, add this in the AndroidManifest.xml, outside application-tag
<queries>
<provider android:authorities="com.authoritiesname.in.contentprovider.app" />
</queries>
Setting the exported attribute to true in the provider tag in the manifest worked for me :
android:exported="true"
According to the documentation(http://developer.android.com/guide/topics/manifest/provider-element.html#exported), export is required only if the provider is to be available for other applications. But this is the only solution that worked for me.
The android:authorities= in the XML file is the content authority that is located in the contract class that you probably built. The content authority is added to the scheme to make the base content URI. Plain English, the reverse domain you used to make your app no caps here com.domain.sub.appName.
The android:name is the folder plus class your provider is named, do not forget the dot .folder.ProviderClassContentAuthorityIsIn.
Hope this helps :)
you have a capital letter and on the other line, one lowercase letter.
android:name= "my.package.provider.-C-ountryContentProvider"
android:authorities="my.package.provider.-c-ountrycontentprovider"
it must be the same everywhere.
public static final String PROVIDER =
"my.package.provider.countrycontentprovider";
Register your provider in the Android Manifest
<provider
android:authorities="your_content_authority"
android:name="yourProviderClass"/>

When to use which constructor for ComponentName in Android?

I am a little bit confused about the ComponentName class in Android.
There are different ways to get to a component name object, but I don't know when to use which... and why!
Example:
Application package is de.zordid.sampleapp
but widget provider class is de.zordid.sampleapp.widget.WidgetProvider
Using
ComponentName cn = new ComponentName("de.zordid.sampleapp.widget",
"WidgetProvider");
I got this component info: ComponentInfo{de.zordid.sampleapp.widget/WidgetProvider}, but I could not use this - the component is unknown!
But the JavaDoc says I should give the package and the class within that package - and that is what I did, didn't I??
Using
ComponentName cn = new ComponentName(context, WidgetProvider.class);
yields ComponentInfo{de.zordid.sampleapp/de.zordid.sampleapp.widget.WidgetProvider} - and that works fine!!
There is even another way to get a ComponentName - by context and a string.
Which one should be used where and when??
Thanks!
The ComponentName constructor taking two Strings can be used to refer to a component in another application. But, the first argument is not the package name of the class; it is the package name of the application---the package attribute of the manifest element in that application's AndroidManifest.xml. So your first example should be
ComponentName cn = new ComponentName("de.zordid.sampleapp",
"de.zordid.sampleapp.widget.WidgetProvider");
That constructor could certainly be used to refer to components in your own application, but since you already have hold of a Context from your own application you might as well use it and use one of the other constructors. In my opinion, the one taking a Class should be preferred whenever usable. You could use the one taking a String if you only know the class dynamically for some reason; in that case, it should take the fully-qualified class name as above.
Robert Tupelo-Schneck's answer is right about preferring objects against Strings. Here's how I see it with details on how all the different prefixes work.
To refer to your own components, use:
new ComponentName(getApplicationContext(), WidgetProvider.class);
To refer to some dynamically referenced component in your own app, use:
// values/strings.xml: <string name="provider">de.zordid.sampleapp.widget.WidgetProvider</string>
String fqcn = getResources().getString(R.string.provider);
new ComponentName(getApplicationContext(), fqcn);
This is useful when you want to use Android's resource qualifiers to decide which component to use, you can override the default string in values-*/strings.xml.
To refer to another application's component, use:
int componentFlags = GET_ACTIVITIES | GET_PROVIDERS | GET_RECEIVERS | GET_SERVICES;
PackageInfo otherApp = context.getPackageManager().getPackageInfo("com.other.app", componentFlags);
ComponentInfo info = otherApp.activities[i]; // or providers/receivers/...
new ComponentName(info.packageName, info.name);
#About .Names and <manifest package="
There may be some confusion here because I think historically Robert's statement was true:
it is the package name of the application---the package attribute of the manifest element in that application's AndroidManifest.xml
but not any more. Since the new Gradle build system was introduced there has been some changes around here, and then they changed it again in AGP 7.3, and made it mandatory in AGP 8.0.
If you have an android.defaultConfig.applicationId specified in your build.gradle that'll be the app package name, and then package attribute in manifest (or later namespace in build.gradle) is a separate thing when building your app. The first argument of ComponentName now refers to applicationId + applicationIdSuffix. The tricky thing is that after the final manifest merge and packaging the APK will have <manifest package=applicationId + applicationIdSuffix and all the .Names will be expanded to FQCNs.
Example app for learning name resolution
Here's an example structure based on the structure of one of my apps. Consider the following classes in a hypothetical app called "app":
net.twisterrob.app.android.App
net.twisterrob.app.android.GlideSetup
net.twisterrob.app.android.subpackage.SearchResultsActivity
net.twisterrob.app.android.subpackage.Activity
net.twisterrob.app.android.content.AppProvider
on the server side backend of the app and/or some shared model classes:
net.twisterrob.app.data.*
net.twisterrob.app.backend.*
net.twisterrob.app.web.*
in my Android helper library:
net.twisterrob.android.activity.AboutActivity
other libraries:
android.support.v4.content.FileProvider
This way everything is namespaced in net.twisterrob.app. The android app being just a single part of the whole inside it's own subpackage.
AndroidManifest.xml (irrelevant parts omitted)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.twisterrob.app.android">
<!--
`package` above defines the base package for .Names
to simplify reading/writing the manifest.
Notice that it's different than the `applicationId` in build.gradle
and can be independently changed in case you want to refactor your packages.
This way you can still publish the same app with the same name.
-->
<!-- Will be expanded to net.twisterrob.app.android.App in the manifest merging phase. -->
<application android:name=".App">
<!-- meta-data needs FQCNs because the merger can't know if you want to expand them or not.
Also notice that name and value both can contain class names, depending on what you use. -->
<meta-data android:name="net.twisterrob.app.android.GlideSetup" android:value="GlideModule" />
<meta-data android:name="android.app.default_searchable" android:value="net.twisterrob.app.android.subpackage.SearchResultsActivity" />
<!-- Will be expanded to net.twisterrob.app.android.subpackage.Activity in the manifest merging phase. -->
<activity android:name=".subpackage.Activity" />
<!-- Needs full qualification because it's not under the package defined on manifest element. -->
<activity android:name="net.twisterrob.android.activity.AboutActivity" />
<!-- Will be expanded to net.twisterrob.app.android.content.AppProvider in the manifest merging phase. -->
<provider android:name=".content.AppProvider" android:authorities="${applicationId}" />
<!-- Needs full qualification because it's not under the package defined on manifest element. -->
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.share" />
</application>
<!-- ${applicationId} will be replaced with what's defined in `build.gradle` -->
</manifest>
build.gradle
android {
defaultConfig {
// this is what will be used when you upload it to the Play Store
applicationId 'net.twisterrob.app'
// in later AGP versions, move manifest's package here:
// namespace 'net.twisterrob.app.android'
}
buildTypes {
debug {
// The neatest trick ever!
// Released application: net.twisterrob.app
// IDE built debug application: net.twisterrob.app.debug
// This will allow you to have your installed released version
// and sideloaded debug application at the same time working independently.
// All the ContentProvider authorities within a system must have a unique name
// so using ${applicationId} as authority will result in having two different content providers.
applicationIdSuffix '.debug'
}
}
}
To check out what your final manifest will look like after all the merging open build\intermediates\manifests\full\debug\AndroidManifest.xml.
Or you can use like this inside BroadcastReceiver :
ComponentName smsReceiver = new ComponentName(this, SMSReceiver.class);

Unable to resolve activity for: Intent

I am having a problem in running Android unit test. I got this error when I tried to run a simple test.
Here's the log:
Blockquote
java.lang.RuntimeException: Unable to resolve activity for: Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=com.wsandroid.Activities/.SplashActivity }
at android.app.Instrumentation.startActivitySync(Instrumentation.java:371)
at android.test.InstrumentationTestCase.launchActivityWithIntent(InstrumentationTestCase.java:120)
at android.test.InstrumentationTestCase.launchActivity(InstrumentationTestCase.java:98)
at android.test.ActivityInstrumentationTestCase2.getActivity(ActivityInstrumentationTestCase2.java:87)
at com.wsandroid.test.activity.TestEULA.setUp(TestEULA.java:15)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:430)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
This error occurs for Android less than 2.2. It works fine for Android 2.2 emulator. Yet Android 2.2 emulator has a bug of sending a key twice even though we only press it one. Application to be tested runs on Android 2.2 platform.
Appreciate if anyone of you can help me.
Dzung.
This can also be cause by a missing
Make sure you have a corresponding entry in your manifest.
<activity android:name=".SplashActivity" ...
I had a similar problem with a simple test project for an app that was just a splash screen. I found that I had implemented the constructor wrong. My initial implementation of the constructor was this...
public SplashScreenTest(){
super("com.mycomp.myapp.SplashScreen", SplashScreen.class);
}
After some beating my head against the wall, I somehow decided to remove the SplashScreen from the pkg argument of super(). My successful implementation is now like this...
public SplashScreenTest() {
super("com.mycomp.myapp", SplashScreen.class);
}
I hope that this helps you or others solve the problem.
Try to check your Manifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tablet.test"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="#drawable/icon" android:label="#string/app_name">
<uses-library android:name="android.test.runner" />
</application>
<uses-sdk android:minSdkVersion="8" />
<!-- This line below! -->
<instrumentation android:targetPackage="com.tablet.tablet"
android:name="android.test.InstrumentationTestRunner" />
</manifest>
You need to check the following line:
<instrumentation android:targetPackage="com.tablet.tablet"
android:name="android.test.InstrumentationTestRunner" />
So the targetPackage must be the same as in your code.
I had specific similar problem while using the AndroidAnnotations lib.
Later, I found out it was due to forgetting to use the generated class (MyActivity_ instead of MyActivity).
In my case the problem was that TestFragmentActivity, meaning the Activity used in our test
extends ActivityInstrumentationTestCase2<TestFragmentActivity>
must be available in the package defined in Manifest.xml as targetPackage:
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="de.my.androidhd" />
My solution was to move TestFragmentActivity into tested application package.
For the keys being sent twice issue, are you sure you're not now getting both the Down and Up actions? I had this issue when using Robotium, and generated this to make things easier:
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.widget.EditText;
import com.jayway.android.robotium.solo.Solo;
public static void type(Solo robot, EditText edit, String text) {
int index = 0;
//Find the index of this control, as Robotium doesn't seem to like R.id
for (int i = 0; i < robot.getCurrentEditTexts().size(); i++) {
if (robot.getCurrentEditTexts().get(i).getId() == edit.getId()) {
index = i;
}
}
robot.clickOnEditText(index);
KeyCharacterMap map = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
KeyEvent[] events = map.getEvents(text.toCharArray());
for (int event = 0; event < events.length; event++) {
if (events[event].getAction() == KeyEvent.ACTION_DOWN) {
robot.sendKey(events[event].getKeyCode());
}
}
}
I've had two activities with same name in different packages. Issue was about importing from the wrong package. I spend much time on it maybe it will save someone some time.

Categories

Resources