Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codenameone.developerguide.snippets.generated;

import com.codename1.gpu.*;
import com.codename1.ui.*;
import com.codename1.ui.animations.*;
import com.codename1.ui.events.*;
import com.codename1.ui.geom.*;
import com.codename1.ui.layouts.*;
import com.codename1.ui.list.*;
import com.codename1.ui.plaf.*;
import com.codename1.ui.util.*;
import com.codename1.components.*;
import com.codename1.charts.models.*;
import com.codename1.charts.renderers.*;
import com.codename1.charts.views.*;
import com.codename1.capture.*;
import com.codename1.io.*;
import com.codename1.l10n.*;
import com.codename1.location.*;
import com.codename1.maps.*;
import com.codename1.media.*;
import com.codename1.messaging.*;
import com.codename1.payment.*;
import com.codename1.processing.*;
import com.codename1.properties.*;
import com.codename1.push.*;
import com.codename1.security.*;
import com.codename1.social.*;
import com.codename1.ui.spinner.*;
import java.io.*;
import com.codename1.analytics.*;
import com.codename1.appreview.*;
import com.codename1.ads.*;
import com.codename1.util.*;
import java.util.*;


class AdvertisingJava009Snippet {

Form form;

// tag::advertising-java-009[]
void startAds() {
AdConfig cfg = new AdConfig()
.testMode(true)
.addTestDevice("YOUR_TEST_DEVICE_ID")
.tagForChildDirectedTreatment(AdConfig.TAG_FALSE)
.maxAdContentRating(AdConfig.RATING_G);

AdManager.initialize(cfg, ready -> {
// false when no provider was installed. It is not a report that
// the network is up: every provider hands its config to the native
// bridge and answers true straight away, while the SDK behind it
// initializes asynchronously. Worth checking anyway, because with
// no provider at all AdConsent reports consent as not required and
// canRequestAds() answers true, so carrying on would walk into
// loadAds() with nothing behind it.
if (!ready) {
showAdFreeUi();
return;
}

// Consent has to be settled before the first load, not before
// initialize: requestConsent presents the GDPR form and, on iOS,
// the App Tracking Transparency prompt, and both need the SDK up.
AdConsent.requestConsent(status -> {
if (AdConsent.canRequestAds()) {
loadAds();
Comment thread
shai-almog marked this conversation as resolved.
Comment thread
shai-almog marked this conversation as resolved.
} else {
// STATUS_REQUIRED with consent withheld. Personalized ads
// are off the table; show the app without them rather
// than blocking on a prompt the user already declined.
showAdFreeUi();
}
});
});
}

void loadAds() {
}

void showAdFreeUi() {
}
// end::advertising-java-009[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Generated from docs/developer-guide source blocks. Edit the guide snippets here, not inline.

// tag::deep-links-routing-json-001[]
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{ "/": "/users/*" },
{ "/": "/promo/*" }
]
}
]
}
}
// end::deep-links-routing-json-001[]

// tag::deep-links-routing-json-002[]
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5", "7B:5A:1F:C9:0E:52:44:7C:82:19:6E:38:0A:B7:D4:11:5C:63:9F:28:E1:47:8B:30:A6:C2:5D:94:F0:6B:38:19"]
}
}
]
// end::deep-links-routing-json-002[]
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
// tag::deep-links-routing-properties-001[]
codename1.arg.ios.associatedDomains=applinks:example.com,applinks:www.example.com
// end::deep-links-routing-properties-001[]

