AndroidManifest file inspection

hello there, 

I developed an android studio plugin for code inspection, in certain point I need to add some xml tags to androidmanifest file programmatically. therefore I would like to ask how can I get androidmanifest file of the project? then, In order to add the xml tags can I use xmlTagBuilder?

Thanks in advance

0
Avatar
Permanently deleted user

I tried the following code but it did not work

String pathToManifest = myProject.getBasePath() + "/app/src/main/AndroidManifest.xml";
VirtualFile manifestFile = LocalFileSystem.getInstance().findFileByPath(pathToManifest);
Module module = ModuleUtil.findModuleForFile(manifestFile,myProject);
XmlFile manifestContainingFile = (XmlFile) ManifestUtils.getAndroidManifestPsi(module).getContainingFile();
StringBuilder xmlTagBuilder = new StringBuilder();
xmlTagBuilder.append(" <activity android:name=\".TokenActivity");
xmlTagBuilder.append("\" />");
String xmlTag = xmlTagBuilder.toString();
XmlTag tagFromText = XmlElementFactory.getInstance(myProject).createTagFromText(xmlTag);
manifestContainingFile.getRootTag().addSubTag(tagFromText, true);

it give me an error that can not add the element.
0

Could you be more specific with error? Stacktrace or smth?

0
Avatar
Permanently deleted user

Yes, for sure. this is the stack trace of error:


com.intellij.util.IncorrectOperationException: Element cannot be added
at com.intellij.psi.impl.source.tree.CompositePsiElement.addInnerBefore(CompositePsiElement.java:322)
at com.intellij.psi.impl.source.tree.CompositePsiElement.addBefore(CompositePsiElement.java:156)
at com.intellij.psi.impl.source.xml.XmlTagImpl.addSubTag(XmlTagImpl.java:1028)

During the debugging I understand that the following line of code become Null and throws the exception:


TreeElement treeElement = addInternal(elementCopy, elementCopy, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.FALSE);

 

0

Seems the only way to debug and see, why addInternal fails. Have you checked that tagFromText is ok and doesn't contains error elements?

0
Avatar
Permanently deleted user

In order to check that every thing is fine I changed the code and write the code in the following way:

 AndroidFacet facet=AndroidFacet.getInstance(module);
final Manifest manifest = facet.getManifest();
if (manifest==null){
return;
}
final XmlTag manifestTag = manifest.getXmlTag();
if (manifestTag == null){
return;
}

final XmlTag applicationTag = manifestTag.findFirstSubTag("application");
if (applicationTag==null){
return;
}
// final XmlTag activityTag = applicationTag.findFirstSubTag("activity");
final PsiFile manifestFile = applicationTag.getContainingFile();
if (manifestFile == null){
return;
}
final VirtualFile vManifestFile = manifestFile.getVirtualFile();
if (vManifestFile == null || !ReadonlyStatusHandler.ensureFilesWritable(manifestFile.getProject(),vManifestFile)){
return;
}
XmlTag addTag = manifestTag.createChildTag("uses-sdk", "", null, false);
if (addTag != null) {
addTag = manifestTag.addSubTag(addTag, true);
addTag.setAttribute("minSdkVersion", SdkConstants.NS_RESOURCES,"16" );

I see all the values and it seems fine. the only thing that I think (not sure) that can be the reason is the fact that maybe anchor is the reason of the fail. Actually, I don't understand what does it mean?

If you have some suggestion to check for debugging, I will appreciate it. 

0

Also, check `addSubTag`/`createChildTag` usages in idea itself. May help.

0

Could you submit the AndroidManifest.xml file content to reproduce the problem?

0
Avatar
Permanently deleted user

Yes, Of course:

I enclosed the manifest file for your consideration. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.amirsh.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>

0
Avatar
Permanently deleted user

I would like to ask if there is a possibility for this error because I tried to insert in a wrong place? because the new activity tag should be inserted after the last </activity>.

so, I don't know that this fact can be the reason for the problem?

0

Thanks, the operation should be wrapped into a command, like

WriteCommandAction.runWriteCommandAction(myProject, () -> {file.getRootTag().addSubTag(subtag, true);});

I will try to add more descriptive error message here.

0
Avatar
Permanently deleted user

By modify the code to this version, it works for you?

0
Avatar
Permanently deleted user

I should wrap all part of the code into WriteCommandAction.runWriteCommandAction? 

0

No, just parts that modify PSI (in this case, addSubTag() call).

0
Avatar
Permanently deleted user

Thanks for your hint. Now, it is working for me as well. 

0
Avatar
Permanently deleted user

Final question in order to get the android facet I used the following code

Module[] modules = ModuleManager.getInstance(ProjectManager.getInstance().getOpenProjects()[0]).getSortedModules();
Module module = modules[modules.length-2];
AndroidFacet facet=AndroidFacet.getInstance(module);

Is there any better way to get the module object, because this method, maybe didn't work for different scenarios. 

Thanks in advance

0

Usually you have some context, which can help you to choose a module. Which one do you want? You are just taking arbitrary module from arbitrary project. What if you have several projects with several modules opened and some of them has no AndroidFacet?

What is the context of this exact code?

If you really need to get it in some very isolated place, you may iterate all opened projects and all their modules and try to get facet to for each module with first success winning. But such approach more likely indicates design flaw.

0
Avatar
Permanently deleted user

let me clarify the scenario. the scenario is as following, for example I have an android studio project with 4 modules that one of them are the app module that contains the Android Manifest file within it and the others can be different app modules like libraries, resources, etc. so, I need to get the app module, and then use it as an input to get the adnroidfacet. 

Thanks in advance 

0

Ok, and where do you need to do this? Some extension? Service? Action? Inspection?

0
Avatar
Permanently deleted user

I have an action class with different methods that the manifest modification happened in one of the methods that is placed in the action class.

0

`com.intellij.openapi.actionSystem.AnAction#actionPerformed` method invoked with `com.intellij.openapi.actionSystem.AnActionEvent` which may contains DataContext with different useful context information.

E.g. if your actin been invoked on psiFile, it's going to be in context. And by this psiFile you may find a module this file in.

To start your investigation check:

  • com.intellij.openapi.actionSystem.AnAction#getEventProject 
  • com.intellij.openapi.actionSystem.CommonDataKeys

 

0

请先登录再写评论。