Java tutorial
/* * Copyright @ 2018-present 8x8, Inc. * Copyright @ 2018 Atlassian 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 org.jitsi.meet.sdk; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.ReadableMap; import com.rnimmersive.RNImmersiveModule; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.WeakHashMap; /** * Base class for all views which are backed by a React Native view. */ public abstract class BaseReactView<ListenerT> extends FrameLayout { /** * Background color used by {@code BaseReactView} and the React Native root * view. */ protected static int BACKGROUND_COLOR = 0xFF111111; /** * The collection of all existing {@code BaseReactView}s. Used to find the * {@code BaseReactView} when delivering events coming from * {@link ExternalAPIModule}. */ static final Set<BaseReactView> views = Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>()); /** * Finds a {@code BaseReactView} which matches a specific external API * scope. * * @param externalAPIScope - The external API scope associated with the * {@code BaseReactView} to find. * @return The {@code BaseReactView}, if any, associated with the specified * {@code externalAPIScope}; otherwise, {@code null}. */ public static BaseReactView findViewByExternalAPIScope(String externalAPIScope) { synchronized (views) { for (BaseReactView view : views) { if (view.externalAPIScope.equals(externalAPIScope)) { return view; } } } return null; } /** * Gets all registered React views. * * @return An {@link ArrayList} containing all views currently held by React. */ static ArrayList<BaseReactView> getViews() { return new ArrayList<>(views); } /** * The unique identifier of this {@code BaseReactView} within the process * for the purposes of {@link ExternalAPIModule}. The name scope was * inspired by postis which we use on Web for the similar purposes of the * iframe-based external API. */ protected final String externalAPIScope; /** * The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting * events occurring in Jitsi Meet. */ private ListenerT listener; /** * React Native root view. */ private ReactRootView reactRootView; public BaseReactView(@NonNull Context context) { super(context); setBackgroundColor(BACKGROUND_COLOR); ReactInstanceManagerHolder.initReactInstanceManager((Activity) context); // Hook this BaseReactView into ExternalAPI. externalAPIScope = UUID.randomUUID().toString(); synchronized (views) { views.add(this); } } /** * Creates the {@code ReactRootView} for the given app name with the given * props. Once created it's set as the view of this {@code FrameLayout}. * * @param appName - The name of the "app" (in React Native terms) to load. * @param props - The React Component props to pass to the app. */ public void createReactRootView(String appName, @Nullable Bundle props) { if (props == null) { props = new Bundle(); } props.putString("externalAPIScope", externalAPIScope); if (reactRootView == null) { reactRootView = new ReactRootView(getContext()); reactRootView.startReactApplication(ReactInstanceManagerHolder.getReactInstanceManager(), appName, props); reactRootView.setBackgroundColor(BACKGROUND_COLOR); addView(reactRootView); } else { reactRootView.setAppProperties(props); } } /** * Releases the React resources (specifically the {@link ReactRootView}) * associated with this view. * * MUST be called when the {@link Activity} holding this view is destroyed, * typically in the {@code onDestroy} method. */ public void dispose() { if (reactRootView != null) { removeView(reactRootView); reactRootView.unmountReactApplication(); reactRootView = null; } } /** * Gets the listener set on this {@code BaseReactView}. * * @return The listener set on this {@code BaseReactView}. */ public ListenerT getListener() { return listener; } /** * Abstract method called by {@link ExternalAPIModule} when an event is * received for this view. * * @param name - The name of the event. * @param data - The details of the event associated with/specific to the * specified {@code name}. */ protected abstract void onExternalAPIEvent(String name, ReadableMap data); protected void onExternalAPIEvent(Map<String, Method> listenerMethods, String name, ReadableMap data) { ListenerT listener = getListener(); if (listener != null) { ListenerUtils.runListenerMethod(listener, listenerMethods, name, data); } } /** * Called when the window containing this view gains or loses focus. * * @param hasFocus If the window of this view now has focus, {@code true}; * otherwise, {@code false}. */ @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); // https://github.com/mockingbot/react-native-immersive#restore-immersive-state RNImmersiveModule immersive = RNImmersiveModule.getInstance(); if (hasFocus && immersive != null) { immersive.emitImmersiveStateChangeEvent(); } } /** * Sets a specific listener on this {@code BaseReactView}. * * @param listener The listener to set on this {@code BaseReactView}. */ public void setListener(ListenerT listener) { this.listener = listener; } }