Skip to content

Commit

Permalink
APL-CORE: March 2022 Release of APL 1.9 compilant core engine (1.9.1)
Browse files Browse the repository at this point in the history
For more details on this release refer to CHANGELOG.md

To learn about APL see: https://developer.amazon.com/docs/alexa-presentation-language/understand-apl.html
  • Loading branch information
durjo-amz committed Mar 25, 2022
1 parent 4ad5548 commit 569f477
Show file tree
Hide file tree
Showing 45 changed files with 2,009 additions and 668 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [1.9.1]

### Changed

- Performance improvements.
- Bug fixes.

## [1.9.0]

This release adds support for version 1.9 of the APL specification.
Expand Down
15 changes: 5 additions & 10 deletions aplcore/include/apl/component/multichildscrollablecomponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,6 @@ class MultiChildScrollableComponent : public ScrollableComponent {
void ensureChildLayout(const CoreComponentPtr& child, bool useDirtyFlag) override;

protected:
/**
* Finds the immediate child, if any, at the given position.
*
* @param position Point to test for a child.
* @return Component Pointer to an immediate child, or null.
*/
ComponentPtr findDirectChildAtPosition(const Point& position) const;

/**
* Some components may need to apply adjustment logic to child spacing.
Expand Down Expand Up @@ -176,21 +169,23 @@ class MultiChildScrollableComponent : public ScrollableComponent {

/**
* Find child which center is closest to provided position. Search reduced to the current scrolling direction.
* @param position position to measure from.
* @param position position to search.
* @param byDistance account for distance, uses order otherwise.
* @return child pointer.
*/
ComponentPtr findChildCloseToPosition(const Point& position) const;
ComponentPtr findChildCloseToPosition(const Point& position, bool byDistance = false) const;

void attachYogaNodeIfRequired(const CoreComponentPtr& coreChild, int index) override;
bool attachChild(const CoreComponentPtr& child, size_t index);
void runLayoutHeuristics(size_t anchorIdx, float childCache, float pageSize, bool useDirtyFlag, bool first);
void fixScrollPosition(const Rect& oldAnchorRect, const Rect& anchorRect);
Point getPaddedScrollPosition(LayoutDirection layoutDirection) const;
void processLayoutChangesInternal(bool useDirtyFlag, bool first, bool delayed);
void processLayoutChangesInternal(bool useDirtyFlag, bool first, bool delayed, bool needsFullReProcess);

private:
Range mIndexesSeen;
Range mEnsuredChildren;
Range mAvailableRange;
bool mChildrenVisibilityStale = false;

// These cache variables are being used for event property calculation (lazy calculation)
Expand Down
78 changes: 47 additions & 31 deletions aplcore/include/apl/content/jsondata.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ namespace apl {
* JSON data with a consistent surface area.
*/
class JsonData {
private:
enum Type { kDocument, kValue, kNullPtr };

public:
/**
* Initialize by moving an existing JSON document.
* @param document
*/
JsonData(rapidjson::Document&& document)
: mHasDocument(true),
mDocument(std::move(document)),
mValue(mDocument)
{
}
: mDocument(std::move(document)),
mType(kDocument)
{}

/**
* Initialize by reference to an existing JSON document. The document
Expand All @@ -51,32 +52,33 @@ class JsonData {
* @param value
*/
JsonData(const rapidjson::Value& value)
: mHasDocument(false),
mValue(value)
: mValuePtr(&value),
mType(kValue)
{}

/**
* Initialize by parsing a std::string.
* @param raw
*/
JsonData(const std::string& raw)
: mHasDocument(true),
mDocument(),
mOk(mDocument.Parse<rapidjson::kParseValidateEncodingFlag | rapidjson::kParseStopWhenDoneFlag>(raw.c_str())),
mValue(mDocument)
{}
: mType(kDocument)
{
mDocument.Parse<rapidjson::kParseValidateEncodingFlag |
rapidjson::kParseStopWhenDoneFlag>(raw.c_str());
}

/**
* Initialize by parsing a raw string. The string may be released
* immediately.
* @param raw
*/
JsonData(const char *raw)
: mHasDocument(true),
mDocument(),
mOk(mDocument.Parse<rapidjson::kParseValidateEncodingFlag | rapidjson::kParseStopWhenDoneFlag>(raw)),
mValue(mDocument)
{}
: mType(raw ? kDocument : kNullPtr)
{
if (raw != nullptr)
mDocument.Parse<rapidjson::kParseValidateEncodingFlag |
rapidjson::kParseStopWhenDoneFlag>(raw);
}

/**
* Initialize by parsing a raw string in situ. The string may be
Expand All @@ -85,39 +87,54 @@ class JsonData {
* @param raw
*/
JsonData(char *raw)
: mHasDocument(true),
mDocument(),
mOk(mDocument.Parse<rapidjson::kParseValidateEncodingFlag | rapidjson::kParseStopWhenDoneFlag>(raw)),
mValue(mDocument)
{}
: mType(raw ? kDocument : kNullPtr)
{
if (raw != nullptr)
mDocument.ParseInsitu<rapidjson::kParseValidateEncodingFlag |
rapidjson::kParseStopWhenDoneFlag>(raw);
}

/**
* @return True if this appears to be a valid JSON object.
*/
operator bool() const {
return !mOk.IsError();
switch (mType) {
case kDocument:
return !mDocument.HasParseError();
case kValue:
return true;
case kNullPtr:
default:
return false;
}
}

/**
* @return The offset of the first parse error.
*/
unsigned int offset() const {
return mOk.Offset();
return mType == kDocument ? mDocument.GetErrorOffset() : 0;
}

/**
* @return The human-readable error state of the parser.
*/
const char * error() const {
return rapidjson::GetParseError_En(mOk.Code());
switch (mType) {
case kDocument:
return rapidjson::GetParseError_En(mDocument.GetParseError());
case kValue:
return "Value-constructed; no error";
case kNullPtr:
default:
return "Nullptr";
}
}

/**
* @return A reference to the top-level rapidjson Value.
*/
const rapidjson::Value& get() const {
return mHasDocument ? mDocument : mValue;
}
const rapidjson::Value& get() const { return mType == kValue ? *mValuePtr : mDocument; }

/**
* @return JSON serialized to a string.
Expand All @@ -130,10 +147,9 @@ class JsonData {
std::string toDebugString() const;

private:
bool mHasDocument;
rapidjson::Document mDocument;
rapidjson::ParseResult mOk;
const rapidjson::Value& mValue;
const rapidjson::Value *mValuePtr = nullptr;
Type mType;
};

} // namespace APL
Expand Down
2 changes: 1 addition & 1 deletion aplcore/include/apl/content/rootconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class RootConfig {
kExperimentalFeatureExtensionProvider,
/// Focus EditText component on tap
kExperimentalFeatureFocusEditTextOnTap,
/// Send even when core assumes keyboard input is required
/// Send event when core assumes keyboard input is required
kExperimentalFeatureRequestKeyboard,
};

Expand Down
12 changes: 6 additions & 6 deletions aplcore/include/apl/engine/componentdependant.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class CoreComponent;
class ComponentDependant : public Dependant {
public:
/**
*
* Construct a downstream component dependency
* @param downstreamComponent The downstream or target component
* @param downstreamKey The property that will be modified
* @param equation The expression which will be evaluated to recalculate downstream.
Expand All @@ -54,11 +54,11 @@ class ComponentDependant : public Dependant {
/**
* Internal constructor: do not call. Use ComponentDependant::create instead.
*
* @param downstreamComponent
* @param downstreamKey
* @param equation
* @param bindingContext
* @param bindingFunction
* @param downstreamComponent The downstream or target component
* @param downstreamKey The property that will be modified
* @param equation The expression which will be evaluated to recalculate downstream.
* @param bindingContext The context where the equation will be bound
* @param bindingFunction The binding function that will be applied after evaluating the equation
*/
ComponentDependant(const CoreComponentPtr& downstreamComponent,
PropertyKey downstreamKey,
Expand Down
14 changes: 10 additions & 4 deletions aplcore/include/apl/engine/propdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,22 @@ inline Object asVectorGraphicSource(const Context& context, const Object& object
}

inline Object asImageSourceArray(const Context& context, const Object& object) {
// We don't want to break current runtimes
if (object.isString())
return object;

std::vector<Object> data;
for (auto& m : arrayify(context, object)) {
auto request = m.isString() ? m : URLRequest::create(context, m);
if (!request.isNull())
data.emplace_back(std::move(request));
}

if (data.empty())
return "";

// In case of URLs the spec enforces an array of elements.
// We don't want to break runtimes that are not using urls and depend
// on the old logic that evaluates the strings via arrayify
if (data.size() == 1 && data.front().isString())
return data.front();

return Object(std::move(data));
}

Expand Down
1 change: 1 addition & 0 deletions aplcore/include/apl/engine/rootcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ class RootContext : public std::enable_shared_from_this<RootContext>,
void scheduleTickHandler(const Object& handler, double delay);
void processTickHandlers();
void clearPendingInternal(bool first) const;
void updateTimeInternal(apl_time_t elapsedTime, apl_time_t utcTime);

private:
ContentPtr mContent;
Expand Down
12 changes: 10 additions & 2 deletions aplcore/include/apl/extension/extensionmediator.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,27 +267,35 @@ class ExtensionMediator : public std::enable_shared_from_this<ExtensionMediator>
/**
* Get the extension client corresponding to requested uri.
* @param uri extension URI.
* @return Proxy, if exists, null otherwise.
* @return ExtensionClient, if exists, null otherwise.
*/
ExtensionClientPtr getClient(const std::string& uri);

/**
* Send a resource to an extension.
* @param component ExtensionComponent reference.
* @param uri Extension URI.
* @param resourceHolder ResourceHolder reference.
*/
void sendResourceReady(const std::string& uri,
const alexaext::ResourceHolderPtr& resourceHolder);

/**
* Component resource could not be acquired.
* @param component ExtensionComponent reference.
* @param errorCode Failure error code.
* @param error Message associated with error code.
*/
void resourceFail(const ExtensionComponentPtr& component, int errorCode, const std::string& error);

// access to the extensions
std::weak_ptr<alexaext::ExtensionProvider> mProvider;
// access to the extension resources
std::weak_ptr<alexaext::ExtensionResourceProvider> mResourceProvider;
// context the context that events and data updates are forwarded to
// the context that events and data updates are forwarded to
std::weak_ptr<RootContext> mRootContext;
// reference to associated config
std::weak_ptr<RootConfig> mRootConfig;
// retro extension wrapper used for message passing
std::map<std::string, std::shared_ptr<ExtensionClient>> mClients;
// executor to enqueue/sequence message processing
Expand Down
44 changes: 25 additions & 19 deletions aplcore/include/apl/livedata/livedataobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class LiveMapObject;
*
* This is an abstract base class. Do not create instances of this object.
*/
class LiveDataObject : public ObjectData,
class LiveDataObject : public BaseArrayData,
public std::enable_shared_from_this<LiveDataObject> {
public:
using FlushCallback = std::function<void(const std::string, LiveDataObject&)>;
Expand Down Expand Up @@ -76,20 +76,23 @@ class LiveDataObject : public ObjectData,
virtual DataSourceConnectionPtr getDataSourceConnection() const { return nullptr; }

/**
* Flush tracking changes
* Called on all live data objects before any are flushed.
*
* This is done to give an opportunity to freeze any information that should not change during the course of the
* overall live data flush. For example, we do not want to call new flush callback listeners that are added during
* the course of flushing data, since they already have access to the latest data. Note that this assumes that live
* data con only change outside of the data flushing stage. If we add a feature that allows the data flushing
* pathway to modify the data in some way, we'll have to revisit this assumption.
*/
virtual void flush()
{
auto context = mContext.lock();
if (context)
context->recalculateDownstream(mKey, true);

for (const auto& m : mFlushCallbacks)
m.second(mKey, *this);

mReplaced = false;
void preFlush() {
mMaxWatcherTokenBeforeFlush = mWatcherToken;
}

/**
* Flush tracking changes
*/
virtual void flush();

/**
* @return The data-binding context that the object is defined within.
*/
Expand All @@ -105,18 +108,19 @@ class LiveDataObject : public ObjectData,
* @param callback The callback to be executed.
* @return An opaque token for the removeFlushCallback(int) routine.
*/
int addFlushCallback(FlushCallback&& callback) {
int token = mWatcherToken++;
mFlushCallbacks.emplace(token, std::move(callback));
return token;
}
int addFlushCallback(FlushCallback&& callback);

/**
* Remove a watcher
* @param token The opaque token returned when the watcher was registered.
*/
void removeFlushCallback(int token) {
mFlushCallbacks.erase(mFlushCallbacks.find(token));
void removeFlushCallback(int token);

// ObjectData overrides.
bool operator==(const ObjectData& rhs) const override {
// In progress of changes propagation. Considered not-equal for comparisons.
if (mIsFlushing) return false;
return BaseArrayData::operator==(rhs);
}

protected:
Expand All @@ -128,8 +132,10 @@ class LiveDataObject : public ObjectData,
std::string mKey;
std::map<int, FlushCallback> mFlushCallbacks;
int mWatcherToken = 100;
int mMaxWatcherTokenBeforeFlush = -1;
bool mReplaced = false;
int mToken = -1;
bool mIsFlushing = false;
};

} // namespace apl
Expand Down
Loading

0 comments on commit 569f477

Please sign in to comment.