Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,13 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.widget.Toast;

import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Callback;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.devsupport.DoubleTapReloadRecognizer;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionListener;
Expand All @@ -31,12 +26,6 @@
*/
public class ReactActivityDelegate {

private final int REQUEST_OVERLAY_PERMISSION_CODE = 1111;
private static final String REDBOX_PERMISSION_GRANTED_MESSAGE =
"Overlay permissions have been granted.";
private static final String REDBOX_PERMISSION_MESSAGE =
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";

private final @Nullable Activity mActivity;
private final @Nullable FragmentActivity mFragmentActivity;
private final @Nullable String mMainComponentName;
Expand Down Expand Up @@ -84,19 +73,7 @@ public ReactInstanceManager getReactInstanceManager() {
}

protected void onCreate(Bundle savedInstanceState) {
boolean needsOverlayPermission = false;
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(getContext())) {
needsOverlayPermission = true;
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
}
}

if (mMainComponentName != null && !needsOverlayPermission) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
Expand Down Expand Up @@ -147,16 +124,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager()
.onActivityResult(getPlainActivity(), requestCode, resultCode, data);
} else {
// Did we request overlay permissions?
if (requestCode == REQUEST_OVERLAY_PERMISSION_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Settings.canDrawOverlays(getContext())) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
Toast.makeText(getContext(), REDBOX_PERMISSION_GRANTED_MESSAGE, Toast.LENGTH_LONG).show();
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Process;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.view.View;
import com.facebook.common.logging.FLog;
Expand Down Expand Up @@ -65,7 +66,7 @@
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.devsupport.DevSupportManagerFactory;
import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
import com.facebook.react.devsupport.ReactInstanceManagerDevHelper;
import com.facebook.react.devsupport.RedBoxHandler;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
Expand Down Expand Up @@ -221,7 +222,7 @@ public static ReactInstanceManagerBuilder builder() {
mDevSupportManager =
DevSupportManagerFactory.create(
applicationContext,
createDevInterface(),
createDevHelperInterface(),
mJSMainModulePath,
useDeveloperSupport,
redBoxHandler,
Expand Down Expand Up @@ -261,8 +262,8 @@ public void invokeDefaultOnBackPressed() {
}
}

private ReactInstanceDevCommandsHandler createDevInterface() {
return new ReactInstanceDevCommandsHandler() {
private ReactInstanceManagerDevHelper createDevHelperInterface() {
return new ReactInstanceManagerDevHelper() {
@Override
public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
ReactInstanceManager.this.onReloadWithJSDebugger(jsExecutorFactory);
Expand All @@ -277,6 +278,11 @@ public void onJSBundleLoadedFromServer() {
public void toggleElementInspector() {
ReactInstanceManager.this.toggleElementInspector();
}

@Override
public @Nullable Activity getCurrentActivity() {
return ReactInstanceManager.this.mCurrentActivity;
}
};
}

Expand Down Expand Up @@ -563,11 +569,40 @@ public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaul
UiThreadUtil.assertOnUiThread();

mDefaultBackButtonImpl = defaultBackButtonImpl;
mCurrentActivity = activity;

if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(true);
// Resume can be called from one of two different states:
// a) when activity was paused
// b) when activity has just been created
// In case of (a) the activity is attached to window and it is ok to add new views to it or
// open dialogs. In case of (b) there is often a slight delay before such a thing happens.
// As dev support manager can add views or open dialogs immediately after it gets enabled
// (e.g. in the case when JS bundle is being fetched in background) we only want to enable
// it once we know for sure the current activity is attached.

// We check if activity is attached to window by checking if decor view is attached
final View decorView = mCurrentActivity.getWindow().getDecorView();
if (!ViewCompat.isAttachedToWindow(decorView)) {
decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
// we can drop listener now that we know the view is attached
decorView.removeOnAttachStateChangeListener(this);
mDevSupportManager.setDevSupportEnabled(true);
}

@Override
public void onViewDetachedFromWindow(View v) {
// do nothing
}
});
} else {
// activity is attached to window, we can enable dev support immediately
mDevSupportManager.setDevSupportEnabled(true);
}
}

mCurrentActivity = activity;
moveToResumedLifecycleState(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,83 @@

package com.facebook.react.devsupport;

import javax.annotation.Nullable;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.view.WindowManager;
import android.widget.FrameLayout;

import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;

import javax.annotation.Nullable;

/**
* Helper class for controlling overlay view with FPS and JS FPS info
* that gets added directly to @{link WindowManager} instance.
*/
/* package */ class DebugOverlayController {

public static void requestPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show debug overlay in dev builds.
if (!Settings.canDrawOverlays(context)) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
FLog.w(ReactConstants.TAG, "Overlay permissions needs to be granted in order for react native apps to run in dev mode");
if (canHandleIntent(context, intent)) {
context.startActivity(intent);
}
}
}
}

private static boolean permissionCheck(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show debug overlay in dev builds.
if (!Settings.canDrawOverlays(context)) {
// overlay permission not yet granted
return false;
} else {
return true;
}
}
// on pre-M devices permission needs to be specified in manifest
return hasPermission(context, Manifest.permission.SYSTEM_ALERT_WINDOW);
}

private static boolean hasPermission(Context context, String permission) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(),
PackageManager.GET_PERMISSIONS);
if (info.requestedPermissions != null) {
for (String p : info.requestedPermissions) {
if (p.equals(permission)) {
return true;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
FLog.e(ReactConstants.TAG, "Error while retrieving package info", e);
}
return false;
}

private static boolean canHandleIntent(Context context, Intent intent) {
PackageManager packageManager = context.getPackageManager();
return intent.resolveActivity(packageManager) != null;
}

private final WindowManager mWindowManager;
private final ReactContext mReactContext;

Expand All @@ -36,6 +98,10 @@ public DebugOverlayController(ReactContext reactContext) {

public void setFpsDebugViewVisible(boolean fpsDebugViewVisible) {
if (fpsDebugViewVisible && mFPSDebugViewContainer == null) {
if (!permissionCheck(mReactContext)) {
FLog.d(ReactConstants.TAG, "Wait for overlay permission to be set");
return;
}
mFPSDebugViewContainer = new FpsView(mReactContext);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
Expand Down
Loading