Browse Source

android: SDK v2 pass one

Add JitsiMeetFragment and refactor the app to use it.
master
Saúl Ibarra Corretgé 6 years ago
parent
commit
90803c8ff6

+ 110
- 77
android/app/src/main/java/org/jitsi/meet/MainActivity.java View File

@@ -17,105 +17,121 @@
17 17
 
18 18
 package org.jitsi.meet;
19 19
 
20
+import android.content.Intent;
20 21
 import android.net.Uri;
22
+import android.os.Build;
21 23
 import android.os.Bundle;
22
-import android.util.Log;
24
+import android.provider.Settings;
25
+import android.support.annotation.Nullable;
26
+import android.support.v4.app.FragmentActivity;
27
+import android.view.KeyEvent;
23 28
 
24
-import org.jitsi.meet.sdk.JitsiMeetActivity;
25
-import org.jitsi.meet.sdk.JitsiMeetView;
26
-import org.jitsi.meet.sdk.JitsiMeetViewListener;
29
+import org.jitsi.meet.sdk.JitsiMeet;
30
+import org.jitsi.meet.sdk.JitsiMeetFragment;
31
+import org.jitsi.meet.sdk.JitsiMeetActivityInterface;
32
+import org.jitsi.meet.sdk.JitsiMeetActivityDelegate;
27 33
 
28 34
 import com.crashlytics.android.Crashlytics;
29
-import com.facebook.react.bridge.UiThreadUtil;
35
+import com.facebook.react.modules.core.PermissionListener;
30 36
 import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
31 37
 import io.fabric.sdk.android.Fabric;
32 38
 
33
-import java.net.MalformedURLException;
34
-import java.net.URL;
35
-import java.util.Map;
36
-
37 39
 /**
38 40
  * The one and only {@link Activity} that the Jitsi Meet app needs. The
39 41
  * {@code Activity} is launched in {@code singleTask} mode, so it will be
40 42
  * created upon application initialization and there will be a single instance
41 43
  * of it. Further attempts at launching the application once it was already
42 44
  * launched will result in {@link Activity#onNewIntent(Intent)} being called.
43
- *
44
- * This {@code Activity} extends {@link JitsiMeetActivity} to keep the React
45
- * Native CLI working, since the latter always tries to launch an
46
- * {@code Activity} named {@code MainActivity} when doing
47
- * {@code react-native run-android}.
48 45
  */
49
-public class MainActivity extends JitsiMeetActivity {
46
+public class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {
47
+    /**
48
+     * The request code identifying requests for the permission to draw on top
49
+     * of other apps. The value must be 16-bit and is arbitrarily chosen here.
50
+     */
51
+    private static final int OVERLAY_PERMISSION_REQUEST_CODE
52
+        = (int) (Math.random() * Short.MAX_VALUE);
53
+
54
+    private JitsiMeetFragment getFragment() {
55
+        return (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
56
+    }
57
+
58
+    private void initialize() {
59
+        JitsiMeetFragment fragment = getFragment();
60
+        fragment.setWelcomePageEnabled(true);
61
+        fragment.loadURL(getIntentUrl(getIntent()));
62
+    }
63
+
64
+    private @Nullable String getIntentUrl(Intent intent) {
65
+        Uri uri;
66
+
67
+        if (Intent.ACTION_VIEW.equals(intent.getAction())
68
+                && (uri = intent.getData()) != null) {
69
+            return uri.toString();
70
+        }
71
+
72
+        return null;
73
+    }
74
+
75
+    private boolean canRequestOverlayPermission() {
76
+        return
77
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
78
+                && getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
79
+    }
50 80
 
51 81
     @Override
52
-    protected JitsiMeetView initializeView() {
53
-        JitsiMeetView view = super.initializeView();
54
-
55
-        // XXX In order to increase (1) awareness of API breakages and (2) API
56
-        // coverage, utilize JitsiMeetViewListener in the Debug configuration of
57
-        // the app.
58
-        if (BuildConfig.DEBUG && view != null) {
59
-            view.setListener(new JitsiMeetViewListener() {
60
-                private void on(String name, Map<String, Object> data) {
61
-                    UiThreadUtil.assertOnUiThread();
62
-
63
-                    // Log with the tag "ReactNative" in order to have the log
64
-                    // visible in react-native log-android as well.
65
-                    Log.d(
66
-                        "ReactNative",
67
-                        JitsiMeetViewListener.class.getSimpleName() + " "
68
-                            + name + " "
69
-                            + data);
70
-                }
71
-
72
-                @Override
73
-                public void onConferenceFailed(Map<String, Object> data) {
74
-                    on("CONFERENCE_FAILED", data);
75
-                }
76
-
77
-                @Override
78
-                public void onConferenceJoined(Map<String, Object> data) {
79
-                    on("CONFERENCE_JOINED", data);
80
-                }
81
-
82
-                @Override
83
-                public void onConferenceLeft(Map<String, Object> data) {
84
-                    on("CONFERENCE_LEFT", data);
85
-                }
86
-
87
-                @Override
88
-                public void onConferenceWillJoin(Map<String, Object> data) {
89
-                    on("CONFERENCE_WILL_JOIN", data);
90
-                }
91
-
92
-                @Override
93
-                public void onConferenceWillLeave(Map<String, Object> data) {
94
-                    on("CONFERENCE_WILL_LEAVE", data);
95
-                }
96
-
97
-                @Override
98
-                public void onLoadConfigError(Map<String, Object> data) {
99
-                    on("LOAD_CONFIG_ERROR", data);
100
-                }
101
-            });
82
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
83
+        if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
84
+                && canRequestOverlayPermission()) {
85
+            if (Settings.canDrawOverlays(this)) {
86
+                initialize();
87
+            }
88
+
89
+            return;
90
+        }
91
+
92
+        super.onActivityResult(requestCode, resultCode, data);
93
+    }
94
+
95
+    @Override
96
+    public void onBackPressed() {
97
+        JitsiMeetActivityDelegate.onBackPressed();
98
+    }
102 99
 
100
+    // ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
101
+    @Override
102
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
103
+        if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_MENU) {
104
+            JitsiMeet.showDevOptions();
105
+            return true;
103 106
         }
104 107
 
105
-        return view;
108
+        return super.onKeyUp(keyCode, event);
106 109
     }
107 110
 
108 111
     @Override
109
-    protected void onCreate(Bundle savedInstanceState) {
110
-        // As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do
111
-        // want to enable some options.
112
+    public void onNewIntent(Intent intent) {
113
+        String url;
112 114
 
113
-        // The welcome page defaults to disabled in the SDK at the time of this
114
-        // writing but it is clearer to be explicit about what we want anyway.
115
-        setWelcomePageEnabled(true);
115
+        if ((url = getIntentUrl(intent)) != null) {
116
+            getFragment().loadURL(url);
117
+            return;
118
+        }
119
+
120
+        JitsiMeetActivityDelegate.onNewIntent(intent);
121
+    }
116 122
 
123
+    @Override
124
+    protected void onUserLeaveHint() {
125
+        getFragment().enterPictureInPicture();
126
+    }
127
+
128
+    @Override
129
+    protected void onCreate(Bundle savedInstanceState) {
117 130
         super.onCreate(savedInstanceState);
118 131
 
132
+        // Set the Activity's content view.
133
+        setContentView(R.layout.main_layout);
134
+
119 135
         // Setup Crashlytics and Firebase Dynamic Links
120 136
         if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
121 137
             Fabric.with(this, new Crashlytics());
@@ -129,14 +145,31 @@ public class MainActivity extends JitsiMeetActivity {
129 145
                     }
130 146
 
131 147
                     if (dynamicLink != null) {
132
-                        try {
133
-                            loadURL(new URL(dynamicLink.toString()));
134
-                        } catch (MalformedURLException e) {
135
-                            Log.d("ReactNative", "Malformed dynamic link", e);
136
-                        }
148
+                        getFragment().loadURL(dynamicLink.toString());
137 149
                     }
138 150
                 });
139 151
         }
152
+
153
+        // In Debug builds React needs permission to write over other apps in
154
+        // order to display the warning and error overlays.
155
+        if (BuildConfig.DEBUG) {
156
+            if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
157
+                Intent intent
158
+                    = new Intent(
159
+                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
160
+                    Uri.parse("package:" + getPackageName()));
161
+
162
+                startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
163
+                return;
164
+            }
165
+        }
166
+
167
+        initialize();
168
+    }
169
+
170
+    @Override
171
+    public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
172
+        JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
140 173
     }
141 174
 
142 175
 }

+ 10
- 0
android/app/src/main/res/layout/main_layout.xml View File

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+    android:layout_width="match_parent"
4
+    android:layout_height="match_parent">
5
+    <fragment
6
+        android:layout_width="match_parent"
7
+        android:layout_height="match_parent"
8
+        android:name="org.jitsi.meet.sdk.JitsiMeetFragment"
9
+        android:id="@+id/jitsiFragment"/>
10
+</FrameLayout>

+ 1
- 1
android/sdk/src/main/java/org/jitsi/meet/sdk/DefaultHardwareBackBtnHandlerImpl.java View File

@@ -22,7 +22,7 @@ import android.app.Activity;
22 22
 import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
23 23
 
24 24
 /**
25
- * Defines the default behavior of {@code JitsiMeetActivity} and
25
+ * Defines the default behavior of {@code JitsiMeetFragment} and
26 26
  * {@code JitsiMeetView} upon invoking the back button if no
27 27
  * {@code JitsiMeetView} handles the invocation. For example, a
28 28
  * {@code JitsiMeetView} may (1) handle the invocation of the back button

+ 30
- 0
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeet.java View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * Copyright @ 2018-present 8x8, Inc.
3
+ * Copyright @ 2017-2018 Atlassian Pty Ltd
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ *     http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+package org.jitsi.meet.sdk;
18
+
19
+import com.facebook.react.ReactInstanceManager;
20
+
21
+public class JitsiMeet {
22
+    public static void showDevOptions() {
23
+        ReactInstanceManager reactInstanceManager
24
+            = ReactInstanceManagerHolder.getReactInstanceManager();
25
+
26
+        if (reactInstanceManager != null) {
27
+            reactInstanceManager.showDevOptionsDialog();
28
+        }
29
+    }
30
+}

android/sdk/src/main/java/org/jitsi/meet/sdk/ReactActivityLifecycleCallbacks.java → android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivityDelegate.java View File

@@ -32,7 +32,13 @@ import com.facebook.react.modules.core.PermissionListener;
32 32
  * {@link Activity} lifecycle methods in order for the React side to be aware of
33 33
  * it.
34 34
  */
35
-public class ReactActivityLifecycleCallbacks {
35
+public class JitsiMeetActivityDelegate {
36
+    /**
37
+     * Needed for making sure this class working with the "PermissionsAndroid"
38
+     * React Native module.
39
+     */
40
+    private static PermissionListener permissionListener;
41
+    private static Callback permissionsCallback;
36 42
 
37 43
     /**
38 44
      * {@link Activity} lifecycle method which should be called from
@@ -57,13 +63,6 @@ public class ReactActivityLifecycleCallbacks {
57 63
         }
58 64
     }
59 65
 
60
-    /**
61
-     * Needed for making sure this class working with the "PermissionsAndroid"
62
-     * React Native module.
63
-     */
64
-    private static PermissionListener permissionListener;
65
-    private static Callback permissionsCallback;
66
-
67 66
     /**
68 67
      * {@link Activity} lifecycle method which should be called from
69 68
      * {@link Activity#onBackPressed} so we can do the required internal

+ 1
- 1
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivityInterface.java View File

@@ -6,7 +6,7 @@ import com.facebook.react.modules.core.PermissionAwareActivity;
6 6
 
7 7
 /**
8 8
  * This interface serves as the umbrella interface that applications not using
9
- * {@code JitsiMeetActivity} must implement in order to ensure full
9
+ * {@code JitsiMeetFragment} must implement in order to ensure full
10 10
  * functionality.
11 11
  */
12 12
 public interface JitsiMeetActivityInterface

android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java → android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetFragment.java View File

@@ -18,16 +18,13 @@
18 18
 package org.jitsi.meet.sdk;
19 19
 
20 20
 import android.content.Intent;
21
-import android.net.Uri;
22
-import android.os.Build;
23 21
 import android.os.Bundle;
24
-import android.provider.Settings;
22
+import android.support.annotation.NonNull;
25 23
 import android.support.annotation.Nullable;
26
-import android.support.v7.app.AppCompatActivity;
27
-import android.view.KeyEvent;
28
-
29
-import com.facebook.react.ReactInstanceManager;
30
-import com.facebook.react.modules.core.PermissionListener;
24
+import android.support.v4.app.Fragment;
25
+import android.view.LayoutInflater;
26
+import android.view.View;
27
+import android.view.ViewGroup;
31 28
 
32 29
 import java.net.URL;
33 30
 
@@ -42,15 +39,7 @@ import java.net.URL;
42 39
  * hooked to the React Native subsystem via proxy calls through the
43 40
  * {@code JitsiMeetView} static methods.
44 41
  */
45
-public class JitsiMeetActivity
46
-    extends AppCompatActivity implements JitsiMeetActivityInterface {
47
-
48
-    /**
49
-     * The request code identifying requests for the permission to draw on top
50
-     * of other apps. The value must be 16-bit and is arbitrarily chosen here.
51
-     */
52
-    private static final int OVERLAY_PERMISSION_REQUEST_CODE
53
-        = (int) (Math.random() * Short.MAX_VALUE);
42
+public class JitsiMeetFragment extends Fragment {
54 43
 
55 44
     /**
56 45
      * A color scheme object to override the default color is the SDK.
@@ -81,14 +70,6 @@ public class JitsiMeetActivity
81 70
      */
82 71
     private boolean welcomePageEnabled;
83 72
 
84
-    private boolean canRequestOverlayPermission() {
85
-        return
86
-            BuildConfig.DEBUG
87
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
88
-                && getApplicationInfo().targetSdkVersion
89
-                    >= Build.VERSION_CODES.M;
90
-    }
91
-
92 73
     /**
93 74
      *
94 75
      * @see JitsiMeetView#getDefaultURL()
@@ -97,22 +78,12 @@ public class JitsiMeetActivity
97 78
         return view == null ? defaultURL : view.getDefaultURL();
98 79
     }
99 80
 
100
-    /**
101
-     * Initializes the {@link #view} of this {@code JitsiMeetActivity} with a
102
-     * new {@link JitsiMeetView} instance.
103
-     */
104
-    private void initializeContentView() {
105
-        JitsiMeetView view = initializeView();
106
-
107
-        if (view != null) {
108
-            // XXX Allow extenders who override initializeView() to configure
109
-            // the view before the first loadURL(). Probably works around a
110
-            // problem related to ReactRootView#setAppProperties().
111
-            view.loadURL(null);
81
+    @Nullable
82
+    @Override
83
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
84
+        this.view = initializeView();
112 85
 
113
-            this.view = view;
114
-            setContentView(this.view);
115
-        }
86
+        return this.view;
116 87
     }
117 88
 
118 89
     /**
@@ -121,7 +92,7 @@ public class JitsiMeetActivity
121 92
      * @return a new {@code JitsiMeetView} instance.
122 93
      */
123 94
     protected JitsiMeetView initializeView() {
124
-        JitsiMeetView view = new JitsiMeetView(this);
95
+        JitsiMeetView view = new JitsiMeetView(getActivity());
125 96
 
126 97
         // XXX Before calling JitsiMeetView#loadURL, make sure to call whatever
127 98
         // is documented to need such an order in order to take effect:
@@ -161,97 +132,31 @@ public class JitsiMeetActivity
161 132
      *
162 133
      * @param url The conference URL.
163 134
      */
164
-    public void loadURL(@Nullable URL url) {
165
-        view.loadURL(url);
166
-    }
167
-
168
-    @Override
169
-    protected void onActivityResult(
170
-            int requestCode,
171
-            int resultCode,
172
-            Intent data) {
173
-        if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
174
-                && canRequestOverlayPermission()) {
175
-            if (Settings.canDrawOverlays(this)) {
176
-                initializeContentView();
177
-            }
178
-
179
-            return;
180
-        }
181
-
182
-        ReactActivityLifecycleCallbacks.onActivityResult(
183
-                this, requestCode, resultCode, data);
184
-    }
185
-
186
-    @Override
187
-    public void onBackPressed() {
188
-        ReactActivityLifecycleCallbacks.onBackPressed();
135
+    public void loadURL(@Nullable String url) {
136
+        view.loadURLString(url);
189 137
     }
190 138
 
191 139
     @Override
192
-    protected void onCreate(Bundle savedInstanceState) {
193
-        super.onCreate(savedInstanceState);
194
-
195
-        // In Debug builds React needs permission to write over other apps in
196
-        // order to display the warning and error overlays.
197
-        if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
198
-            Intent intent
199
-                = new Intent(
200
-                        Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
201
-                        Uri.parse("package:" + getPackageName()));
202
-
203
-            startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
204
-            return;
205
-        }
206
-
207
-        initializeContentView();
140
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
141
+        JitsiMeetActivityDelegate.onActivityResult(
142
+                getActivity(), requestCode, resultCode, data);
208 143
     }
209 144
 
210 145
     @Override
211
-    protected void onDestroy() {
212
-        super.onDestroy();
213
-
146
+    public void onDestroyView() {
214 147
         if (view != null) {
215 148
             view.dispose();
216 149
             view = null;
217 150
         }
218 151
 
219
-        ReactActivityLifecycleCallbacks.onHostDestroy(this);
220
-    }
221
-
222
-    // ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
223
-    @Override
224
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
225
-        ReactInstanceManager reactInstanceManager;
226
-
227
-        if (!super.onKeyUp(keyCode, event)
228
-                && BuildConfig.DEBUG
229
-                && (reactInstanceManager
230
-                        = ReactInstanceManagerHolder.getReactInstanceManager())
231
-                    != null
232
-                && keyCode == KeyEvent.KEYCODE_MENU) {
233
-            reactInstanceManager.showDevOptionsDialog();
234
-            return true;
235
-        }
236
-        return false;
152
+        super.onDestroyView();
237 153
     }
238 154
 
239 155
     @Override
240
-    public void onNewIntent(Intent intent) {
241
-        // XXX At least twice we received bug reports about malfunctioning
242
-        // loadURL in the Jitsi Meet SDK while the Jitsi Meet app seemed to
243
-        // functioning as expected in our testing. But that was to be expected
244
-        // because the app does not exercise loadURL. In order to increase the
245
-        // test coverage of loadURL, channel deep linking through loadURL.
246
-        Uri uri;
247
-
248
-        if (Intent.ACTION_VIEW.equals(intent.getAction())
249
-                && (uri = intent.getData()) != null
250
-                && JitsiMeetView.loadURLStringInViews(uri.toString())) {
251
-            return;
252
-        }
156
+    public void onDestroy() {
157
+        super.onDestroy();
253 158
 
254
-        ReactActivityLifecycleCallbacks.onNewIntent(intent);
159
+        JitsiMeetActivityDelegate.onHostDestroy(getActivity());
255 160
     }
256 161
 
257 162
     // https://developer.android.com/reference/android/support/v4/app/ActivityCompat.OnRequestPermissionsResultCallback
@@ -260,39 +165,31 @@ public class JitsiMeetActivity
260 165
             final int requestCode,
261 166
             final String[] permissions,
262 167
             final int[] grantResults) {
263
-        ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults);
168
+        JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
264 169
     }
265 170
 
266 171
     @Override
267
-    protected void onResume() {
172
+    public void onResume() {
268 173
         super.onResume();
269 174
 
270
-        ReactActivityLifecycleCallbacks.onHostResume(this);
175
+        JitsiMeetActivityDelegate.onHostResume(getActivity());
271 176
     }
272 177
 
273 178
     @Override
274 179
     public void onStop() {
275 180
         super.onStop();
276 181
 
277
-        ReactActivityLifecycleCallbacks.onHostPause(this);
182
+        JitsiMeetActivityDelegate.onHostPause(getActivity());
278 183
     }
279 184
 
280
-    @Override
281
-    protected void onUserLeaveHint() {
185
+    public void enterPictureInPicture() {
282 186
         if (view != null) {
283 187
             view.enterPictureInPicture();
284 188
         }
285 189
     }
286 190
 
287 191
     /**
288
-     * Implementation of the {@code PermissionAwareActivity} interface.
289
-     */
290
-    @Override
291
-    public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
292
-        ReactActivityLifecycleCallbacks.requestPermissions(this, permissions, requestCode, listener);
293
-    }
294
-
295
-    /**
192
+     *
296 193
      * @see JitsiMeetView#setColorScheme(Bundle)
297 194
      */
298 195
     public void setColorScheme(Bundle colorScheme) {

Loading…
Cancel
Save