Commit c976f01e authored by William S. Byrne's avatar William S. Byrne Committed by Jeffrey Schiller

Added support for activity starting from any component.

Summary:
Using the @ActivityElement and @UsesActivities annotations, it is now
possible to launch an arbitrary activity from a component without
hacking Compiler.java to register that activity in AndroidManifest.xml.

Using the @ReceiverElement and @UsesBroadcastReceivers annotations, it is
now possible to leverage the full functionality of the Android
framework when creating <receiver> elements in AndroidManifest.xml.

Both of these new annotations allow App Inventor component developers
to add <intent-filter> and <meta-data> subelements to activities and
broadcast receivers declared in AndroidManifest.xml with the
@IntentFilterElement and @MetaDataElement annotations.

-ListPicker has been refactored to use these new annotations.
-BarcodeScanner has been refactored to use these new annotations.
-Twitter has been refactored to use these new annotations.
-Texting has been refactored to use these new annotations.
-Since the NearField component makes changes to the main activity contingent
 on information that is only available at build time, Form and NearField
 cannot be refactored to use these annotations at the moment.
-New unit tests for broadcast receiver and activity generation were added in
 com.google.appinventor.buildserver.CompilerTest.java.
-Added legacy support for the deprecated @SimpleBroadcastReceiver annotation
 so that we don't break extensions that are currently using it.

Change-Id: I2114f2b522bdbcf51e1e1b79a642010ff040e183
parent 2ad58dd7
......@@ -49,16 +49,70 @@ public class CompilerTest extends TestCase {
}
public void testGenerateBroadcastReceiver() throws Exception {
Set<String> componentTypes = Sets.newHashSet("com.google.appinventor.components.runtime.Texting");
String texting = "com.google.appinventor.components.runtime.Texting";
String label = "com.google.appinventor.components.runtime.Label";
Set<String> componentTypes = Sets.newHashSet(texting);
Compiler compiler = new Compiler(null, componentTypes, System.out, System.err, System.err, false, 2048, null);
Set<String> classNames = compiler.generateBroadcastReceiver();
assertEquals(1, classNames.size());
assertTrue(classNames.contains("com.google.appinventor.components.runtime.util.SmsBroadcastReceiver,android.provider.Telephony.SMS_RECEIVED,com.google.android.apps.googlevoice.SMS_RECEIVED"));
compiler.generateBroadcastReceivers();
Map<String, Set<String>> componentReceivers = compiler.getBroadcastReceivers();
Set<String> receivers = componentReceivers.get(texting);
assertEquals(1, receivers.size());
String receiverElementString = receivers.iterator().next();
assertTrue(receiverElementString.contains("com.google.appinventor.components.runtime.util.SmsBroadcastReceiver"));
assertTrue(receiverElementString.contains("android.provider.Telephony.SMS_RECEIVED"));
assertTrue(receiverElementString.contains("com.google.android.apps.googlevoice.SMS_RECEIVED"));
componentTypes = Sets.newHashSet("com.google.appinventor.components.runtime.Texting",
"com.google.appinventor.components.runtime.Label");
componentTypes = Sets.newHashSet(texting, label);
compiler = new Compiler(null, componentTypes, System.out, System.err, System.err, false, 2048, null);
classNames = compiler.generateBroadcastReceiver();
assertEquals(1, classNames.size());
compiler.generateBroadcastReceivers();
componentReceivers = compiler.getBroadcastReceivers();
receivers = componentReceivers.get(texting);
assertEquals(1, receivers.size());
assertTrue(componentReceivers.get(label) == null);
}
public void testGenerateActivities() throws Exception {
String barcodeScanner = "com.google.appinventor.components.runtime.BarcodeScanner";
String listPicker = "com.google.appinventor.components.runtime.ListPicker";
String twitter = "com.google.appinventor.components.runtime.Twitter";
Set<String> componentTypes = Sets.newHashSet(barcodeScanner);
Compiler compiler = new Compiler(null, componentTypes, System.out, System.err, System.err, false, 2048, null);
compiler.generateActivities();
Map<String, Set<String>> componentActivities = compiler.getActivities();
Set<String> activities = componentActivities.get(barcodeScanner);
assertEquals(1, activities.size());
String activityElementString = activities.iterator().next();
assertTrue(activityElementString.contains("name=\"com.google.zxing.client.android.AppInvCaptureActivity\""));
assertTrue(activityElementString.contains("screenOrientation=\"landscape\""));
assertTrue(activityElementString.contains("stateNotNeeded=\"true\""));
assertTrue(activityElementString.contains("configChanges=\"orientation|keyboardHidden\""));
assertTrue(activityElementString.contains("theme=\"@android:style/Theme.NoTitleBar.Fullscreen\""));
assertTrue(activityElementString.contains("windowSoftInputMode=\"stateAlwaysHidden\""));
componentTypes = Sets.newHashSet(listPicker);
compiler = new Compiler(null, componentTypes, System.out, System.err, System.err, false, 2048, null);
compiler.generateActivities();
componentActivities = compiler.getActivities();
activities = componentActivities.get(listPicker);
assertEquals(1, activities.size());
activityElementString = activities.iterator().next();
assertTrue(activityElementString.contains("name=\"com.google.appinventor.components.runtime.ListPickerActivity\""));
assertTrue(activityElementString.contains("configChanges=\"orientation|keyboardHidden\""));
assertTrue(activityElementString.contains("screenOrientation=\"behind\""));
componentTypes = Sets.newHashSet(twitter);
compiler = new Compiler(null, componentTypes, System.out, System.err, System.err, false, 2048, null);
compiler.generateActivities();
componentActivities = compiler.getActivities();
activities = componentActivities.get(twitter);
assertEquals(1, activities.size());
activityElementString = activities.iterator().next();
assertTrue(activityElementString.contains("name=\"com.google.appinventor.components.runtime.WebViewActivity\""));
assertTrue(activityElementString.contains("configChanges=\"orientation|keyboardHidden\""));
assertTrue(activityElementString.contains("screenOrientation=\"behind\""));
// Finally, test for the name attribute of the <intent-filter>'s <action> subelement
assertTrue(activityElementString.contains("name=\"android.intent.action.MAIN\""));
}
}
......@@ -277,6 +277,7 @@
<ai.javac5 destdir="${ComponentProcessingLib-class.dir}">
<include name="${components.pkg}/scripts/ComponentProcessor.java" />
<include name="${components.pkg}/annotations/*.java" />
<include name="${components.pkg}/annotations/androidmanifest/*.java" />
<classpath>
<pathelement location="${public.build.dir}/CommonConstants.jar" />
<pathelement location="${lib.dir}/guava/guava-14.0.1.jar" />
......
......@@ -19,6 +19,7 @@ import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Deprecated
public @interface SimpleBroadcastReceiver {
/**
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.ActivityElement;
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 activities required by
* a component so that corresponding <activity> elements can be added
* to AndroidManifest.xml.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsesActivities {
/**
* An array containing each {@link ActivityElement}
* that is required by the component.
*
* @return the array containing the relevant activities
*/
ActivityElement[] activities();
}
\ No newline at end of file
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.ReceiverElement;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to indicate any broadcast receivers used by
* a component so that corresponding <receiver> elements can be
* created in AndroidManifest.xml.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UsesBroadcastReceivers {
/**
* An array containing each {@link ReceiverElement}
* that is required by the component.
*
* @return the array containing the relevant receivers
*/
ReceiverElement[] receivers();
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to describe an <action> element required by an <intent-filter>
* element so that it can be added to AndroidManifest.xml.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/action-element.html">
* https://developer.android.com/guide/topics/manifest/action-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ActionElement {
/**
* The fully qualified name of the action. For standard actions defined in
* the {@link android.content.Intent} class, prepend "android.intent.action" to
* the "string" in each ACTION_string constant. For example, to specify
* ACTION_MAIN the fully qualified name would be "android.intent.action.MAIN".
* Custom defined actions are conventionally prepended with the package name
* of their containing class, e.g. "com.example.project.ACTION". The name attribute
* is required in any @ActionElement annotation and hence has no default value.
*
* @return the fully qualified name of the action
*/
String name();
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to describe a <category> element, which is an optional subelement
* of an <intent-filter>. A <category> element is a string containing
* additional information about the kind of component that should handle the
* intent. Any number of category descriptions can be placed in an intent, but
* most intents do not require a category.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/category-element.html">
* https://developer.android.com/guide/topics/manifest/category-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CategoryElement {
/**
* The name of the category. Standard categories are defined in the
* {@link android.content.Intent} class as CATEGORY_name constants. The
* name assigned here can be derived from those constants by prefixing
* "android.intent.category." to the name that follows CATEGORY_. For
* example, the string value for {@link android.content.Intent#CATEGORY_LAUNCHER}
* is "android.intent.category.LAUNCHER".
*
* Note: In order to receive implicit intents, you must include the
* CATEGORY_DEFAULT category in the intent filter.
*
* Custom categories should use the package name as a prefix, to ensure that
* they are unique.
*
* The name attribute is required in any @CategoryElement annotation and
* hence has no default value.
*
* @return the name of the category specified by this <category> element
*/
String name();
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Adds a data specification to an <intent-filter> as a <data> subelement.
* The specification can be just a data type {@link #mimeType()}, just a URI,
* or both a data type and a URI. A URI is specified by separate attributes
* for each of its parts:
*
* {@link #scheme()}://{@link #host()}:{@link #port()}[{@link #path()}|{@link #pathPrefix()}|{@link #pathPattern()}]
*
* When neither the MIME data type nor the data URI is specified, the <data> element
* is ignored and not added to the manifest.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/data-element.html">
* https://developer.android.com/guide/topics/manifest/data-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DataElement {
/**
* The scheme part of a URI. This is the minimal essential attribute for
* specifying a URI. If this is not specified, all other URI attributes are
* ignored.
*
* Note: Scheme matching is case sensitive in Android.
*
* @return the scheme for the URI
*/
String scheme() default "";
/**
* The host part of a URI authority. This attribute is meaningless unless
* a scheme attribute is also specified for the filter. If a host is not
* specified for the filter, the port attribute and all the path attributes
* are ignored.
*
* Note: Host matching is case sensitive in Android.
*
* @return the host for the URI
*/
String host() default "";
/**
* The port part of a URI authority. This attribute is meaningful only if the
* scheme and host attributes are also specified for the filter.
*
* @return the port for the URI
*/
String port() default "";
/**
* The path attribute specifies a complete path that is matched against the
* complete path in an Intent object.
*
* @return the complete URI path to the data
*/
String path() default "";
/**
* The pathPrefix attribute specifies a partial path that is matched against
* only the initial part of the path in the Intent object.
*
* @return the specified prefix of the complete data URI path
*/
String pathPrefix() default "";
/**
* The pathPattern attribute specifies a complete path that is matched against
* the complete path in the Intent object, but it can contain the following
* wildcards:
*
* -> An asterisk ('*') matches a sequence of 0 to many occurrences of the
* immediately preceding character.
*
* -> A period followed by an asterisk (".*") matches any sequence of 0 to
* many characters.
*
* @return the complete URI path to the data with any of the aforementioned
* wildcards
*/
String pathPattern() default "";
/**
* A MIME media type, such as image/jpeg or audio/mpeg4-generic. The subtype
* can be the asterisk wildcard (*) to indicate that any subtype matches.
*
* Note: MIME type matching is case sensitive in Android.
*
* @return the MIME media type
*/
String mimeType() default "";
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
/**
* Annotation to describe an <intent-filter> element required by an <activity>
* or a <receiver> element so that it can be added to AndroidManifest.xml.
* <intent-filter> element attributes that are not set explicitly default
* to "" or {} and are ignored when the element is created in the manifest.
* In order for an <intent-filter> element to work properly, it must include
* at least one <action> element in the {@link #actionElements()} attribute.
*
* See {@link ActionElement} for more information.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/intent-filter-element.html">
* https://developer.android.com/guide/topics/manifest/intent-filter-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
public @interface IntentFilterElement {
/**
* The array of actions accepted by this <intent-filter>. By construction,
* <intent-filter> elements must have at least one <action> subelement.
* Thus, this attribute of @IntentFilterElement is required and has no default.
*
* @return the array of actions accepted by this <intent-filter>
*/
ActionElement[] actionElements();
/**
* The array of categories accepted by this <intent-filter>. According to
* the AndroidMainfest.xml specification, these subelements are optional.
*
* @return the array of categories accepted by this <intent-filter>
*/
CategoryElement[] categoryElements() default {};
/**
* The array of data specifications accepted by this <intent-filter>. According to
* the AndroidMainfest.xml specification, these subelements are optional.
*
* @return the array of data URIs accepted by this <intent-filter>
*/
DataElement[] dataElements() default {};
/**
* A reference to a drawable resource representing the parent activity
* or broadcast receiver when that component is presented to
* the user as having the capability described by the filter.
*
* @return a reference to the drawable resource for the filter's parent
*/
String icon() default "";
/**
* A user-readable label for the parent component specified as a reference
* to a string resource. If this attribute is left unspecified, the label
* will default to the label set by the parent component
*
* @return a reference to the string resource to be used as a label
*/
String label() default "";
/**
* The priority that should be given to the parent activity or broadcast
* receiver with regard to handling intents of the type described by the
* filter. This must be specified as an integer in the interval
* (-1000, 1000). If the priority is not set, it will default to 0.
*
* @return the priority of the parent activity or broadcast receiver w.r.t.
* handling intents described by this filter
*/
String priority() default "";
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to describe a <meta-data> element. A <meta-data> element consists
* of a name-value pair for an item of additional, arbitrary data that can be
* supplied to the parent component. For our purposes, the parent component is
* either an <activity> or a <receiver>. A component element can contain any
* number of <meta-data> subelements. The values from all of them are collected
* in a single Bundle object and made available to the component as the
* {@link android.content.pm.PackageItemInfo#metaData} field.
*
* Ordinary values are specified through the value attribute. However, to
* assign a resource ID as the value, use the resource attribute instead.
*
* When using a MetaDataElement, the {@link #name} attribute must be specified along
* with either the {@link #resource()} or the {@link #value()} attribute.
* <meta-data> element attributes that are not set explicitly default to "" and
* are ignored when the element is created in the manifest.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/meta-data-element.html">
* https://developer.android.com/guide/topics/manifest/meta-data-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MetaDataElement {
/**
* A unique name for the data item. By convention, this name should follow
* the Java package name format, e.g. "com.example.project.activity.data".
* The name attribute is required in any @MetaDataElement annotation and
* hence has no default value.
*
* @return the name of the data item
*/
String name();
/**
* A reference to a resource. The ID of the resource is the value assigned
* to the data item. The ID can be retrieved from the meta-data Bundle by the
* {@link android.os.BaseBundle#getInt(String)} method.
*
* @return a reference to the specified resource
*/
String resource() default "";
/**
* The value assigned to the item. The data types that can be assigned as
* values and the {@link android.os.Bundle} methods that components use to
* retrieve those values are detailed below:
*
* -> A String value, using double backslashes (\\) to escape characters,
* such as "\\n" and "\\uxxxxx" for a Unicode character, can be accessed
* using {@link android.os.Bundle#getString(String)}.
*
* -> An Integer value, such as "100", can be accessed using
* {@link android.os.Bundle#getInt(String)}.
*
* -> A Boolean value, either "true" or "false", can be accessed using
* {@link android.os.Bundle#getBoolean(String)}.
*
* -> A Color value, in the form "#rgb", "#argb", "#rrggbb", or "#aarrggbb",
* can be accessed using {@link android.os.Bundle#getInt(String)}.
*
* -> A Float value, such as "1.23", can be accessed using
* {@link android.os.Bundle#getFloat(String)}
*
* @return the value to be assigned to this data item
*/
String value() default "";
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 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.androidmanifest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to describe a <receiver> element required by a component so that
* it can be added to AndroidManifest.xml. <receiver> elements indicate that
* a component is a broadcast receiver. <receiver> element attributes that are not
* set explicitly default to "" or {} and are ignored when the element is created
* in the manifest.
*
* Note: Some of this documentation is adapted from the Android framework specification
* linked below. That documentation is licensed under the
* {@link <a href="https://creativecommons.org/licenses/by/2.5/">
* Creative Commons Attribution license v2.5
* </a>}.
*
* See {@link <a href="https://developer.android.com/guide/topics/manifest/meta-data-element.html">
* https://developer.android.com/guide/topics/manifest/meta-data-element.html
* </a>}.
*
* @author will2596@gmail.com (William Byrne)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ReceiverElement {
/**
* An array containing any intent filters used by this <receiver> element.
*
* @return an array containing the <intent-filter> subelements for this
* <receiver> element
*/
IntentFilterElement[] intentFilters() default {};
/**
* An array containing any meta data used by this <receiver> element.
*
* @return an array containing the <meta-data> subelements for this
* <receiver> element
*/
MetaDataElement[] metaDataElements() default {};
/**
* The name of the class that implements the broadcast receiver, a subclass
* of {@link android.content.BroadcastReceiver}. This should be a fully
* qualified class name (such as, "com.example.project.ReportReceiver").
* The name attribute is required in any @ReceiverElement annotation and
* hence has no default value.
*
* @return the broadcast receiver class name
*/
String name();
/**
* Whether or not the broadcast receiver can be instantiated by the system.
* "true" if it can be, and "false" if not. The default value is "true".
*
* The <application> element has its own enabled attribute that applies
* to all application components, including broadcast receivers. The
* <application> and <receiver> attributes must both be "true" for the
* broadcast receiver to be enabled. If either is "false", it is disabled;
* it cannot be instantiated.
*
* @return the receiver enabled attribute
*/
String enabled() default "";
/**
* Whether or not the broadcast receiver can receive messages from sources
* outside its application — "true" if it can, and "false" if not. If "false",
* the only messages the broadcast receiver can receive are those sent by
* components of the same application or applications with the same user ID.
* For our purposes, those components are other broadcast receivers and
* activities.
*
* The default value depends on whether the broadcast receiver contains intent
* filters. The absence of any filters means that it can be invoked only by
* Intent objects that specify its exact class name. This implies that the
* receiver is intended only for application-internal use (since others would
* not normally know the class name). So in this case, the default value is
* "false". On the other hand, the presence of at least one filter implies
* that the broadcast receiver is intended to receive intents broadcast by
* the system or other applications, so the default value is "true".
*
* This attribute is not the only way to limit a broadcast receiver's external
* exposure. You can also use a permission to limit the external entities that
* can send it messages (see the {@link #permission()} attribute).
*
* @return the receiver exported attribute
*/
String exported() default "";
/**
* An icon representing the broadcast receiver. This attribute must be set as
* a reference to a drawable resource containing the image definition. If it is
* not set, the icon specified for the application as a whole is used instead.
*
* The broadcast receiver's icon — whether set here or by the <application>
* element — is also the default icon for all the receiver's intent filters
* (see the {@link IntentFilterElement#icon()} attribute).
*
* @return the receiver icon attribute
*/
String icon() default "";
/**
* A user-readable label for the broadcast receiver. If this attribute is not
* set, the label set for the application as a whole is used instead. The
* broadcast receiver's label — whether set here or by the <application>
* element — is also the default label for all the receiver's intent filters
* (see the {@link IntentFilterElement#label()} attribute).
*
* The label should be set as a reference to a string resource, so that it can
* be localized like other strings in the user interface. However, as a
* convenience while you're developing the application, it can also be set as
* a raw string.
*
* @return the receiver label attribute
*/
String label() default "";
/**
* The name of a permission that broadcasters must have to send a message to
* the broadcast receiver. If this attribute is not set, the permission set
* by the <application> element's permission attribute applies to the broadcast
* receiver. If neither attribute is set, the receiver is not protected by a
* permission.
*
* @return the receiver permission attribute
*/
String permission() default "";
/**
* The name of the process in which the broadcast receiver should run. Normally,
* all components of an application run in the default process created for the
* application. For our purposes, those components are broadcast receivers and
* activities. It has the same name as the application package. Each component
* can override the default with its own process attribute, allowing you to
* spread your application across multiple processes.
*
* If the name assigned to this attribute begins with a colon (':'), a new
* process, private to the application, is created when it's needed and the
* broadcast receiver runs in that process. If the process name begins with
* a lowercase character, the receiver will run in a global process of that
* name, provided that it has permission to do so. This allows components
* (broadcast receivers and activities) in different applications to share
* a process, reducing resource usage.
*
* @return the receiver process attribute
*/
String process() default "";
}
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2016 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
/**
* Defines annotations to model XML elements for use later in writing
* AndroidManifest.xml.
*
* @see com.google.appinventor.components.scripts
*
* @author will2596@gmail.com (William Byrne)
*/
package com.google.appinventor.components.annotations.androidmanifest;
\ No newline at end of file
......@@ -15,6 +15,8 @@ import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesActivities;
import com.google.appinventor.components.annotations.androidmanifest.ActivityElement;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
......@@ -25,7 +27,6 @@ import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.ComponentName;
import android.util.Log;
/**
* Component for scanning a barcode and getting back the resulting string.
......@@ -38,6 +39,14 @@ import android.util.Log;
nonVisible = true,
iconName = "images/barcodeScanner.png")
@SimpleObject
@UsesActivities(activities = {
@ActivityElement(name = "com.google.zxing.client.android.AppInvCaptureActivity",
screenOrientation = "landscape",
stateNotNeeded = "true",
configChanges = "orientation|keyboardHidden",
theme = "@android:style/Theme.NoTitleBar.Fullscreen",
windowSoftInputMode = "stateAlwaysHidden")
})
@UsesPermissions(permissionNames = "android.permission.CAMERA")
@UsesLibraries(libraries = "Barcode.jar,core.jar")
public class BarcodeScanner extends AndroidNonvisibleComponent
......
......@@ -95,7 +95,8 @@ import com.google.appinventor.components.runtime.util.ViewUtil;
description = "Top-level component containing all other components in the program",
showOnPalette = false)
@SimpleObject
@UsesPermissions(permissionNames = "android.permission.INTERNET,android.permission.ACCESS_WIFI_STATE,android.permission.ACCESS_NETWORK_STATE")
@UsesPermissions(permissionNames = "android.permission.INTERNET,android.permission.ACCESS_WIFI_STATE," +
"android.permission.ACCESS_NETWORK_STATE")
public class Form extends Activity
implements Component, ComponentContainer, HandlesEventDispatching,
OnGlobalLayoutListener {
......
......@@ -11,6 +11,8 @@ import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesActivities;
import com.google.appinventor.components.annotations.androidmanifest.ActivityElement;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
......@@ -41,6 +43,11 @@ import android.view.WindowManager;
"(<code>TextAlignment</code>, <code>BackgroundColor</code>, etc.) and " +
"whether it can be clicked on (<code>Enabled</code>).</p>")
@SimpleObject
@UsesActivities(activities = {
@ActivityElement(name = "com.google.appinventor.components.runtime.ListPickerActivity",
configChanges = "orientation|keyboardHidden",
screenOrientation = "behind")
})
public class ListPicker extends Picker implements ActivityResultListener, Deleteable, OnResumeListener {
private static final String LIST_ACTIVITY_CLASS = ListPickerActivity.class.getName();
......
......@@ -20,6 +20,8 @@ import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
......@@ -36,9 +38,12 @@ import com.google.appinventor.components.annotations.SimpleEvent;
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.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesBroadcastReceivers;
import com.google.appinventor.components.annotations.androidmanifest.ActionElement;
import com.google.appinventor.components.annotations.androidmanifest.IntentFilterElement;
import com.google.appinventor.components.annotations.androidmanifest.ReceiverElement;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.ComponentConstants;
import com.google.appinventor.components.common.PropertyTypeConstants;
......@@ -122,9 +127,15 @@ import android.widget.Toast;
"google-http-client-android3-beta.jar," +
"google-oauth-client-beta.jar," +
"guava-14.0.1.jar")
@SimpleBroadcastReceiver(
className = "com.google.appinventor.components.runtime.util.SmsBroadcastReceiver",
actions = "android.provider.Telephony.SMS_RECEIVED, com.google.android.apps.googlevoice.SMS_RECEIVED")
@UsesBroadcastReceivers(receivers = {
@ReceiverElement(name = "com.google.appinventor.components.runtime.util.SmsBroadcastReceiver",
intentFilters = {
@IntentFilterElement(actionElements = {
@ActionElement(name = "android.provider.Telephony.SMS_RECEIVED"),
@ActionElement(name = "com.google.android.apps.googlevoice.SMS_RECEIVED")
})
})
})
public class Texting extends AndroidNonvisibleComponent
implements Component, OnResumeListener, OnPauseListener, OnInitializeListener, OnStopListener {
......
......@@ -37,6 +37,10 @@ import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesActivities;
import com.google.appinventor.components.annotations.androidmanifest.ActivityElement;
import com.google.appinventor.components.annotations.androidmanifest.IntentFilterElement;
import com.google.appinventor.components.annotations.androidmanifest.ActionElement;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
......@@ -78,6 +82,16 @@ import com.google.appinventor.components.runtime.util.ErrorMessages;
@SimpleObject
@UsesPermissions(permissionNames = "android.permission.INTERNET")
@UsesLibraries(libraries = "twitter4j.jar," + "twitter4jmedia.jar")
@UsesActivities(activities = {
@ActivityElement(name = "com.google.appinventor.components.runtime.WebViewActivity",
configChanges = "orientation|keyboardHidden",
screenOrientation = "behind",
intentFilters = {
@IntentFilterElement(actionElements = {
@ActionElement(name = "android.intent.action.MAIN")
})
})
})
public final class Twitter extends AndroidNonvisibleComponent implements
ActivityResultListener, Component {
private static final String ACCESS_TOKEN_TAG = "TwitterOauthAccessToken";
......
......@@ -14,8 +14,8 @@ import javax.tools.Diagnostic;
import javax.tools.FileObject;
/**
* Tool to generate a list of the simple component types, permissions, libraries, and Broadcast Receivers
* (build info) required for each component.
* Tool to generate a list of the simple component types, permissions, libraries, activities
* and Broadcast Receivers (build info) required for each component.
*
* @author lizlooney@google.com (Liz Looney)
*/
......@@ -25,7 +25,15 @@ public final class ComponentListGenerator extends ComponentProcessor {
private static final String LIBRARIES_TARGET = "libraries";
private static final String ASSETS_TARGET = "assets";
private static final String NATIVE_TARGET = "native";
private static final String BROADCAST_RECEIVERS_TARGET = "broadcastReceivers";
private static final String ACTIVITIES_TARGET = "activities";
// TODO(Will): Remove the following target once the deprecated
// @SimpleBroadcastReceiver annotation is removed. It should
// should remain for the time being because otherwise we'll break
// extensions currently using @SimpleBroadcastReceiver.
private static final String BROADCAST_RECEIVER_TARGET = "broadcastReceiver";
// Where to write results. Build Info is the collection of permissions, asset and library info.
private static final String COMPONENT_LIST_OUTPUT_FILE_NAME = "simple_components.txt";
private static final String COMPONENT_BUILD_INFO_OUTPUT_FILE_NAME =
......@@ -77,18 +85,24 @@ public final class ComponentListGenerator extends ComponentProcessor {
private static void outputComponentBuildInfo(ComponentInfo component, StringBuilder sb) {
sb.append("{\"type\": \"");
sb.append(component.type + "\"");
sb.append(component.type).append("\"");
appendComponentInfo(sb, PERMISSIONS_TARGET, component.permissions);
appendComponentInfo(sb, LIBRARIES_TARGET, component.libraries);
appendComponentInfo(sb, NATIVE_TARGET, component.nativeLibraries);
appendComponentInfo(sb, ASSETS_TARGET, component.assets);
appendComponentInfo(sb, ACTIVITIES_TARGET, component.activities);
appendComponentInfo(sb, BROADCAST_RECEIVERS_TARGET, component.broadcastReceivers);
// TODO(Will): Remove the following call once the deprecated
// @SimpleBroadcastReceiver annotation is removed. It should
// should remain for the time being because otherwise we'll break
// extensions currently using @SimpleBroadcastReceiver.
appendComponentInfo(sb, BROADCAST_RECEIVER_TARGET, component.classNameAndActionsBR);
sb.append("}");
}
private static void appendComponentInfo(StringBuilder sb,
String infoName, Set<String> infoEntries) {
sb.append(", \"" + infoName + "\": [");
sb.append(", \"").append(infoName).append("\": [");
String separator = "";
for (String infoEntry : infoEntries) {
sb.append(separator).append("\"").append(infoEntry).append("\"");
......
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