Skip to content

Conversation

@n8fr8
Copy link

@n8fr8 n8fr8 commented Oct 30, 2025

Changes in this pull request

Adds new package to Kotlin code for providing a ManifestBuilder interface for creating valid C2PA JSON Manifests

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • [ X ] New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • All applicable changes have been documented
  • Any TO DO items (or similar) have been entered as GitHub issues and the link to that issue has been included in a comment

@codecov
Copy link

codecov bot commented Dec 4, 2025

Codecov Report

❌ Patch coverage is 0% with 707 lines in your changes missing coverage. Please review.
✅ Project coverage is 27.61%. Comparing base (ea68831) to head (f73f171).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...n/org/contentauth/c2pa/manifest/ManifestHelpers.kt 0.00% 281 Missing ⚠️
...rg/contentauth/c2pa/manifest/AttestationBuilder.kt 0.00% 273 Missing ⚠️
...n/org/contentauth/c2pa/manifest/ManifestBuilder.kt 0.00% 153 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (ea68831) and HEAD (f73f171). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (ea68831) HEAD (f73f171)
4 2
Additional details and impacted files
@@             Coverage Diff             @@
##             main       #7       +/-   ##
===========================================
- Coverage   53.65%   27.61%   -26.04%     
===========================================
  Files          13       16        +3     
  Lines         863     1503      +640     
  Branches      116      186       +70     
===========================================
- Hits          463      415       -48     
- Misses        327     1015      +688     
  Partials       73       73               

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@scouten-adobe scouten-adobe requested a review from ok-nick December 4, 2025 19:24
Copy link

@ok-nick ok-nick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I have a few comments.

const val OPENED = "c2pa.opened"
const val PLACED = "c2pa.placed"
const val DRAWING = "c2pa.drawing"
const val COLOR_ADJUSTMENTS = "c2pa.color_adjustments"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are new actions here such as c2pa.adjustedColor and perhaps a few more defined here. We should probably add them upstream in the Rust SDK too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We kind of went overboard in adding possible actions. We were also considering paring back, to just focus on the typical ones for a mobile app in this release. For now, we can just leave them, and then review and plan after the merge.

data class Ingredient(
val title: String? = null,
val format: String,
val instanceId: String = UUID.randomUUID().toString(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to stick to the default instance ID generation that happens in the Rust SDK. Are there any downsides in doing that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I don't think so. We can rely on that. Let me make sure we don't have any other reason to generate it here.

val documentId: String? = null,
val provenance: String? = null,
val hash: String? = null,
val relationship: String = "parentOf",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can only be 1 parent ingredient in a manifest, thus the Rust SDK defaults to "componentOf." I have the same question if we need to default here or in the Rust SDK.

data class Thumbnail(
val format: String,
val identifier: String,
val contentType: String = "image/jpeg"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come the format and contentType are separate fields? Should we be defaulting to JPEG? The SDK will infer the format from the identifier and may auto-generate PNG, JPEG, or other formats depending on the circumstance.

private var claimGenerator: ClaimGenerator? = null
private var format: String? = null
private var title: String? = null
private var instanceId: String = UUID.randomUUID().toString()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question about the UUID default here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workong on removing these now, and will update with a push soon, once testing is complete.

)

class ManifestBuilder {
private var claimGenerator: ClaimGenerator? = null
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be multiple claim generators.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of this generator though, there is only 1 instance active within our builder.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claimGenerator is now an array, and multiple can be added

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ok-nick this was true in C2PA version 1.x (and for that reason, may still be exposed in the core SDK). In C2PA 2.2, there can only be one claim generator. (See https://spec.c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_schema.)

Comment on lines +12 to +13
claimGeneratorName: String = "Android C2PA SDK",
claimGeneratorVersion: String = "1.0.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Rust SDK we inject our Rust SDK specific claim generator into the user-specified list. Maybe we should consider doing that here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the Rust SDK specific one would be in addition or included in the name of what the user provides?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scouten-adobe clarified in #7 (comment) that there's only one claim generator in v2 claims. I agree with what you have implemented here already then.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach to consider is generating these type definitions automatically from the JSON schemas exported in the Rust SDK. I believe the Node and JS SDK take a similar approach, not sure if that's possible here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. We did some variation on that at first. I will review and consider. This code is not just type definition however, as there is some additional logic of the builder, I but I understand what you mean. We are trying to keep this Kotlin/Android like while also making it easy to maintain.

Comment on lines 37 to 38
const val DATA_HASH = "c2pa.data_hash"
const val HASH_DATA = "c2pa.hash.data"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c2pa.hash.data is the correct label here, I'm not sure if the other one exists.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

}
}

object C2PAAssertionTypes {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if all of these assertion types are defined in the spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants