android: SDK v2 pass one
Add JitsiMeetFragment and refactor the app to use it.
This commit is contained in:
parent
dbc88b972e
commit
90803c8ff6
|
@ -17,105 +17,121 @@
|
|||
|
||||
package org.jitsi.meet;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
import org.jitsi.meet.sdk.JitsiMeetView;
|
||||
import org.jitsi.meet.sdk.JitsiMeetViewListener;
|
||||
import org.jitsi.meet.sdk.JitsiMeet;
|
||||
import org.jitsi.meet.sdk.JitsiMeetFragment;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivityInterface;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivityDelegate;
|
||||
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The one and only {@link Activity} that the Jitsi Meet app needs. The
|
||||
* {@code Activity} is launched in {@code singleTask} mode, so it will be
|
||||
* created upon application initialization and there will be a single instance
|
||||
* of it. Further attempts at launching the application once it was already
|
||||
* launched will result in {@link Activity#onNewIntent(Intent)} being called.
|
||||
*
|
||||
* This {@code Activity} extends {@link JitsiMeetActivity} to keep the React
|
||||
* Native CLI working, since the latter always tries to launch an
|
||||
* {@code Activity} named {@code MainActivity} when doing
|
||||
* {@code react-native run-android}.
|
||||
*/
|
||||
public class MainActivity extends JitsiMeetActivity {
|
||||
public class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {
|
||||
/**
|
||||
* The request code identifying requests for the permission to draw on top
|
||||
* of other apps. The value must be 16-bit and is arbitrarily chosen here.
|
||||
*/
|
||||
private static final int OVERLAY_PERMISSION_REQUEST_CODE
|
||||
= (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
@Override
|
||||
protected JitsiMeetView initializeView() {
|
||||
JitsiMeetView view = super.initializeView();
|
||||
private JitsiMeetFragment getFragment() {
|
||||
return (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
|
||||
}
|
||||
|
||||
// XXX In order to increase (1) awareness of API breakages and (2) API
|
||||
// coverage, utilize JitsiMeetViewListener in the Debug configuration of
|
||||
// the app.
|
||||
if (BuildConfig.DEBUG && view != null) {
|
||||
view.setListener(new JitsiMeetViewListener() {
|
||||
private void on(String name, Map<String, Object> data) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
private void initialize() {
|
||||
JitsiMeetFragment fragment = getFragment();
|
||||
fragment.setWelcomePageEnabled(true);
|
||||
fragment.loadURL(getIntentUrl(getIntent()));
|
||||
}
|
||||
|
||||
// Log with the tag "ReactNative" in order to have the log
|
||||
// visible in react-native log-android as well.
|
||||
Log.d(
|
||||
"ReactNative",
|
||||
JitsiMeetViewListener.class.getSimpleName() + " "
|
||||
+ name + " "
|
||||
+ data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceFailed(Map<String, Object> data) {
|
||||
on("CONFERENCE_FAILED", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceJoined(Map<String, Object> data) {
|
||||
on("CONFERENCE_JOINED", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceLeft(Map<String, Object> data) {
|
||||
on("CONFERENCE_LEFT", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceWillJoin(Map<String, Object> data) {
|
||||
on("CONFERENCE_WILL_JOIN", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConferenceWillLeave(Map<String, Object> data) {
|
||||
on("CONFERENCE_WILL_LEAVE", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadConfigError(Map<String, Object> data) {
|
||||
on("LOAD_CONFIG_ERROR", data);
|
||||
}
|
||||
});
|
||||
private @Nullable String getIntentUrl(Intent intent) {
|
||||
Uri uri;
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())
|
||||
&& (uri = intent.getData()) != null) {
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
return view;
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean canRequestOverlayPermission() {
|
||||
return
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
|
||||
&& canRequestOverlayPermission()) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
JitsiMeetActivityDelegate.onBackPressed();
|
||||
}
|
||||
|
||||
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
JitsiMeet.showDevOptions();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
String url;
|
||||
|
||||
if ((url = getIntentUrl(intent)) != null) {
|
||||
getFragment().loadURL(url);
|
||||
return;
|
||||
}
|
||||
|
||||
JitsiMeetActivityDelegate.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
getFragment().enterPictureInPicture();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do
|
||||
// want to enable some options.
|
||||
|
||||
// The welcome page defaults to disabled in the SDK at the time of this
|
||||
// writing but it is clearer to be explicit about what we want anyway.
|
||||
setWelcomePageEnabled(true);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Set the Activity's content view.
|
||||
setContentView(R.layout.main_layout);
|
||||
|
||||
// Setup Crashlytics and Firebase Dynamic Links
|
||||
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
|
||||
Fabric.with(this, new Crashlytics());
|
||||
|
@ -129,14 +145,31 @@ public class MainActivity extends JitsiMeetActivity {
|
|||
}
|
||||
|
||||
if (dynamicLink != null) {
|
||||
try {
|
||||
loadURL(new URL(dynamicLink.toString()));
|
||||
} catch (MalformedURLException e) {
|
||||
Log.d("ReactNative", "Malformed dynamic link", e);
|
||||
}
|
||||
getFragment().loadURL(dynamicLink.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// In Debug builds React needs permission to write over other apps in
|
||||
// order to display the warning and error overlays.
|
||||
if (BuildConfig.DEBUG) {
|
||||
if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
|
||||
Intent intent
|
||||
= new Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
|
||||
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<fragment
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetFragment"
|
||||
android:id="@+id/jitsiFragment"/>
|
||||
</FrameLayout>
|
|
@ -22,7 +22,7 @@ import android.app.Activity;
|
|||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
|
||||
/**
|
||||
* Defines the default behavior of {@code JitsiMeetActivity} and
|
||||
* Defines the default behavior of {@code JitsiMeetFragment} and
|
||||
* {@code JitsiMeetView} upon invoking the back button if no
|
||||
* {@code JitsiMeetView} handles the invocation. For example, a
|
||||
* {@code JitsiMeetView} may (1) handle the invocation of the back button
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-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 com.facebook.react.ReactInstanceManager;
|
||||
|
||||
public class JitsiMeet {
|
||||
public static void showDevOptions() {
|
||||
ReactInstanceManager reactInstanceManager
|
||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||
|
||||
if (reactInstanceManager != null) {
|
||||
reactInstanceManager.showDevOptionsDialog();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,13 @@ import com.facebook.react.modules.core.PermissionListener;
|
|||
* {@link Activity} lifecycle methods in order for the React side to be aware of
|
||||
* it.
|
||||
*/
|
||||
public class ReactActivityLifecycleCallbacks {
|
||||
public class JitsiMeetActivityDelegate {
|
||||
/**
|
||||
* Needed for making sure this class working with the "PermissionsAndroid"
|
||||
* React Native module.
|
||||
*/
|
||||
private static PermissionListener permissionListener;
|
||||
private static Callback permissionsCallback;
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
|
@ -57,13 +63,6 @@ public class ReactActivityLifecycleCallbacks {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed for making sure this class working with the "PermissionsAndroid"
|
||||
* React Native module.
|
||||
*/
|
||||
private static PermissionListener permissionListener;
|
||||
private static Callback permissionsCallback;
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
* {@link Activity#onBackPressed} so we can do the required internal
|
|
@ -6,7 +6,7 @@ import com.facebook.react.modules.core.PermissionAwareActivity;
|
|||
|
||||
/**
|
||||
* This interface serves as the umbrella interface that applications not using
|
||||
* {@code JitsiMeetActivity} must implement in order to ensure full
|
||||
* {@code JitsiMeetFragment} must implement in order to ensure full
|
||||
* functionality.
|
||||
*/
|
||||
public interface JitsiMeetActivityInterface
|
||||
|
|
|
@ -18,16 +18,13 @@
|
|||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
|
@ -42,15 +39,7 @@ import java.net.URL;
|
|||
* hooked to the React Native subsystem via proxy calls through the
|
||||
* {@code JitsiMeetView} static methods.
|
||||
*/
|
||||
public class JitsiMeetActivity
|
||||
extends AppCompatActivity implements JitsiMeetActivityInterface {
|
||||
|
||||
/**
|
||||
* The request code identifying requests for the permission to draw on top
|
||||
* of other apps. The value must be 16-bit and is arbitrarily chosen here.
|
||||
*/
|
||||
private static final int OVERLAY_PERMISSION_REQUEST_CODE
|
||||
= (int) (Math.random() * Short.MAX_VALUE);
|
||||
public class JitsiMeetFragment extends Fragment {
|
||||
|
||||
/**
|
||||
* A color scheme object to override the default color is the SDK.
|
||||
|
@ -81,14 +70,6 @@ public class JitsiMeetActivity
|
|||
*/
|
||||
private boolean welcomePageEnabled;
|
||||
|
||||
private boolean canRequestOverlayPermission() {
|
||||
return
|
||||
BuildConfig.DEBUG
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& getApplicationInfo().targetSdkVersion
|
||||
>= Build.VERSION_CODES.M;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see JitsiMeetView#getDefaultURL()
|
||||
|
@ -97,22 +78,12 @@ public class JitsiMeetActivity
|
|||
return view == null ? defaultURL : view.getDefaultURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link #view} of this {@code JitsiMeetActivity} with a
|
||||
* new {@link JitsiMeetView} instance.
|
||||
*/
|
||||
private void initializeContentView() {
|
||||
JitsiMeetView view = initializeView();
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.view = initializeView();
|
||||
|
||||
if (view != null) {
|
||||
// XXX Allow extenders who override initializeView() to configure
|
||||
// the view before the first loadURL(). Probably works around a
|
||||
// problem related to ReactRootView#setAppProperties().
|
||||
view.loadURL(null);
|
||||
|
||||
this.view = view;
|
||||
setContentView(this.view);
|
||||
}
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,7 +92,7 @@ public class JitsiMeetActivity
|
|||
* @return a new {@code JitsiMeetView} instance.
|
||||
*/
|
||||
protected JitsiMeetView initializeView() {
|
||||
JitsiMeetView view = new JitsiMeetView(this);
|
||||
JitsiMeetView view = new JitsiMeetView(getActivity());
|
||||
|
||||
// XXX Before calling JitsiMeetView#loadURL, make sure to call whatever
|
||||
// is documented to need such an order in order to take effect:
|
||||
|
@ -161,97 +132,31 @@ public class JitsiMeetActivity
|
|||
*
|
||||
* @param url The conference URL.
|
||||
*/
|
||||
public void loadURL(@Nullable URL url) {
|
||||
view.loadURL(url);
|
||||
public void loadURL(@Nullable String url) {
|
||||
view.loadURLString(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(
|
||||
int requestCode,
|
||||
int resultCode,
|
||||
Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
|
||||
&& canRequestOverlayPermission()) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
initializeContentView();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ReactActivityLifecycleCallbacks.onActivityResult(
|
||||
this, requestCode, resultCode, data);
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
JitsiMeetActivityDelegate.onActivityResult(
|
||||
getActivity(), requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
ReactActivityLifecycleCallbacks.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// In Debug builds React needs permission to write over other apps in
|
||||
// order to display the warning and error overlays.
|
||||
if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
|
||||
Intent intent
|
||||
= new Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
initializeContentView();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
public void onDestroyView() {
|
||||
if (view != null) {
|
||||
view.dispose();
|
||||
view = null;
|
||||
}
|
||||
|
||||
ReactActivityLifecycleCallbacks.onHostDestroy(this);
|
||||
}
|
||||
|
||||
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
ReactInstanceManager reactInstanceManager;
|
||||
|
||||
if (!super.onKeyUp(keyCode, event)
|
||||
&& BuildConfig.DEBUG
|
||||
&& (reactInstanceManager
|
||||
= ReactInstanceManagerHolder.getReactInstanceManager())
|
||||
!= null
|
||||
&& keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
reactInstanceManager.showDevOptionsDialog();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
// XXX At least twice we received bug reports about malfunctioning
|
||||
// loadURL in the Jitsi Meet SDK while the Jitsi Meet app seemed to
|
||||
// functioning as expected in our testing. But that was to be expected
|
||||
// because the app does not exercise loadURL. In order to increase the
|
||||
// test coverage of loadURL, channel deep linking through loadURL.
|
||||
Uri uri;
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(intent.getAction())
|
||||
&& (uri = intent.getData()) != null
|
||||
&& JitsiMeetView.loadURLStringInViews(uri.toString())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReactActivityLifecycleCallbacks.onNewIntent(intent);
|
||||
JitsiMeetActivityDelegate.onHostDestroy(getActivity());
|
||||
}
|
||||
|
||||
// https://developer.android.com/reference/android/support/v4/app/ActivityCompat.OnRequestPermissionsResultCallback
|
||||
|
@ -260,39 +165,31 @@ public class JitsiMeetActivity
|
|||
final int requestCode,
|
||||
final String[] permissions,
|
||||
final int[] grantResults) {
|
||||
ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
ReactActivityLifecycleCallbacks.onHostResume(this);
|
||||
JitsiMeetActivityDelegate.onHostResume(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
ReactActivityLifecycleCallbacks.onHostPause(this);
|
||||
JitsiMeetActivityDelegate.onHostPause(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
public void enterPictureInPicture() {
|
||||
if (view != null) {
|
||||
view.enterPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the {@code PermissionAwareActivity} interface.
|
||||
*/
|
||||
@Override
|
||||
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
|
||||
ReactActivityLifecycleCallbacks.requestPermissions(this, permissions, requestCode, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see JitsiMeetView#setColorScheme(Bundle)
|
||||
*/
|
||||
public void setColorScheme(Bundle colorScheme) {
|
Loading…
Reference in New Issue