Commit 9db6dc29 authored by Bart Mathijssen's avatar Bart Mathijssen Committed by Jeffrey Schiller

Implement metadata annotations for extensions

parent 03a021a5
...@@ -154,6 +154,10 @@ public final class Compiler { ...@@ -154,6 +154,10 @@ public final class Compiler {
new ConcurrentHashMap<String, Set<String>>(); new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> activitiesNeeded = private final ConcurrentMap<String, Set<String>> activitiesNeeded =
new ConcurrentHashMap<String, Set<String>>(); new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> metadataNeeded =
new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> activityMetadataNeeded =
new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> broadcastReceiversNeeded = private final ConcurrentMap<String, Set<String>> broadcastReceiversNeeded =
new ConcurrentHashMap<String, Set<String>>(); new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> libsNeeded = private final ConcurrentMap<String, Set<String>> libsNeeded =
...@@ -511,6 +515,56 @@ public final class Compiler { ...@@ -511,6 +515,56 @@ public final class Compiler {
System.out.println("Component activities needed, n = " + n); System.out.println("Component activities needed, n = " + n);
} }
/**
* Generate a set of conditionally included metadata needed by this project.
*/
@VisibleForTesting
void generateMetadata() {
try {
loadJsonInfo(metadataNeeded, ComponentDescriptorConstants.METADATA_TARGET);
} catch (IOException e) {
// This is fatal.
e.printStackTrace();
userErrors.print(String.format(ERROR_IN_STAGE, "Metadata"));
} catch (JSONException e) {
// This is fatal, but shouldn't actually ever happen.
e.printStackTrace();
userErrors.print(String.format(ERROR_IN_STAGE, "Metadata"));
}
int n = 0;
for (String type : metadataNeeded.keySet()) {
n += metadataNeeded.get(type).size();
}
System.out.println("Component metadata needed, n = " + n);
}
/**
* Generate a set of conditionally included activity metadata needed by this project.
*/
@VisibleForTesting
void generateActivityMetadata() {
try {
loadJsonInfo(activityMetadataNeeded, ComponentDescriptorConstants.ACTIVITY_METADATA_TARGET);
} catch (IOException e) {
// This is fatal.
e.printStackTrace();
userErrors.print(String.format(ERROR_IN_STAGE, "Activity Metadata"));
} catch (JSONException e) {
// This is fatal, but shouldn't actually ever happen.
e.printStackTrace();
userErrors.print(String.format(ERROR_IN_STAGE, "Activity Metadata"));
}
int n = 0;
for (String type : activityMetadataNeeded.keySet()) {
n += activityMetadataNeeded.get(type).size();
}
System.out.println("Component metadata needed, n = " + n);
}
/* /*
* Generate a set of conditionally included broadcast receivers needed by this project. * Generate a set of conditionally included broadcast receivers needed by this project.
*/ */
...@@ -1012,6 +1066,20 @@ public final class Compiler { ...@@ -1012,6 +1066,20 @@ public final class Compiler {
out.write(" <data android:mimeType=\"text/plain\" />\n"); out.write(" <data android:mimeType=\"text/plain\" />\n");
out.write(" </intent-filter>\n"); out.write(" </intent-filter>\n");
} }
Set<Map.Entry<String, Set<String>>> metadataElements = activityMetadataNeeded.entrySet();
// If any component needs to register additional activity metadata,
// insert them into the manifest here.
if (!metadataElements.isEmpty()) {
for (Map.Entry<String, Set<String>> metadataElementSetPair : metadataElements) {
Set<String> metadataElementSet = metadataElementSetPair.getValue();
for (String metadataElement : metadataElementSet) {
out.write(metadataElement);
}
}
}
out.write(" </activity>\n"); out.write(" </activity>\n");
// Companion display a splash screen... define it's activity here // Companion display a splash screen... define it's activity here
...@@ -1027,6 +1095,7 @@ public final class Compiler { ...@@ -1027,6 +1095,7 @@ public final class Compiler {
// Collect any additional <application> subelements into a single set. // Collect any additional <application> subelements into a single set.
Set<Map.Entry<String, Set<String>>> subelements = Sets.newHashSet(); Set<Map.Entry<String, Set<String>>> subelements = Sets.newHashSet();
subelements.addAll(activitiesNeeded.entrySet()); subelements.addAll(activitiesNeeded.entrySet());
subelements.addAll(metadataNeeded.entrySet());
subelements.addAll(broadcastReceiversNeeded.entrySet()); subelements.addAll(broadcastReceiversNeeded.entrySet());
...@@ -1140,6 +1209,8 @@ public final class Compiler { ...@@ -1140,6 +1209,8 @@ public final class Compiler {
compiler.generateAssets(); compiler.generateAssets();
compiler.generateActivities(); compiler.generateActivities();
compiler.generateMetadata();
compiler.generateActivityMetadata();
compiler.generateBroadcastReceivers(); compiler.generateBroadcastReceivers();
compiler.generateLibNames(); compiler.generateLibNames();
compiler.generateNativeLibNames(); compiler.generateNativeLibNames();
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2020 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.annotations;
import com.google.appinventor.components.annotations.androidmanifest.MetaDataElement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to indicate any additional metadata required by
* a component so that corresponding <meta-data> elements can be added
* to AndroidManifest.xml.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsesActivityMetadata {
/**
* An array containing each {@link MetaDataElement}
* that is required by the component.
*
* @return the array containing the relevant metadata
*/
MetaDataElement[] metaDataElements();
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2020 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.annotations;
import com.google.appinventor.components.annotations.androidmanifest.MetaDataElement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to indicate any additional metadata required by
* a component so that corresponding <meta-data> elements can be added
* to AndroidManifest.xml.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsesApplicationMetadata {
/**
* An array containing each {@link MetaDataElement}
* that is required by the component.
*
* @return the array containing the relevant metadata
*/
MetaDataElement[] metaDataElements();
}
...@@ -20,6 +20,8 @@ public final class ComponentDescriptorConstants { ...@@ -20,6 +20,8 @@ public final class ComponentDescriptorConstants {
public static final String ASSET_DIRECTORY = "component"; public static final String ASSET_DIRECTORY = "component";
public static final String ASSETS_TARGET = "assets"; public static final String ASSETS_TARGET = "assets";
public static final String ACTIVITIES_TARGET = "activities"; public static final String ACTIVITIES_TARGET = "activities";
public static final String METADATA_TARGET = "metadata";
public static final String ACTIVITY_METADATA_TARGET = "activityMetadata";
public static final String LIBRARIES_TARGET = "libraries"; public static final String LIBRARIES_TARGET = "libraries";
public static final String NATIVE_TARGET = "native"; public static final String NATIVE_TARGET = "native";
public static final String PERMISSIONS_TARGET = "permissions"; public static final String PERMISSIONS_TARGET = "permissions";
......
...@@ -81,6 +81,8 @@ public final class ComponentListGenerator extends ComponentProcessor { ...@@ -81,6 +81,8 @@ public final class ComponentListGenerator extends ComponentProcessor {
appendComponentInfo(sb, ComponentDescriptorConstants.NATIVE_TARGET, component.nativeLibraries); appendComponentInfo(sb, ComponentDescriptorConstants.NATIVE_TARGET, component.nativeLibraries);
appendComponentInfo(sb, ComponentDescriptorConstants.ASSETS_TARGET, component.assets); appendComponentInfo(sb, ComponentDescriptorConstants.ASSETS_TARGET, component.assets);
appendComponentInfo(sb, ComponentDescriptorConstants.ACTIVITIES_TARGET, component.activities); appendComponentInfo(sb, ComponentDescriptorConstants.ACTIVITIES_TARGET, component.activities);
appendComponentInfo(sb, ComponentDescriptorConstants.METADATA_TARGET, component.metadata);
appendComponentInfo(sb, ComponentDescriptorConstants.ACTIVITY_METADATA_TARGET, component.activityMetadata);
appendComponentInfo(sb, ComponentDescriptorConstants.ANDROIDMINSDK_TARGET, Collections.singleton(Integer.toString(component.getAndroidMinSdk()))); appendComponentInfo(sb, ComponentDescriptorConstants.ANDROIDMINSDK_TARGET, Collections.singleton(Integer.toString(component.getAndroidMinSdk())));
appendComponentInfo(sb, ComponentDescriptorConstants.BROADCAST_RECEIVERS_TARGET, component.broadcastReceivers); appendComponentInfo(sb, ComponentDescriptorConstants.BROADCAST_RECEIVERS_TARGET, component.broadcastReceivers);
appendConditionalComponentInfo(component, sb); appendConditionalComponentInfo(component, sb);
......
...@@ -15,6 +15,8 @@ import com.google.appinventor.components.annotations.SimpleFunction; ...@@ -15,6 +15,8 @@ import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.SimpleBroadcastReceiver; import com.google.appinventor.components.annotations.SimpleBroadcastReceiver;
import com.google.appinventor.components.annotations.UsesActivityMetadata;
import com.google.appinventor.components.annotations.UsesApplicationMetadata;
import com.google.appinventor.components.annotations.UsesAssets; import com.google.appinventor.components.annotations.UsesAssets;
import com.google.appinventor.components.annotations.UsesLibraries; import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesNativeLibraries; import com.google.appinventor.components.annotations.UsesNativeLibraries;
...@@ -671,6 +673,16 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -671,6 +673,16 @@ public abstract class ComponentProcessor extends AbstractProcessor {
*/ */
protected final Set<String> activities; protected final Set<String> activities;
/**
* Metadata required by this component.
*/
protected final Set<String> metadata;
/**
* Activity metadata required by this component.
*/
protected final Set<String> activityMetadata;
/** /**
* Broadcast receivers required by this component. * Broadcast receivers required by this component.
*/ */
...@@ -753,6 +765,8 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -753,6 +765,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
nativeLibraries = Sets.newHashSet(); nativeLibraries = Sets.newHashSet();
assets = Sets.newHashSet(); assets = Sets.newHashSet();
activities = Sets.newHashSet(); activities = Sets.newHashSet();
metadata = Sets.newHashSet();
activityMetadata = Sets.newHashSet();
broadcastReceivers = Sets.newHashSet(); broadcastReceivers = Sets.newHashSet();
classNameAndActionsBR = Sets.newHashSet(); classNameAndActionsBR = Sets.newHashSet();
designerProperties = Maps.newTreeMap(); designerProperties = Maps.newTreeMap();
...@@ -1099,6 +1113,8 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -1099,6 +1113,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
componentInfo.nativeLibraries.addAll(parentComponent.nativeLibraries); componentInfo.nativeLibraries.addAll(parentComponent.nativeLibraries);
componentInfo.assets.addAll(parentComponent.assets); componentInfo.assets.addAll(parentComponent.assets);
componentInfo.activities.addAll(parentComponent.activities); componentInfo.activities.addAll(parentComponent.activities);
componentInfo.metadata.addAll(parentComponent.metadata);
componentInfo.activityMetadata.addAll(parentComponent.activityMetadata);
componentInfo.broadcastReceivers.addAll(parentComponent.broadcastReceivers); componentInfo.broadcastReceivers.addAll(parentComponent.broadcastReceivers);
// TODO(Will): Remove the following call once the deprecated // TODO(Will): Remove the following call once the deprecated
// @SimpleBroadcastReceiver annotation is removed. It should // @SimpleBroadcastReceiver annotation is removed. It should
...@@ -1187,6 +1203,42 @@ public abstract class ComponentProcessor extends AbstractProcessor { ...@@ -1187,6 +1203,42 @@ public abstract class ComponentProcessor extends AbstractProcessor {
} }
} }
// Gather the required metadata and build their element strings.
UsesApplicationMetadata usesApplicationMetadata = element.getAnnotation(UsesApplicationMetadata.class);
if (usesApplicationMetadata != null) {
try {
for (MetaDataElement me : usesApplicationMetadata.metaDataElements()) {
updateWithNonEmptyValue(componentInfo.metadata, metaDataElementToString(me));
}
} catch (IllegalAccessException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "IllegalAccessException when gathering " +
"application metadata and subelements for component " + componentInfo.name);
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "InvocationTargetException when gathering " +
"application metadata and subelements for component " + componentInfo.name);
throw new RuntimeException(e);
}
}
// Gather the required activity metadata and build their element strings.
UsesActivityMetadata usesActivityMetadata = element.getAnnotation(UsesActivityMetadata.class);
if (usesActivityMetadata != null) {
try {
for (MetaDataElement me : usesActivityMetadata.metaDataElements()) {
updateWithNonEmptyValue(componentInfo.activityMetadata, metaDataElementToString(me));
}
} catch (IllegalAccessException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "IllegalAccessException when gathering " +
"application metadata and subelements for component " + componentInfo.name);
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "InvocationTargetException when gathering " +
"application metadata and subelements for component " + componentInfo.name);
throw new RuntimeException(e);
}
}
// Gather the required broadcast receivers and build their element strings. // Gather the required broadcast receivers and build their element strings.
UsesBroadcastReceivers usesBroadcastReceivers = element.getAnnotation(UsesBroadcastReceivers.class); UsesBroadcastReceivers usesBroadcastReceivers = element.getAnnotation(UsesBroadcastReceivers.class);
if (usesBroadcastReceivers != null) { if (usesBroadcastReceivers != null) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment