I try to use Google ConsentSDK to show in Android app consent form.
When I call form.show() I get this error:
"Consent form error Consent form is not ready to be displayed."
Who can help me?
My code:
ConsentForm form = new ConsentForm.Builder(context, privacyUrl)
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form loaded successfully.
Log.d("SplashScreen", "Consent form Loaded ");
}
#Override
public void onConsentFormOpened() {
// Consent form was displayed.
Log.d("SplashScreen", "Consent form opened ");
}
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
// Consent form was closed.
Log.d("SplashScreen", "Consent form Closed ");
}
#Override
public void onConsentFormError(String errorDescription) {
// Consent form error.
Log.d("SplashScreen", "Consent form error " + errorDescription);
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.build();
form.load();
form.show();
Here is my helper class for the Google Consent SDK that I use in my app. To initialise the consent information and display the Consent Form if needed, I have following code in onCreate() method of my main activity:
GdprHelper gdprHelper = new GdprHelper(this);
gdprHelper.initialise();
Similarly, I run following code when user clicks on "Reset my privacy consent" in preferences:
GdprHelper gdprHelper = new GdprHelper(this);
gdprHelper.resetConsent();
where both times, this is referencing current running activity.
Full implementation of the helper class:
package com.example.app;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.google.ads.consent.ConsentForm;
import com.google.ads.consent.ConsentFormListener;
import com.google.ads.consent.ConsentInfoUpdateListener;
import com.google.ads.consent.ConsentInformation;
import com.google.ads.consent.ConsentStatus;
import java.net.MalformedURLException;
import java.net.URL;
public class GdprHelper {
private static final String PUBLISHER_ID = "YOUR-PUBLISHER-ID";
private static final String PRIVACY_URL = "YOUR-PRIVACY-URL";
private static final String MARKET_URL_PAID_VERSION = "market://details?id=com.example.app.pro";
private final Context context;
private ConsentForm consentForm;
public GdprHelper(Context context) {
this.context = context;
}
// Initialises the consent information and displays consent form if needed
public void initialise() {
ConsentInformation consentInformation = ConsentInformation.getInstance(context);
consentInformation.requestConsentInfoUpdate(new String[]{PUBLISHER_ID}, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
// User's consent status successfully updated.
if (consentStatus == ConsentStatus.UNKNOWN) {
displayConsentForm();
}
}
#Override
public void onFailedToUpdateConsentInfo(String errorDescription) {
// Consent form error. Would be nice to have proper error logging. Happens also when user has no internet connection
if (BuildConfig.BUILD_TYPE.equals("debug")) {
Toast.makeText(context, errorDescription, Toast.LENGTH_LONG).show();
}
}
});
}
// Resets the consent. User will be again displayed the consent form on next call of initialise method
public void resetConsent() {
ConsentInformation consentInformation = ConsentInformation.getInstance(context);
consentInformation.reset();
}
private void displayConsentForm() {
consentForm = new ConsentForm.Builder(context, getPrivacyUrl())
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form has loaded successfully, now show it
consentForm.show();
}
#Override
public void onConsentFormOpened() {
// Consent form was displayed.
}
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
// Consent form was closed. This callback method contains all the data about user's selection, that you can use.
if (userPrefersAdFree) {
redirectToPaidVersion();
}
}
#Override
public void onConsentFormError(String errorDescription) {
// Consent form error. Would be nice to have some proper logging
if (BuildConfig.BUILD_TYPE.equals("debug")) {
Toast.makeText(context, errorDescription, Toast.LENGTH_LONG).show();
}
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.withAdFreeOption()
.build();
consentForm.load();
}
private URL getPrivacyUrl() {
URL privacyUrl = null;
try {
privacyUrl = new URL(PRIVACY_URL);
} catch (MalformedURLException e) {
// Since this is a constant URL, the exception should never(or always) occur
e.printStackTrace();
}
return privacyUrl;
}
private void redirectToPaidVersion() {
Intent i = new Intent(
Intent.ACTION_VIEW,
Uri.parse(MARKET_URL_PAID_VERSION));
context.startActivity(i);
}
}
Ok I solved this way: create an instance of your form with the Builder and then you have to call the form.load().
Wait for the form to be loaded and call the .show() inside:
#Override
public void onConsentFormLoaded() {
// Consent form loaded successfully... now you can show it.
Log.d("SplashScreen", "Consent form Loaded ");
showConsentForm();
}
To accomplish this I created a private function :
private showConsentForm(){ form.show(); }
To see how the form works you can try this app:
https://play.google.com/store/apps/details?id=com.mapkcode.whereis
The simple answer is that the form cannot be shown until the form has completed loading. The docs on this nuance are rather dreadful. The fix is to call form.show() within onConsentFormLoaded() like this:
public class MyGdprHelper {
private ConsentForm form;
private void getUsersConsent() {
form = new ConsentForm.Builder(context, privacyUrl)
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form loaded successfully.
form.show();
}
#Override public void onConsentFormOpened() {...}
#Override public void onConsentFormClosed(ConsentStatus consentStatus, Boolean userPrefersAdFree) {...}
#Override public void onConsentFormError(String errorDescription) {...}
)
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.withAdFreeOption()
.build();
form.load();
}
...
}
Based on #WebMajstr answer and #Frank's comment, here is my own class that has two additional features: A callback listener and non EEA users are checked.
package com.levionsoftware.photos.utils.consensus;
import android.content.Context;
import android.util.Log;
import com.google.ads.consent.ConsentForm;
import com.google.ads.consent.ConsentFormListener;
import com.google.ads.consent.ConsentInfoUpdateListener;
import com.google.ads.consent.ConsentInformation;
import com.google.ads.consent.ConsentStatus;
import com.google.ads.consent.DebugGeography;
import com.levionsoftware.photos.MyApplication;
import com.levionsoftware.photos.R;
import java.net.MalformedURLException;
import java.net.URL;
public class GdprHelper {
private static final String PUBLISHER_ID = "pub-2308843076741286";
private final Context context;
private final ConsensusUpdatedListener consensusUpdatedListener;
private ConsentForm consentForm;
public GdprHelper(Context context, ConsensusUpdatedListener consensusUpdatedListener) {
this.context = context;
this.consensusUpdatedListener = consensusUpdatedListener;
}
// Initialises the consent information and displays consent form if needed
public void initialise() {
ConsentInformation consentInformation = ConsentInformation.getInstance(context);
consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);
consentInformation.requestConsentInfoUpdate(new String[]{PUBLISHER_ID}, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
Log.d("GdprHelper", "onConsentInfoUpdated: " + consentStatus.toString());
if(consentInformation.isRequestLocationInEeaOrUnknown()) {
Log.d("GdprHelper", "isRequestLocationInEeaOrUnknown: true");
// If the isRequestLocationInEeaOrUnknown() method returns true:
// If the returned ConsentStatus is PERSONALIZED or NON_PERSONALIZED, the user has already provided consent. You can now forward consent to the Google Mobile Ads SDK.
// If the returned ConsentStatus is UNKNOWN, see the Collect consent section below, which describes the use of utility methods to collect consent.
// User's consent status successfully updated.
if (consentStatus == ConsentStatus.UNKNOWN) {
consensusUpdatedListener.reset();
displayConsentForm();
} else {
consensusUpdatedListener.set(consentStatus == ConsentStatus.NON_PERSONALIZED, false);
}
} else {
Log.d("GdprHelper", "isRequestLocationInEeaOrUnknown: false");
// If the isRequestLocationInEeaOrUnknown() method returns false:
// the user is not located in the European Economic Area and consent is not required under the EU User Consent Policy. You can make ad requests to the Google Mobile Ads SDK.
consensusUpdatedListener.set(false, true);
}
}
#Override
public void onFailedToUpdateConsentInfo(String errorDescription) {
// Consent form error. Would be nice to have proper error logging. Happens also when user has no internet connection
MyApplication.toastSomething(new Exception(errorDescription));
}
});
}
// Resets the consent. User will be again displayed the consent form on next call of initialise method
public void resetConsent() {
ConsentInformation consentInformation = ConsentInformation.getInstance(context);
consentInformation.reset();
}
private void displayConsentForm() {
consentForm = new ConsentForm.Builder(context, getPrivacyUrl())
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form has loaded successfully, now show it
consentForm.show();
}
#Override
public void onConsentFormOpened() {
// Consent form was displayed.
}
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
// Consent form was closed. This callback method contains all the data about user's selection, that you can use.
Log.d("GdprHelper", "onConsentFormClosed: " + consentStatus.toString());
if (consentStatus == ConsentStatus.UNKNOWN) {
consensusUpdatedListener.reset();
displayConsentForm();
} else {
consensusUpdatedListener.set(consentStatus == ConsentStatus.NON_PERSONALIZED, false);
}
}
#Override
public void onConsentFormError(String errorDescription) {
// Consent form error. Would be nice to have some proper logging
MyApplication.toastSomething(new Exception(errorDescription));
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
//.withAdFreeOption()
.build();
consentForm.load();
}
private URL getPrivacyUrl() {
URL privacyUrl = null;
try {
privacyUrl = new URL(MyApplication.get().getString(R.string.privacyPolicyURL));
} catch (MalformedURLException e) {
// Since this is a constant URL, the exception should never(or always) occur
e.printStackTrace();
}
return privacyUrl;
}
}
Listener:
package com.levionsoftware.photos.utils.consensus;
public interface ConsensusUpdatedListener {
void set(Boolean npa, Boolean consensusNotNeeded);
void reset();
}
Edit: See Android: Getting user's location using Admob's Consent SDK, isRequestLocationInEeaOrUnknown must be called AFTER onConsentInfoUpdated.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import com.google.ads.consent.ConsentForm;
import com.google.ads.consent.ConsentFormListener;
import com.google.ads.consent.ConsentInfoUpdateListener;
import com.google.ads.consent.ConsentInformation;
import com.google.ads.consent.ConsentStatus;
import com.google.ads.consent.DebugGeography;
import com.google.ads.mediation.admob.AdMobAdapter;
import com.google.android.gms.ads.AdRequest;
import java.net.MalformedURLException;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private ConsentForm consentForm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getConsentInformation();
String android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
Log.d("Android", "Android ID : " + android_id);
}
private void getConsentInformation() {
final ConsentInformation consentInformation = ConsentInformation.getInstance(MainActivity.this);
String[] publisherIds = {"pub-0123456789012345"};
consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
boolean inEEA = ConsentInformation.getInstance(MainActivity.this).isRequestLocationInEeaOrUnknown();
if (inEEA) {
switch (consentStatus) {
case PERSONALIZED:
personalizeAds(true);
break;
case NON_PERSONALIZED:
personalizeAds(false);
break;
case UNKNOWN:
displayConsentDialog();
break;
default:
break;
}
}
}
#Override
public void onFailedToUpdateConsentInfo(String reason) {
}
});
consentInformation.addTestDevice("33BE2250B43518CCDA7DE426D04EE231");
consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);
}
private void personalizeAds(boolean isPersonalize) {
AdRequest adRequest;
if (isPersonalize) {
adRequest = new AdRequest.Builder().build();
} else {
Bundle extras = new Bundle();
extras.putString("npa", "1");
adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build();
}
}
private void displayConsentDialog() {
URL privacyUrl = null;
try {
// TODO: Replace with your app's privacy policy URL.
privacyUrl = new URL("PRIVACY_POLICY");
} catch (MalformedURLException e) {
e.printStackTrace();
// Handle error.
}
consentForm = new ConsentForm.Builder(this, privacyUrl).withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
super.onConsentFormLoaded();
consentForm.show();
}
#Override
public void onConsentFormClosed(ConsentStatus consentStatus, Boolean userPrefersAdFree) {
super.onConsentFormClosed(consentStatus, userPrefersAdFree);
if (consentStatus.equals(ConsentStatus.PERSONALIZED))
personalizeAds(true);
else
personalizeAds(false);
}
})
.withNonPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.build();
consentForm.load();
}
}
Related
I want to call method on App Start on Enter Foreground (when Activity created). But
appOpenAdManager.showAdAndWaitUntilLoaded(currentActivity);
doesnt execute method. Why?
Expected Logs:
LogAdMaster: AdMaster_autoInit: true
LogAdMaster: AdMaster_autoAppOpenAd: true
LogAdMaster: AdMaster_isAppOpenShowed: false
LogAdMaster: ACTIVITY: NOT NULL
LogAdMaster: appOpenAdManager: NOT NULL
LogAdMaster: INSIDE METHOD
LogAdMaster: Show App Open Ad attempt:
...
But I get only:
LogAdMaster: AdMaster_autoInit: true
LogAdMaster: AdMaster_autoAppOpenAd: true
LogAdMaster: AdMaster_isAppOpenShowed: false
LogAdMaster: ACTIVITY: NOT NULL
LogAdMaster: appOpenAdManager: NOT NULL
...
package com.epicgames.ue4;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.YourCompany.AdMobPlugin.R;
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.appopen.AppOpenAd;
import com.google.android.gms.ads.initialization.AdapterStatus;
import com.epicgames.ue4.network.NetworkChangedManager;
public class GameApplication extends Application implements LifecycleObserver, Application.ActivityLifecycleCallbacks {
private static final Logger Log = new Logger("UE4", "GameApp");
private static final String LOG_TAG = "LogAdMaster";
private static boolean isForeground = false;
private AppOpenAdManager appOpenAdManager;
private Activity currentActivity;
private boolean AdMaster_autoInit = true;
private boolean AdMaster_autoAppOpenAd = false;
private String AdMaster_appOpenAdId = "ca-app-pub-3940256099942544/3419835294";
private boolean AdMaster_isAppOpenShowed = false;
public Context getContext() { return (Context)this; }
public void AndroidThunk_AdMaster_initialize() {
MobileAds.initialize(this, initializationStatus -> {
Map<String, AdapterStatus> statusMap = initializationStatus.getAdapterStatusMap();
for (String adapterClass : statusMap.keySet()) {
AdapterStatus status = statusMap.get(adapterClass);
android.util.Log.d(LOG_TAG, String.format(
"Adapter name: %s, Description: %s, Latency: %d",
adapterClass, status.getDescription(), status.getLatency()));
}
});
}
/** Show the ad if one isn't already showing. */
public void AndroidThunk_AdMaster_showAppOpenAdIfAvailable() {
// We wrap the showAdIfAvailable to enforce that other classes only interact with MyApplication
// class.
appOpenAdManager.showAdIfAvailable(currentActivity);
}
private void loadProperties() {
android.util.Log.d(LOG_TAG, "LOADING");
Resources resources = getContext().getResources();
try {
InputStream rawResource = resources.openRawResource(R.raw.config);
Properties properties = new Properties();
properties.load(rawResource);
String autoInit = properties.getProperty("admaster.auto_init");
String autoAppOpenAd = properties.getProperty("admaster.auto_app_open_ad");
String appOpenAdId = properties.getProperty("admaster.app_open_ad_id");
if (autoInit != null) {
AdMaster_autoInit = Boolean.parseBoolean(autoInit);
}
if (autoAppOpenAd != null) {
AdMaster_autoAppOpenAd = Boolean.parseBoolean(autoAppOpenAd);
}
if (appOpenAdId != null) {
AdMaster_appOpenAdId = appOpenAdId;
}
} catch (Resources.NotFoundException e) {
android.util.Log.e(LOG_TAG, "Unable to find the config file: " + e.getMessage());
} catch (IOException e) {
android.util.Log.e(LOG_TAG, "Failed to open config file.");
}
}
#Override
public void onCreate() {
super.onCreate();
loadProperties();
this.registerActivityLifecycleCallbacks(this);
if (AdMaster_autoInit) {
AndroidThunk_AdMaster_initialize();
}
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
appOpenAdManager = new AppOpenAdManager(AdMaster_appOpenAdId);
NetworkChangedManager.getInstance().initNetworkCallback(this);
}
#Override
public void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
#Override
public void onLowMemory() {
super.onLowMemory();
}
#Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
#Override
public void onConfigurationChanged (Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
void onEnterForeground() {
Log.verbose("App in foreground");
isForeground = true;
android.util.Log.d(LOG_TAG, "AdMaster_autoInit: " + AdMaster_autoInit + "\r\nAdMaster_autoAppOpenAd: " + AdMaster_autoAppOpenAd + "\r\nAdMaster_isAppOpenShowed: " + AdMaster_isAppOpenShowed);
if (AdMaster_autoInit && AdMaster_autoAppOpenAd && !AdMaster_isAppOpenShowed) {
android.util.Log.d(LOG_TAG, "ACTIVITY: " + (currentActivity != null ? "NOT NULL" : "NULL"));
android.util.Log.d(LOG_TAG, "appOpenAdManager: " + (currentActivity != null ? "NOT NULL" : "NULL"));
// Show the ad (if available) when the app moves to foreground.
appOpenAdManager.showAdAndWaitUntilLoaded(currentActivity);
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onEnterBackground() {
Log.verbose("App in background");
isForeground = false;
}
#SuppressWarnings("unused")
public static boolean isAppInForeground() {
return isForeground;
}
public static boolean isAppInBackground() {
return !isForeground;
}
/** ActivityLifecycleCallback methods. */
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
#Override
public void onActivityStarted(Activity activity) {
// Updating the currentActivity only when an ad is not showing.
if (!appOpenAdManager.isShowingAd) {
currentActivity = activity;
}
}
#Override
public void onActivityResumed(Activity activity) {}
#Override
public void onActivityStopped(Activity activity) {}
#Override
public void onActivityPaused(Activity activity) {}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}
#Override
public void onActivityDestroyed(Activity activity) {}
/** Interface definition for a callback to be invoked when an app open ad is complete. */
public interface OnShowAdCompleteListener {
void onShowAdComplete();
}
private class AppOpenAdManager {
private static final String LOG_TAG = "AppOpenAdManager";
private String AD_UNIT_ID = "ca-app-pub-3940256099942544/3419835294";
private AppOpenAd appOpenAd = null;
private boolean isLoadingAd = false;
private boolean isShowingAd = false;
/** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
private long loadTime = 0;
/** Constructor. */
public AppOpenAdManager(String adUnitAd) { AD_UNIT_ID = adUnitAd; }
/** Request an ad. */
private void loadAd(Context context) {
// Do not load ad if there is an unused ad or one is already loading.
if (isLoadingAd || isAdAvailable()) {
return;
}
isLoadingAd = true;
AdRequest request = new AdRequest.Builder().build();
AppOpenAd.load(
context, AD_UNIT_ID, request,
AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
new AppOpenAd.AppOpenAdLoadCallback() {
#Override
public void onAdLoaded(AppOpenAd ad) {
// Called when an app open ad has loaded.
android.util.Log.d(LOG_TAG, "Open App Ad was loaded.");
appOpenAd = ad;
isLoadingAd = false;
loadTime = (new Date()).getTime();
}
#Override
public void onAdFailedToLoad(LoadAdError loadAdError) {
// Called when an app open ad has failed to load.
android.util.Log.d(LOG_TAG, loadAdError.getMessage());
isLoadingAd = false;
}
});
}
/** Utility method to check if ad was loaded more than n hours ago. */
private boolean wasLoadTimeLessThanNHoursAgo(long numHours) {
long dateDifference = (new Date()).getTime() - this.loadTime;
long numMilliSecondsPerHour = 3600000;
return (dateDifference < (numMilliSecondsPerHour * numHours));
}
/** Check if ad exists and can be shown. */
private boolean isAdAvailable() {
return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
}
public void showAdAndWaitUntilLoaded(#NonNull final Activity activity) {
android.util.Log.d(LOG_TAG, "INSIDE METHOD");
int attempts = 5;
while (!isShowingAd && attempts != 0) {
android.util.Log.d(LOG_TAG, "Show App Open Ad attempt: " + attempts);
showAdIfAvailable(activity);
attempts--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
AdMaster_isAppOpenShowed = true;
}
/** Shows the ad if one isn't already showing. */
private void showAdIfAvailable(#NonNull final Activity activity) {
showAdIfAvailable(
activity,
() -> {
// Empty because the user will go back to the activity that shows the ad.
});
}
public void showAdIfAvailable(
#NonNull final Activity activity,
#NonNull OnShowAdCompleteListener onShowAdCompleteListener) {
// If the app open ad is already showing, do not show the ad again.
if (isShowingAd) {
android.util.Log.d(LOG_TAG, "The app open ad is already showing.");
return;
}
// If the app open ad is not available yet, invoke the callback then load the ad.
if (!isAdAvailable()) {
android.util.Log.d(LOG_TAG, "The app open ad is not ready yet.");
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
return;
}
appOpenAd.setFullScreenContentCallback(
new FullScreenContentCallback() {
#Override
public void onAdDismissedFullScreenContent() {
// Called when fullscreen content is dismissed.
// Set the reference to null so isAdAvailable() returns false.
android.util.Log.d(LOG_TAG, "Ad dismissed fullscreen content.");
appOpenAd = null;
isShowingAd = false;
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
}
#Override
public void onAdFailedToShowFullScreenContent(AdError adError) {
// Called when fullscreen content failed to show.
// Set the reference to null so isAdAvailable() returns false.
android.util.Log.d(LOG_TAG, adError.getMessage());
appOpenAd = null;
isShowingAd = false;
onShowAdCompleteListener.onShowAdComplete();
loadAd(activity);
}
#Override
public void onAdShowedFullScreenContent() {
// Called when fullscreen content is shown.
android.util.Log.d(LOG_TAG, "Ad showed fullscreen content.");
}
});
isShowingAd = true;
appOpenAd.show(activity);
}
}
}
I am attempting to to create a widget from a native view navigation map using method channels and AndroidViews but I keep running into this problem:
E/MethodChannel#flutter/platform_views( 2258): Failed to handle method call
E/MethodChannel#flutter/platform_views( 2258): java.lang.IllegalArgumentException: Cannot add a null child view to a ViewGroup.
Here is the flutter Code :
Main.dart
import 'navigation_view.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'customButton1.dart';
import 'Constantes.dart';
void main() => runApp(MaterialApp(home: TextViewExample()));
class TextViewExample extends StatefulWidget {
#override
_TextViewExampleState createState() => _TextViewExampleState();
}
class _TextViewExampleState extends State<TextViewExample>
with SingleTickerProviderStateMixin {
#override
void initState() {
super.initState();
}
Widget build(BuildContext context) {
// var s = NavigationView(
// onNavigationViewCreated: _onNavigationViewCreated,
// );
double screenWidth, screenHeight;
Size size = MediaQuery.of(context).size;
screenHeight = size.height;
screenWidth = size.width;
print("FLUTTER INIT");
return Scaffold(
body: SafeArea(
child: Stack(children: [
Positioned(
width: screenWidth,
height: screenHeight - 220,
child: Container(
child: NavigationView(
onNavigationViewCreated: _onNavigationViewCreated,
),
),
),
Positioned(
bottom: 75,
child: Container(
height: screenHeight,
width: screenWidth,
void _onNavigationViewCreated(NavigationViewController controller) {}
}
Here is navigation_view.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
typedef void NavigationViewCreatedCallback(NavigationViewController controller);
class NavigationView extends StatefulWidget {
const NavigationView({
Key key,
this.onNavigationViewCreated,
}) : super(key: key);
final NavigationViewCreatedCallback onNavigationViewCreated;
#override
State<StatefulWidget> createState() => _NavigationViewState();
}
class _NavigationViewState extends State<NavigationView> {
#override
Widget build(BuildContext context) {
if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView(
viewType: 'com.Mavic.Cabinn/navigationview',
onPlatformViewCreated: _onPlatformViewCreated,
);
}
return Text(
'$defaultTargetPlatform is not yet supported by the navigation_view plugin');
}
void _onPlatformViewCreated(int id) {
if (widget.onNavigationViewCreated == null) {
return;
}
widget.onNavigationViewCreated(new NavigationViewController._(id));
}
}
class NavigationViewController {
NavigationViewController._(int id)
: _channel = new MethodChannel(
'com.Mavic.Cabinn/navigationview$id');
final MethodChannel _channel;
// Future<void> setNavigation() async {
// try {
// Future.delayed(const Duration(seconds: 1), () async {
// await _channel.invokeMethod('EmpezarNavegacion');
//
// return;
// });
// } on PlatformException catch (e) {
// print(e);
// }
// Future<void> setNavigation() async {
// assert(AndroidView != null);
// return _channel.invokeMethod('EmpezarNavegacion');
// }
//}
Future<dynamic> setNavigation() async {
String message;
try {
final String result = await _channel.invokeMethod('EmpezarNagevacion');
message = result;
} on PlatformException catch (e) {
print(message);
}
}
}
And Finally the Java Code
package com.example.embeded_java_test;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.location.Location;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.models.BannerInstructions;
import com.mapbox.api.directions.v5.models.DirectionsResponse;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute;
import com.example.embeded_java_test.R;
import com.mapbox.services.android.navigation.ui.v5.NavigationView;
import com.mapbox.services.android.navigation.ui.v5.NavigationViewOptions;
import com.mapbox.services.android.navigation.ui.v5.OnNavigationReadyCallback;
//import com.mapbox.services.android.navigation.ui.v5.listeners.BannerInstructionsListener;
//import com.mapbox.services.android.navigation.ui.v5.listeners.InstructionListListener;
import com.mapbox.services.android.navigation.ui.v5.listeners.BannerInstructionsListener;
import com.mapbox.services.android.navigation.ui.v5.listeners.InstructionListListener;
import com.mapbox.services.android.navigation.ui.v5.listeners.NavigationListener;
//import com.mapbox.services.android.navigation.ui.v5.listeners.SpeechAnnouncementListener;
//import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
import com.mapbox.services.android.navigation.ui.v5.listeners.SpeechAnnouncementListener;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute;
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import java.util.Locale;
import java.util.concurrent.Future;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import org.jetbrains.annotations.NotNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformView;
import com.example.embeded_java_test.MainActivity;
public class EmbeddedNavigationActivity extends AppCompatActivity implements OnNavigationReadyCallback,
NavigationListener, ProgressChangeListener, InstructionListListener, SpeechAnnouncementListener,
BannerInstructionsListener,PlatformView, MethodCallHandler {
private Point ORIGIN = Point.fromLngLat(-77.03194990754128, 38.909664963450105);
private Point DESTINATION = Point.fromLngLat(-77.0270025730133, 38.91057077063121);
private final int INITIAL_ZOOM = 16;
private NavigationView navigationView;
private View spacer;
private TextView speedWidget;
private final MethodChannel methodChannel;
private boolean bottomSheetVisible = true;
private boolean instructionListShown = false;
private NavigationMapRoute navigationMapRoute;
// #Override
// public void onCreate(#Nullable Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// navigationView = findViewById(R.id.navigationView);
// navigationView.onCreate(savedInstanceState);
// setTheme(R.style.Theme_AppCompat_Light_NoActionBar);
// //initNightMode();
#Override
public void dispose() {}
private Context context;
private Activity activity;
private Bundle savedInstance;
public EmbeddedNavigationActivity(Context currentContext, Activity currentActivity, Bundle currentBundle,BinaryMessenger messenger, int id)
{
new MainActivity();
MainActivity newMain = new MainActivity();
this.activity=this;
System.out.println(activity + "Activity");
this.context=this;
System.out.println(context + "Context");
System.out.println(activity);
Log.i("Here", "Constructor!!!");
System.out.println(activity + " This Activity");
this.savedInstance=currentBundle;
System.out.println(currentContext + "Context");
//
methodChannel = new MethodChannel(messenger, "com.Mavic.Cabinn/navigationview_" + id);
methodChannel.setMethodCallHandler(this);
}
#Override
public View getView() {
Log.i("Here", "GETVIEW");
return navigationView;
}
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
Log.i("MSG", "CALLING SET TEXT");
switch (methodCall.method) {
case "EmpezarNavegacion":
EmpezarNavegacion(methodCall, result);
result.success(null);
break;
default:
result.notImplemented();
}
}
public void EmpezarNavegacion(MethodCall methodCall, Result result)
{
double LtLang = (double) methodCall.arguments;
result.success(null);
Mapbox.getInstance(context, activity.getString(R.string.access_token));
activity.setContentView(R.layout.cabinn_nav);
navigationView =activity.findViewById(R.id.navigationView);
//super.onCreate(savedInstanceState);
//Mapbox.getInstance(this,getString(R.string.access_token));
//navigationView =(NavigationView)findViewById(R.id.navigationView);
if(navigationView==null)
{
Log.e("ERROR NAVEGACION","NAVIGATION VIEW ES NULL, NO ESTA SETEADO EL CONTENT");
return;
}
//fabNightModeToggle = findViewById(R.id.fabToggleNightMode);
//speedWidget = findViewById(R.id.speed_limit);
//spacer = findViewById(R.id.spacer);
//setSpeedWidgetAnchor(R.id.summaryBottomSheet);
// ORIGIN=ORIGEN;
// DESTINATION=DESTINO;
CameraPosition initialPosition = new CameraPosition.Builder()
.target(new LatLng(ORIGIN.latitude(), ORIGIN.longitude()))
.zoom(INITIAL_ZOOM)
.build();
//navigationView.onCreate(savedInstanceState);
navigationView.onCreate(this.savedInstance);
navigationView.initialize(this, initialPosition);
}
#Override
public void onNavigationReady(boolean isRunning) {
//navigationView.findViewById(R.id.instructionLayoutText).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.instructionListLayout).setVisibility(View.INVISIBLE);
//navigationView.findViewById(R.id.maneuverView).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.summaryBottomSheet).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.feedbackFab).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.subStepText).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.subStepLayout).setVisibility(View.INVISIBLE);
navigationView.findViewById(R.id.summaryContentLayout).setVisibility(View.INVISIBLE);
//navigationView.findViewById(R.id.subManeuverView).setVisibility(View.INVISIBLE);
//navigationView.findViewById(R.id.stepDistanceText).setVisibility(View.INVISIBLE);
//navigationView.findViewById(R.id.stepPrimaryText).setVisibility(View.INVISIBLE);
//navigationView.findViewById(R.id.stepSecondaryText).setVisibility(View.INVISIBLE);
fetchRoute();
}
public void onStart_() {
Log.i("NAV","NAVIGATION START");
navigationView.onStart();
}
public void onResume_() {
navigationView.onResume();
}
public void onLowMemory_() {
navigationView.onLowMemory();
}
public void onBackPressed_() {
// If the navigation view didn't need to do anything, call super
if (!navigationView.onBackPressed()) {
super.onBackPressed();
}
}
protected void onSaveInstanceState_(Bundle outState) {
navigationView.onSaveInstanceState(outState);
}
protected void onRestoreInstanceState_(Bundle savedInstanceState) {
navigationView.onRestoreInstanceState(savedInstanceState);
}
public void onPause_() {
navigationView.onPause();
}
public void onStop_() {
navigationView.onStop();
}
protected void onDestroy_() {
navigationView.onDestroy();
if (isFinishing()) {
saveNightModeToPreferences(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
}
}
#Override
public void onCancelNavigation() {
// Navigation canceled, finish the activity
finish();
}
#Override
public void onNavigationFinished() {
// Intentionally empty
}
#Override
public void onNavigationRunning() {
// Intentionally empty
}
#Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
setSpeed(location);
}
#Override
public void onInstructionListVisibilityChanged(boolean shown) {
instructionListShown = shown;
speedWidget.setVisibility(shown ? View.GONE : View.VISIBLE);
if (instructionListShown) {
// fabNightModeToggle.hide();
} else if (bottomSheetVisible) {
//fabNightModeToggle.show();
}
}
#Override
public SpeechAnnouncement willVoice(SpeechAnnouncement announcement) {
return SpeechAnnouncement.builder().announcement("All announcements will be the same.").build();
}
#Override
public BannerInstructions willDisplay(BannerInstructions instructions) {
return instructions;
}
private void startNavigation(DirectionsRoute directionsRoute) {
NavigationViewOptions.Builder options =
NavigationViewOptions.builder()
.navigationListener(this)
.directionsRoute(directionsRoute)
.shouldSimulateRoute(true)
.progressChangeListener(this);
//.instructionListListener(this)
//.speechAnnouncementListener(this)
// .bannerInstructionsListener(this);
setBottomSheetCallback(options);
//setupNightModeFab();
navigationView.startNavigation(options.build());
}
private void fetchRoute() {
NavigationRoute.builder(this.context)
.accessToken(this.activity.getString(R.string.access_token))
.origin(ORIGIN)
.voiceUnits(DirectionsCriteria.METRIC)
.destination(DESTINATION)
.alternatives(true)
.build()
.getRoute(new Callback<DirectionsResponse>() {
#Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
if(response.body() == null)
{
Log.e("ERROR RUTA","NO RUTA ENCONTRADA");
return;
}else if(response.body().routes().size()==0)
{
Log.e("ERROR RUTA","SIN RUTA ENCONTRADA");
return;
}
DirectionsRoute currentRoute = response.body().routes().get(0);
//navigationMapRoute.addRoute(currentRoute);
startNavigation(currentRoute);
}
#Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
}
});
}
void simpleCallBack(Call<DirectionsResponse> call, Response<DirectionsResponse> response)
{
DirectionsRoute directionsRoute = response.body().routes().get(0);
startNavigation(directionsRoute);
}
private void setSpeedWidgetAnchor(#IdRes int res) {
//CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) spacer.getLayoutParams();
//layoutParams.setAnchorId(res);
//spacer.setLayoutParams(layoutParams);
}
private void setBottomSheetCallback(NavigationViewOptions.Builder options) {
options.bottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
bottomSheetVisible = false;
//fabNightModeToggle.hide();
setSpeedWidgetAnchor(R.id.recenterBtn);
break;
case BottomSheetBehavior.STATE_EXPANDED:
bottomSheetVisible = true;
break;
case BottomSheetBehavior.STATE_SETTLING:
if (!bottomSheetVisible) {
// View needs to be anchored to the bottom sheet before it is finished expanding
// because of the animation
//fabNightModeToggle.show();
setSpeedWidgetAnchor(R.id.summaryBottomSheet);
}
break;
default:
return;
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
}
private void setupNightModeFab() {
//fabNightModeToggle.setOnClickListener(view -> toggleNightMode());
}
private void toggleNightMode() {
int currentNightMode = getCurrentNightMode();
alternateNightMode(currentNightMode);
}
private void initNightMode() {
// int nightMode = retrieveNightModeFromPreferences();
//AppCompatDelegate.setDefaultNightMode(nightMode);
}
private int getCurrentNightMode() {
return getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK;
}
private void alternateNightMode(int currentNightMode) {
int newNightMode;
if (currentNightMode == Configuration.UI_MODE_NIGHT_YES) {
newNightMode = AppCompatDelegate.MODE_NIGHT_NO;
} else {
newNightMode = AppCompatDelegate.MODE_NIGHT_YES;
}
saveNightModeToPreferences(newNightMode);
recreate();
}
private void saveNightModeToPreferences(int nightMode) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
//editor.putInt(getString(R.string.current_night_mode), nightMode);
editor.apply();
}
private void setSpeed(Location location) {
String string = String.format("%d\nMPH", (int) (location.getSpeed() * 2.2369));
//int mphTextSize = getResources().getDimensionPixelSize(R.dimen.mph_text_size);
//int speedTextSize = getResources().getDimensionPixelSize(R.dimen.speed_text_size);
SpannableString spannableString = new SpannableString(string);
// spannableString.setSpan(new AbsoluteSizeSpan(mphTextSize),
// string.length() - 4, string.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// spannableString.setSpan(new AbsoluteSizeSpan(speedTextSize),
// 0, string.length() - 3, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
}
I used this article as reference in order to attempt to replicate this but I hit many a speed bump trying to get this to work properly: https://medium.com/flutter-community/flutter-platformview-how-to-create-flutter-widgets-from-native-views-366e378115b6
This is my flutter doctor output:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.12.13+hotfix.9, on Linux, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Android Studio (version 3.6)
[✓] Connected device (1 available)
• No issues found!
Like I was saying ive been attempting to find a null value and searched high and low for questions similar to this one but Ive had no such luck as this is not something Ive seen done. I also checked if there were any plugins to create and embedded map and unfortunately there arent any at the moment.
im trying to make a function that translate a text and print the result. im using firebasetranslator.
by debugging, the app starts but the textbox is empty. trying to put a marker on
private void setRisultato(String string){
this.risultato = string;
}
I see that the translated text is correctly assigned to the result variable.
but if I put the marker up
public String getRisultato(){
return risultato;
}
I see what result is null (not empty), can someone explain me why?
UPDATE:
Traduttore.java
import android.app.Activity;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.ml.common.modeldownload.FirebaseModelDownloadConditions;
import com.google.firebase.ml.naturallanguage.FirebaseNaturalLanguage;
import com.google.firebase.ml.naturallanguage.translate.FirebaseTranslateLanguage;
import com.google.firebase.ml.naturallanguage.translate.FirebaseTranslator;
import com.google.firebase.ml.naturallanguage.translate.FirebaseTranslatorOptions;
public class Traduttore extends Activity {
private static String text1;
public interface callbacktranslate {
void onCallback(String risultato);
}
public void traduci(String textblock, final callbacktranslate callback){
//clear string and start translate
String blocktext = textblock.replaceAll("\n"," ");
String blocktext2 = blocktext.replaceAll("\\.","");
String blocktext3 = blocktext2.toUpperCase();
text1 = blocktext3;
downloadTranslatorAndTranslate(callback);
}
public void translateText(FirebaseTranslator langTranslator, final callbacktranslate callback) {
//translate source text to english
langTranslator.translate(text1).addOnSuccessListener(
new OnSuccessListener<String>() {
#Override
public void onSuccess(#NonNull String translatedText) {
//make variable all uppercase and save it
String blocktext4 = translatedText.toUpperCase();
callback.onCallback(blocktext4);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
public void downloadTranslatorAndTranslate(final callbacktranslate callback) {
int sourceLanguage = 11; //language source(en=11)
//create translator for source and target languages
FirebaseTranslatorOptions options =
new FirebaseTranslatorOptions.Builder()
.setSourceLanguage(sourceLanguage)
.setTargetLanguage(FirebaseTranslateLanguage.IT)
.build();
final FirebaseTranslator langTranslator =
FirebaseNaturalLanguage.getInstance().getTranslator(options);
//download language models if needed
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
.build();
langTranslator.downloadModelIfNeeded(conditions)
.addOnSuccessListener(
new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void v) {
translateText(langTranslator,callback);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
}
method in MainActivity.java:
public ConstraintLayout vpp;
public TextView[] textViewArray = new TextView[500];
public int i;
public void StartReadImage(){
FirebaseVisionImage image2 = FirebaseVisionImage.fromBitmap(resizedBitmap);
FirebaseVisionTextRecognizer textRecognizer2 = FirebaseVision.getInstance()
.getOnDeviceTextRecognizer();
final Task<FirebaseVisionText> result2 =
textRecognizer2.processImage(image2)
.addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
#Override
public void onSuccess(FirebaseVisionText firebaseVisionText) {
// Task completed successfully
i = 0;
resultText = firebaseVisionText.getText();
for (FirebaseVisionText.TextBlock block: firebaseVisionText.getTextBlocks()) {
String blockText = block.getText();
Float blockConfidence = block.getConfidence();
List<RecognizedLanguage> blockLanguages = block.getRecognizedLanguages();
final Point[] blockCornerPoints = block.getCornerPoints();
final Rect blockFrame = block.getBoundingBox();
traduttore.traduci(blockText, new Traduttore.callbacktranslate() {
#Override
public void onCallback(String result) {
createCover(blockCornerPoints, blockFrame, result, i);
i++;
}
});
}
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Task failed with an exception
}
});
}
public void createCover(Point[] CornerPoint,Rect blockFrame,String blockText, int i){
textViewArray[i] = new TextView(context);
textViewArray[i].setTextSize(9);
textViewArray[i].setGravity(Gravity.CENTER);
textViewArray[i].setTextColor(Color.BLACK);
textViewArray[i].setBackgroundColor(Color.rgb(225, 225, 225));
textViewArray[i].setHeight(blockFrame.height());
textViewArray[i].setWidth(blockFrame.width());
textViewArray[i].setX(CornerPoint[0].x);
textViewArray[i].setY(CornerPoint[0].y);
textViewArray[i].setText(blockText);
vpp.addView(textViewArray[i]);
}
You'll need to set the callback somewhere, and in order to do that, you'll need to add it a few places, and pass it on appropriately
traduci(String textblock, final Callback callback)
downloadTranslatorAndTranslate(final Callback callback)
translateText(FirebaseTranslator langTranslator, final Callback callback)
From there, you need to add the callback to the Activity method call, and that method should be void since the callback comes later from when the method actually returns
public void Traduciblocco(String abc){
traduttore.traduci(abc, new Callback() {
#Override
public void onCallback(String result) {
// resultTxt.setText(result);
}
});
}
In my app, I am relying on onUserLeaveHint() method when the user presses the home button, but this method is also being called when you are starting multi window mode in android 7.0 by long pressing "recents button" (which I don't want to perform same thing that I do when home button been pressed). So I want to know if there is a way to detect which is which. Cheers!
Note: onMultiWindowModeChanged() being called after onUserLeaveHint()
I think this is what you're looking for.
HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
#Override
public void onHomePressed() {
// do something here...
}
#Override
public void onHomeLongPressed() {
}
});
mHomeWatcher.startWatch();
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
public class HomeWatcher {
static final String TAG = "hg";
private Context mContext;
private IntentFilter mFilter;
private OnHomePressedListener mListener;
private InnerRecevier mRecevier;
public HomeWatcher(Context context) {
mContext = context;
mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
}
public void setOnHomePressedListener(OnHomePressedListener listener) {
mListener = listener;
mRecevier = new InnerRecevier();
}
public void startWatch() {
if (mRecevier != null) {
mContext.registerReceiver(mRecevier, mFilter);
}
}
public void stopWatch() {
if (mRecevier != null) {
mContext.unregisterReceiver(mRecevier);
}
}
class InnerRecevier extends BroadcastReceiver {
final String SYSTEM_DIALOG_REASON_KEY = "reason";
final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
final String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist";
final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (reason != null) {
Log.e(TAG, "action:" + action + ",reason:" + reason);
if (mListener != null) {
if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
mListener.onHomePressed();
} else if (reason.equals(SYSTEM_DIALOG_REASON_LONG_PRESS)) {
mListener.onHomeLongPressed();
}
}
}
}
}
}
}
public interface OnHomePressedListener {
public void onHomePressed();
public void onHomeLongPressed();
}
Yesterday i ask a simplified question of my problem, but think its too simplified.
What my programm should do, is to hear a keyword and when he hear it, he should listen to what i said. (like if you told to siri or google now, by saying siri or ok google).
I'm using pocketsphinx for the keyword and the google speechrecognizer for the longer parts. It works, but only for one time. The pocketsphinx is in the MainActivity and the google recognizer is in an extra class (Jarvis).
The programm starts with the pocketsphinx listener, when he hear the KEYPHRASE, he starts the google listener by calling jarvis.startListener() (by the next()-method) and there is the problem, when the googlelistener is done, i dont come back from the Jarvis-class to the MainActivity to call the next() method again.
(when the google recognizer is done, the last things he do is in the onResult() in Jarvis-class, but from there i cant call the next()-method from MainActivity-class)
MainActivity
package com.example.superuser.jarvis;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import edu.cmu.pocketsphinx.Assets;
import edu.cmu.pocketsphinx.Hypothesis;
import edu.cmu.pocketsphinx.SpeechRecognizer;
import edu.cmu.pocketsphinx.SpeechRecognizerSetup;
import static android.widget.Toast.makeText;
import static edu.cmu.pocketsphinx.SpeechRecognizerSetup.defaultSetup;
public class MainActivity extends Activity implements edu.cmu.pocketsphinx.RecognitionListener {
private String LOG_TAG = "Jarvis_hears_anything";
private TextView tv;
private Jarvis jarvis;
private boolean wannahearjarvis = false;
/* Named searches allow to quickly reconfigure the decoder */
private static final String KWS_SEARCH = "wakeup";
/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "jarvis";
private edu.cmu.pocketsphinx.SpeechRecognizer recognizer;
//private HashMap<String, Integer> captions;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.b1);
tv = (TextView) findViewById(R.id.tv1);
//captions = new HashMap<String, Integer>();
//captions.put(KWS_SEARCH, R.string.kws_caption);
jarvis = new Jarvis(getApplicationContext());
new AsyncTask<Void, Void, Exception>() {
#Override
protected Exception doInBackground(Void... params) {
try {
Assets assets = new Assets(MainActivity.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
((TextView) findViewById(R.id.tv1))
.setText("Failed to init recognizer " + result);
} else {
//switchSearch(KWS_SEARCH);
recognizer.startListening(KWS_SEARCH);
}
}
}.execute();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "geht", Toast.LENGTH_SHORT).show();
}
});
}
public void next(){
if (wannahearjarvis){
recognizer.startListening(KWS_SEARCH);
wannahearjarvis = false;
}
else{
jarvis.startListening();
wannahearjarvis = true;
}
}
#Override
public void onDestroy() {
super.onDestroy();
recognizer.cancel();
recognizer.shutdown();
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE)){
tv.append("found");
recognizer.stop();
//switchSearch(KWS_SEARCH);
}
else {
//((TextView) findViewById(R.id.tv1)).append(text+"PR");
//Log.i(LOG_TAG, text+"PR");
}
}
/**
* This callback is called when we stop the recognizer.
*/
#Override
public void onResult(Hypothesis hypothesis) {
//((TextView) findViewById(R.id.tv1)).setText("");
((TextView) findViewById(R.id.tv1)).append("oR");
if (hypothesis != null) {
String text = hypothesis.getHypstr();
makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
next();
}
#Override
public void onBeginningOfSpeech() {
}
/**
* We stop recognizer here to get a final result
*/
#Override
public void onEndOfSpeech() {
if (!recognizer.getSearchName().equals(KWS_SEARCH)){
tv.append("fuck");
}
//switchSearch(KWS_SEARCH);
}
/*private void switchSearch(String searchName) {
recognizer.stop();
// If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
if (searchName.equals(KWS_SEARCH))
recognizer.startListening(searchName);
else
recognizer.startListening(searchName, 10000);
//String caption = getResources().getString(captions.get(searchName));
//((TextView) findViewById(R.id.tv1)).setText(caption);
//((TextView) findViewById(R.id.tv1)).append(caption);
}*/
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
// To disable logging of raw audio comment out this call (takes a lot of space on the device)
.setRawLogDir(assetsDir)
// Threshold to tune for keyphrase to balance between false alarms and misses
.setKeywordThreshold(1e-20f)
// Use context-independent phonetic search, context-dependent is too slow for mobile
.setBoolean("-allphone_ci", true)
.getRecognizer();
recognizer.addListener(this);
/** In your application you might not need to add all those searches.
* They are added here for demonstration. You can leave just one.
*/
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
}
#Override
public void onError(Exception error) {
((TextView) findViewById(R.id.tv1)).setText(error.getMessage());
}
#Override
public void onTimeout() {
//switchSearch(KWS_SEARCH);
}
}
Jarvis
package com.example.superuser.jarvis;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.widget.Toast;
import java.util.ArrayList;
public class Jarvis implements RecognitionListener{
private AudioManager audiom;
private SpeechRecognizer speech;
private Intent recogIntent;
private Toast m;
private Context c;
private String text;
public Jarvis(Context context){
speech = SpeechRecognizer.createSpeechRecognizer(context);
speech.setRecognitionListener(this);
recogIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recogIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "de");
//recogIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName());
m = new Toast(context);
c=context;
}
public void startListening(){
speech.startListening(recogIntent);
}
public void destroy(){
speech.stopListening();
speech.cancel();
speech.destroy();
}
#Override
public void onReadyForSpeech(Bundle params) {
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float rmsdB) {
}
#Override
public void onBufferReceived(byte[] buffer) {
}
#Override
public void onEndOfSpeech() {
}
#Override
public void onError(int error) {
}
#Override
public void onResults(Bundle results) {
ArrayList<String> matches = results
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Toast.makeText(c, matches.get(0), Toast.LENGTH_LONG).show();
speech.cancel();
//tried
//MainActivity m = new MainActivity();
//m.next();
//but got a Nullpointer Exception
}
#Override
public void onPartialResults(Bundle partialResults) {
}
#Override
public void onEvent(int eventType, Bundle params) {
}
}
You can store reference to the main activity in Jarvis object in a field:
class Jarvis {
....
private MainActivity m;
....
public Jarvis(MainActivity m) {
this.m = m;
}
....
public void onResults(Bundle results) {
....
m.next();
}
You can also send intents to the main activity as described here. This might be overkill in your case though.