private void drawOutput() { if (mSurface == null || !mSurface.isValid()) { markChildrenUpdatesSeen(this); return; } try { Canvas canvas = mSurface.lockCanvas(null); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); Paint paint = new Paint(); for (int i = 0; i < getChildCount(); i++) { ARTVirtualNode child = (ARTVirtualNode) getChildAt(i); child.draw(canvas, paint, 1f); child.markUpdateSeen(); } if (mSurface == null) { return; } mSurface.unlockCanvasAndPost(canvas); } catch (IllegalArgumentException | IllegalStateException e) { Log.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Surface.unlockCanvasAndPost"); } }
/** * Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true} * if the fill should be drawn, {@code false} if not. */ protected boolean setupFillPaint(Paint paint, float opacity) { if (mFillColor != null && mFillColor.length > 0) { paint.reset(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL); int colorType = (int) mFillColor[0]; switch (colorType) { case 0: paint.setARGB( (int) (mFillColor.length > 4 ? mFillColor[4] * opacity * 255 : opacity * 255), (int) (mFillColor[1] * 255), (int) (mFillColor[2] * 255), (int) (mFillColor[3] * 255)); break; default: // TODO(6352048): Support gradients etc. FLog.w(ReactConstants.TAG, "ART: Color type " + colorType + " not supported!"); } return true; } return false; }
private void drawOutput() { if (mSurface == null || !mSurface.isValid()) { markChildrenUpdatesSeen(this); return; } try { Canvas canvas = mSurface.lockCanvas(null); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); Paint paint = new Paint(); for (int i = 0; i < getChildCount(); i++) { ARTVirtualNode child = (ARTVirtualNode) getChildAt(i); child.draw(canvas, paint, 1f); child.markUpdateSeen(); } if (mSurface == null) { return; } mSurface.unlockCanvasAndPost(canvas); } catch (IllegalArgumentException | IllegalStateException e) { FLog.e(ReactConstants.TAG, e.getClass().getSimpleName() + " in Surface.unlockCanvasAndPost"); } }
@Override public void setElevation(ReactDrawerLayout view, float elevation) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Facebook is using an older version of the support lib internally that doesn't support // setDrawerElevation so we invoke it using reflection. // TODO: Call the method directly when this is no longer needed. try { Method method = ReactDrawerLayout.class.getMethod("setDrawerElevation", float.class); method.invoke(view, PixelUtil.toPixelFromDIP(elevation)); } catch (Exception ex) { FLog.w( ReactConstants.TAG, "setDrawerElevation is not available in this version of the support lib.", ex); } } }
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://")) { return false; } else { try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); view.getContext().startActivity(intent); } catch (ActivityNotFoundException e) { FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); } return true; } }
@Override public void initialize() { super.initialize(); getReactApplicationContext().addLifecycleEventListener(this); if (!hasBeenInitialized()) { // Make sure the SoLoaderShim is configured to use our loader for native libraries. // This code can be removed if using Fresco from Maven rather than from source SoLoaderShim.setHandler(new FrescoHandler()); if (mConfig == null) { mConfig = getDefaultConfig(getReactApplicationContext()); } Context context = getReactApplicationContext().getApplicationContext(); Fresco.initialize(context, mConfig); sHasBeenInitialized = true; } else if (mConfig != null) { FLog.w( ReactConstants.TAG, "Fresco has already been initialized with a different config. " + "The new Fresco configuration will be ignored!"); } mConfig = null; }
private static void copyExif(Context context, Uri oldImage, File newFile) throws IOException { File oldFile = getFileFromUri(context, oldImage); if (oldFile == null) { FLog.w(ReactConstants.TAG, "Couldn't get real path for uri: " + oldImage); return; } ExifInterface oldExif = new ExifInterface(oldFile.getAbsolutePath()); ExifInterface newExif = new ExifInterface(newFile.getAbsolutePath()); for (String attribute : EXIF_ATTRIBUTES) { String value = oldExif.getAttribute(attribute); if (value != null) { newExif.setAttribute(attribute, value); } } newExif.saveAttributes(); }
@Override public void callFunction( ExecutorToken executorToken, final String module, final String method, final NativeArray arguments) { if (mDestroyed) { FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed."); return; } if (!mAcceptCalls) { // Most of the time the instance is initialized and we don't need to acquire the lock synchronized (mJSCallsPendingInitLock) { if (!mAcceptCalls) { mJSCallsPendingInit.add(new PendingJSCall(executorToken, module, method, arguments)); return; } } } jniCallJSFunction(executorToken, module, method, arguments); }
@ReactMethod public void setHidden(final boolean hidden) { final Activity activity = getCurrentActivity(); if (activity == null) { FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null."); return; } UiThreadUtil.runOnUiThread( new Runnable() { @Override public void run() { if (hidden) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } else { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } } }); }
@ReactMethod public void setStyle(final String style) { final Activity activity = getCurrentActivity(); if (activity == null) { FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null."); return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { UiThreadUtil.runOnUiThread( new Runnable() { @TargetApi(Build.VERSION_CODES.M) @Override public void run() { View decorView = activity.getWindow().getDecorView(); decorView.setSystemUiVisibility( style.equals("dark-content") ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0); } } ); } }
/** * Clears the database. */ @ReactMethod public void clear(final Callback callback) { new GuardedAsyncTask<Void, Void>(getReactApplicationContext()) { @Override protected void doInBackgroundGuarded(Void... params) { if (!mReactDatabaseSupplier.ensureDatabase()) { callback.invoke(AsyncStorageErrorUtil.getDBError(null)); return; } try { mReactDatabaseSupplier.clear(); callback.invoke(); } catch (Exception e) { FLog.w(ReactConstants.TAG, e.getMessage(), e); callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage())); } } }.execute(); }
@Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { ExecutorToken executorToken = mExecutorToken.get(); if (executorToken == null) { FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away..."); return null; } NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray(); mCatalystInstance.callFunction( executorToken, mModuleRegistration.getName(), method.getName(), jsArgs ); return null; }
@Override protected Void doInBackground(String... clipBoardString) { try { String sendClipBoardUrl = Uri.parse(mDevSupportManager.getSourceUrl()).buildUpon() .path("/copy-to-clipboard") .query(null) .build() .toString(); for (String string: clipBoardString) { OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(null, string); Request request = new Request.Builder().url(sendClipBoardUrl).post(body).build(); client.newCall(request).execute(); } } catch (Exception e) { FLog.e(ReactConstants.TAG, "Could not copy to the host clipboard", e); } return null; }
/** * This method will give JS the opportunity to receive intents via Linking. */ public void onNewIntent(Intent intent) { if (mCurrentReactContext == null) { FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); } else { String action = intent.getAction(); Uri uri = intent.getData(); if (Intent.ACTION_VIEW.equals(action) && uri != null) { DeviceEventManagerModule deviceEventManagerModule = Assertions.assertNotNull(mCurrentReactContext).getNativeModule(DeviceEventManagerModule.class); deviceEventManagerModule.emitNewIntentReceived(uri); } mCurrentReactContext.onNewIntent(mCurrentActivity, intent); } }
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) { loadApp(mMainComponentName); } mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); }
private void dispatchCancelEvent(MotionEvent androidEvent, EventDispatcher eventDispatcher) { // This means the gesture has already ended, via some other CANCEL or UP event. This is not // expected to happen very often as it would mean some child View has decided to intercept the // touch stream and start a native gesture only upon receiving the UP/CANCEL event. if (mTargetTag == -1) { FLog.w( ReactConstants.TAG, "Can't cancel already finished gesture. Is a child View trying to start a gesture from " + "an UP/CANCEL event?"); return; } Assertions.assertCondition( !mChildIsHandlingNativeGesture, "Expected to not have already sent a cancel for this gesture"); Assertions.assertNotNull(eventDispatcher).dispatchEvent( TouchEvent.obtain( mTargetTag, TouchEventType.CANCEL, androidEvent, mGestureStartTime, mTargetCoordinates[0], mTargetCoordinates[1], mTouchEventCoalescingKeyHelper)); }
/** * Parse a DebugServerException from the server json string. * @param str json string returned by the debug server * @return A DebugServerException or null if the string is not of proper form. */ @Nullable public static DebugServerException parse(String str) { if (TextUtils.isEmpty(str)) { return null; } try { JSONObject jsonObject = new JSONObject(str); String fullFileName = jsonObject.getString("filename"); return new DebugServerException( jsonObject.getString("description"), shortenFileName(fullFileName), jsonObject.getInt("lineNumber"), jsonObject.getInt("column")); } catch (JSONException e) { // I'm not sure how strict this format is for returned errors, or what other errors there can // be, so this may end up being spammy. Can remove it later if necessary. FLog.w(ReactConstants.TAG, "Could not parse DebugServerException from: " + str, e); return null; } }
@Override protected Void doInBackground(String... jsonData) { try { String jscProfileUrl = Uri.parse(mSourceUrl).buildUpon() .path("/jsc-profile") .query(null) .build() .toString(); OkHttpClient client = new OkHttpClient(); for (String json: jsonData) { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder().url(jscProfileUrl).post(body).build(); client.newCall(request).execute(); } } catch (IOException e) { FLog.e(ReactConstants.TAG, "Failed not talk to server", e); } return null; }
@Override public void handleException(Exception e) { if (mIsDevSupportEnabled) { if (e instanceof JSException) { FLog.e(ReactConstants.TAG, "Exception in native call from JS", e); // TODO #11638796: convert the stack into something useful showNewError( e.getMessage() + "\n\n" + ((JSException) e).getStack(), new StackFrame[] {}, JSEXCEPTION_ERROR_COOKIE, ErrorType.JS); } else { showNewJavaError(e.getMessage(), e); } } else { mDefaultNativeModuleCallExceptionHandler.handleException(e); } }
/** * @return {@code true} if {@link com.facebook.react.ReactInstanceManager} should use downloaded JS bundle file * instead of using JS file from assets. This may happen when app has not been updated since * the last time we fetched the bundle. */ @Override public boolean hasUpToDateJSBundleInCache() { if (mIsDevSupportEnabled && mJSBundleTempFile.exists()) { try { String packageName = mApplicationContext.getPackageName(); PackageInfo thisPackage = mApplicationContext.getPackageManager() .getPackageInfo(packageName, 0); if (mJSBundleTempFile.lastModified() > thisPackage.lastUpdateTime) { // Base APK has not been updated since we donwloaded JS, but if app is using exopackage // it may only be a single dex that has been updated. We check for exopackage dir update // time in that case. File exopackageDir = new File( String.format(Locale.US, EXOPACKAGE_LOCATION_FORMAT, packageName)); if (exopackageDir.exists()) { return mJSBundleTempFile.lastModified() > exopackageDir.lastModified(); } return true; } } catch (PackageManager.NameNotFoundException e) { // Ignore this error and just fallback to loading JS from assets FLog.e(ReactConstants.TAG, "DevSupport is unable to get current app info"); } } return false; }
private WebsocketJavaScriptExecutor.JSExecutorConnectCallback getExecutorConnectCallback( final SimpleSettableFuture<Boolean> future) { return new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() { @Override public void onSuccess() { future.set(true); mDevLoadingViewController.hide(); mDevLoadingViewVisible = false; } @Override public void onFailure(final Throwable cause) { mDevLoadingViewController.hide(); mDevLoadingViewVisible = false; FLog.e(ReactConstants.TAG, "Unable to connect to remote debugger", cause); future.setException( new IOException( mApplicationContext.getString(R.string.catalyst_remotedbg_error), cause)); } }; }
@Override protected Void doInBackground(StackFrame... stackFrames) { try { String openStackFrameUrl = Uri.parse(mDevSupportManager.getSourceUrl()).buildUpon() .path("/open-stack-frame") .query(null) .build() .toString(); OkHttpClient client = new OkHttpClient(); for (StackFrame frame: stackFrames) { String payload = stackFrameToJson(frame).toString(); RequestBody body = RequestBody.create(JSON, payload); Request request = new Request.Builder().url(openStackFrameUrl).post(body).build(); client.newCall(request).execute(); } } catch (Exception e) { FLog.e(ReactConstants.TAG, "Could not open stack frame", e); } return null; }
@Override public void doFrameGuarded(long frameTimeNanos) { if (mIsInIllegalUIState) { FLog.w( ReactConstants.TAG, "Not flushing pending UI operations because of previously thrown Exception"); return; } Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations"); try { dispatchPendingNonBatchedOperations(frameTimeNanos); } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } flushPendingBatches(); ReactChoreographer.getInstance().postFrameCallback( ReactChoreographer.CallbackType.DISPATCH_UI, this); }
/** * Invoked when native view that corresponds to a root node, or acts as a root view (ie. Modals) * has its size changed. */ public void updateNodeSize( int nodeViewTag, int newWidth, int newHeight) { ReactShadowNode cssNode = mShadowNodeRegistry.getNode(nodeViewTag); if (cssNode == null) { FLog.w( ReactConstants.TAG, "Tried to update size of non-existent tag: " + nodeViewTag); return; } cssNode.setStyleWidth(newWidth); cssNode.setStyleHeight(newHeight); // If we're in the middle of a batch, the change will automatically be dispatched at the end of // the batch. As all batches are executed as a single runnable on the event queue this should // always be empty, but that calling architecture is an implementation detail. if (mOperationsQueue.isEmpty()) { dispatchViewUpdates(-1); // -1 = no associated batch id } }
public int resolveRootTagFromReactTag(int reactTag) { if (mShadowNodeRegistry.isRootNode(reactTag)) { return reactTag; } ReactShadowNode node = resolveShadowNode(reactTag); int rootTag = 0; if (node != null) { rootTag = node.getRootNode().getReactTag(); } else { FLog.w( ReactConstants.TAG, "Warning : attempted to resolve a non-existent react shadow node. reactTag=" + reactTag); } return rootTag; }
public void linkBridge() { if (messagingEnabled) { if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // See isNative in lodash String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')"; evaluateJavascript(testPostMessageNative, new ValueCallback<String>() { @Override public void onReceiveValue(String value) { if (value.equals("true")) { FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"); } } }); } loadUrl("javascript:(" + "window.originalPostMessage = window.postMessage," + "window.postMessage = function(data) {" + BRIDGE_NAME + ".postMessage(String(data));" + "}" + ")"); } }
@ReactMethod public void close(int code, String reason, int id) { WebSocket client = mWebSocketConnections.get(id); if (client == null) { // WebSocket is already closed // Don't do anything, mirror the behaviour on web return; } try { client.close(code, reason); mWebSocketConnections.remove(id); } catch (Exception e) { FLog.e( ReactConstants.TAG, "Could not close WebSocket connection for id " + id, e); } }