diff --git a/android/build.gradle b/android/build.gradle index c3fa67ee..d16e2e1b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -138,5 +138,5 @@ dependencies { implementation "com.facebook.react:react-native:+" // From node_modules - implementation 'com.github.Dimezis:BlurView:version-2.0.3' + implementation 'com.github.Dimezis:BlurView:version-2.0.4' } diff --git a/android/src/main/java/com/reactnativecommunity/blurview/BlurViewManagerImpl.java b/android/src/main/java/com/reactnativecommunity/blurview/BlurViewManagerImpl.java deleted file mode 100644 index 44443134..00000000 --- a/android/src/main/java/com/reactnativecommunity/blurview/BlurViewManagerImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.reactnativecommunity.blurview; - -import android.view.View; -import com.facebook.react.uimanager.ThemedReactContext; - -import eightbitlab.com.blurview.BlurView; - -import java.util.Objects; -import javax.annotation.Nonnull; - -@SuppressWarnings("unused") -class BlurViewManagerImpl { - - public static final String REACT_CLASS = "AndroidBlurView"; - - public static final int defaultRadius = 10; - public static final int defaultSampling = 10; - - public static @Nonnull BlurView createViewInstance(@Nonnull ThemedReactContext ctx) { - BlurView blurView = new BlurView(ctx); - View decorView = Objects - .requireNonNull(ctx.getCurrentActivity()) - .getWindow() - .getDecorView(); - blurView - .setupWith(decorView.findViewById(android.R.id.content)) - .setFrameClearDrawable(decorView.getBackground()) - .setBlurRadius(defaultRadius); - return blurView; - } - - public static void setRadius(BlurView view, int radius) { - view.setBlurRadius(radius); - view.invalidate(); - } - - public static void setColor(BlurView view, int color) { - view.setOverlayColor(color); - view.invalidate(); - } - - public static void setDownsampleFactor(BlurView view, int factor) {} - - public static void setAutoUpdate(BlurView view, boolean autoUpdate) { - view.setBlurAutoUpdate(autoUpdate); - view.invalidate(); - } - - public static void setBlurEnabled(BlurView view, boolean enabled) { - view.setBlurEnabled(enabled); - } -} diff --git a/android/src/main/java/com/reactnativecommunity/blurview/ReactBlurView.java b/android/src/main/java/com/reactnativecommunity/blurview/ReactBlurView.java new file mode 100644 index 00000000..4ca842b7 --- /dev/null +++ b/android/src/main/java/com/reactnativecommunity/blurview/ReactBlurView.java @@ -0,0 +1,150 @@ +package com.reactnativecommunity.blurview; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.os.Build; +import android.util.Log; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.facebook.react.views.view.ReactViewGroup; + +import eightbitlab.com.blurview.BlurAlgorithm; +import eightbitlab.com.blurview.BlurController; +import eightbitlab.com.blurview.BlurViewFacade; +import eightbitlab.com.blurview.NoOpController; +import eightbitlab.com.blurview.PreDrawBlurController; +import eightbitlab.com.blurview.RenderEffectBlur; +import eightbitlab.com.blurview.RenderScriptBlur; + +/** + * ReactViewGroup that blurs its underlying content. + * Can have children and draw them over blurred background. + */ +public class ReactBlurView extends ReactViewGroup { + + private static final String TAG = ReactBlurView.class.getSimpleName(); + private String blurType; + + private final Paint overlayPaint; + + BlurController blurController = new NoOpController(); + + public ReactBlurView(Context context) { + super(context); + setOutlineProvider(ViewOutlineProvider.BACKGROUND); + setClipToOutline(true); + overlayPaint = new Paint(); + setBlurType("dark"); + } + + @Override + public void draw(Canvas canvas) { + boolean shouldDraw = blurController.draw(canvas); + if (shouldDraw) { + if (overlayPaint.getColor() != Color.TRANSPARENT) { + canvas.drawPaint(overlayPaint); + } + super.draw(canvas); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + blurController.updateBlurViewSize(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + blurController.setBlurAutoUpdate(false); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!isHardwareAccelerated()) { + Log.e(TAG, "BlurView can't be used in not hardware-accelerated window!"); + } else { + blurController.setBlurAutoUpdate(true); + } + } + + public BlurViewFacade setupWith(@NonNull ViewGroup rootView, BlurAlgorithm algorithm) { + this.blurController.destroy(); + BlurController blurController = new PreDrawBlurController(this, rootView, getOverlayColor(), algorithm); + this.blurController = blurController; + return blurController; + } + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public BlurViewFacade setupWith(@NonNull ViewGroup rootView) { + return setupWith(rootView, getBlurAlgorithm()); + } + + public BlurViewFacade setBlurRadius(float radius) { + return blurController.setBlurRadius(radius); + } + + public BlurViewFacade setBlurType(String type) { + blurType = type; + overlayPaint.setXfermode(getXfermode()); + overlayPaint.setColor(getOverlayColor()); + return blurController; + } + + public BlurViewFacade setBlurAutoUpdate(boolean enabled) { + return blurController.setBlurAutoUpdate(enabled); + } + + public BlurViewFacade setBlurEnabled(boolean enabled) { + return blurController.setBlurEnabled(enabled); + } + + @Override + public void setBackgroundColor(int color) { + blurController.setOverlayColor(color); + } + + @NonNull + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + private BlurAlgorithm getBlurAlgorithm() { + BlurAlgorithm algorithm; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + algorithm = new RenderEffectBlur(); + } else { + algorithm = new RenderScriptBlur(getContext()); + } + return algorithm; + } + + private int getOverlayColor() { + switch (blurType) { + case "xlight": + return 0xFFBEBEBE; + case "light": + return 0xFFD2D2D2; + default: + return 0xFFAAAAAA; + } + } + + private PorterDuffXfermode getXfermode() { + switch (blurType) { + case "xlight": + return new PorterDuffXfermode(PorterDuff.Mode.SCREEN); + case "light": + return new PorterDuffXfermode(PorterDuff.Mode.OVERLAY); + default: + return new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY); + } + } +} diff --git a/android/src/main/res/values/attr.xml b/android/src/main/res/values/attr.xml index 97fc4280..38dab22e 100644 --- a/android/src/main/res/values/attr.xml +++ b/android/src/main/res/values/attr.xml @@ -3,6 +3,5 @@ - diff --git a/android/src/main/res/values/defaults.xml b/android/src/main/res/values/defaults.xml index e9e00a2b..a1d69462 100644 --- a/android/src/main/res/values/defaults.xml +++ b/android/src/main/res/values/defaults.xml @@ -2,5 +2,4 @@ 15 8 - #AAFFFFFF diff --git a/android/src/oldarch/java/com/reactnativecommunity/blurview/BlurViewManager.java b/android/src/oldarch/java/com/reactnativecommunity/blurview/BlurViewManager.java index 1a282095..f1ee6fab 100644 --- a/android/src/oldarch/java/com/reactnativecommunity/blurview/BlurViewManager.java +++ b/android/src/oldarch/java/com/reactnativecommunity/blurview/BlurViewManager.java @@ -1,53 +1,53 @@ -package com.reactnativecommunity.blurview; - -import androidx.annotation.NonNull; - -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; - -import eightbitlab.com.blurview.BlurView; - -class BlurViewManager extends ViewGroupManager { - - ReactApplicationContext mCallerContext; - - public BlurViewManager(ReactApplicationContext reactContext) { - mCallerContext = reactContext; - } - - @Override - public BlurView createViewInstance(ThemedReactContext context) { - return BlurViewManagerImpl.createViewInstance(context); - } - - @NonNull - @Override - public String getName() { - return BlurViewManagerImpl.REACT_CLASS; - } - - @ReactProp(name = "blurRadius", defaultInt = BlurViewManagerImpl.defaultRadius) - public void setRadius(BlurView view, int radius) { - BlurViewManagerImpl.setRadius(view, radius); - } - - @ReactProp(name = "overlayColor", customType = "Color") - public void setColor(BlurView view, int color) { - BlurViewManagerImpl.setColor(view, color); - } - - @ReactProp(name = "downsampleFactor", defaultInt = BlurViewManagerImpl.defaultSampling) - public void setDownsampleFactor(BlurView view, int factor) {} - - @ReactProp(name = "autoUpdate", defaultBoolean = true) - public void setAutoUpdate(BlurView view, boolean autoUpdate) { - BlurViewManagerImpl.setAutoUpdate(view, autoUpdate); - } - - @ReactProp(name = "enabled", defaultBoolean = true) - public void setBlurEnabled(BlurView view, boolean enabled) { - BlurViewManagerImpl.setBlurEnabled(view, enabled); - } -} +package com.reactnativecommunity.blurview; + +import androidx.annotation.NonNull; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import eightbitlab.com.blurview.BlurView; + +class BlurViewManager extends ViewGroupManager { + + ReactApplicationContext mCallerContext; + + public BlurViewManager(ReactApplicationContext reactContext) { + mCallerContext = reactContext; + } + + @Override + public BlurView createViewInstance(ThemedReactContext context) { + return BlurViewManagerImpl.createViewInstance(context); + } + + @NonNull + @Override + public String getName() { + return BlurViewManagerImpl.REACT_CLASS; + } + + @ReactProp(name = "blurRadius", defaultInt = BlurViewManagerImpl.defaultRadius) + public void setRadius(BlurView view, int radius) { + BlurViewManagerImpl.setRadius(view, radius); + } + + @ReactProp(name = "overlayColor", customType = "Color") + public void setColor(BlurView view, int color) { + BlurViewManagerImpl.setColor(view, color); + } + + @ReactProp(name = "downsampleFactor", defaultInt = BlurViewManagerImpl.defaultSampling) + public void setDownsampleFactor(BlurView view, int factor) {} + + @ReactProp(name = "autoUpdate", defaultBoolean = true) + public void setAutoUpdate(BlurView view, boolean autoUpdate) { + BlurViewManagerImpl.setAutoUpdate(view, autoUpdate); + } + + @ReactProp(name = "enabled", defaultBoolean = true) + public void setBlurEnabled(BlurView view, boolean enabled) { + BlurViewManagerImpl.setBlurEnabled(view, enabled); + } +} diff --git a/src/components/BlurView.android.tsx b/src/components/BlurView.android.tsx index aa572fb1..a0d4de64 100644 --- a/src/components/BlurView.android.tsx +++ b/src/components/BlurView.android.tsx @@ -8,18 +8,9 @@ import { } from 'react-native'; import NativeBlurView from '../fabric/BlurViewNativeComponentAndroid'; -const OVERLAY_COLORS = { - light: 'rgba(255, 255, 255, 0.2)', - xlight: 'rgba(255, 255, 255, 0.75)', - dark: 'rgba(16, 12, 12, 0.64)', -}; - export type BlurViewProps = ViewProps & { blurAmount?: number; blurType?: 'dark' | 'light' | 'xlight'; - blurRadius?: number; - downsampleFactor?: number; - overlayColor?: string; enabled?: boolean; autoUpdate?: boolean; }; @@ -27,14 +18,10 @@ export type BlurViewProps = ViewProps & { const BlurView = forwardRef( ( { - downsampleFactor, - blurRadius, - blurAmount = 10, blurType = 'dark', - overlayColor, + blurAmount = 10, enabled, autoUpdate, - children, style, ...rest }, @@ -50,58 +37,16 @@ const BlurView = forwardRef( }; }, []); - const getOverlayColor = () => { - if (overlayColor != null) { - return overlayColor; - } - - return OVERLAY_COLORS[blurType] || OVERLAY_COLORS.dark; - }; - - const getBlurRadius = () => { - if (blurRadius != null) { - if (blurRadius > 25) { - throw new Error( - `[ReactNativeBlur]: blurRadius cannot be greater than 25! (was: ${blurRadius})` - ); - } - return blurRadius; - } - - // iOS seems to use a slightly different blurring algorithm (or scale?). - // Android blurRadius + downsampleFactor is approximately 80% of blurAmount. - const equivalentBlurRadius = Math.round(blurAmount * 0.8); - - if (equivalentBlurRadius > 25) { - return 25; - } - return equivalentBlurRadius; - }; - - const getDownsampleFactor = () => { - if (downsampleFactor != null) { - return downsampleFactor; - } - - return blurRadius; - }; - return ( - - {children} - + ); } ); diff --git a/src/components/BlurView.ios.tsx b/src/components/BlurView.ios.tsx index 62d05ec4..b24d944a 100644 --- a/src/components/BlurView.ios.tsx +++ b/src/components/BlurView.ios.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef } from 'react'; +import React, { forwardRef, useEffect, useState } from "react"; import { StyleSheet, ViewProps, ViewStyle, View } from 'react-native'; import NativeBlurView from '../fabric/BlurViewNativeComponent'; @@ -32,19 +32,52 @@ export type BlurViewProps = ViewProps & { }; const BlurView = forwardRef( - ({ blurType = 'dark', blurAmount = 10, style, ...rest }, ref) => ( - - ) + ({ blurType = 'dark', blurAmount = 10, style, children, ...rest }, ref) => { + + const [borderStyle, setBorderStyle] = useState({}); + + useEffect(() => { + const baseStyles = StyleSheet.flatten(style) as ViewStyle; + const borderWidth = baseStyles.borderWidth || 0; + const brdrStyle = { + position: 'absolute', + top: -borderWidth, + left: -borderWidth, + right: -borderWidth, + bottom: -borderWidth + } as ViewStyle; + + if (baseStyles) { + Object.keys(baseStyles).forEach((key) => { + if (key.startsWith('border')) { + // @ts-ignore + brdrStyle[key] = baseStyles[key]; + return; + } + }); + } + setBorderStyle(brdrStyle); + }, [style]); + + return ( + + + + + {children} + + ); + } ); const styles = StyleSheet.create<{ transparent: ViewStyle }>({ - transparent: { backgroundColor: 'transparent' }, + transparent: { backgroundColor: 'transparent', overflow: 'hidden' }, }); export default BlurView; diff --git a/src/fabric/BlurViewNativeComponentAndroid.ts b/src/fabric/BlurViewNativeComponentAndroid.ts index 59fd78f6..beef5e47 100644 --- a/src/fabric/BlurViewNativeComponentAndroid.ts +++ b/src/fabric/BlurViewNativeComponentAndroid.ts @@ -15,6 +15,6 @@ interface NativeProps extends ViewProps { autoUpdate?: boolean; } -export default codegenNativeComponent('AndroidBlurView', { +export default codegenNativeComponent('BlurView', { excludedPlatforms: ['iOS'], }) as HostComponent;