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.
This commit is contained in:
parent
3b1ad9faff
commit
4b1743bb2f
|
@ -7,6 +7,9 @@
|
|||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.content.APP_RESTRICTIONS"
|
||||
android:resource="@xml/app_restrictions" />
|
||||
<activity
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -16,9 +16,15 @@
|
|||
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.RestrictionEntry;
|
||||
import android.content.RestrictionsManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -31,6 +37,7 @@ import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
|||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -48,6 +55,27 @@ public class MainActivity extends JitsiMeetActivity {
|
|||
private static final int OVERLAY_PERMISSION_REQUEST_CODE
|
||||
= (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* ServerURL configuration key for restriction configuration using {@link android.content.RestrictionsManager}
|
||||
*/
|
||||
public static final String RESTRICTION_SERVER_URL = "SERVER_URL";
|
||||
|
||||
/**
|
||||
* Broadcast receiver for restrictions handling
|
||||
*/
|
||||
private BroadcastReceiver broadcastReceiver;
|
||||
|
||||
/**
|
||||
* Flag if configuration is provided by RestrictionManager
|
||||
*/
|
||||
private boolean configurationByRestrictions = false;
|
||||
|
||||
/**
|
||||
* Default URL as could be obtained from RestrictionManager
|
||||
*/
|
||||
private String defaultURL;
|
||||
|
||||
|
||||
// JitsiMeetActivity overrides
|
||||
//
|
||||
|
||||
|
@ -85,16 +113,66 @@ public class MainActivity extends JitsiMeetActivity {
|
|||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// As new restrictions including server URL are received,
|
||||
// conference should be restarted with new configuration.
|
||||
leave();
|
||||
recreate();
|
||||
}
|
||||
};
|
||||
registerReceiver(broadcastReceiver,
|
||||
new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED));
|
||||
|
||||
resolveRestrictions();
|
||||
setJitsiMeetConferenceDefaultOptions();
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (broadcastReceiver != null) {
|
||||
unregisterReceiver(broadcastReceiver);
|
||||
broadcastReceiver = null;
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void setJitsiMeetConferenceDefaultOptions() {
|
||||
// Set default options
|
||||
JitsiMeetConferenceOptions defaultOptions
|
||||
= new JitsiMeetConferenceOptions.Builder()
|
||||
.setWelcomePageEnabled(true)
|
||||
.setServerURL(buildURL("https://meet.jit.si"))
|
||||
.setFeatureFlag("call-integration.enabled", false)
|
||||
.build();
|
||||
.setWelcomePageEnabled(true)
|
||||
.setServerURL(buildURL(defaultURL))
|
||||
.setFeatureFlag("call-integration.enabled", false)
|
||||
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
|
||||
.build();
|
||||
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
|
||||
}
|
||||
|
||||
super.initialize();
|
||||
private void resolveRestrictions() {
|
||||
RestrictionsManager manager =
|
||||
(RestrictionsManager) getSystemService(Context.RESTRICTIONS_SERVICE);
|
||||
Bundle restrictions = manager.getApplicationRestrictions();
|
||||
Collection<RestrictionEntry> 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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<resources>
|
||||
<string name="app_name">Jitsi Meet</string>
|
||||
<string name="restriction_server_url_description">URL of Jitsi Meet server instance to connect to</string>
|
||||
<string name="restriction_server_url_title">Server URL</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Server URL configuration -->
|
||||
<restriction
|
||||
android:defaultValue="https://meet.jit.si"
|
||||
android:description="@string/restriction_server_url_description"
|
||||
android:key="SERVER_URL"
|
||||
android:restrictionType="string"
|
||||
android:title="@string/restriction_server_url_title"/>
|
||||
</restrictions>
|
|
@ -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 || {}));
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<Props, State> {
|
|||
<TextInput
|
||||
autoCapitalize = 'none'
|
||||
autoCorrect = { false }
|
||||
editable = { this.props._serverURLChangeEnabled }
|
||||
onBlur = { this._onBlurServerURL }
|
||||
onChangeText = { this._onChangeServerURL }
|
||||
placeholder = { this.props._serverURL }
|
||||
|
@ -514,7 +529,8 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state)
|
||||
..._abstractMapStateToProps(state),
|
||||
_serverURLChangeEnabled: isServerURLChangeEnabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue