Closed DontGiveAFck closed 2 years ago
I think activityListener
may be null in BridgeWebChromeClient
. I'm trying to see what circumstances may be leading to that, can you tell us more about what your app is doing or share the source if possible? Thank you
Hi @carlpoole , thanks for your answer! This issue happens on last app version, some of the changes that were included in this version:
Since issue happens right after app launch I'm adding code of app.component.ts file:
import { Component, NgZone, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Platform, Config, AlertController, LoadingController } from '@ionic/angular';
import { Capacitor, Plugins } from "@capacitor/core";
import { MobileAccessibility } from '@ionic-native/mobile-accessibility/ngx';
import { AngularFireAuth } from "@angular/fire/auth";
import OneSignal from 'onesignal-cordova-plugin';
import '@joinflux/firebase-remote-config';
import { FirebaseCrashlytics } from '@capacitor-community/firebase-crashlytics';
import { FirebaseAnalytics } from "@capacitor-community/firebase-analytics";
import { AdMob } from '@capacitor-community/admob';
import { Keyboard } from '@capacitor/keyboard';
import { GoogleAuth } from '@reslear/capacitor-google-auth'
import { Network } from "@capacitor/network";
import { SplashScreen } from "@capacitor/splash-screen";
import { StatusBar, Style } from '@capacitor/status-bar';
import { AppApi } from './services/core/api/app.api';
import { DeviceApi } from './services/core/api/device.api';
import { IAppState } from './services/core/store/app.reducers';
import { PurchaseService } from './services/core/services/purchase.service';
import { APP_CONFIG } from "./services/app-config";
import { environment } from "../environments/environment";
import { AuthService } from "./services/core/services/auth.service";
import { UpdateIsUserLoggedIn } from "./services/core/store/core";
import { GlobalFuncs } from "./services/global-funcs";
import { selectUserData, selectIsUserLoggedIn, SetIsTabletLandscape } from "./services/core/store/core";
import { isJsonString } from "./helpers/helpers";
import { DeepLinkService } from "./services/core/services/deepLink.service";
import * as CoreStore from './services/core/store/core';
import { DeepLink } from "../constants/types";
const { FirebaseRemoteConfig } = Plugins;
@Component({
selector: 'sam-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {
private isUserDataUpdatedAfterAppLaunch: boolean = false;
private firebaseConfigFetched: boolean = false;
private isUserPremium: boolean = false;
private refreshReConfigTimer() {
setTimeout(x => {
console.log("RE-CONFIGing");
this.appApi.fetchFireBaseConfig();
this.refreshReConfigTimer();
}, APP_CONFIG.TIMER_RECONFIG_MSEC);
}
constructor(
private platform: Platform,
private config: Config,
private appApi: AppApi,
private deviceApi: DeviceApi,
private mobileAccessibility: MobileAccessibility,
private store: Store<IAppState>,
private purchaseService: PurchaseService,
private authService: AuthService,
private globalFuncs: GlobalFuncs,
private fireAuth: AngularFireAuth,
private deepLinkService: DeepLinkService,
private ngZone: NgZone,
private alertCtrl: AlertController,
private loadingCtrl: LoadingController,
) {}
async ngOnInit(): Promise<void> {
try {
await this.checkForNetworkConnectionOnAppStart();
// initialize BI for vwc
(window as any).vwcFirebaseLogger = this.globalFuncs.sendAnalEvent;
this.appApi.fetchVersion();
this.appApi.fetchCategoryTree();
this.refreshReConfigTimer();
this.platform.ready().then(() => {
const isAndroid = Capacitor.getPlatform() === 'android';
// initialize google plus for authentication via Google
GoogleAuth.initialize();
// initialize One Signal
this.initOneSignalSDK();
// Initialize AdMob:
if (Capacitor.isNativePlatform()) {
AdMob.initialize({
requestTrackingAuthorization: false,
}).then(() => {
// prepare interstitial ad
const options = {
adId: isAndroid
? environment.ANDROID_UNIT_ID_FOR_INTERSTITIAL
: environment.IOS_AD_UNIT_ID_FOR_INTERSTITIAL,
// show npa only for ios
npa: !isAndroid,
// isTesting: !environment.production,
};
AdMob.prepareInterstitial(options);
// ---
});
}
// portrait orientation lock
if (this.platform.is('mobile')) {
window.screen.orientation.lock('portrait');
}
this.updateAndSubscribeToTabletLandscape();
// fix keyboard issue on iPad
if (!isAndroid) {
Keyboard.addListener('keyboardDidShow', () => {
const activeElement = document.activeElement;
setTimeout(() => {
const ionContent = document.querySelector('page-editor ion-content');
if (ionContent && ionContent.shadowRoot) {
const scrollElement = ionContent.shadowRoot.querySelector('.inner-scroll');
scrollElement.scroll({
top: scrollElement.scrollTop + 2,
behavior: 'smooth'
});
}
}, 200);
});
}
// enable crashlytics
FirebaseCrashlytics.setEnabled({
enabled: true
});
this.store.select(selectIsUserLoggedIn).subscribe((isLoggedIn) => {
// skip initial value and don't configure purchasing before emitting value from onAuthStateChanged
if (isLoggedIn === null) return;
if (isLoggedIn) {
// get favorites
this.store.dispatch(new CoreStore.GetFavorites());
} else {
this.store.dispatch(new CoreStore.UpdateFavorites([]));
this.appApi.updateUserData({
isPremiumOnWebsite: false
});
}
if (this.firebaseConfigFetched) {
// configure purchases (receiving data from app store/google play)
// if user has subscription in app store/google play premium feature will be granted
// also it will call validation service with firebaseID if user logged in.
this.purchaseService.configurePurchasing();
} else {
FirebaseRemoteConfig.initialize({
// @ts-ignore
minimumFetchIntervalInSeconds: 5,
});
this.appApi.fetchFireBaseConfig();
this.firebaseConfigFetched = true;
}
});
this.fireAuth.authState.subscribe(async (user) => {
if (user) {
// update user data on app init
if (!this.isUserDataUpdatedAfterAppLaunch) {
this.store.dispatch(new UpdateIsUserLoggedIn(true));
const res = await this.authService.loginUserOnServer();
if (res.isPremium) {
this.globalFuncs.runIOSCallbackInAngularScope(() => {
this.appApi.updateUserData({
isPremiumOnWebsite: true
})
});
}
}
try {
if (Capacitor.isNativePlatform()) {
FirebaseAnalytics.setUserId({
userId: user.uid
});
}
}
catch (ex){
console.error("Failed at FirebaseAnalytics.setUserId", ex);
}
} else {
this.store.dispatch(new UpdateIsUserLoggedIn(false));
}
this.isUserDataUpdatedAfterAppLaunch = true;
});
this.store.select(selectUserData).subscribe((userData) => {
this.isUserPremium = userData.isPremiumOnWebsite || userData.isPremiumInMobileStore;
});
// AppsFlyer configuration
this.initAppsflyerSDK();
});
try {
this.mobileAccessibility.usePreferredTextZoom(false);
}
catch (ee) {
console.error("usePreferredTextZoom failed", ee);
}
if (Capacitor.isNativePlatform()) {
StatusBar.setStyle({
style: Style.Light
});
// not supported on iOS:
if (Capacitor.getPlatform() !== 'ios') {
StatusBar.setBackgroundColor({ color: '#f8f8f8' });
}
}
} catch (error) {
this.globalFuncs.sendAnalError('error.sam.init', error);
}
}
async initAppsflyerSDK() {
// checking network connection before first
// required for SDK init
await this.globalFuncs.awaitForConnectionToNetwork();
const isAndroid = this.platform.is('android');
// @ts-ignore
const appsFlyer: any = window.plugins.appsFlyer;
const onSuccess = (res) => {
try {
// for some reason can be object or serialized object
const conversionData = JSON.parse(res);
const data = isJsonString(conversionData.data)
? JSON.parse(conversionData.data)
: conversionData.data;
// for some reason behaves differently on ios/android
const shouldHandleDeepLinks = data.is_first_launch;
if (shouldHandleDeepLinks) {
this.deepLinkService.openDeepLink(data.deep_link_value, data.deep_link_sub1 ? { categoryId: data.deep_link_sub1 } : {}, 'deeplink');
}
} catch (error) {
console.error('Appsflyer initSdk onSuccess error: ', error);
}
};
appsFlyer.registerDeepLink((res) => {
try {
const parsedRes = isJsonString(res) ? JSON.parse(res) : res;
const data = isJsonString(parsedRes.data)
? JSON.parse(parsedRes.data)
: parsedRes.data;
this.deepLinkService.openDeepLink(data.deep_link_value, data.deep_link_sub1 ? { categoryId: data.deep_link_sub1 } : {}, 'deeplink');
} catch (error) {
console.error('registerDeepLink error: ', error);
}
});
try {
const options = {
devKey: 'KEY',
isDebug: !environment.production,
appId: 'KEY',
onDeepLinkListener: true,
onInstallConversionDataListener: true
};
appsFlyer.initSdk(options, isAndroid ? onSuccess : () => {});
} catch (error) {
console.error('appsFlyer.initSdk error', error);
}
}
private initOneSignalSDK(): void {
// Uncomment to set OneSignal device logging to VERBOSE
// OneSignal.setLogLevel(6, 0);
// NOTE: Update the setAppId value below with your OneSignal AppId.
OneSignal.setAppId(Capacitor.getPlatform() === 'ios' ? 'KEY' : 'KEY');
OneSignal.setNotificationOpenedHandler((jsonData) => {
const deepLink: DeepLink = (jsonData.notification.additionalData as any).deepLink;
const categoryId: any = (jsonData.notification.additionalData as any).categoryId;
this.deepLinkService.openDeepLink(deepLink, { categoryId }, 'notification_link');
});
}
private async checkForNetworkConnectionOnAppStart() {
const { connected } = await Network.getStatus();
if (!connected) {
const loader = await this.loadingCtrl.create({
message: 'Please establish an internet connection.',
backdropDismiss: false
});
await loader.present();
await SplashScreen.hide();
await this.awaitForConnectionToNetwork();
await SplashScreen.show();
await loader.dismiss();
}
}
private updateAndSubscribeToTabletLandscape() {
const isTabletLandscape = this.platform.is('tablet') && this.platform.isLandscape();
this.store.dispatch(new SetIsTabletLandscape(isTabletLandscape));
// async actions that runs out of angular scope - ngZone.run() required.
window.screen.orientation.onchange = (orientation) => {
this.globalFuncs.runIOSCallbackInAngularScope(() => {
const isTabletLandscape = this.platform.is('tablet') && this.platform.isLandscape();
this.store.dispatch(new SetIsTabletLandscape(isTabletLandscape));
});
};
}
}
// promise will be resolved only when connection to network will be established
awaitForConnectionToNetwork(): Promise<void> {
return new Promise(async (resolve) => {
const { connected } = await Network.getStatus();
if (connected) {
resolve()
} else {
const listener = Network.addListener('networkStatusChange', async ({ connected }) => {
if (connected) {
await listener.remove();
resolve();
}
});
}
})
}
Also adding packages list:
"dependencies": {
"@angular/common": "~12.1.5",
"@angular/core": "~12.1.5",
"@angular/fire": "^6.1.5",
"@angular/forms": "~12.1.5",
"@angular/platform-browser": "~12.1.5",
"@angular/platform-browser-dynamic": "~12.1.5",
"@angular/router": "~12.1.5",
"@capacitor-community/admob": "^3.2.0",
"@capacitor-community/apple-sign-in": "^1.0.1",
"@capacitor-community/facebook-login": "^3.1.1",
"@capacitor-community/firebase-analytics": "^1.0.0",
"@capacitor-community/firebase-crashlytics": "^1.1.0",
"@capacitor-community/media": "git+https://github.com/dragermrb/media.git",
"@capacitor/android": "^3.2.4",
"@capacitor/app": "^1.0.3",
"@capacitor/app-launcher": "^1.0.4",
"@capacitor/camera": "^1.1.0",
"@capacitor/core": "^3.2.4",
"@capacitor/filesystem": "^1.0.3",
"@capacitor/haptics": "^1.1.0",
"@capacitor/ios": "^3.2.4",
"@capacitor/keyboard": "^1.1.0",
"@capacitor/network": "^1.0.3",
"@capacitor/share": "^1.0.4",
"@capacitor/splash-screen": "^1.1.3",
"@capacitor/status-bar": "^1.0.3",
"@firebase/remote-config": "^0.1.41",
"@ionic-native/app-rate": "5.32.1",
"@ionic-native/appsflyer": "^5.34.0",
"@ionic-native/core": "^5.34.0",
"@ionic-native/file": "^5.34.0",
"@ionic-native/in-app-purchase-2": "^5.34.0",
"@ionic-native/mobile-accessibility": "^5.34.0",
"@ionic-native/native-storage": "^5.34.0",
"@ionic-native/open-native-settings": "^5.34.0",
"@ionic-native/printer": "^5.36.0",
"@ionic/angular": "^5.8.4",
"@ionic/storage": "^2.3.1",
"@joinflux/firebase-remote-config": "^0.5.0",
"@ngrx/effects": "^12.1.0",
"@ngrx/store": "^12.1.0",
"@ngrx/store-devtools": "^12.1.0",
"@reslear/capacitor-google-auth": "^3.1.0",
"capacitor-email-composer": "^1.0.0",
"cordova-open-native-settings": "^1.5.5",
"cordova-plugin-apprate": "1.5.0",
"cordova-plugin-appsflyer-sdk": "6.2.60",
"cordova-plugin-device": "git+https://github.com/apache/cordova-plugin-device.git",
"cordova-plugin-dialogs": "^2.0.2",
"cordova-plugin-file": "^6.0.2",
"cordova-plugin-globalization": "^1.11.0",
"cordova-plugin-inappbrowser": "^4.1.0",
"cordova-plugin-nativestorage": "^2.3.2",
"cordova-plugin-network-information": "git+https://github.com/apache/cordova-plugin-network-information.git",
"cordova-plugin-printer": "^0.8.0",
"cordova-plugin-purchase": "^10.6.0",
"cordova-plugin-screen-orientation": "^3.0.2",
"cordova-sqlite-storage": "^6.0.0",
"es6-promise-plugin": "^4.2.2",
"firebase": "^8.7.0",
"globalthis": "^1.0.2",
"jetifier": "^1.6.8",
"onesignal-cordova-plugin": "^3.0.0",
"phonegap-plugin-mobile-accessibility": "^1.0.5",
"rxjs": "^6.6.7",
"rxjs-compat": "^6.6.7",
"tslib": "^2.3.0",
"vue": "^2.6.14",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1000.0",
"@angular/cli": "~12.1.4",
"@angular/compiler": "~12.1.4",
"@angular/compiler-cli": "~12.1.4",
"@angular/language-service": "~12.1.4",
"@capacitor/cli": "^3.2.4",
"@firebase/util": "^0.4.1",
"@ionic/angular-toolkit": "^2.3.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "^2.0.9",
"@types/node": "^12.20.15",
"codelyzer": "^6.0.2",
"cross-env": "^7.0.3",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.6.0",
"prop-types": "^15.7.2",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "^4.2.3"
}
Does you app has some input with file type? If so, can you share it? I think that's the only thing in the WebChromeClient that could cause the callback to be called, but not sure what could lead to activityListener
being null.
Does it happen in specific android versions or devices? I think crashlytics should provide that kind of information.
Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Capacitor, please create a new issue and ensure the template is fully filled out.
Bug Report
Capacitor Version
Platform(s)
Android - doesn't depend on version/device
Current Behavior
After releasing app to prod some users get error on 1-5s after app launch (according to Crashlytics): Stacktrace:
Caused by java.lang.NullPointerException: Attempt to invoke interface method 'void com.getcapacitor.BridgeWebChromeClient$ActivityResultListener.onActivityResult(androidx.activity.result.ActivityResult)' on a null object reference at com.getcapacitor.BridgeWebChromeClient.lambda$new$1$BridgeWebChromeClient(BridgeWebChromeClient.java:72) at com.getcapacitor.-$$Lambda$BridgeWebChromeClient$nK4HrLIPc8JbAwJyF2CGTpDio8A.onActivityResult(:4) at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.java:362) at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.java:322) at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.java:634) at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:164) at com.getcapacitor.BridgeActivity.onActivityResult(BridgeActivity.java:216) at android.app.Activity.dispatchActivityResult(Activity.java:8413) at android.app.ActivityThread.deliverResults(ActivityThread.java:5464) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4776) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4832) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:190) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:105) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2386) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:213) at android.app.ActivityThread.main(ActivityThread.java:8178) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
Any ideas why it can happen?