From 4b1743bb2f7723b98cb63e580c4e38ebef305861 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 26 Jun 2020 12:47:48 +0300 Subject: [PATCH] android: add serverURL configuration for MDM/EMM environments Android for Enterprise provides special feature for applications to obtain configuration through RestrictionManager remotely by some MDM solution. Jitsi Meet can be remotely installed and provisioned with a proper URL (making URL not editable by the user) inside the Work Profile or Fully managed device. --- android/app/src/main/AndroidManifest.xml | 3 + .../java/org/jitsi/meet/MainActivity.java | 88 +++++++++++++++++-- android/app/src/main/res/values/strings.xml | 2 + .../app/src/main/res/xml/app_restrictions.xml | 11 +++ react/features/app/components/App.native.js | 16 +++- react/features/base/flags/constants.js | 6 ++ .../components/native/SettingsView.js | 22 ++++- react/features/settings/functions.js | 16 ++++ 8 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 android/app/src/main/res/xml/app_restrictions.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e4411d8e9..8d805f038 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,9 @@ android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/AppTheme"> + entries = manager.getManifestRestrictions( + getApplicationContext().getPackageName()); + for (RestrictionEntry restrictionEntry : entries) { + String key = restrictionEntry.getKey(); + if (RESTRICTION_SERVER_URL.equals(key)) { + // If restrictions are passed to the application. + if (restrictions != null && + restrictions.containsKey(RESTRICTION_SERVER_URL)) { + defaultURL = restrictions.getString(RESTRICTION_SERVER_URL); + configurationByRestrictions = true; + // Otherwise use default URL from app-restrictions.xml. + } else { + defaultURL = restrictionEntry.getSelectedString(); + configurationByRestrictions = false; + } + } + } } @Override diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index ebde5be0c..bd1265237 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ Jitsi Meet + URL of Jitsi Meet server instance to connect to + Server URL diff --git a/android/app/src/main/res/xml/app_restrictions.xml b/android/app/src/main/res/xml/app_restrictions.xml new file mode 100644 index 000000000..150c4c7ca --- /dev/null +++ b/android/app/src/main/res/xml/app_restrictions.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index f0343253e..5b692ac18 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -4,7 +4,7 @@ import React from 'react'; import { setColorScheme } from '../../base/color-scheme'; import { DialogContainer } from '../../base/dialog'; -import { CALL_INTEGRATION_ENABLED, updateFlags } from '../../base/flags'; +import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED, updateFlags } from '../../base/flags'; import { Platform } from '../../base/react'; import { DimensionsDetector, clientResized } from '../../base/responsive-ui'; import { updateSettings } from '../../base/settings'; @@ -86,6 +86,20 @@ export class App extends AbstractApp { // We set these early enough so then we avoid any unnecessary re-renders. const { dispatch } = this.state.store; + // Check if serverURL is configured externally and not allowed to change. + const serverURLChangeEnabled = this.props.flags[SERVER_URL_CHANGE_ENABLED]; + + if (!serverURLChangeEnabled) { + // As serverURL is provided externally, so we push it to settings. + if (typeof this.props.url !== 'undefined') { + const { serverURL } = this.props.url; + + if (typeof serverURL !== 'undefined') { + dispatch(updateSettings({ serverURL })); + } + } + } + dispatch(setColorScheme(this.props.colorScheme)); dispatch(updateFlags(this.props.flags)); dispatch(updateSettings(this.props.userInfo || {})); diff --git a/react/features/base/flags/constants.js b/react/features/base/flags/constants.js index dfad73667..bc94c382d 100644 --- a/react/features/base/flags/constants.js +++ b/react/features/base/flags/constants.js @@ -81,6 +81,12 @@ export const RAISE_HAND_ENABLED = 'raise-hand.enabled'; */ export const RECORDING_ENABLED = 'recording.enabled'; +/** + * Flag indicating if server URL change is enabled. + * Default: enabled (true) + */ +export const SERVER_URL_CHANGE_ENABLED = 'server-url-change.enabled'; + /** * Flag indicating if tile view feature should be enabled. * Default: enabled. diff --git a/react/features/settings/components/native/SettingsView.js b/react/features/settings/components/native/SettingsView.js index cfa8fedf7..c5decf726 100644 --- a/react/features/settings/components/native/SettingsView.js +++ b/react/features/settings/components/native/SettingsView.js @@ -7,11 +7,11 @@ import { translate } from '../../../base/i18n'; import { JitsiModal } from '../../../base/modal'; import { connect } from '../../../base/redux'; import { SETTINGS_VIEW_ID } from '../../constants'; -import { normalizeUserInputURL } from '../../functions'; +import { normalizeUserInputURL, isServerURLChangeEnabled } from '../../functions'; import { AbstractSettingsView, _mapStateToProps as _abstractMapStateToProps, - type Props + type Props as AbstractProps } from '../AbstractSettingsView'; import FormRow from './FormRow'; @@ -70,6 +70,20 @@ type State = { startWithVideoMuted: boolean, } +/** + * The type of the React {@code Component} props of + * {@link SettingsView}. + */ +type Props = AbstractProps & { + + /** + * Flag indicating if URL can be changed by user. + * + * @protected + */ + _serverURLChangeEnabled: boolean +} + /** * The native container rendering the app settings page. * @@ -168,6 +182,7 @@ class SettingsView extends AbstractSettingsView { { */ function _mapStateToProps(state) { return { - ..._abstractMapStateToProps(state) + ..._abstractMapStateToProps(state), + _serverURLChangeEnabled: isServerURLChangeEnabled(state) }; } diff --git a/react/features/settings/functions.js b/react/features/settings/functions.js index 4dfd58e24..dcf6f6ee0 100644 --- a/react/features/settings/functions.js +++ b/react/features/settings/functions.js @@ -1,4 +1,6 @@ // @flow + +import { SERVER_URL_CHANGE_ENABLED, getFeatureFlag } from '../base/flags'; import { i18next, DEFAULT_LANGUAGE, LANGUAGES } from '../base/i18n'; import { createLocalTrack } from '../base/lib-jitsi-meet/functions'; import { @@ -23,6 +25,20 @@ export function isSettingEnabled(settingName: string) { return interfaceConfig.SETTINGS_SECTIONS.includes(settingName); } +/** + * Returns true if user is allowed to change Server URL. + * + * @param {(Function|Object)} stateful - The (whole) redux state, or redux's + * {@code getState} function to be used to retrieve the state. + * @returns {boolean} True to indicate that user can change Server URL, false otherwise. + */ +export function isServerURLChangeEnabled(stateful: Object | Function) { + const state = toState(stateful); + const flag = getFeatureFlag(state, SERVER_URL_CHANGE_ENABLED, true); + + return flag; +} + /** * Normalizes a URL entered by the user. * FIXME: Consider adding this to base/util/uri.