Skip to content

Commit 4cd5b28

Browse files
fix(apk): banner de AdMob en franja propia debajo de la WebView (guia AdMob, no superpuesto)
- MainActivity: raiz vertical (mainFrame=webview+progress arriba, bannerSlot abajo), el banner nunca tapa contenido - BannerAdManager: el SMART_BANNER vive en el slot inferior; show/hide ocultan el slot completo
1 parent 163e649 commit 4cd5b28

3 files changed

Lines changed: 40 additions & 17 deletions

File tree

apk/android/app/src/main/java/com/codehub/app/BannerAdManager.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import com.google.android.gms.ads.AdView;
1212

1313
/**
14-
* Banner nativo de AdMob, anclado al fondo de la pantalla (sobre la WebView).
14+
* Banner nativo de AdMob, en una franja PROPIA debajo de la WebView
15+
* (recomendación de AdMob: el banner nunca se superpone al contenido).
16+
* Se coloca dentro del contenedor inferior (bannerSlot) de MainActivity.
1517
*
1618
* Control desde JS (CodeHubBridge):
1719
* CodeHubNative.showNativeBanner() / hideNativeBanner()
@@ -27,25 +29,28 @@ final class BannerAdManager {
2729
private static final String AD_UNIT_ID_BANNER = "ca-app-pub-3780093322926832/1968391001";
2830

2931
private static AdView adView;
32+
private static View slot;
3033

3134
private BannerAdManager() {}
3235

3336
static boolean hasUnitId() {
3437
return !AD_UNIT_ID_BANNER.contains("0000000000");
3538
}
3639

37-
/** Crea el banner (una sola vez) y lo ancla abajo, centrado. */
38-
static void setup(final Activity activity, final FrameLayout root) {
39-
if (adView != null || !hasUnitId() || !ConsentManager.canRequestAds()) return;
40+
/** Crea el banner (una sola vez) dentro de la franja inferior reservada. */
41+
static void setup(final Activity activity, final View bottomSlot) {
42+
if (adView != null || bottomSlot == null || !hasUnitId() || !ConsentManager.canRequestAds()) return;
4043
try {
44+
slot = bottomSlot;
4145
adView = new AdView(activity);
4246
adView.setAdUnitId(AD_UNIT_ID_BANNER);
4347
adView.setAdSize(AdSize.SMART_BANNER);
4448
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
4549
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
46-
lp.gravity = android.view.Gravity.BOTTOM | android.view.Gravity.CENTER_HORIZONTAL;
47-
root.addView(adView, lp);
50+
lp.gravity = android.view.Gravity.CENTER_HORIZONTAL;
51+
((FrameLayout) bottomSlot).addView(adView, lp);
4852
adView.loadAd(new AdRequest.Builder().build());
53+
bottomSlot.setVisibility(View.VISIBLE);
4954
Log.i(TAG, "banner configurado en APP_ID=" + adView.getAdUnitId());
5055
} catch (Throwable t) {
5156
Log.w(TAG, "banner setup error", t);
@@ -54,9 +59,11 @@ static void setup(final Activity activity, final FrameLayout root) {
5459

5560
static void show() {
5661
if (adView != null) adView.setVisibility(View.VISIBLE);
62+
if (slot != null) slot.setVisibility(View.VISIBLE);
5763
}
5864

5965
static void hide() {
6066
if (adView != null) adView.setVisibility(View.GONE);
67+
if (slot != null) slot.setVisibility(View.GONE);
6168
}
6269
}

apk/android/app/src/main/java/com/codehub/app/MainActivity.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.view.ViewGroup;
2828
import android.widget.FrameLayout;
2929
import android.widget.ProgressBar;
30+
import android.widget.LinearLayout;
3031
import android.content.res.ColorStateList;
3132
import android.graphics.Bitmap;
3233
import android.webkit.CookieManager;
@@ -69,6 +70,8 @@ public class MainActivity extends Activity {
6970
private FusedLocationProviderClient fusedLocation;
7071
private boolean backPressedOnce = false;
7172
private final Handler backHandler = new Handler(Looper.getMainLooper());
73+
/** Franja inferior reservada para el banner de AdMob (nunca superpuesto al contenido). */
74+
private FrameLayout bannerSlot;
7275

7376
@SuppressLint("SetJavaScriptEnabled")
7477
@Override
@@ -102,19 +105,31 @@ protected void onCreate(Bundle savedInstanceState) {
102105
progressBar.setLayoutParams(pbParams);
103106
progressBar.setVisibility(View.GONE);
104107

105-
FrameLayout rootLayout = new FrameLayout(this);
108+
// Estructura vertical (AdMob — banners SIEMPRE abajo, nunca sobre el
109+
// contenido): mainFrame (webview + barra de progreso) arriba, banner abajo.
110+
LinearLayout rootLayout = new LinearLayout(this);
111+
rootLayout.setOrientation(LinearLayout.VERTICAL);
106112
rootLayout.setBackgroundColor(0xFF080810);
107-
rootLayout.addView(swipeRefreshLayout, new FrameLayout.LayoutParams(
113+
114+
FrameLayout mainFrame = new FrameLayout(this);
115+
mainFrame.addView(swipeRefreshLayout, new FrameLayout.LayoutParams(
108116
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
109-
rootLayout.addView(progressBar);
117+
mainFrame.addView(progressBar);
118+
rootLayout.addView(mainFrame, new LinearLayout.LayoutParams(
119+
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
120+
121+
bannerSlot = new FrameLayout(this);
122+
bannerSlot.setVisibility(View.GONE);
123+
rootLayout.addView(bannerSlot, new LinearLayout.LayoutParams(
124+
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
110125

111126
setContentView(rootLayout);
112127

113128
try { setupStatusBar(); } catch (Throwable t) { crashLog("statusBar", t); }
114129
try { createNotificationChannels(); } catch (Throwable t) { crashLog("notifChannels", t); }
115130
try { requestAllPermissions(); } catch (Throwable t) { crashLog("permissions", t); }
116131
try { setupWebView(); } catch (Throwable t) { crashLog("webView", t); }
117-
try { initAdMob(rootLayout); } catch (Throwable t) { crashLog("adMob", t); }
132+
try { initAdMob(); } catch (Throwable t) { crashLog("adMob", t); }
118133
try { registerFCMToken(); } catch (Throwable t) { crashLog("fcm", t); }
119134
try { checkInternetAndLoad(); } catch (Throwable t) { crashLog("internet", t); }
120135

@@ -133,11 +148,11 @@ protected void onCreate(Bundle savedInstanceState) {
133148
// Nada de AdMob se pide antes de que UMP resuelva el consentimiento
134149
// (EEE/UK/Canadá). Fuera de esas regiones lookup UMP resuelve inmediato
135150
// con canRequestAds=true y no se bloquea ningún anuncio.
136-
private void initAdMob(final FrameLayout rootLayout) {
151+
private void initAdMob() {
137152
ConsentManager.init(this, () -> {
138153
runOnUiThread(() -> {
139154
if (!ConsentManager.canRequestAds()) return;
140-
BannerAdManager.setup(MainActivity.this, rootLayout);
155+
BannerAdManager.setup(MainActivity.this, bannerSlot);
141156
RewardedAdManager.load(MainActivity.this);
142157
InterstitialAdManager.load(MainActivity.this);
143158
});

docs/ADS-COMPLIANCE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@ apps salvo integraciones aprobadas; la app monetiza con AdMob, ver §2).
6969
- **Interstitial spam**: no mostrar intersticiales que bloqueen la UI sin interacción.
7070
- Clics propios en la app (usar **test ads** durante desarrollo, no ads reales).
7171

72-
### Mejora recomendada (no urgente)
73-
El banner (`BannerAdManager`) se ancla sobre el FrameLayout raíz (cubre el borde
74-
inferior del WebView). Google prefiere que el banner NO solape contenido
75-
interactivo: idealmente la WebView debería quedar encima con padding inferior y
76-
el banner debajo (Layout vertical WebView+banner), o `WEBVIEW_PADDING` al mostrar.
72+
### Banner según guía de AdMob (implementado)
73+
`MainActivity` usa estructura vertical: **WebView arriba** (`mainFrame`, weight 1) y
74+
**franja inferior reservada** (`bannerSlot`) para el banner. `BannerAdManager`
75+
coloca el SMART_BANNER dentro de esa franja debajo del contenido; `show/hide`
76+
(desde JS) ocultan todo el slot → **el banner nunca se superpone a la UI**
77+
(patrón "recommended" de AdMob, no el "discouraged" que cubre contenido).
7778

7879
## 3) Cruzado (aplica en ambos)
7980
- Membresía y conflictos: no distributions de ads vía software (toolbars/ext) ni

0 commit comments

Comments
 (0)