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
27 changes: 23 additions & 4 deletions .github/workflows/ad-cn1lib-ios-native-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ on:
- 'maven/cn1-admob/**'
- 'maven/cn1-applovin/**'
- 'maven/cn1-unity-levelplay/**'
- 'vm/ByteCodeTranslator/src/cn1_globals.h'
- 'vm/ByteCodeTranslator/src/cn1_virtual_thread.h'
- 'scripts/check-admob-ios-link.sh'
- 'vm/ByteCodeTranslator/**'
- 'vm/pom.xml'
- '.github/workflows/ad-cn1lib-ios-native-check.yml'
push:
branches: [master]
paths:
- 'maven/cn1-admob/**'
- 'maven/cn1-applovin/**'
- 'maven/cn1-unity-levelplay/**'
- 'vm/ByteCodeTranslator/src/cn1_globals.h'
- 'vm/ByteCodeTranslator/src/cn1_virtual_thread.h'
- 'scripts/check-admob-ios-link.sh'
- 'vm/ByteCodeTranslator/**'
- 'vm/pom.xml'
- '.github/workflows/ad-cn1lib-ios-native-check.yml'

concurrency:
Expand Down Expand Up @@ -200,3 +202,20 @@ jobs:
-destination 'generic/platform=iOS Simulator' \
CODE_SIGNING_ALLOWED=NO \
build

link-admob-app:
name: Link AdMob app (${{ matrix.sdk }})
runs-on: macos-15
strategy:
fail-fast: false
matrix:
sdk: [iphoneos, iphonesimulator]
steps:
- uses: actions/checkout@v6
# Temurin 8 is unavailable on macOS ARM64; Zulu supplies native Java 8.
- uses: actions/setup-java@v5
with:
distribution: zulu
java-version: '8'
- name: Link the native bridge and SDK into an app
run: scripts/check-admob-ios-link.sh "$RUNNER_TEMP/admob-link" "${{ matrix.sdk }}"
45 changes: 45 additions & 0 deletions .github/workflows/admob-android-runtime-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: AdMob Android banner runtime check

