flet-ads 0.85.3.dev0__tar.gz → 0.86.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flet_ads-0.85.3.dev0/src/flet_ads.egg-info → flet_ads-0.86.0.dev0}/PKG-INFO +2 -2
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/pyproject.toml +2 -2
- flet_ads-0.86.0.dev0/src/flet_ads/__init__.py +29 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads/base_ad.py +2 -1
- flet_ads-0.86.0.dev0/src/flet_ads/consent_manager.py +136 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads/native_ad.py +2 -2
- flet_ads-0.86.0.dev0/src/flet_ads/types.py +265 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0/src/flet_ads.egg-info}/PKG-INFO +2 -2
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads.egg-info/SOURCES.txt +3 -0
- flet_ads-0.86.0.dev0/src/flet_ads.egg-info/requires.txt +1 -0
- flet_ads-0.86.0.dev0/src/flutter/flet_ads/lib/src/consent_manager.dart +92 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/src/extension.dart +3 -0
- flet_ads-0.86.0.dev0/src/flutter/flet_ads/lib/utils/consent.dart +25 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/pubspec.yaml +1 -1
- flet_ads-0.85.3.dev0/src/flet_ads/__init__.py +0 -17
- flet_ads-0.85.3.dev0/src/flet_ads/types.py +0 -131
- flet_ads-0.85.3.dev0/src/flet_ads.egg-info/requires.txt +0 -1
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/LICENSE +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/README.md +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/setup.cfg +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads/banner_ad.py +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads/interstitial_ad.py +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads.egg-info/dependency_links.txt +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flet_ads.egg-info/top_level.txt +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/flet_ads.dart +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/src/banner.dart +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/src/interstitial.dart +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/src/native.dart +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/utils/ads.dart +0 -0
- {flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/utils/native.dart +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-ads
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.86.0.dev0
|
|
4
4
|
Summary: Display Google Ads in Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==0.
|
|
14
|
+
Requires-Dist: flet==0.86.0.dev0
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-ads
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flet-ads"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.86.0.dev0"
|
|
4
4
|
description = "Display Google Ads in Flet apps."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Flet contributors", email = "hello@flet.dev" }]
|
|
7
7
|
license = "Apache-2.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"flet==0.
|
|
10
|
+
"flet==0.86.0.dev0",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[project.urls]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from flet_ads.banner_ad import BannerAd
|
|
2
|
+
from flet_ads.base_ad import BaseAd
|
|
3
|
+
from flet_ads.consent_manager import ConsentManager
|
|
4
|
+
from flet_ads.interstitial_ad import InterstitialAd
|
|
5
|
+
from flet_ads.types import (
|
|
6
|
+
AdRequest,
|
|
7
|
+
ConsentDebugSettings,
|
|
8
|
+
ConsentRequestParameters,
|
|
9
|
+
ConsentStatus,
|
|
10
|
+
DebugGeography,
|
|
11
|
+
PaidAdEvent,
|
|
12
|
+
PrecisionType,
|
|
13
|
+
PrivacyOptionsRequirementStatus,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AdRequest",
|
|
18
|
+
"BannerAd",
|
|
19
|
+
"BaseAd",
|
|
20
|
+
"ConsentDebugSettings",
|
|
21
|
+
"ConsentManager",
|
|
22
|
+
"ConsentRequestParameters",
|
|
23
|
+
"ConsentStatus",
|
|
24
|
+
"DebugGeography",
|
|
25
|
+
"InterstitialAd",
|
|
26
|
+
"PaidAdEvent",
|
|
27
|
+
"PrecisionType",
|
|
28
|
+
"PrivacyOptionsRequirementStatus",
|
|
29
|
+
]
|
|
@@ -66,7 +66,8 @@ class BaseAd(ft.BaseControl):
|
|
|
66
66
|
Called when this ad is clicked.
|
|
67
67
|
"""
|
|
68
68
|
|
|
69
|
-
def
|
|
69
|
+
def init(self):
|
|
70
|
+
super().init()
|
|
70
71
|
if self.page.web or not self.page.platform.is_mobile():
|
|
71
72
|
raise ft.FletUnsupportedPlatformException(
|
|
72
73
|
f"{self.__class__.__name__} is only supported on "
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import flet as ft
|
|
4
|
+
from flet_ads.types import (
|
|
5
|
+
ConsentRequestParameters,
|
|
6
|
+
ConsentStatus,
|
|
7
|
+
PrivacyOptionsRequirementStatus,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = ["ConsentManager"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@ft.control("ConsentManager")
|
|
14
|
+
class ConsentManager(ft.Service):
|
|
15
|
+
"""
|
|
16
|
+
Gathers and manages user consent for ads using
|
|
17
|
+
Google's User Messaging Platform (UMP).
|
|
18
|
+
|
|
19
|
+
Use this service to request consent information, present the consent form
|
|
20
|
+
(for example, the GDPR/EEA consent dialog) and the privacy options form,
|
|
21
|
+
and query the consent status before requesting ads.
|
|
22
|
+
|
|
23
|
+
A typical flow looks like this:
|
|
24
|
+
|
|
25
|
+
1. :meth:`request_consent_info_update` (at every app launch);
|
|
26
|
+
2. :meth:`load_and_show_consent_form_if_required` (shows the form only if needed);
|
|
27
|
+
3. :meth:`can_request_ads` (to decide whether to start loading ads).
|
|
28
|
+
|
|
29
|
+
More info in the [AdMob documentation](https://developers.google.com/admob/flutter/privacy).
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
FletUnsupportedPlatformException: When any of its methods are called on
|
|
33
|
+
a web and/or non-mobile platform.
|
|
34
|
+
""" # noqa: E501
|
|
35
|
+
|
|
36
|
+
def init(self):
|
|
37
|
+
super().init()
|
|
38
|
+
if self.page.web or not self.page.platform.is_mobile():
|
|
39
|
+
raise ft.FletUnsupportedPlatformException(
|
|
40
|
+
f"{self.__class__.__name__} is only supported on "
|
|
41
|
+
f"Mobile (Android and iOS)"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async def request_consent_info_update(
|
|
45
|
+
self, params: Optional[ConsentRequestParameters] = None
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Requests an update of the user's consent information.
|
|
49
|
+
|
|
50
|
+
This should be called (and awaited) at every app launch, before
|
|
51
|
+
loading/showing a consent form or reading the consent status.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
params: Parameters such as debug settings (useful for
|
|
55
|
+
testing the form during development) or the under-age tag.
|
|
56
|
+
If `None`, default parameters are used.
|
|
57
|
+
"""
|
|
58
|
+
await self._invoke_method("request_consent_info_update", {"params": params})
|
|
59
|
+
|
|
60
|
+
async def is_consent_form_available(self) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Checks whether a consent form is available to be loaded and shown.
|
|
63
|
+
|
|
64
|
+
Note:
|
|
65
|
+
:meth:`request_consent_info_update` should be awaited before calling
|
|
66
|
+
this method.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
`True` if a consent form is available, `False` otherwise.
|
|
70
|
+
"""
|
|
71
|
+
return await self._invoke_method("is_consent_form_available")
|
|
72
|
+
|
|
73
|
+
async def get_consent_status(self) -> ConsentStatus:
|
|
74
|
+
"""
|
|
75
|
+
Get the user’s consent status.
|
|
76
|
+
|
|
77
|
+
This value is cached between app sessions and can be read before
|
|
78
|
+
requesting an update of the consent information.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The user's current consent status.
|
|
82
|
+
"""
|
|
83
|
+
return ConsentStatus(await self._invoke_method("get_consent_status"))
|
|
84
|
+
|
|
85
|
+
async def can_request_ads(self) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Indicates whether the app has gathered enough consent to request ads.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
`True` if ads can be requested, `False` otherwise.
|
|
91
|
+
"""
|
|
92
|
+
return bool(await self._invoke_method("can_request_ads"))
|
|
93
|
+
|
|
94
|
+
async def get_privacy_options_requirement_status(
|
|
95
|
+
self,
|
|
96
|
+
) -> PrivacyOptionsRequirementStatus:
|
|
97
|
+
"""
|
|
98
|
+
Gets the requirement status for showing a privacy options entry point.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Whether a privacy options entry point (for example, a button
|
|
102
|
+
that calls :meth:`show_privacy_options_form`) is required.
|
|
103
|
+
"""
|
|
104
|
+
return PrivacyOptionsRequirementStatus(
|
|
105
|
+
await self._invoke_method("get_privacy_options_requirement_status")
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
async def load_and_show_consent_form_if_required(self):
|
|
109
|
+
"""
|
|
110
|
+
Loads a consent form and immediately shows it if consent is required.
|
|
111
|
+
|
|
112
|
+
If consent is not required, this method completes without showing
|
|
113
|
+
anything. This is the simplest way to gather consent: call
|
|
114
|
+
:meth:`request_consent_info_update` first, then this method.
|
|
115
|
+
"""
|
|
116
|
+
await self._invoke_method("load_and_show_consent_form_if_required")
|
|
117
|
+
|
|
118
|
+
async def show_privacy_options_form(self):
|
|
119
|
+
"""
|
|
120
|
+
Presents the privacy options form to the user.
|
|
121
|
+
|
|
122
|
+
This lets users change or withdraw their consent after the initial
|
|
123
|
+
choice. Only present it when :meth:`get_privacy_options_requirement_status`
|
|
124
|
+
returns :attr:`flet_ads.PrivacyOptionsRequirementStatus.REQUIRED`.
|
|
125
|
+
"""
|
|
126
|
+
await self._invoke_method("show_privacy_options_form")
|
|
127
|
+
|
|
128
|
+
async def reset(self):
|
|
129
|
+
"""
|
|
130
|
+
Resets the consent state.
|
|
131
|
+
|
|
132
|
+
Warning:
|
|
133
|
+
This is intended only for testing, allowing you to simulate a first-time
|
|
134
|
+
user. It should not be used in production.
|
|
135
|
+
"""
|
|
136
|
+
await self._invoke_method("reset")
|
|
@@ -27,7 +27,7 @@ class NativeAd(BannerAd):
|
|
|
27
27
|
ValueError: When neither :attr:`factory_id` nor `template_style` is set.
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
-
def
|
|
31
|
-
super().
|
|
30
|
+
def init(self):
|
|
31
|
+
super().init()
|
|
32
32
|
if self.factory_id is None and self.template_style is None:
|
|
33
33
|
raise ValueError("factory_id or template_style must be set")
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import flet as ft
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AdRequest",
|
|
9
|
+
"ConsentDebugSettings",
|
|
10
|
+
"ConsentRequestParameters",
|
|
11
|
+
"ConsentStatus",
|
|
12
|
+
"DebugGeography",
|
|
13
|
+
"NativeAdTemplateStyle",
|
|
14
|
+
"NativeAdTemplateTextStyle",
|
|
15
|
+
"NativeAdTemplateType",
|
|
16
|
+
"NativeTemplateFontStyle",
|
|
17
|
+
"PaidAdEvent",
|
|
18
|
+
"PrecisionType",
|
|
19
|
+
"PrivacyOptionsRequirementStatus",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PrecisionType(Enum):
|
|
24
|
+
"""Describes how accurately a paid ad value is reported."""
|
|
25
|
+
|
|
26
|
+
UNKNOWN = "unknown"
|
|
27
|
+
"""An ad value with unknown precision."""
|
|
28
|
+
|
|
29
|
+
ESTIMATED = "estimated"
|
|
30
|
+
"""An ad value estimated from aggregated data."""
|
|
31
|
+
|
|
32
|
+
PUBLISHER_PROVIDED = "publisherProvided"
|
|
33
|
+
"""A publisher-provided ad value, such as manual CPMs in a mediation group."""
|
|
34
|
+
|
|
35
|
+
PRECISE = "precise"
|
|
36
|
+
"""The precise value paid for this ad."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class PaidAdEvent(ft.Event[ft.EventControlType]):
|
|
41
|
+
"""
|
|
42
|
+
Event data for paid ad events.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
value: float
|
|
46
|
+
"""
|
|
47
|
+
The monetary value of the ad.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
precision: PrecisionType
|
|
51
|
+
"""
|
|
52
|
+
The precision of the ad value.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
currency_code: str
|
|
56
|
+
"""
|
|
57
|
+
The currency code of the ad value.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@ft.value
|
|
62
|
+
class AdRequest:
|
|
63
|
+
"""
|
|
64
|
+
Targeting info per the AdMob API.
|
|
65
|
+
|
|
66
|
+
This class's properties mirror the native AdRequest API. See for example:
|
|
67
|
+
[AdRequest.Builder for Android](https://developers.google.com/android/reference/com/google/android/gms/ads/AdRequest.Builder).
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
keywords: Optional[list[str]] = None
|
|
71
|
+
"""
|
|
72
|
+
Words or phrases describing the current user activity.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
content_url: Optional[str] = None
|
|
76
|
+
"""
|
|
77
|
+
URL string for a webpage whose content matches the app’s primary content.
|
|
78
|
+
|
|
79
|
+
This webpage content is used for targeting and brand safety purposes.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
neighboring_content_urls: Optional[list[str]] = None
|
|
83
|
+
"""
|
|
84
|
+
URLs representing web content near an ad.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
non_personalized_ads: Optional[bool] = None
|
|
88
|
+
"""
|
|
89
|
+
Non-personalized ads are ads that are not based on a user’s past behavior.
|
|
90
|
+
|
|
91
|
+
For more information: https://support.google.com/admob/answer/7676680?hl=en
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
http_timeout: Optional[int] = None
|
|
95
|
+
"""
|
|
96
|
+
A custom timeout (in milliseconds) for HTTPS calls during an ad request.
|
|
97
|
+
|
|
98
|
+
Note:
|
|
99
|
+
This is only supported in Android. (ignored on iOS)
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
extras: Optional[dict[str, str]] = None
|
|
103
|
+
"""
|
|
104
|
+
Extras to pass to the AdMob adapter.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class NativeAdTemplateType(Enum):
|
|
109
|
+
SMALL = "small"
|
|
110
|
+
MEDIUM = "medium"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class NativeTemplateFontStyle(Enum):
|
|
114
|
+
NORMAL = "normal"
|
|
115
|
+
BOLD = "bold"
|
|
116
|
+
ITALIC = "italic"
|
|
117
|
+
MONOSPACE = "monospace"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@ft.value
|
|
121
|
+
class NativeAdTemplateTextStyle:
|
|
122
|
+
size: Optional[ft.Number] = None
|
|
123
|
+
text_color: Optional[ft.ColorValue] = None
|
|
124
|
+
bgcolor: Optional[ft.ColorValue] = None
|
|
125
|
+
style: Optional[NativeTemplateFontStyle] = None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@ft.value
|
|
129
|
+
class NativeAdTemplateStyle:
|
|
130
|
+
template_type: NativeAdTemplateType = NativeAdTemplateType.MEDIUM
|
|
131
|
+
main_bgcolor: Optional[ft.ColorValue] = None
|
|
132
|
+
corner_radius: Optional[ft.Number] = None
|
|
133
|
+
call_to_action_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
134
|
+
primary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
135
|
+
secondary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
136
|
+
tertiary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ConsentStatus(Enum):
|
|
140
|
+
"""
|
|
141
|
+
The state of the consent-gathering process, as reported by the
|
|
142
|
+
User Messaging Platform (UMP).
|
|
143
|
+
|
|
144
|
+
Note:
|
|
145
|
+
This describes whether a consent decision has been *collected* — not
|
|
146
|
+
what the user chose. It is never "granted" versus "denied": the user's
|
|
147
|
+
actual selections live in the IAB TCF consent string, which the ads SDK reads
|
|
148
|
+
automatically. Use :meth:`flet_ads.ConsentManager.can_request_ads` to decide
|
|
149
|
+
whether to request ads.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
NOT_REQUIRED = "notRequired"
|
|
153
|
+
"""Consent is not required (e.g. the user is outside a regulated region)."""
|
|
154
|
+
|
|
155
|
+
OBTAINED = "obtained"
|
|
156
|
+
"""
|
|
157
|
+
A consent decision has been collected.
|
|
158
|
+
|
|
159
|
+
Set once the user completes the form — whether they consented, declined
|
|
160
|
+
("Do not consent"), or saved custom choices. This does **not** imply that
|
|
161
|
+
consent was granted.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
REQUIRED = "required"
|
|
165
|
+
"""Consent is required but has not yet been collected."""
|
|
166
|
+
|
|
167
|
+
UNKNOWN = "unknown"
|
|
168
|
+
"""Consent status is unknown."""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class PrivacyOptionsRequirementStatus(Enum):
|
|
172
|
+
"""
|
|
173
|
+
Whether your app must show a privacy options entry point: a persistent
|
|
174
|
+
control (for example, a button in a settings menu) that lets the user
|
|
175
|
+
change or withdraw their consent at any time after their initial choice.
|
|
176
|
+
|
|
177
|
+
Note:
|
|
178
|
+
Some regulations (e.g. GDPR) require this ongoing entry point.
|
|
179
|
+
When the user triggers it, call
|
|
180
|
+
:meth:`flet_ads.ConsentManager.show_privacy_options_form`.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
NOT_REQUIRED = "notRequired"
|
|
184
|
+
"""A privacy options entry point is not required."""
|
|
185
|
+
|
|
186
|
+
REQUIRED = "required"
|
|
187
|
+
"""A privacy options entry point must be shown."""
|
|
188
|
+
|
|
189
|
+
UNKNOWN = "unknown"
|
|
190
|
+
"""
|
|
191
|
+
The requirement status is unknown — for example, before
|
|
192
|
+
:meth:`flet_ads.ConsentManager.request_consent_info_update` completes.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class DebugGeography(Enum):
|
|
197
|
+
"""
|
|
198
|
+
Debug geography values used to test consent flows.
|
|
199
|
+
|
|
200
|
+
Note:
|
|
201
|
+
These only take effect on devices registered as test devices through
|
|
202
|
+
:attr:`flet_ads.ConsentDebugSettings.test_identifiers`.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
DISABLED = "disabled"
|
|
206
|
+
"""Debug geography disabled."""
|
|
207
|
+
|
|
208
|
+
EEA = "eea"
|
|
209
|
+
"""Geography appears as in the EEA (European Economic Area) for debug devices."""
|
|
210
|
+
|
|
211
|
+
REGULATED_US_STATE = "regulatedUsState"
|
|
212
|
+
"""Geography appears as in a regulated US State for debug devices."""
|
|
213
|
+
|
|
214
|
+
OTHER = "other"
|
|
215
|
+
"""Geography appears as in a region with no regulation in force."""
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@ft.value
|
|
219
|
+
class ConsentDebugSettings:
|
|
220
|
+
"""
|
|
221
|
+
Debug settings to hardcode in consent requests, useful for testing the
|
|
222
|
+
consent flow during development.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
debug_geography: Optional[DebugGeography] = None
|
|
226
|
+
"""
|
|
227
|
+
The geography to simulate when gathering consent on test devices.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
test_identifiers: Optional[list[str]] = None
|
|
231
|
+
"""
|
|
232
|
+
A list of device identifiers for which debug features
|
|
233
|
+
(such as :attr:`debug_geography`) are enabled.
|
|
234
|
+
|
|
235
|
+
Note:
|
|
236
|
+
Simulators and emulators are automatically registered as test devices,
|
|
237
|
+
so this is only needed for physical devices.
|
|
238
|
+
|
|
239
|
+
A physical device's hashed ID is printed to the **native** device log
|
|
240
|
+
(Xcode console/Console.app on iOS, `adb logcat` on Android)
|
|
241
|
+
the first time a consent request is made from it.
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@ft.value
|
|
246
|
+
class ConsentRequestParameters:
|
|
247
|
+
"""
|
|
248
|
+
Parameters sent when updating the user's consent information.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
tag_for_under_age_of_consent: Optional[bool] = None
|
|
252
|
+
"""
|
|
253
|
+
Whether the user is tagged as being under the age of consent.
|
|
254
|
+
|
|
255
|
+
`False` means users are not under the age of consent.
|
|
256
|
+
|
|
257
|
+
Warning:
|
|
258
|
+
When `True`, the consent form is not shown, as users under the age of
|
|
259
|
+
consent cannot be asked to provide consent.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
consent_debug_settings: Optional[ConsentDebugSettings] = None
|
|
263
|
+
"""
|
|
264
|
+
Debug settings to hardcode in test requests.
|
|
265
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-ads
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.86.0.dev0
|
|
4
4
|
Summary: Display Google Ads in Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==0.
|
|
14
|
+
Requires-Dist: flet==0.86.0.dev0
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-ads
|
|
@@ -4,6 +4,7 @@ pyproject.toml
|
|
|
4
4
|
src/flet_ads/__init__.py
|
|
5
5
|
src/flet_ads/banner_ad.py
|
|
6
6
|
src/flet_ads/base_ad.py
|
|
7
|
+
src/flet_ads/consent_manager.py
|
|
7
8
|
src/flet_ads/interstitial_ad.py
|
|
8
9
|
src/flet_ads/native_ad.py
|
|
9
10
|
src/flet_ads/types.py
|
|
@@ -15,8 +16,10 @@ src/flet_ads.egg-info/top_level.txt
|
|
|
15
16
|
src/flutter/flet_ads/pubspec.yaml
|
|
16
17
|
src/flutter/flet_ads/lib/flet_ads.dart
|
|
17
18
|
src/flutter/flet_ads/lib/src/banner.dart
|
|
19
|
+
src/flutter/flet_ads/lib/src/consent_manager.dart
|
|
18
20
|
src/flutter/flet_ads/lib/src/extension.dart
|
|
19
21
|
src/flutter/flet_ads/lib/src/interstitial.dart
|
|
20
22
|
src/flutter/flet_ads/lib/src/native.dart
|
|
21
23
|
src/flutter/flet_ads/lib/utils/ads.dart
|
|
24
|
+
src/flutter/flet_ads/lib/utils/consent.dart
|
|
22
25
|
src/flutter/flet_ads/lib/utils/native.dart
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet==0.86.0.dev0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import 'dart:async';
|
|
2
|
+
|
|
3
|
+
import 'package:flet/flet.dart';
|
|
4
|
+
import 'package:flutter/foundation.dart';
|
|
5
|
+
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
|
6
|
+
|
|
7
|
+
import '../utils/consent.dart';
|
|
8
|
+
|
|
9
|
+
class ConsentManagerService extends FletService {
|
|
10
|
+
ConsentManagerService({required super.control});
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
void init() {
|
|
14
|
+
super.init();
|
|
15
|
+
debugPrint("ConsentManager(${control.id}).init");
|
|
16
|
+
control.addInvokeMethodListener(_invokeMethod);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
Future<dynamic> _invokeMethod(String name, dynamic args) async {
|
|
20
|
+
debugPrint("ConsentManager.$name($args)");
|
|
21
|
+
switch (name) {
|
|
22
|
+
case "request_consent_info_update":
|
|
23
|
+
final completer = Completer<void>();
|
|
24
|
+
ConsentInformation.instance.requestConsentInfoUpdate(
|
|
25
|
+
parseConsentRequestParameters(args?["params"]),
|
|
26
|
+
() {
|
|
27
|
+
if (!completer.isCompleted) completer.complete();
|
|
28
|
+
},
|
|
29
|
+
(FormError error) {
|
|
30
|
+
if (!completer.isCompleted) {
|
|
31
|
+
completer.completeError(Exception(
|
|
32
|
+
"Consent info update failed (${error.errorCode}): ${error.message}"));
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
return completer.future;
|
|
37
|
+
|
|
38
|
+
case "is_consent_form_available":
|
|
39
|
+
return await ConsentInformation.instance.isConsentFormAvailable();
|
|
40
|
+
|
|
41
|
+
case "get_consent_status":
|
|
42
|
+
return (await ConsentInformation.instance.getConsentStatus()).name;
|
|
43
|
+
|
|
44
|
+
case "can_request_ads":
|
|
45
|
+
return await ConsentInformation.instance.canRequestAds();
|
|
46
|
+
|
|
47
|
+
case "get_privacy_options_requirement_status":
|
|
48
|
+
return (await ConsentInformation.instance
|
|
49
|
+
.getPrivacyOptionsRequirementStatus())
|
|
50
|
+
.name;
|
|
51
|
+
|
|
52
|
+
case "load_and_show_consent_form_if_required":
|
|
53
|
+
final completer = Completer<void>();
|
|
54
|
+
ConsentForm.loadAndShowConsentFormIfRequired((FormError? error) {
|
|
55
|
+
if (completer.isCompleted) return;
|
|
56
|
+
if (error != null) {
|
|
57
|
+
completer.completeError(Exception(
|
|
58
|
+
"Consent form error (${error.errorCode}): ${error.message}"));
|
|
59
|
+
} else {
|
|
60
|
+
completer.complete();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return completer.future;
|
|
64
|
+
|
|
65
|
+
case "show_privacy_options_form":
|
|
66
|
+
final completer = Completer<void>();
|
|
67
|
+
ConsentForm.showPrivacyOptionsForm((FormError? error) {
|
|
68
|
+
if (completer.isCompleted) return;
|
|
69
|
+
if (error != null) {
|
|
70
|
+
completer.completeError(Exception(
|
|
71
|
+
"Privacy options form error (${error.errorCode}): ${error.message}"));
|
|
72
|
+
} else {
|
|
73
|
+
completer.complete();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return completer.future;
|
|
77
|
+
|
|
78
|
+
case "reset":
|
|
79
|
+
return await ConsentInformation.instance.reset();
|
|
80
|
+
|
|
81
|
+
default:
|
|
82
|
+
throw Exception("Unknown ConsentManager method: $name");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@override
|
|
87
|
+
void dispose() {
|
|
88
|
+
debugPrint("ConsentManager(${control.id}).dispose()");
|
|
89
|
+
control.removeInvokeMethodListener(_invokeMethod);
|
|
90
|
+
super.dispose();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
|
|
|
3
3
|
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
|
4
4
|
|
|
5
5
|
import 'banner.dart';
|
|
6
|
+
import 'consent_manager.dart';
|
|
6
7
|
import 'interstitial.dart';
|
|
7
8
|
|
|
8
9
|
class Extension extends FletExtension {
|
|
@@ -18,6 +19,8 @@ class Extension extends FletExtension {
|
|
|
18
19
|
switch (control.type) {
|
|
19
20
|
case "InterstitialAd":
|
|
20
21
|
return InterstitialAdService(control: control);
|
|
22
|
+
case "ConsentManager":
|
|
23
|
+
return ConsentManagerService(control: control);
|
|
21
24
|
default:
|
|
22
25
|
return null;
|
|
23
26
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import 'package:flet/flet.dart';
|
|
2
|
+
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
|
3
|
+
|
|
4
|
+
DebugGeography? parseDebugGeography(String? value,
|
|
5
|
+
[DebugGeography? defaultValue]) =>
|
|
6
|
+
parseEnum(DebugGeography.values, "debugGeography$value", defaultValue);
|
|
7
|
+
|
|
8
|
+
ConsentDebugSettings? parseConsentDebugSettings(dynamic value,
|
|
9
|
+
[ConsentDebugSettings? defaultValue]) {
|
|
10
|
+
if (value == null) return defaultValue;
|
|
11
|
+
return ConsentDebugSettings(
|
|
12
|
+
debugGeography: parseDebugGeography(value["debug_geography"]),
|
|
13
|
+
testIdentifiers:
|
|
14
|
+
(value["test_identifiers"] as List?)?.map((e) => e.toString()).toList(),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ConsentRequestParameters parseConsentRequestParameters(dynamic value) {
|
|
19
|
+
if (value == null) return ConsentRequestParameters();
|
|
20
|
+
return ConsentRequestParameters(
|
|
21
|
+
tagForUnderAgeOfConsent: value["tag_for_under_age_of_consent"],
|
|
22
|
+
consentDebugSettings:
|
|
23
|
+
parseConsentDebugSettings(value["consent_debug_settings"]),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from flet_ads.banner_ad import BannerAd
|
|
2
|
-
from flet_ads.base_ad import BaseAd
|
|
3
|
-
from flet_ads.interstitial_ad import InterstitialAd
|
|
4
|
-
from flet_ads.types import (
|
|
5
|
-
AdRequest,
|
|
6
|
-
PaidAdEvent,
|
|
7
|
-
PrecisionType,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
"AdRequest",
|
|
12
|
-
"BannerAd",
|
|
13
|
-
"BaseAd",
|
|
14
|
-
"InterstitialAd",
|
|
15
|
-
"PaidAdEvent",
|
|
16
|
-
"PrecisionType",
|
|
17
|
-
]
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from enum import Enum
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
import flet as ft
|
|
6
|
-
|
|
7
|
-
__all__ = [
|
|
8
|
-
"AdRequest",
|
|
9
|
-
"NativeAdTemplateStyle",
|
|
10
|
-
"NativeAdTemplateTextStyle",
|
|
11
|
-
"NativeAdTemplateType",
|
|
12
|
-
"NativeTemplateFontStyle",
|
|
13
|
-
"PaidAdEvent",
|
|
14
|
-
"PrecisionType",
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class PrecisionType(Enum):
|
|
19
|
-
"""Describes how accurately a paid ad value is reported."""
|
|
20
|
-
|
|
21
|
-
UNKNOWN = "unknown"
|
|
22
|
-
"""An ad value with unknown precision."""
|
|
23
|
-
|
|
24
|
-
ESTIMATED = "estimated"
|
|
25
|
-
"""An ad value estimated from aggregated data."""
|
|
26
|
-
|
|
27
|
-
PUBLISHER_PROVIDED = "publisherProvided"
|
|
28
|
-
"""A publisher-provided ad value, such as manual CPMs in a mediation group."""
|
|
29
|
-
|
|
30
|
-
PRECISE = "precise"
|
|
31
|
-
"""The precise value paid for this ad."""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass
|
|
35
|
-
class PaidAdEvent(ft.Event[ft.EventControlType]):
|
|
36
|
-
"""
|
|
37
|
-
Event data for paid ad events.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
value: float
|
|
41
|
-
"""
|
|
42
|
-
The monetary value of the ad.
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
precision: PrecisionType
|
|
46
|
-
"""
|
|
47
|
-
The precision of the ad value.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
currency_code: str
|
|
51
|
-
"""
|
|
52
|
-
The currency code of the ad value.
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@ft.value
|
|
57
|
-
class AdRequest:
|
|
58
|
-
"""
|
|
59
|
-
Targeting info per the AdMob API.
|
|
60
|
-
|
|
61
|
-
This class's properties mirror the native AdRequest API. See for example:
|
|
62
|
-
[AdRequest.Builder for Android](https://developers.google.com/android/reference/com/google/android/gms/ads/AdRequest.Builder).
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
keywords: Optional[list[str]] = None
|
|
66
|
-
"""
|
|
67
|
-
Words or phrases describing the current user activity.
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
content_url: Optional[str] = None
|
|
71
|
-
"""
|
|
72
|
-
URL string for a webpage whose content matches the app’s primary content.
|
|
73
|
-
|
|
74
|
-
This webpage content is used for targeting and brand safety purposes.
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
neighboring_content_urls: Optional[list[str]] = None
|
|
78
|
-
"""
|
|
79
|
-
URLs representing web content near an ad.
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
non_personalized_ads: Optional[bool] = None
|
|
83
|
-
"""
|
|
84
|
-
Non-personalized ads are ads that are not based on a user’s past behavior.
|
|
85
|
-
|
|
86
|
-
For more information: https://support.google.com/admob/answer/7676680?hl=en
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
http_timeout: Optional[int] = None
|
|
90
|
-
"""
|
|
91
|
-
A custom timeout (in milliseconds) for HTTPS calls during an ad request.
|
|
92
|
-
|
|
93
|
-
Note:
|
|
94
|
-
This is only supported in Android. (ignored on iOS)
|
|
95
|
-
"""
|
|
96
|
-
|
|
97
|
-
extras: Optional[dict[str, str]] = None
|
|
98
|
-
"""
|
|
99
|
-
Extras to pass to the AdMob adapter.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class NativeAdTemplateType(Enum):
|
|
104
|
-
SMALL = "small"
|
|
105
|
-
MEDIUM = "medium"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class NativeTemplateFontStyle(Enum):
|
|
109
|
-
NORMAL = "normal"
|
|
110
|
-
BOLD = "bold"
|
|
111
|
-
ITALIC = "italic"
|
|
112
|
-
MONOSPACE = "monospace"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
@ft.value
|
|
116
|
-
class NativeAdTemplateTextStyle:
|
|
117
|
-
size: Optional[ft.Number] = None
|
|
118
|
-
text_color: Optional[ft.ColorValue] = None
|
|
119
|
-
bgcolor: Optional[ft.ColorValue] = None
|
|
120
|
-
style: Optional[NativeTemplateFontStyle] = None
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@ft.value
|
|
124
|
-
class NativeAdTemplateStyle:
|
|
125
|
-
template_type: NativeAdTemplateType = NativeAdTemplateType.MEDIUM
|
|
126
|
-
main_bgcolor: Optional[ft.ColorValue] = None
|
|
127
|
-
corner_radius: Optional[ft.Number] = None
|
|
128
|
-
call_to_action_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
129
|
-
primary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
130
|
-
secondary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
131
|
-
tertiary_text_style: Optional[NativeAdTemplateTextStyle] = None
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
flet==0.85.3.dev0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flet_ads-0.85.3.dev0 → flet_ads-0.86.0.dev0}/src/flutter/flet_ads/lib/src/interstitial.dart
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|