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 {
new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> activitiesNeeded =
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 =
new ConcurrentHashMap<String, Set<String>>();
private final ConcurrentMap<String, Set<String>> libsNeeded =
......@@ -511,6 +515,56 @@ public final class Compiler {
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.
*/
......@@ -1012,6 +1066,20 @@ public final class Compiler {
out.write(" <data android:mimeType=\"text/plain\" />\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");
// Companion display a splash screen... define it's activity here
......@@ -1027,6 +1095,7 @@ public final class Compiler {
// Collect any additional <application> subelements into a single set.
Set<Map.Entry<String, Set<String>>> subelements = Sets.newHashSet();
subelements.addAll(activitiesNeeded.entrySet());
subelements.addAll(metadataNeeded.entrySet());
subelements.addAll(broadcastReceiversNeeded.entrySet());
......@@ -1140,6 +1209,8 @@ public final class Compiler {
compiler.generateAssets();
compiler.generateActivities();
compiler.generateMetadata();
compiler.generateActivityMetadata();
compiler.generateBroadcastReceivers();
compiler.generateLibNames();
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 {
public static final String ASSET_DIRECTORY = "component";
public static final String ASSETS_TARGET = "assets";
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 NATIVE_TARGET = "native";
public static final String PERMISSIONS_TARGET = "permissions";
......
......@@ -81,6 +81,8 @@ public final class ComponentListGenerator extends ComponentProcessor {
appendComponentInfo(sb, ComponentDescriptorConstants.NATIVE_TARGET, component.nativeLibraries);
appendComponentInfo(sb, ComponentDescriptorConstants.ASSETS_TARGET, component.assets);
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.BROADCAST_RECEIVERS_TARGET, component.broadcastReceivers);
appendConditionalComponentInfo(component, sb);
......
......@@ -15,6 +15,8 @@ import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
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.UsesLibraries;
import com.google.appinventor.components.annotations.UsesNativeLibraries;
......@@ -671,6 +673,16 @@ public abstract class ComponentProcessor extends AbstractProcessor {
*/
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.
*/
......@@ -753,6 +765,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
nativeLibraries = Sets.newHashSet();
assets = Sets.newHashSet();
activities = Sets.newHashSet();
metadata = Sets.newHashSet();
activityMetadata = Sets.newHashSet();
broadcastReceivers = Sets.newHashSet();
classNameAndActionsBR = Sets.newHashSet();
designerProperties = Maps.newTreeMap();
......@@ -1099,6 +1113,8 @@ public abstract class ComponentProcessor extends AbstractProcessor {
componentInfo.nativeLibraries.addAll(parentComponent.nativeLibraries);
componentInfo.assets.addAll(parentComponent.assets);
componentInfo.activities.addAll(parentComponent.activities);
componentInfo.metadata.addAll(parentComponent.metadata);
componentInfo.activityMetadata.addAll(parentComponent.activityMetadata);
componentInfo.broadcastReceivers.addAll(parentComponent.broadcastReceivers);
// TODO(Will): Remove the following call once the deprecated
// @SimpleBroadcastReceiver annotation is removed. It should
......@@ -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.
UsesBroadcastReceivers usesBroadcastReceivers = element.getAnnotation(UsesBroadcastReceivers.class);
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