// tag::deep-links-routing-properties-002[]
codename1.arg.android.xintent_filter=<intent-filter android:autoVerify="true">\n <action android:name="android.intent.action.VIEW"/>\n <category android:name="android.intent.category.DEFAULT"/>\n <category android:name="android.intent.category.BROWSABLE"/>\n <data android:scheme="https" android:host="your.domain" android:pathPrefix="/users"/>\n</intent-filter>
// end::deep-links-routing-properties-002[]
35 changes: 33 additions & 2 deletions docs/developer-guide/Advertising.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ include::../demos/common/src/main/snippets/developer-guide/advertising.xml[tag=a

=== Enabling a provider

Enable a provider once at startup. Each library exposes a static `install()`
method that registers its provider with `AdManager`:
Enable a provider once at startup, from your app's `init(Object)`, by calling
the static `install()` method on the provider class named in the table above --
`AdMobProvider.install()`, `AppLovinProvider.install()`,
`LevelPlayProvider.install()` or `MockAdProvider.install()`. Each one registers
its provider with `AdManager` and takes no arguments.


That single call binds the provider; the rest of your code uses only the
Expand All @@ -92,6 +95,34 @@ wrap Google's User Messaging Platform or an equivalent), and on iOS the App
Tracking Transparency prompt must be presented to access the advertising
identifier. The recommended order is to initialize, gather consent, then load:

[source,java]
----
include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/AdvertisingJava009Snippet.java[tag=advertising-java-009,indent=0]
----


Two things about that flow are worth knowing before you rely on it.

The readiness flag says a provider was installed and took the config; it isn't
a report that the network came up. Every provider passes the config to its
native bridge and answers `true` immediately, while the SDK behind it
initializes asynchronously -- so an invalid SDK key surfaces as failing loads
later, not as `false` here.

And `AdConsent.requestConsent` presents a form only where the provider
implements one. AdMob does, through Google's User Messaging Platform. AppLovin
MAX and Unity LevelPlay read consent from an external CMP and the IAB TCF
string instead, so their implementations report `STATUS_NOT_REQUIRED` at once
and `canRequestAds()` always answers `true`. On those two the consent flow is
the CMP's, not this one, and it has to be in place before the first load for
EEA/UK users.

WARNING: On iOS 14 and later the AdMob provider starts the App Tracking
Transparency prompt alongside the User Messaging Platform request and fires its
consent callback when UMP finishes, without waiting for the tracking answer. The
callback can therefore arrive while the system prompt is still on screen, and a
load started from it runs before tracking authorization is settled. Present the ATT
prompt yourself at a point you control, and load after the user has answered.

`AdConfig` also carries the global compliance flags every network requires:
test mode, test device ids, child directed treatment, under-age-of-consent
Expand Down
32 changes: 30 additions & 2 deletions docs/developer-guide/Deep-Links-Routing.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ Host an `apple-app-site-association` JSON file at
`https://your.domain/.well-known/apple-app-site-association` over HTTPS
without redirects. The plugin's `AasaBuilder` produces the payload:

[source,json]
----
include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.json[tag=deep-links-routing-json-001,indent=0]
----


Tell iOS which domains your app claims by setting the
`ios.associatedDomains` build hint -- a comma-separated list of
Expand All @@ -103,10 +108,33 @@ Host an `assetlinks.json` file at
`https://your.domain/.well-known/assetlinks.json`. The plugin's
`AssetLinksBuilder` produces the payload:

[source,json]
----
include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.json[tag=deep-links-routing-json-002,indent=0]
----


Tell Android which URLs to intercept by setting the
`android.xintent_filter` build hint with a verified intent filter for
your domain. The Android builder injects the filter into the manifest.
`android.xintent_filter` build hint. The Android builder injects the
value into the manifest verbatim, so it's the whole `<intent-filter>`
element, with `\n` for the line breaks:

[source,properties]
----
include::../demos/common/src/main/snippets/developer-guide/deep-links-routing.properties[tag=deep-links-routing-properties-002,indent=0]
----

Three parts of that carry weight. `android:autoVerify="true"` is what makes
Android fetch the `assetlinks.json` above at install time and route the link
straight into your app. Leaving it out doesn't fall back to an app chooser:
from Android 12 onward an unverified web link goes to the default browser, and
your filter never sees it until the user turns the association on by hand under
**Settings > Apps > Open by default**. The `<data>` element needs
`android:scheme="https"` and the host, and narrowing it with
`android:pathPrefix` keeps the app from claiming every URL on the domain. And
the hint holds one value, so an app that also registers a custom scheme puts
both `<intent-filter>` elements in the same string rather than setting the hint
twice.

The SHA-256 fingerprint comes from `keytool -list -v -keystore ...`, or
from the Play Console under **Setup > App integrity** when using Play
Expand Down
4 changes: 0 additions & 4 deletions scripts/developer-guide/missing-code-blocks-baseline.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
# Prose that promises a code block where none follows.
# A ratchet: entries may be removed as holes are filled, never added.
# Regenerate with check-missing-code-blocks.py --write-baseline.
Advertising.asciidoc identifier. The recommended order is to initialize, gather consent, then load:
Advertising.asciidoc method that registers its provider with `AdManager`:
Annotation-JSON-XML-Mapping.asciidoc Hand-write a `Mapper<T>` and register it at startup:
Authentication-And-Identity.asciidoc Firebase Auth isn't an OIDC provider -- it issues Google-Identity-Toolkit-style tokens via REST endpoints. `com.codename1.social.FirebaseAuth` wraps those endpoints:
Deep-Links-Routing.asciidoc `AssetLinksBuilder` produces the payload:
Deep-Links-Routing.asciidoc without redirects. The plugin's `AasaBuilder` produces the payload:
Maven-Creating-CN1Libs.adoc Now try it out. Try adding the following code to your application project's main class (or anywhere in the application project, for that matter):
Maven-Creating-CN1Libs.adoc The simulator dispatches every action on the Codename One EDT through `Display.callSerially`, so your method can call `Display.getInstance()`, `Form.show()`, `Dialog.show()`, `ToastBar.showInfoMessage()` and any other CN1 API. Reflection uses the same classloader that loaded `Display`, so cn1lib internals (including package-private classes) resolve normally:
Miscellaneous-Features.asciidoc To solve this sort of used case you have two APIs in `Display`:
Expand Down
Loading