on:
workflow_dispatch:
pull_request:
branches: [master]
paths:
- 'maven/cn1-admob/**'
- 'scripts/cn1lib-api-check/admob-runtime/**'
- '.github/workflows/admob-android-runtime-check.yml'
push:
branches: [master]
paths:
- 'maven/cn1-admob/**'
- 'scripts/cn1lib-api-check/admob-runtime/**'
- '.github/workflows/admob-android-runtime-check.yml'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
banner-measurement:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: '8.13'
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 35
arch: x86_64
disable-animations: true
script: gradle -p scripts/cn1lib-api-check/admob-runtime connectedDebugAndroidTest
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,14 @@ public View createBanner(final int handle, final String adUnitId, final int size
public void run() {
AdView adView = new AdView(activity);
adView.setAdUnitId(adUnitId);
adView.setAdSize(mapSize(activity, sizeType, widthDp));
AdSize size = mapSize(activity, sizeType, widthDp);
adView.setAdSize(size);
// AndroidPeer reads measured dimensions in its default peer mode.
// The AdView has no parent yet, so Android has not measured it:
// wrapping it now would cache a 1px preferred height in CN1.
adView.measure(
View.MeasureSpec.makeMeasureSpec(size.getWidthInPixels(activity), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(size.getHeightInPixels(activity), View.MeasureSpec.EXACTLY));
banners.put(handle, adView);
out[0] = adView;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# Reserved for build hints appended to the consuming app's properties.
# Google Mobile Ads contains C++ objects even when the host app is Objective-C.
# Append, preserving any frameworks the application already links.
codename1.arg.ios.add_libs=;libc++.tbd
Comment thread
shai-almog marked this conversation as resolved.
2 changes: 2 additions & 0 deletions maven/cn1-admob/common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
<mkdir dir="${project.build.outputDirectory}/META-INF/codenameone/${project.groupId}/${project.artifactId}" />
<copy file="${basedir}/codenameone_library_required.properties"
todir="${project.build.outputDirectory}/META-INF/codenameone/${project.groupId}/${project.artifactId}/" />
<copy file="${basedir}/codenameone_library_appended.properties"
todir="${project.build.outputDirectory}/META-INF/codenameone/${project.groupId}/${project.artifactId}/" />
</target>
</configuration>
<goals>
Expand Down
26 changes: 26 additions & 0 deletions maven/cn1-admob/ios/src/main/objectivec/CN1AdMobSwiftSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.
*/
// Google Mobile Ads includes prebuilt Swift objects. An app-target Swift source
// activates CN1's existing Swift support and Xcode's compatibility-library
// linkage, including when all of the application's own native code is Objective-C.
import Foundation
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@
import com.codename1.ads.spi.BannerAdSession;
import com.codename1.ads.spi.FullScreenAdSession;
import com.codename1.ads.spi.NativeAdProvider;
import com.codename1.ui.Button;
import com.codename1.ui.CN;
import com.codename1.ui.Command;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Form;
import com.codename1.ui.Label;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.geom.Dimension;
import com.codename1.ui.layouts.BorderLayout;

Expand Down Expand Up @@ -111,11 +116,15 @@ public void loadNativeAd(String adUnitId, AdRequest request,
}

/// Deterministic full screen ad: fires the lifecycle events and presents a
/// fixed close-able form.
/// separate Form with a Close button.
private static final class MockFullScreen implements FullScreenAdSession {
private final AdFormat format;
private AdSessionCallback cb;
private boolean loaded;
// Mutated on the EDT; readiness may be queried by a worker.
private volatile boolean loaded;
private boolean disposed;
private MockAdForm adForm;
private static MockAdForm activeForm;

MockFullScreen(AdFormat format) {
this.format = format;
Expand All @@ -132,6 +141,13 @@ public void setServerSideVerificationOptions(ServerSideVerificationOptions optio

@Override
public void load(AdRequest request) {
if (!CN.isEdt()) {
CN.callSerially(() -> load(request));
return;
}
if (disposed) {
return;
}
loaded = true;
cb.onLoaded();
}
Expand All @@ -143,17 +159,106 @@ public boolean isLoaded() {

@Override
public void show() {
if (!CN.isEdt()) {
CN.callSerially(this::show);
return;
}
if (disposed) {
return;
}
if (!loaded) {
cb.onShowFailed(new AdError(AdError.CODE_INTERNAL, "mock", "No ad loaded"));
return;
}
if (activeForm != null) {
cb.onShowFailed(new AdError(AdError.CODE_INTERNAL, "mock", "An ad is already showing"));
return;
}
Form previous = CN.getCurrentForm();
if (previous == null) {
previous = new Form();
}
loaded = false;
adForm = new MockAdForm(previous);
activeForm = adForm;
Button close = new Button("Close ad");
close.addActionListener(evt -> closeAd(true));
adForm.add(BorderLayout.CENTER, new Label("Mock advertisement"));
adForm.add(BorderLayout.SOUTH, close);
adForm.setBackCommand(new Command("Close ad") {
@Override
public void actionPerformed(ActionEvent evt) {
closeAd(true);
}
});
adForm.show();
close.requestFocus();
cb.onShown();
cb.onImpression();
if (format == AdFormat.REWARDED || format == AdFormat.REWARDED_INTERSTITIAL) {
cb.onUserEarnedReward(new RewardItem("coins", 10));
if (adForm != null) {
cb.onImpression();
}
}

private void closeAd(boolean notify) {
if (adForm == null) {
return;
}
MockAdForm closing = adForm;
adForm = null;
closing.closed = true;
// Restore on the EDT queue so a modal caller can show again without
// blocking dispose(), and deliver callbacks after it becomes current.
CN.callSerially(() -> {
if (activeForm == closing) {
activeForm = null;
}
closing.restorePrevious(() -> {
if (notify) {
if (format == AdFormat.REWARDED || format == AdFormat.REWARDED_INTERSTITIAL) {
cb.onUserEarnedReward(new RewardItem("coins", 10));
}
cb.onDismissed();
}
});
});
}

private static final class MockAdForm extends Form {
private final Form previous;
private boolean closed;

MockAdForm(Form previous) {
super(new BorderLayout());
this.previous = previous;
// If an application dialog covered the ad when it was disposed,
// return to the caller when that dialog eventually uncovers it.
addShowListener(evt -> {
if (closed) {
CN.callSerially(() -> restorePrevious(null));
}
});
}

private void restorePrevious(Runnable afterRestore) {
if (CN.getCurrentForm() != this) {
if (afterRestore != null) {
afterRestore.run();
}
return;
}
if (afterRestore != null) {
// A modal Dialog can flush the EDT before becoming current.
// Its show event, rather than a queued task, marks restoration.
previous.addShowListener(new ActionListener<ActionEvent>() {
@Override
public void actionPerformed(ActionEvent evt) {
previous.removeShowListener(this);
afterRestore.run();
}
});
}
previous.showBack();
}
cb.onDismissed();
}

@Override
Expand All @@ -162,6 +267,13 @@ public void setAutoShowOnForeground(boolean enabled) {

@Override
public void dispose() {
if (!CN.isEdt()) {
CN.callSerially(this::dispose);
return;
}
disposed = true;
loaded = false;
closeAd(false);
}
}

Expand Down
6 changes: 6 additions & 0 deletions maven/core-unittests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@
</build>

<dependencies>
<dependency>
<groupId>com.codenameone</groupId>
<artifactId>cn1-ads-mock</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.codenameone</groupId>
<artifactId>codenameone-factory</artifactId>
Expand Down
Loading
Loading