commit
3ddd53c1ed
|
@ -6,12 +6,17 @@
|
||||||
<clobbers target="cordova.MoodleApp" />
|
<clobbers target="cordova.MoodleApp" />
|
||||||
</js-module>
|
</js-module>
|
||||||
<platform name="android">
|
<platform name="android">
|
||||||
|
<framework src="com.android.installreferrer:installreferrer:2.2" />
|
||||||
<config-file target="res/xml/config.xml" parent="/*">
|
<config-file target="res/xml/config.xml" parent="/*">
|
||||||
<feature name="SecureStorage">
|
<feature name="SecureStorage">
|
||||||
<param name="android-package" value="com.moodle.moodlemobile.SecureStorage"/>
|
<param name="android-package" value="com.moodle.moodlemobile.SecureStorage"/>
|
||||||
</feature>
|
</feature>
|
||||||
|
<feature name="InstallReferrer">
|
||||||
|
<param name="android-package" value="com.moodle.moodlemobile.InstallReferrer"/>
|
||||||
|
</feature>
|
||||||
</config-file>
|
</config-file>
|
||||||
<source-file src="src/android/SecureStorage.java" target-dir="src/com/moodle/moodlemobile" />
|
<source-file src="src/android/SecureStorage.java" target-dir="src/com/moodle/moodlemobile" />
|
||||||
|
<source-file src="src/android/InstallReferrer.java" target-dir="src/com/moodle/moodlemobile" />
|
||||||
</platform>
|
</platform>
|
||||||
<platform name="ios">
|
<platform name="ios">
|
||||||
<config-file target="config.xml" parent="/*">
|
<config-file target="config.xml" parent="/*">
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.moodle.moodlemobile;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.PluginResult;
|
||||||
|
|
||||||
|
import com.android.installreferrer.api.InstallReferrerClient;
|
||||||
|
import com.android.installreferrer.api.InstallReferrerStateListener;
|
||||||
|
import com.android.installreferrer.api.ReferrerDetails;
|
||||||
|
|
||||||
|
public class InstallReferrer extends CordovaPlugin implements InstallReferrerStateListener {
|
||||||
|
|
||||||
|
private static final String TAG = "InstallReferrer";
|
||||||
|
private static final int UNKNOWN_ERROR = 1;
|
||||||
|
private static final int FEATURE_NOT_SUPPORTED = 2;
|
||||||
|
private static final int SERVICE_UNAVAILABLE = 3;
|
||||||
|
|
||||||
|
private InstallReferrerClient referrerClient;
|
||||||
|
private CallbackContext callbackContext;
|
||||||
|
private JSONObject referrerResult;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
|
try {
|
||||||
|
switch (action) {
|
||||||
|
case "getReferrer":
|
||||||
|
this.getReferrer(callbackContext);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "Failed executing action: " + action, e);
|
||||||
|
callbackContext.error(e.getMessage());
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the referrer client and obtain the referrer data when connected.
|
||||||
|
*
|
||||||
|
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||||
|
*/
|
||||||
|
private void getReferrer(CallbackContext callbackContext) {
|
||||||
|
if (this.referrerResult != null) {
|
||||||
|
callbackContext.success(this.referrerResult);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.callbackContext = callbackContext;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.referrerClient == null) {
|
||||||
|
this.referrerClient = InstallReferrerClient.newBuilder(this.cordova.getActivity().getApplicationContext()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.referrerClient.startConnection(this);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Log.e(TAG, "startConnection error: " + exception.getMessage());
|
||||||
|
callbackContext.error(exception.getMessage());
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get referral data from an already established connection and pass it to current callback context.
|
||||||
|
*/
|
||||||
|
private void getReferralData() {
|
||||||
|
try {
|
||||||
|
ReferrerDetails response = referrerClient.getInstallReferrer();
|
||||||
|
JSONObject referrerResult = new JSONObject();
|
||||||
|
|
||||||
|
referrerResult.put("referrer", response.getInstallReferrer());
|
||||||
|
referrerResult.put("clickTime", response.getReferrerClickTimestampSeconds());
|
||||||
|
referrerResult.put("appInstallTime", response.getInstallBeginTimestampSeconds());
|
||||||
|
referrerResult.put("instantExperienceLaunched", response.getGooglePlayInstantParam());
|
||||||
|
this.referrerResult = referrerResult;
|
||||||
|
|
||||||
|
if (this.callbackContext != null) {
|
||||||
|
this.callbackContext.success(this.referrerResult);
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Log.e(TAG, "getReferralData error: " + exception.getMessage());
|
||||||
|
if (this.callbackContext != null) {
|
||||||
|
this.callbackContext.error(exception.getMessage());
|
||||||
|
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.referrerClient.endConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.referrerClient.endConnection();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInstallReferrerSetupFinished(int responseCode) {
|
||||||
|
switch (responseCode) {
|
||||||
|
case InstallReferrerClient.InstallReferrerResponse.OK:
|
||||||
|
// Connection established.
|
||||||
|
this.getReferralData();
|
||||||
|
break;
|
||||||
|
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
|
||||||
|
// API not available on the current Play Store app.
|
||||||
|
if (this.callbackContext != null) {
|
||||||
|
this.callbackContext.error("Referrer feature not supported.");
|
||||||
|
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FEATURE_NOT_SUPPORTED));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
|
||||||
|
// Connection couldn't be established.
|
||||||
|
if (this.callbackContext != null) {
|
||||||
|
this.callbackContext.error("Referrer service unavailable.");
|
||||||
|
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, SERVICE_UNAVAILABLE));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInstallReferrerServiceDisconnected() {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,10 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { InstallReferrer } from './plugins/InstallReferrer';
|
||||||
import { SecureStorage } from './plugins/SecureStorage';
|
import { SecureStorage } from './plugins/SecureStorage';
|
||||||
|
|
||||||
const api: MoodleAppPlugins = {
|
const api: MoodleAppPlugins = {
|
||||||
secureStorage: new SecureStorage(),
|
secureStorage: new SecureStorage(),
|
||||||
|
installReferrer: new InstallReferrer(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is necessary to work around the default transpilation behavior,
|
// This is necessary to work around the default transpilation behavior,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows retrieving install referrer data.
|
||||||
|
* https://developer.android.com/google/play/installreferrer
|
||||||
|
*/
|
||||||
|
export class InstallReferrer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get referrer data.
|
||||||
|
*
|
||||||
|
* @returns Referrer data.
|
||||||
|
*/
|
||||||
|
async getReferrer(): Promise<InstallReferrerResult> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
cordova.exec(resolve, reject, 'InstallReferrer', 'getReferrer', []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InstallReferrerResult = {
|
||||||
|
referrer: string;
|
||||||
|
clickTime: number;
|
||||||
|
appInstallTime: number;
|
||||||
|
instantExperienceLaunched: boolean;
|
||||||
|
};
|
|
@ -12,12 +12,14 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { InstallReferrer } from '../src/ts/plugins/InstallReferrer';
|
||||||
import { SecureStorage as SecureStorageImpl } from '../src/ts/plugins/SecureStorage';
|
import { SecureStorage as SecureStorageImpl } from '../src/ts/plugins/SecureStorage';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
||||||
interface MoodleAppPlugins {
|
interface MoodleAppPlugins {
|
||||||
secureStorage: SecureStorageImpl;
|
secureStorage: SecureStorageImpl;
|
||||||
|
installReferrer: InstallReferrer;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Cordova {
|
interface Cordova {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="ion-text-center ion-padding ion-margin-bottom core-login-site-logo" [class.hidden]="hasSites || enteredSiteUrl">
|
<div class="ion-text-center ion-padding ion-margin-bottom core-login-site-logo" [class.hidden]="hasSites || enteredSiteUrl">
|
||||||
<img src="assets/img/login_logo.png" class="avatar-full login-logo" role="presentation" alt="">
|
<img src="assets/img/login_logo.png" class="avatar-full login-logo" role="presentation" alt="">
|
||||||
</div>
|
</div>
|
||||||
<form [formGroup]="siteForm" (ngSubmit)="connect($event, siteForm.value.siteUrl)" *ngIf="!fixedSites && siteForm" #siteFormEl>
|
<form [formGroup]="siteForm" (ngSubmit)="connect(siteForm.value.siteUrl, $event)" *ngIf="!fixedSites && siteForm" #siteFormEl>
|
||||||
<!-- Form to input the site URL if there are no fixed sites. -->
|
<!-- Form to input the site URL if there are no fixed sites. -->
|
||||||
<ng-container *ngIf=" siteSelector=='url'">
|
<ng-container *ngIf=" siteSelector=='url'">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
<h2 class="item-heading">{{ 'core.login.selectsite' | translate }}</h2>
|
<h2 class="item-heading">{{ 'core.login.selectsite' | translate }}</h2>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button *ngIf="enteredSiteUrl" (click)="connect($event, enteredSiteUrl.url)"
|
<ion-item button *ngIf="enteredSiteUrl" (click)="connect(enteredSiteUrl.url, $event)"
|
||||||
[attr.aria-label]="'core.login.connect' | translate" detail="true" class="core-login-entered-site">
|
[attr.aria-label]="'core.login.connect' | translate" detail="true" class="core-login-entered-site">
|
||||||
<ion-thumbnail slot="start" aria-hidden="true">
|
<ion-thumbnail slot="start" aria-hidden="true">
|
||||||
<ion-icon name="fas-pen" aria-hidden="true"></ion-icon>
|
<ion-icon name="fas-pen" aria-hidden="true"></ion-icon>
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
|
|
||||||
<!-- Template site selector. -->
|
<!-- Template site selector. -->
|
||||||
<ng-template #sitelisting let-site="site">
|
<ng-template #sitelisting let-site="site">
|
||||||
<ion-item button (click)="connect($event, site.url, site)" [ngClass]="site.className" [attr.aria-label]="site.name" detail="true">
|
<ion-item button (click)="connect(site.url, $event, site)" [ngClass]="site.className" [attr.aria-label]="site.name" detail="true">
|
||||||
<ion-thumbnail *ngIf="siteFinderSettings.displayimage" slot="start">
|
<ion-thumbnail *ngIf="siteFinderSettings.displayimage" slot="start">
|
||||||
<img [src]="site.imageurl" *ngIf="site.imageurl" onError="this.src='assets/icon/icon.png'" alt="" role="presentation">
|
<img [src]="site.imageurl" *ngIf="site.imageurl" onError="this.src='assets/icon/icon.png'" alt="" role="presentation">
|
||||||
<img src="assets/icon/icon.png" *ngIf="!site.imageurl" class="core-login-default-icon" alt="" role="presentation">
|
<img src="assets/icon/icon.png" *ngIf="!site.imageurl" class="core-login-default-icon" alt="" role="presentation">
|
||||||
|
|
|
@ -46,6 +46,7 @@ import { CoreUserSupportConfig } from '@features/user/classes/support/support-co
|
||||||
import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest-support-config';
|
import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest-support-config';
|
||||||
import { CoreLoginError } from '@classes/errors/loginerror';
|
import { CoreLoginError } from '@classes/errors/loginerror';
|
||||||
import { CorePlatform } from '@services/platform';
|
import { CorePlatform } from '@services/platform';
|
||||||
|
import { CoreReferrer } from '@services/referrer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Site (url) chooser when adding a new site.
|
* Site (url) chooser when adding a new site.
|
||||||
|
@ -98,9 +99,22 @@ export class CoreLoginSitePage implements OnInit {
|
||||||
|
|
||||||
if (sites.length) {
|
if (sites.length) {
|
||||||
url = await this.initSiteSelector();
|
url = await this.initSiteSelector();
|
||||||
} else if (CoreConstants.CONFIG.enableonboarding && !CorePlatform.isIOS()) {
|
} else {
|
||||||
|
url = await this.consumeInstallReferrerUrl() ?? '';
|
||||||
|
|
||||||
|
const showOnboarding = CoreConstants.CONFIG.enableonboarding && !CorePlatform.isIOS();
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
this.connect(url);
|
||||||
|
|
||||||
|
if (showOnboarding) {
|
||||||
|
// Don't display onboarding in this case, and don't display it again later.
|
||||||
|
CoreConfig.set(CoreLoginHelperProvider.ONBOARDING_DONE, 1);
|
||||||
|
}
|
||||||
|
} else if (showOnboarding) {
|
||||||
this.initOnboarding();
|
this.initOnboarding();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.showScanQR = CoreLoginHelper.displayQRInSiteScreen();
|
this.showScanQR = CoreLoginHelper.displayQRInSiteScreen();
|
||||||
|
|
||||||
|
@ -150,6 +164,26 @@ export class CoreLoginSitePage implements OnInit {
|
||||||
return this.fixedSites[0].url;
|
return this.fixedSites[0].url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume install referrer URL.
|
||||||
|
*
|
||||||
|
* @returns Referrer URL, undefined if no URL to use.
|
||||||
|
*/
|
||||||
|
protected async consumeInstallReferrerUrl(): Promise<string | undefined> {
|
||||||
|
const url = await CoreUtils.ignoreErrors(CoreUtils.timeoutPromise(CoreReferrer.consumeInstallReferrerUrl(), 1000));
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasSites = (await CoreUtils.ignoreErrors(CoreSites.getSites(), [])).length > 0;
|
||||||
|
if (hasSites) {
|
||||||
|
// There are sites stored already, don't use the referrer URL since it's an update or a backup was restored.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize and show onboarding if needed.
|
* Initialize and show onboarding if needed.
|
||||||
*
|
*
|
||||||
|
@ -241,14 +275,14 @@ export class CoreLoginSitePage implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Try to connect to a site.
|
* Try to connect to a site.
|
||||||
*
|
*
|
||||||
* @param e Event.
|
|
||||||
* @param url The URL to connect to.
|
* @param url The URL to connect to.
|
||||||
|
* @param e Event (if any).
|
||||||
* @param foundSite The site clicked, if any, from the found sites list.
|
* @param foundSite The site clicked, if any, from the found sites list.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async connect(e: Event, url: string, foundSite?: CoreLoginSiteInfoExtended): Promise<void> {
|
async connect(url: string, e?: Event, foundSite?: CoreLoginSiteInfoExtended): Promise<void> {
|
||||||
e.preventDefault();
|
e?.preventDefault();
|
||||||
e.stopPropagation();
|
e?.stopPropagation();
|
||||||
|
|
||||||
CoreApp.closeKeyboard();
|
CoreApp.closeKeyboard();
|
||||||
|
|
||||||
|
@ -540,7 +574,7 @@ export class CoreLoginSitePage implements OnInit {
|
||||||
// Put the text in the field (if present).
|
// Put the text in the field (if present).
|
||||||
this.siteForm.controls.siteUrl.setValue(text);
|
this.siteForm.controls.siteUrl.setValue(text);
|
||||||
|
|
||||||
this.connect(new Event('click'), text);
|
this.connect(text);
|
||||||
} else {
|
} else {
|
||||||
CoreDomUtils.showErrorModal('core.errorurlschemeinvalidsite', true);
|
CoreDomUtils.showErrorModal('core.errorurlschemeinvalidsite', true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,13 @@ export class CoreNativeService {
|
||||||
* Get a native plugin instance.
|
* Get a native plugin instance.
|
||||||
*
|
*
|
||||||
* @param plugin Plugin name.
|
* @param plugin Plugin name.
|
||||||
* @returns Plugin instance.
|
* @returns Plugin instance, null if plugin is not supported for current platform.
|
||||||
*/
|
*/
|
||||||
plugin<Plugin extends keyof MoodleAppPlugins>(plugin: Plugin): AsyncInstance<MoodleAppPlugins[Plugin]> {
|
plugin<Plugin extends keyof MoodleAppPlugins>(plugin: Plugin): AsyncInstance<MoodleAppPlugins[Plugin]> | null {
|
||||||
|
if (plugin === 'installReferrer' && !CorePlatform.isAndroid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(plugin in this.plugins)) {
|
if (!(plugin in this.plugins)) {
|
||||||
this.plugins[plugin] = asyncInstance(async () => {
|
this.plugins[plugin] = asyncInstance(async () => {
|
||||||
await CorePlatform.ready();
|
await CorePlatform.ready();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { CoreConfig } from './config';
|
||||||
|
import { CoreNative } from '@features/native/services/native';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage referrers.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CoreReferrerService {
|
||||||
|
|
||||||
|
protected static readonly INSTALL_REFERRER_CONSUMED = 'install_referrer_consumed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consume the install referrer URL (Android only).
|
||||||
|
* This function will try to retrieve the siteurl supplied as referrer to Google Play.
|
||||||
|
*
|
||||||
|
* @returns Referrer URL, undefined if not supported, already consumed or no referrer URL.
|
||||||
|
*/
|
||||||
|
async consumeInstallReferrerUrl(): Promise<string | undefined> {
|
||||||
|
const installReferrerPlugin = CoreNative.plugin('installReferrer');
|
||||||
|
if (!installReferrerPlugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only get the referrer URL once, it's only needed when the app is installed.
|
||||||
|
const referredConsumed = await CoreConfig.get(CoreReferrerService.INSTALL_REFERRER_CONSUMED, 0);
|
||||||
|
if (referredConsumed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await installReferrerPlugin.getReferrer();
|
||||||
|
|
||||||
|
const siteUrlMatch = (result.referrer ?? '').match(/siteurl=([^&]+)/);
|
||||||
|
|
||||||
|
return siteUrlMatch?.[1];
|
||||||
|
} catch {
|
||||||
|
// Error getting referrer, it probably means Google Play is not available.
|
||||||
|
} finally {
|
||||||
|
await CoreConfig.set(CoreReferrerService.INSTALL_REFERRER_CONSUMED, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CoreReferrer = makeSingleton(CoreReferrerService);
|
|
@ -1106,7 +1106,7 @@ export class CoreSitesProvider {
|
||||||
// Site deleted from sites list, now delete the folder.
|
// Site deleted from sites list, now delete the folder.
|
||||||
await site.deleteFolder();
|
await site.deleteFolder();
|
||||||
|
|
||||||
await CoreUtils.ignoreErrors(CoreNative.plugin('secureStorage').deleteCollection(siteId));
|
await CoreUtils.ignoreErrors(CoreNative.plugin('secureStorage')?.deleteCollection(siteId));
|
||||||
|
|
||||||
CoreEvents.trigger(CoreEvents.SITE_DELETED, site, siteId);
|
CoreEvents.trigger(CoreEvents.SITE_DELETED, site, siteId);
|
||||||
}
|
}
|
||||||
|
@ -2063,7 +2063,7 @@ export class CoreSitesProvider {
|
||||||
* @returns Stored tokens.
|
* @returns Stored tokens.
|
||||||
*/
|
*/
|
||||||
protected async getTokensFromSecureStorage(siteId: string): Promise<{ token: string; privateToken?: string }> {
|
protected async getTokensFromSecureStorage(siteId: string): Promise<{ token: string; privateToken?: string }> {
|
||||||
const result = await CoreNative.plugin('secureStorage').get(['token', 'privateToken'], siteId);
|
const result = await CoreNative.plugin('secureStorage')?.get(['token', 'privateToken'], siteId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: result?.token ?? '',
|
token: result?.token ?? '',
|
||||||
|
@ -2083,7 +2083,7 @@ export class CoreSitesProvider {
|
||||||
token: string,
|
token: string,
|
||||||
privateToken?: string,
|
privateToken?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await CoreNative.plugin('secureStorage').store({
|
await CoreNative.plugin('secureStorage')?.store({
|
||||||
token: token,
|
token: token,
|
||||||
privateToken: privateToken ?? '',
|
privateToken: privateToken ?? '',
|
||||||
}, siteId);
|
}, siteId);
|
||||||
|
|
|
@ -260,7 +260,7 @@ export class CoreUtilsProvider {
|
||||||
try {
|
try {
|
||||||
const response = await this.timeoutPromise(window.fetch(url, initOptions), CoreWS.getRequestTimeout());
|
const response = await this.timeoutPromise(window.fetch(url, initOptions), CoreWS.getRequestTimeout());
|
||||||
|
|
||||||
return !!response && response.redirected;
|
return response.redirected;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.timeout && controller) {
|
if (error.timeout && controller) {
|
||||||
// Timeout, abort the request.
|
// Timeout, abort the request.
|
||||||
|
@ -1547,14 +1547,14 @@ export class CoreUtilsProvider {
|
||||||
* @param time Number of milliseconds of the timeout.
|
* @param time Number of milliseconds of the timeout.
|
||||||
* @returns Promise with the timeout.
|
* @returns Promise with the timeout.
|
||||||
*/
|
*/
|
||||||
timeoutPromise<T>(promise: Promise<T>, time: number): Promise<T | void> {
|
timeoutPromise<T>(promise: Promise<T>, time: number): Promise<T> {
|
||||||
return new Promise((resolve, reject): void => {
|
return new Promise((resolve, reject): void => {
|
||||||
let timedOut = false;
|
let timedOut = false;
|
||||||
const resolveBeforeTimeout = () => {
|
const resolveBeforeTimeout = (value: T) => {
|
||||||
if (timedOut) {
|
if (timedOut) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve();
|
resolve(value);
|
||||||
};
|
};
|
||||||
const timeout = setTimeout(
|
const timeout = setTimeout(
|
||||||
() => {
|
() => {
|
||||||
|
|
Loading…
Reference in New Issue