├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── RNScreenshotPrevent.podspec
├── android
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── killserver
│ └── screenshotprev
│ ├── RNScreenshotPreventModule.java
│ └── RNScreenshotPreventPackage.java
├── ios
├── RNScreenshotPrevent.h
├── RNScreenshotPrevent.m
├── RNScreenshotPrevent.xcodeproj
│ └── project.pbxproj
├── UIImage+ImageEffects.h
└── UIImage+ImageEffects.m
├── lefthook.yml
├── package.json
├── src
├── index.d.ts
└── index.ts
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # OSX
3 | #
4 | .DS_Store
5 |
6 | # node.js
7 | #
8 | node_modules/
9 | lib/
10 | npm-debug.log
11 | yarn-error.log
12 |
13 |
14 | # Xcode
15 | #
16 | build/
17 | *.pbxuser
18 | !default.pbxuser
19 | *.mode1v3
20 | !default.mode1v3
21 | *.mode2v3
22 | !default.mode2v3
23 | *.perspectivev3
24 | !default.perspectivev3
25 | xcuserdata
26 | *.xccheckout
27 | *.moved-aside
28 | DerivedData
29 | *.hmap
30 | *.ipa
31 | *.xcuserstate
32 | project.xcworkspace
33 |
34 |
35 | # Android/IntelliJ
36 | #
37 | build/
38 | .idea
39 | .gradle
40 | local.properties
41 | *.iml
42 |
43 | # BUCK
44 | buck-out/
45 | \.buckd/
46 | *.keystore
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 killer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
2 |
3 | # react-native-screenshot-prevent
4 |
5 | ### This fork contains fully working blank screenshot on IOS13+ including screen recording
6 | ### This fork contains fully working image screenshot cover on IOS13+ including screen recording
7 | ### App layout is white / or black in dark theme
8 |
9 | #### For now you might disable RNPreventScreenshot.enableSecureView() in development mode (check __DEV__ variable)
10 | #### because disableSecureView() is not working yet correctly
11 |
12 |
13 | ## Getting started
14 |
15 | `$ npm install react-native-screenshot-prevent --save`
16 |
17 | ### Mostly automatic installation
18 |
19 | ### React-Native version 0.59.X and higher: on IOS you might use only `pod install` in your ios folder
20 |
21 | `$ react-native link react-native-screenshot-prevent`
22 |
23 | ### Manual installation
24 |
25 |
26 | #### iOS
27 |
28 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
29 | 2. Go to `node_modules` ➜ `react-native-screenshot-prevent` and add `RNScreenshotPrevent.xcodeproj`
30 | 3. In XCode, in the project navigator, select your project. Add `libRNScreenshotPrevent.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
31 | 4. Run your project (`Cmd+R`)<
32 |
33 | #### Android
34 |
35 | 1. Open up `android/app/src/main/java/[...]/MainApplication.java`
36 | - Add `import com.killserver.screenshotprev.RNScreenshotPreventPackage;` to the imports at the top of the file
37 | - Add `new RNScreenshotPreventPackage()` to the list returned by the `getPackages()` method
38 | 2. Append the following lines to `android/settings.gradle`:
39 | ```
40 | include ':react-native-screenshot-prevent'
41 | project(':react-native-screenshot-prevent').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screenshot-prevent/android')
42 | ```
43 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`:
44 | ```
45 | implementation project(':react-native-screenshot-prevent')
46 | ```
47 |
48 |
49 |
50 | ## Usage
51 | ```javascript
52 |
53 | // sample code
54 |
55 | import RNScreenshotPrevent, { addListener } from 'react-native-screenshot-prevent';
56 |
57 | /* (IOS, Android) for android might be the only step to get secureView
58 | * on IOS enables blurry view when app goes into inactive state
59 | */
60 | RNScreenshotPrevent.enabled(true/false);
61 |
62 | /* (IOS) enableSecureView for IOS13+
63 | * creates a hidden secureTextField which prevents Application UI capture on screenshots
64 | */
65 | if(!__DEV__) RNScreenshotPrevent.enableSecureView();
66 |
67 | /* (IOS) enableSecureView for IOS13+
68 | * creates a hidden secureTextField which prevents Application UI capture on screenshots
69 | * and uses imgUri as the source of the background image (can be both https://, file:///)
70 | */
71 | if(!__DEV__) RNPreventScreenshot.enableSecureView(imgUri);
72 |
73 | /* (IOS) disableSecureView for IOS13+
74 | * remove a hidden secureTextField which prevents Application UI capture on screenshots
75 | */
76 | if(!__DEV__) RNScreenshotPrevent.disableSecureView();
77 |
78 | /* (IOS) notification handler
79 | * notifies when user has taken screenshot (yes, after taking) - you can show alert or do some actions
80 | *
81 | * @param {function} callback fn
82 | * @returns object with .remove() method
83 | */
84 | addListener(fn);
85 |
86 | /** example using the listener */
87 | useEffect(() => {
88 | const subscription = RNScreenshotPrevent.addListener(() => {
89 | console.log('Screenshot taken');
90 | showAlert({
91 | title: 'Warning',
92 | message: 'You have taken a screenshot of the app. This is prohibited due to security reasons.',
93 | confirmText: 'I understand'
94 | });
95 | })
96 |
97 | return () => {
98 | subscription.remove();
99 | }
100 | }, []);
101 |
102 | ```
103 |
104 |
105 | [](https://www.buymeacoffee.com/killeserver)
106 |
--------------------------------------------------------------------------------
/RNScreenshotPrevent.podspec:
--------------------------------------------------------------------------------
1 | # RNScreenshotPrevent
2 |
3 | require 'json'
4 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
5 |
6 | Pod::Spec.new do |s|
7 | s.name = "RNScreenshotPrevent"
8 | s.version = package['version']
9 | s.summary = package['description']
10 | s.license = package['license']
11 | s.authors = package['author']
12 | s.homepage = package['homepage']
13 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" }
14 | s.platform = :ios, "9.0"
15 | s.source = { :git => "https://github.com/killserver/react-native-screenshot-prevent.git", :tag => "master" }
16 | s.source_files = "ios/**/*.{h,m}"
17 | s.requires_arc = true
18 |
19 |
20 | s.dependency "React"
21 | #s.dependency "others"
22 |
23 | end
24 |
25 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:1.3.1'
10 | }
11 | }
12 |
13 | def safeExtGet(prop, fallback) {
14 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
15 | }
16 |
17 | def DEFAULT_COMPILE_SDK_VERSION = 23
18 | def DEFAULT_BUILD_TOOLS_VERSION = "23.0.1"
19 | def DEFAULT_MIN_SDK_VERSION = 16
20 | def DEFAULT_TARGET_SDK_VERSION = 22
21 |
22 |
23 | android {
24 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
25 | buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
26 |
27 | defaultConfig {
28 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
29 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
30 | versionCode 1
31 | versionName "1.0"
32 | }
33 | lintOptions {
34 | abortOnError false
35 | }
36 | }
37 |
38 | repositories {
39 | mavenCentral()
40 | }
41 |
42 | dependencies {
43 | implementation 'com.facebook.react:react-native:+'
44 | }
45 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/src/main/java/com/killserver/screenshotprev/RNScreenshotPreventModule.java:
--------------------------------------------------------------------------------
1 | package com.killserver.screenshotprev;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Color;
8 | import android.util.Log;
9 | import android.view.ViewTreeObserver;
10 | import android.view.Window;
11 | import android.view.WindowManager;
12 | import android.widget.RelativeLayout;
13 | import android.widget.ImageView;
14 |
15 | import androidx.annotation.NonNull;
16 | import androidx.annotation.Nullable;
17 | import androidx.fragment.app.DialogFragment;
18 | import androidx.fragment.app.Fragment;
19 | import androidx.fragment.app.FragmentActivity;
20 |
21 | import java.net.MalformedURLException;
22 | import java.util.List;
23 |
24 | import com.facebook.react.bridge.ReactApplicationContext;
25 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
26 | import com.facebook.react.bridge.ReactMethod;
27 | import com.facebook.react.modules.dialog.AlertFragment;
28 |
29 | import java.io.IOException;
30 | import java.net.URL;
31 | import java.util.Objects;
32 |
33 | public class RNScreenshotPreventModule extends ReactContextBaseJavaModule implements ViewTreeObserver.OnWindowFocusChangeListener {
34 | private Dialog securedDialog;
35 |
36 | public RNScreenshotPreventModule(ReactApplicationContext reactContext) {
37 | super(reactContext);
38 | }
39 |
40 | @NonNull
41 | @Override
42 | public String getName() {
43 | return "RNScreenshotPrevent";
44 | }
45 |
46 | @ReactMethod
47 | public void enabled(boolean enable) {
48 | Log.i("RNScreenshotPreventModule", "secured called " + enable);
49 |
50 | final Activity activity = this.getReactApplicationContext().getCurrentActivity();
51 | if (!this.getReactApplicationContext().hasCurrentActivity() || activity == null) {
52 | return;
53 | }
54 |
55 | if (enable) {
56 | activity.runOnUiThread(() -> {
57 | activity.getWindow().setFlags(
58 | WindowManager.LayoutParams.FLAG_SECURE,
59 | WindowManager.LayoutParams.FLAG_SECURE
60 | );
61 | });
62 | } else {
63 | activity.runOnUiThread(() -> {
64 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
65 | });
66 | }
67 | }
68 |
69 | @ReactMethod
70 | public void enableSecureView(String imagePath) {
71 | Log.i("RNScreenshotPreventModule", "enable secured view called " + imagePath);
72 |
73 | final Activity activity = this.getReactApplicationContext().getCurrentActivity();
74 | if (!this.getReactApplicationContext().hasCurrentActivity() || activity == null) {
75 | return;
76 | }
77 |
78 | activity.runOnUiThread(() -> {
79 | this.createSecuredDialog(imagePath, activity);
80 | enabled(true);
81 | activity.getWindow().getDecorView().getRootView().getViewTreeObserver().addOnWindowFocusChangeListener(this);
82 | });
83 | }
84 |
85 | @ReactMethod
86 | public void disableSecureView() {
87 | Log.i("RNScreenshotPreventModule", "disable secured view called");
88 |
89 | final Activity activity = this.getReactApplicationContext().getCurrentActivity();
90 | if (!this.getReactApplicationContext().hasCurrentActivity() || activity == null) {
91 | return;
92 | }
93 |
94 | activity.runOnUiThread(() -> {
95 | this.securedDialog = null;
96 | enabled(false);
97 | activity.getWindow().getDecorView().getRootView().getViewTreeObserver().removeOnWindowFocusChangeListener(this);
98 | });
99 | }
100 |
101 | private Bitmap decodeImageUrl(String imagePath) {
102 | try {
103 | URL imageUrl = new URL(imagePath);
104 | return BitmapFactory.decodeStream(imageUrl.openConnection().getInputStream());
105 | } catch (IOException e) {
106 | if (e instanceof MalformedURLException) {
107 | try {
108 | int resourceId = this.getReactApplicationContext().getResources().getIdentifier(imagePath, "drawable", this.getReactApplicationContext().getPackageName());
109 | return BitmapFactory.decodeResource(this.getReactApplicationContext().getResources(), resourceId);
110 | } catch (Exception ee) {
111 | Log.e("RNScreenshotPreventModule", "exception", ee);
112 | return null;
113 | }
114 | }
115 | Log.e("RNScreenshotPreventModule", "exception", e);
116 | return null;
117 | }
118 | }
119 |
120 | private Dialog getSecuredDialog() {
121 | final Activity activity = this.getReactApplicationContext().getCurrentActivity();
122 | if (this.securedDialog == null && activity != null) {
123 | this.createSecuredDialog(null, activity);
124 | }
125 |
126 | return securedDialog;
127 | }
128 |
129 | private void createSecuredDialog(@Nullable String imagePath, @NonNull Activity activity) {
130 | this.securedDialog = new Dialog(activity, android.R.style.Theme_Light);
131 | this.securedDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
132 | Objects.requireNonNull(this.securedDialog.getWindow()).setFlags(
133 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
134 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
135 | );
136 |
137 | RelativeLayout layout = new RelativeLayout(activity);
138 | layout.setBackgroundColor(Color.parseColor("#FFFFFF"));
139 |
140 | ImageView imageView = new ImageView(activity);
141 | RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(
142 | RelativeLayout.LayoutParams.MATCH_PARENT,
143 | RelativeLayout.LayoutParams.WRAP_CONTENT);
144 | imageParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
145 |
146 | imageView.setLayoutParams(imageParams);
147 |
148 | // Set image resource
149 | if (imagePath != null) {
150 | Bitmap bitmap = decodeImageUrl(imagePath);
151 |
152 | if (bitmap != null) {
153 | int imageHeight = (int)(bitmap.getHeight() * ((float) activity.getResources().getDisplayMetrics().widthPixels / bitmap.getWidth()));
154 | Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, activity.getResources().getDisplayMetrics().widthPixels, imageHeight, true);
155 | imageView.setImageBitmap(scaledBitmap);
156 | }
157 |
158 | layout.addView(imageView);
159 | }
160 |
161 | this.securedDialog.setContentView(layout);
162 | }
163 |
164 | @Override
165 | public void onWindowFocusChanged(boolean hasFocus) {
166 | Log.i("RNScreenshotPreventModule", "on window focus changed " + hasFocus);
167 |
168 | final Activity activity = this.getReactApplicationContext().getCurrentActivity();
169 | if (activity == null) {
170 | Log.i("RNScreenshotPreventModule", "activity is null" );
171 | return;
172 | }
173 |
174 | boolean hasAlert = false;
175 | if (activity instanceof FragmentActivity) {
176 | List fragments = ((FragmentActivity) activity).getSupportFragmentManager().getFragments();
177 | for (Fragment fragment : fragments) {
178 | boolean isDiablog = fragment instanceof DialogFragment;
179 | boolean isAlert = fragment instanceof AlertFragment;
180 | if (isDiablog || isAlert) {
181 | hasAlert = true;
182 | break;
183 | }
184 | }
185 | }
186 |
187 | if (!hasFocus && !hasAlert){
188 | Log.i("RNScreenshotPreventModule", "call to hide content");
189 | this.getSecuredDialog().show();
190 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
191 | } else {
192 | Log.i("RNScreenshotPreventModule", "call to show content");
193 | this.getSecuredDialog().hide();
194 | activity.getWindow().setFlags(
195 | WindowManager.LayoutParams.FLAG_SECURE,
196 | WindowManager.LayoutParams.FLAG_SECURE
197 | );
198 | }
199 | }
200 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/killserver/screenshotprev/RNScreenshotPreventPackage.java:
--------------------------------------------------------------------------------
1 |
2 | package com.killserver.screenshotprev;
3 |
4 | import java.util.Arrays;
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.bridge.NativeModule;
10 | import com.facebook.react.bridge.ReactApplicationContext;
11 | import com.facebook.react.uimanager.ViewManager;
12 | import com.facebook.react.bridge.JavaScriptModule;
13 |
14 | public class RNScreenshotPreventPackage implements ReactPackage {
15 | @Override
16 | public List createNativeModules(ReactApplicationContext reactContext) {
17 | return Arrays.asList(new RNScreenshotPreventModule(reactContext));
18 | }
19 |
20 | // Deprecated from RN 0.47
21 | public List> createJSModules() {
22 | return Collections.emptyList();
23 | }
24 |
25 | @Override
26 | public List createViewManagers(ReactApplicationContext reactContext) {
27 | return Collections.emptyList();
28 | }
29 | }
--------------------------------------------------------------------------------
/ios/RNScreenshotPrevent.h:
--------------------------------------------------------------------------------
1 |
2 | #if __has_include("RCTBridgeModule.h")
3 | #import "RCTBridgeModule.h"
4 | #import "RCTConvert.h"
5 | #import "RCTEventEmitter.h"
6 | #else
7 | #import
8 | #import
9 | #import
10 | #endif
11 |
12 | @interface RNScreenshotPrevent : RCTEventEmitter
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/ios/RNScreenshotPrevent.m:
--------------------------------------------------------------------------------
1 | #import "RNScreenshotPrevent.h"
2 | #import "UIImage+ImageEffects.h"
3 |
4 | @implementation RNScreenshotPrevent {
5 | BOOL hasListeners;
6 | BOOL enabled;
7 | UIImageView *obfuscatingView;
8 | UITextField *secureField;
9 |
10 | }
11 |
12 | RCT_EXPORT_MODULE();
13 | - (NSArray *)supportedEvents {
14 | return @[@"userDidTakeScreenshot"];
15 | }
16 |
17 | - (dispatch_queue_t)methodQueue
18 | {
19 | return dispatch_get_main_queue();
20 | }
21 |
22 | #pragma mark - Lifecycle
23 |
24 | - (void) startObserving {
25 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
26 | // handle inactive event
27 | [center addObserver:self selector:@selector(handleAppStateResignActive)
28 | name:UIApplicationWillResignActiveNotification
29 | object:nil];
30 | // handle active event
31 | [center addObserver:self selector:@selector(handleAppStateActive)
32 | name:UIApplicationDidBecomeActiveNotification
33 | object:nil];
34 | // handle screenshot taken event
35 | [center addObserver:self selector:@selector(handleAppScreenshotNotification)
36 | name:UIApplicationUserDidTakeScreenshotNotification
37 | object:nil];
38 |
39 | hasListeners = TRUE;
40 | }
41 |
42 | - (void) stopObserving {
43 | [[NSNotificationCenter defaultCenter] removeObserver:self];
44 |
45 | hasListeners = FALSE;
46 | }
47 |
48 | #pragma mark - App Notification Methods
49 |
50 | /** displays blurry view when app becomes inactive */
51 | - (void)handleAppStateResignActive {
52 | if (self->enabled) {
53 | UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
54 | UIImageView *blurredScreenImageView = [[UIImageView alloc] initWithFrame:keyWindow.bounds];
55 |
56 | UIGraphicsBeginImageContext(keyWindow.bounds.size);
57 | [keyWindow drawViewHierarchyInRect:keyWindow.frame afterScreenUpdates:NO];
58 | UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
59 | UIGraphicsEndImageContext();
60 |
61 | blurredScreenImageView.image = [viewImage applyLightEffect];
62 |
63 | self->obfuscatingView = blurredScreenImageView;
64 | [keyWindow addSubview:self->obfuscatingView];
65 | }
66 | }
67 |
68 | /** removes blurry view when app becomes active */
69 | - (void)handleAppStateActive {
70 | if (self->obfuscatingView) {
71 | [UIView animateWithDuration: 0.3
72 | animations: ^ {
73 | self->obfuscatingView.alpha = 0;
74 | }
75 | completion: ^(BOOL finished) {
76 | [self->obfuscatingView removeFromSuperview];
77 | self->obfuscatingView = nil;
78 | }
79 | ];
80 | }
81 | }
82 |
83 | /** sends screenshot taken event into app */
84 | - (void) handleAppScreenshotNotification {
85 | // only send events when we have some listeners
86 | if(hasListeners) {
87 | [self sendEventWithName:@"userDidTakeScreenshot" body:nil];
88 | }
89 | }
90 |
91 | +(BOOL) requiresMainQueueSetup
92 | {
93 | return YES;
94 | }
95 |
96 | CGSize CGSizeAspectFit(const CGSize aspectRatio, const CGSize boundingSize)
97 | {
98 | CGSize aspectFitSize = CGSizeMake(boundingSize.width, boundingSize.height);
99 | float mW = boundingSize.width / aspectRatio.width;
100 | float mH = boundingSize.height / aspectRatio.height;
101 | if( mH < mW )
102 | aspectFitSize.width = mH * aspectRatio.width;
103 | else if( mW < mH )
104 | aspectFitSize.height = mW * aspectRatio.height;
105 | return aspectFitSize;
106 | }
107 |
108 | CGSize CGSizeAspectFill(const CGSize aspectRatio, const CGSize minimumSize)
109 | {
110 | CGSize aspectFillSize = CGSizeMake(minimumSize.width, minimumSize.height);
111 | float mW = minimumSize.width / aspectRatio.width;
112 | float mH = minimumSize.height / aspectRatio.height;
113 | if( mH > mW )
114 | aspectFillSize.width = mH * aspectRatio.width;
115 | else if( mW > mH )
116 | aspectFillSize.height = mW * aspectRatio.height;
117 | return aspectFillSize;
118 | }
119 |
120 |
121 | /**
122 | * creates secure text field inside rootView of the app
123 | * taken from https://stackoverflow.com/questions/18680028/prevent-screen-capture-in-an-ios-app
124 | *
125 | * converted to ObjC and modified to get it working with RCT
126 | */
127 | -(void) addSecureTextFieldToView:(UIView *) view :(NSString *) imagePath {
128 |
129 | UIView *rootView = [UIApplication sharedApplication].keyWindow.rootViewController.view;
130 |
131 |
132 | // fixes safe-area
133 | secureField = [[UITextField alloc] initWithFrame:rootView.frame];
134 | secureField.secureTextEntry = TRUE;
135 | secureField.userInteractionEnabled = FALSE;
136 |
137 | if (imagePath && ![imagePath isEqualToString:@""]) {
138 | UIView * imgView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, rootView.frame.size.width, rootView.frame.size.height)];
139 | NSURL *url = [NSURL URLWithString:imagePath];
140 | NSData *data = [NSData dataWithContentsOfURL:url];
141 | UIImage *img = [[UIImage alloc] initWithData:data];
142 |
143 | CGSize sizeBeingScaledTo = CGSizeAspectFill(img.size, imgView.frame.size);
144 |
145 | // redraw the image to fit the screen size
146 | UIGraphicsBeginImageContextWithOptions(imgView.frame.size, NO, 0.f);
147 |
148 | float offsetX = (imgView.frame.size.width - sizeBeingScaledTo.width) / 2;
149 | float offsety = (imgView.frame.size.height - sizeBeingScaledTo.height) / 2;
150 |
151 |
152 | [img drawInRect:CGRectMake(offsetX, offsety, sizeBeingScaledTo.width, sizeBeingScaledTo.height)];
153 | UIImage * resultImage = UIGraphicsGetImageFromCurrentImageContext();
154 | UIGraphicsEndImageContext();
155 |
156 | secureField.backgroundColor = [UIColor colorWithPatternImage:resultImage];
157 | }
158 |
159 | [view sendSubviewToBack:secureField];
160 | [view addSubview:secureField];
161 | [view.layer.superlayer addSublayer:secureField.layer];
162 | [[secureField.layer.sublayers lastObject] addSublayer:view.layer];
163 | }
164 |
165 | // TODO: not working now, fix crash on _UITextFieldCanvasView contenttViewInvalidated: unrecognized selector sent to instance
166 | -(void) removeSecureTextFieldFromView:(UIView *) view {
167 | for(UITextField *subview in view.subviews){
168 | if([subview isMemberOfClass:[UITextField class]]) {
169 | if(subview.secureTextEntry == TRUE) {
170 | [subview removeFromSuperview];
171 | subview.secureTextEntry = FALSE;
172 | secureField.userInteractionEnabled = TRUE;
173 | }
174 | }
175 | }
176 | }
177 |
178 | #pragma mark - Public API
179 |
180 | RCT_EXPORT_METHOD(enabled:(BOOL) _enable) {
181 | self->enabled = _enable;
182 | }
183 |
184 | /** adds secure textfield view */
185 | RCT_EXPORT_METHOD(enableSecureView: (NSString *)imagePath) {
186 | [self enabled:YES];
187 | if(secureField.secureTextEntry == false) {
188 | UIView *view = [UIApplication sharedApplication].keyWindow.rootViewController.view;
189 | for(UIView *subview in view.subviews) {
190 | [self addSecureTextFieldToView:subview :imagePath];
191 | }
192 | }
193 | }
194 |
195 | /** removes secure textfield from the view */
196 | RCT_EXPORT_METHOD(disableSecureView) {
197 | [self enabled:NO];
198 | secureField.secureTextEntry = false;
199 | UIView *view = [UIApplication sharedApplication].keyWindow.rootViewController.view;
200 | for(UIView *subview in view.subviews) {
201 | [self removeSecureTextFieldFromView:subview];
202 | }
203 | }
204 |
205 |
206 | @end
207 |
--------------------------------------------------------------------------------
/ios/RNScreenshotPrevent.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B3E7B58A1CC2AC0600A0062D /* RNScreenshotPrevent.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNScreenshotPrevent.m */; };
11 | F7D96EE223047BB300BC2484 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = F7D96EE123047BB300BC2484 /* UIImage+ImageEffects.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = "include/$(PRODUCT_NAME)";
19 | dstSubfolderSpec = 16;
20 | files = (
21 | );
22 | runOnlyForDeploymentPostprocessing = 0;
23 | };
24 | /* End PBXCopyFilesBuildPhase section */
25 |
26 | /* Begin PBXFileReference section */
27 | 134814201AA4EA6300B7C361 /* libRNScreenshotPrevent.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libRNScreenshotPrevent.a; path = libRNScreenshotPrevent.a; sourceTree = BUILT_PRODUCTS_DIR; };
28 | B3E7B5881CC2AC0600A0062D /* RNScreenshotPrevent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNScreenshotPrevent.h; sourceTree = ""; };
29 | B3E7B5891CC2AC0600A0062D /* RNScreenshotPrevent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNScreenshotPrevent.m; sourceTree = ""; };
30 | F7D96EE023047BB300BC2484 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ImageEffects.h"; sourceTree = ""; };
31 | F7D96EE123047BB300BC2484 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ImageEffects.m"; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 134814211AA4EA7D00B7C361 /* Products */ = {
46 | isa = PBXGroup;
47 | children = (
48 | 134814201AA4EA6300B7C361 /* libRNScreenshotPrevent.a */,
49 | );
50 | name = Products;
51 | sourceTree = "";
52 | };
53 | 58B511D21A9E6C8500147676 = {
54 | isa = PBXGroup;
55 | children = (
56 | F7D96EE023047BB300BC2484 /* UIImage+ImageEffects.h */,
57 | F7D96EE123047BB300BC2484 /* UIImage+ImageEffects.m */,
58 | B3E7B5881CC2AC0600A0062D /* RNScreenshotPrevent.h */,
59 | B3E7B5891CC2AC0600A0062D /* RNScreenshotPrevent.m */,
60 | 134814211AA4EA7D00B7C361 /* Products */,
61 | );
62 | sourceTree = "";
63 | };
64 | /* End PBXGroup section */
65 |
66 | /* Begin PBXNativeTarget section */
67 | 58B511DA1A9E6C8500147676 /* RNScreenshotPrevent */ = {
68 | isa = PBXNativeTarget;
69 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNScreenshotPrevent" */;
70 | buildPhases = (
71 | 58B511D71A9E6C8500147676 /* Sources */,
72 | 58B511D81A9E6C8500147676 /* Frameworks */,
73 | 58B511D91A9E6C8500147676 /* CopyFiles */,
74 | );
75 | buildRules = (
76 | );
77 | dependencies = (
78 | );
79 | name = RNScreenshotPrevent;
80 | productName = RCTDataManager;
81 | productReference = 134814201AA4EA6300B7C361 /* libRNScreenshotPrevent.a */;
82 | productType = "com.apple.product-type.library.static";
83 | };
84 | /* End PBXNativeTarget section */
85 |
86 | /* Begin PBXProject section */
87 | 58B511D31A9E6C8500147676 /* Project object */ = {
88 | isa = PBXProject;
89 | attributes = {
90 | LastUpgradeCheck = 0830;
91 | ORGANIZATIONNAME = Facebook;
92 | TargetAttributes = {
93 | 58B511DA1A9E6C8500147676 = {
94 | CreatedOnToolsVersion = 6.1.1;
95 | };
96 | };
97 | };
98 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNScreenshotPrevent" */;
99 | compatibilityVersion = "Xcode 3.2";
100 | developmentRegion = English;
101 | hasScannedForEncodings = 0;
102 | knownRegions = (
103 | English,
104 | en,
105 | );
106 | mainGroup = 58B511D21A9E6C8500147676;
107 | productRefGroup = 58B511D21A9E6C8500147676;
108 | projectDirPath = "";
109 | projectRoot = "";
110 | targets = (
111 | 58B511DA1A9E6C8500147676 /* RNScreenshotPrevent */,
112 | );
113 | };
114 | /* End PBXProject section */
115 |
116 | /* Begin PBXSourcesBuildPhase section */
117 | 58B511D71A9E6C8500147676 /* Sources */ = {
118 | isa = PBXSourcesBuildPhase;
119 | buildActionMask = 2147483647;
120 | files = (
121 | F7D96EE223047BB300BC2484 /* UIImage+ImageEffects.m in Sources */,
122 | B3E7B58A1CC2AC0600A0062D /* RNScreenshotPrevent.m in Sources */,
123 | );
124 | runOnlyForDeploymentPostprocessing = 0;
125 | };
126 | /* End PBXSourcesBuildPhase section */
127 |
128 | /* Begin XCBuildConfiguration section */
129 | 58B511ED1A9E6C8500147676 /* Debug */ = {
130 | isa = XCBuildConfiguration;
131 | buildSettings = {
132 | ALWAYS_SEARCH_USER_PATHS = NO;
133 | CLANG_ANALYZER_NONNULL = YES;
134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
135 | CLANG_CXX_LIBRARY = "libc++";
136 | CLANG_ENABLE_MODULES = YES;
137 | CLANG_ENABLE_OBJC_ARC = YES;
138 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
139 | CLANG_WARN_BOOL_CONVERSION = YES;
140 | CLANG_WARN_COMMA = YES;
141 | CLANG_WARN_CONSTANT_CONVERSION = YES;
142 | CLANG_WARN_DEPRICATED_OBJC_IMPLEMENTATIONS = YES;
143 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
144 | CLANG_WARN_EMPTY_BODY = YES;
145 | CLANG_WARN_ENUM_CONVERSION = YES;
146 | CLANG_WARN_INFINITE_RECURSION = YES;
147 | CLANG_WARN_INT_CONVERSION = YES;
148 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
149 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
150 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
151 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
152 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
153 | CLANG_WARN_STRICT_PROTOTYPES = YES;
154 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
155 | CLANG_WARN_UNREACHABLE_CODE = YES;
156 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
157 | COPY_PHASE_STRIP = NO;
158 | ENABLE_STRICT_OBJC_MSGSEND = YES;
159 | ENABLE_TESTABILITY = YES;
160 | GCC_C_LANGUAGE_STANDARD = gnu99;
161 | GCC_DYNAMIC_NO_PIC = NO;
162 | GCC_NO_COMMON_BLOCKS = YES;
163 | GCC_OPTIMIZATION_LEVEL = 0;
164 | GCC_PREPROCESSOR_DEFINITIONS = (
165 | "DEBUG=1",
166 | "$(inherited)",
167 | );
168 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
169 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
170 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
171 | GCC_WARN_UNDECLARED_SELECTOR = YES;
172 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
173 | GCC_WARN_UNUSED_FUNCTION = YES;
174 | GCC_WARN_UNUSED_VARIABLE = YES;
175 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
176 | MTL_ENABLE_DEBUG_INFO = YES;
177 | ONLY_ACTIVE_ARCH = YES;
178 | SDKROOT = iphoneos;
179 | };
180 | name = Debug;
181 | };
182 | 58B511EE1A9E6C8500147676 /* Release */ = {
183 | isa = XCBuildConfiguration;
184 | buildSettings = {
185 | ALWAYS_SEARCH_USER_PATHS = NO;
186 | CLANG_ANALYZER_NONNULL = YES;
187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
188 | CLANG_CXX_LIBRARY = "libc++";
189 | CLANG_ENABLE_MODULES = YES;
190 | CLANG_ENABLE_OBJC_ARC = YES;
191 | CLANG_WARN_BOOL_CONVERSION = YES;
192 | CLANG_WARN_CONSTANT_CONVERSION = YES;
193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
194 | CLANG_WARN_EMPTY_BODY = YES;
195 | CLANG_WARN_ENUM_CONVERSION = YES;
196 | CLANG_WARN_INFINITE_RECURSION = YES;
197 | CLANG_WARN_INT_CONVERSION = YES;
198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
199 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
200 | CLANG_WARN_UNREACHABLE_CODE = YES;
201 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
202 | COPY_PHASE_STRIP = YES;
203 | ENABLE_NS_ASSERTIONS = NO;
204 | ENABLE_STRICT_OBJC_MSGSEND = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_NO_COMMON_BLOCKS = YES;
207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
209 | GCC_WARN_UNDECLARED_SELECTOR = YES;
210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
211 | GCC_WARN_UNUSED_FUNCTION = YES;
212 | GCC_WARN_UNUSED_VARIABLE = YES;
213 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
214 | MTL_ENABLE_DEBUG_INFO = NO;
215 | SDKROOT = iphoneos;
216 | VALIDATE_PRODUCT = YES;
217 | };
218 | name = Release;
219 | };
220 | 58B511F01A9E6C8500147676 /* Debug */ = {
221 | isa = XCBuildConfiguration;
222 | buildSettings = {
223 | HEADER_SEARCH_PATHS = (
224 | "$(inherited)",
225 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
226 | "$(SRCROOT)/../../../React/",
227 | "$(SRCROOT)/../../react-native/React/",
228 | );
229 | LIBRARY_SEARCH_PATHS = "$(inherited)";
230 | OTHER_LDFLAGS = "-ObjC";
231 | PRODUCT_NAME = RNScreenshotPrevent;
232 | SKIP_INSTALL = YES;
233 | };
234 | name = Debug;
235 | };
236 | 58B511F11A9E6C8500147676 /* Release */ = {
237 | isa = XCBuildConfiguration;
238 | buildSettings = {
239 | HEADER_SEARCH_PATHS = (
240 | "$(inherited)",
241 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
242 | "$(SRCROOT)/../../../React/",
243 | "$(SRCROOT)/../../react-native/React/",
244 | );
245 | LIBRARY_SEARCH_PATHS = "$(inherited)";
246 | OTHER_LDFLAGS = "-ObjC";
247 | PRODUCT_NAME = RNScreenshotPrevent;
248 | SKIP_INSTALL = YES;
249 | };
250 | name = Release;
251 | };
252 | /* End XCBuildConfiguration section */
253 |
254 | /* Begin XCConfigurationList section */
255 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNScreenshotPrevent" */ = {
256 | isa = XCConfigurationList;
257 | buildConfigurations = (
258 | 58B511ED1A9E6C8500147676 /* Debug */,
259 | 58B511EE1A9E6C8500147676 /* Release */,
260 | );
261 | defaultConfigurationIsVisible = 0;
262 | defaultConfigurationName = Release;
263 | };
264 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNScreenshotPrevent" */ = {
265 | isa = XCConfigurationList;
266 | buildConfigurations = (
267 | 58B511F01A9E6C8500147676 /* Debug */,
268 | 58B511F11A9E6C8500147676 /* Release */,
269 | );
270 | defaultConfigurationIsVisible = 0;
271 | defaultConfigurationName = Release;
272 | };
273 | /* End XCConfigurationList section */
274 | };
275 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
276 | }
277 |
--------------------------------------------------------------------------------
/ios/UIImage+ImageEffects.h:
--------------------------------------------------------------------------------
1 | /*
2 | File: UIImage+ImageEffects.h
3 | Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur.
4 | Version: 1.0
5 |
6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
7 | Inc. ("Apple") in consideration of your agreement to the following
8 | terms, and your use, installation, modification or redistribution of
9 | this Apple software constitutes acceptance of these terms. If you do
10 | not agree with these terms, please do not use, install, modify or
11 | redistribute this Apple software.
12 |
13 | In consideration of your agreement to abide by the following terms, and
14 | subject to these terms, Apple grants you a personal, non-exclusive
15 | license, under Apple's copyrights in this original Apple software (the
16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 | Software, with or without modifications, in source and/or binary forms;
18 | provided that if you redistribute the Apple Software in its entirety and
19 | without modifications, you must retain this notice and the following
20 | text and disclaimers in all such redistributions of the Apple Software.
21 | Neither the name, trademarks, service marks or logos of Apple Inc. may
22 | be used to endorse or promote products derived from the Apple Software
23 | without specific prior written permission from Apple. Except as
24 | expressly stated in this notice, no other rights or licenses, express or
25 | implied, are granted by Apple herein, including but not limited to any
26 | patent rights that may be infringed by your derivative works or by other
27 | works in which the Apple Software may be incorporated.
28 |
29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 |
35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 | POSSIBILITY OF SUCH DAMAGE.
43 |
44 | Copyright (C) 2013 Apple Inc. All Rights Reserved.
45 |
46 |
47 | Copyright © 2013 Apple Inc. All rights reserved.
48 | WWDC 2013 License
49 |
50 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013
51 | Session. Please refer to the applicable WWDC 2013 Session for further
52 | information.
53 |
54 | IMPORTANT: This Apple software is supplied to you by Apple Inc.
55 | ("Apple") in consideration of your agreement to the following terms, and
56 | your use, installation, modification or redistribution of this Apple
57 | software constitutes acceptance of these terms. If you do not agree with
58 | these terms, please do not use, install, modify or redistribute this
59 | Apple software.
60 |
61 | In consideration of your agreement to abide by the following terms, and
62 | subject to these terms, Apple grants you a non-exclusive license, under
63 | Apple's copyrights in this original Apple software (the "Apple
64 | Software"), to use, reproduce, modify and redistribute the Apple
65 | Software, with or without modifications, in source and/or binary forms;
66 | provided that if you redistribute the Apple Software in its entirety and
67 | without modifications, you must retain this notice and the following
68 | text and disclaimers in all such redistributions of the Apple Software.
69 | Neither the name, trademarks, service marks or logos of Apple Inc. may
70 | be used to endorse or promote products derived from the Apple Software
71 | without specific prior written permission from Apple. Except as
72 | expressly stated in this notice, no other rights or licenses, express or
73 | implied, are granted by Apple herein, including but not limited to any
74 | patent rights that may be infringed by your derivative works or by other
75 | works in which the Apple Software may be incorporated.
76 |
77 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES
78 | NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
79 | IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR
80 | A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
81 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
82 |
83 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
84 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
85 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
86 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
87 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
88 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
89 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
90 | POSSIBILITY OF SUCH DAMAGE.
91 |
92 | EA1002
93 | 5/3/2013
94 | */
95 |
96 | @import UIKit;
97 |
98 | @interface UIImage (ImageEffects)
99 |
100 | - (UIImage *)applyLightEffect;
101 | - (UIImage *)applyExtraLightEffect;
102 | - (UIImage *)applyDarkEffect;
103 | - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
104 |
105 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;
106 |
107 | - (UIImage *)rotateByDegrees:(CGFloat)degrees;
108 |
109 | + (UIImage *)imagePixelFromColor:(UIColor *)color;
110 | + (UIImage *)imageFromColor:(UIColor *)color withSize:(CGSize)size;
111 |
112 | @end
--------------------------------------------------------------------------------
/ios/UIImage+ImageEffects.m:
--------------------------------------------------------------------------------
1 | /*
2 | File: UIImage+ImageEffects.m
3 | Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur.
4 | Version: 1.0
5 |
6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
7 | Inc. ("Apple") in consideration of your agreement to the following
8 | terms, and your use, installation, modification or redistribution of
9 | this Apple software constitutes acceptance of these terms. If you do
10 | not agree with these terms, please do not use, install, modify or
11 | redistribute this Apple software.
12 |
13 | In consideration of your agreement to abide by the following terms, and
14 | subject to these terms, Apple grants you a personal, non-exclusive
15 | license, under Apple's copyrights in this original Apple software (the
16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 | Software, with or without modifications, in source and/or binary forms;
18 | provided that if you redistribute the Apple Software in its entirety and
19 | without modifications, you must retain this notice and the following
20 | text and disclaimers in all such redistributions of the Apple Software.
21 | Neither the name, trademarks, service marks or logos of Apple Inc. may
22 | be used to endorse or promote products derived from the Apple Software
23 | without specific prior written permission from Apple. Except as
24 | expressly stated in this notice, no other rights or licenses, express or
25 | implied, are granted by Apple herein, including but not limited to any
26 | patent rights that may be infringed by your derivative works or by other
27 | works in which the Apple Software may be incorporated.
28 |
29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 |
35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 | POSSIBILITY OF SUCH DAMAGE.
43 |
44 | Copyright (C) 2013 Apple Inc. All Rights Reserved.
45 |
46 |
47 | Copyright © 2013 Apple Inc. All rights reserved.
48 | WWDC 2013 License
49 |
50 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013
51 | Session. Please refer to the applicable WWDC 2013 Session for further
52 | information.
53 |
54 | IMPORTANT: This Apple software is supplied to you by Apple Inc.
55 | ("Apple") in consideration of your agreement to the following terms, and
56 | your use, installation, modification or redistribution of this Apple
57 | software constitutes acceptance of these terms. If you do not agree with
58 | these terms, please do not use, install, modify or redistribute this
59 | Apple software.
60 |
61 | In consideration of your agreement to abide by the following terms, and
62 | subject to these terms, Apple grants you a non-exclusive license, under
63 | Apple's copyrights in this original Apple software (the "Apple
64 | Software"), to use, reproduce, modify and redistribute the Apple
65 | Software, with or without modifications, in source and/or binary forms;
66 | provided that if you redistribute the Apple Software in its entirety and
67 | without modifications, you must retain this notice and the following
68 | text and disclaimers in all such redistributions of the Apple Software.
69 | Neither the name, trademarks, service marks or logos of Apple Inc. may
70 | be used to endorse or promote products derived from the Apple Software
71 | without specific prior written permission from Apple. Except as
72 | expressly stated in this notice, no other rights or licenses, express or
73 | implied, are granted by Apple herein, including but not limited to any
74 | patent rights that may be infringed by your derivative works or by other
75 | works in which the Apple Software may be incorporated.
76 |
77 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES
78 | NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
79 | IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR
80 | A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
81 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
82 |
83 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
84 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
85 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
86 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
87 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
88 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
89 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
90 | POSSIBILITY OF SUCH DAMAGE.
91 |
92 | EA1002
93 | 5/3/2013
94 | */
95 |
96 | #import "UIImage+ImageEffects.h"
97 |
98 | @import Accelerate;
99 | #import
100 |
101 |
102 | @implementation UIImage (ImageEffects)
103 |
104 |
105 | - (UIImage *)applyLightEffect
106 | {
107 | UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
108 | return [self applyBlurWithRadius:30 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
109 | }
110 |
111 |
112 | - (UIImage *)applyExtraLightEffect
113 | {
114 | UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
115 | return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
116 | }
117 |
118 |
119 | - (UIImage *)applyDarkEffect
120 | {
121 | UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
122 | return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
123 | }
124 |
125 |
126 | - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
127 | {
128 | const CGFloat EffectColorAlpha = 0.6;
129 | UIColor *effectColor = tintColor;
130 | int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
131 | if (componentCount == 2) {
132 | CGFloat b;
133 | if ([tintColor getWhite:&b alpha:NULL]) {
134 | effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
135 | }
136 | }
137 | else {
138 | CGFloat r, g, b;
139 | if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
140 | effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
141 | }
142 | }
143 | return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
144 | }
145 |
146 |
147 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
148 | {
149 | // Check pre-conditions.
150 | if (self.size.width < 1 || self.size.height < 1) {
151 | NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
152 | return nil;
153 | }
154 | if (!self.CGImage) {
155 | NSLog (@"*** error: image must be backed by a CGImage: %@", self);
156 | return nil;
157 | }
158 | if (maskImage && !maskImage.CGImage) {
159 | NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
160 | return nil;
161 | }
162 |
163 | CGRect imageRect = { CGPointZero, self.size };
164 | UIImage *effectImage = self;
165 |
166 | BOOL hasBlur = blurRadius > __FLT_EPSILON__;
167 | BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
168 | if (hasBlur || hasSaturationChange) {
169 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
170 | CGContextRef effectInContext = UIGraphicsGetCurrentContext();
171 | CGContextScaleCTM(effectInContext, 1.0, -1.0);
172 | CGContextTranslateCTM(effectInContext, 0, -self.size.height);
173 | CGContextDrawImage(effectInContext, imageRect, self.CGImage);
174 |
175 | vImage_Buffer effectInBuffer;
176 | effectInBuffer.data = CGBitmapContextGetData(effectInContext);
177 | effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
178 | effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
179 | effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
180 |
181 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
182 | CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
183 | vImage_Buffer effectOutBuffer;
184 | effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
185 | effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
186 | effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
187 | effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
188 |
189 | if (hasBlur) {
190 | // A description of how to compute the box kernel width from the Gaussian
191 | // radius (aka standard deviation) appears in the SVG spec:
192 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
193 | //
194 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
195 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
196 | // approximates the Gaussian kernel to within roughly 3%.
197 | //
198 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
199 | //
200 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
201 | //
202 | CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
203 | NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
204 | if (radius % 2 != 1) {
205 | radius += 1; // force radius to be odd so that the three box-blur methodology works.
206 | }
207 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
208 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
209 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
210 | }
211 | BOOL effectImageBuffersAreSwapped = NO;
212 | if (hasSaturationChange) {
213 | CGFloat s = saturationDeltaFactor;
214 | CGFloat floatingPointSaturationMatrix[] = {
215 | 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
216 | 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
217 | 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
218 | 0, 0, 0, 1,
219 | };
220 | const int32_t divisor = 256;
221 | NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
222 | int16_t saturationMatrix[matrixSize];
223 | for (NSUInteger i = 0; i < matrixSize; ++i) {
224 | saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
225 | }
226 | if (hasBlur) {
227 | vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
228 | effectImageBuffersAreSwapped = YES;
229 | }
230 | else {
231 | vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
232 | }
233 | }
234 | if (!effectImageBuffersAreSwapped)
235 | effectImage = UIGraphicsGetImageFromCurrentImageContext();
236 | UIGraphicsEndImageContext();
237 |
238 | if (effectImageBuffersAreSwapped)
239 | effectImage = UIGraphicsGetImageFromCurrentImageContext();
240 | UIGraphicsEndImageContext();
241 | }
242 |
243 | // Set up output context.
244 | UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
245 | CGContextRef outputContext = UIGraphicsGetCurrentContext();
246 | CGContextScaleCTM(outputContext, 1.0, -1.0);
247 | CGContextTranslateCTM(outputContext, 0, -self.size.height);
248 |
249 | // Draw base image.
250 | CGContextDrawImage(outputContext, imageRect, self.CGImage);
251 |
252 | // Draw effect image.
253 | if (hasBlur) {
254 | CGContextSaveGState(outputContext);
255 | if (maskImage) {
256 | CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
257 | }
258 | CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
259 | CGContextRestoreGState(outputContext);
260 | }
261 |
262 | // Add in color tint.
263 | if (tintColor) {
264 | CGContextSaveGState(outputContext);
265 | CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
266 | CGContextFillRect(outputContext, imageRect);
267 | CGContextRestoreGState(outputContext);
268 | }
269 |
270 | // Output image is ready.
271 | UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
272 | UIGraphicsEndImageContext();
273 |
274 | return outputImage;
275 | }
276 |
277 | - (UIImage *)rotateByDegrees:(CGFloat)degrees {
278 | UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.size.width, self.size.height)];
279 | CGAffineTransform t = CGAffineTransformMakeRotation(degrees * M_PI / 180.0f);
280 | rotatedViewBox.transform = t;
281 | CGSize rotatedSize = rotatedViewBox.frame.size;
282 |
283 | UIGraphicsBeginImageContext(rotatedSize);
284 | CGContextRef bitmap = UIGraphicsGetCurrentContext();
285 | CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
286 | CGContextRotateCTM(bitmap, degrees * M_PI / 180.0f);
287 | CGContextScaleCTM(bitmap, 1.0, -1.0);
288 | CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), self.CGImage);
289 |
290 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
291 | UIGraphicsEndImageContext();
292 | return newImage;
293 | }
294 |
295 | + (UIImage *)imagePixelFromColor:(UIColor *)color {
296 | return [UIImage imageFromColor:color withSize:CGSizeMake(1.0f, 1.0f)];
297 | }
298 |
299 | + (UIImage *)imageFromColor:(UIColor *)color withSize:(CGSize)size {
300 | CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
301 | UIGraphicsBeginImageContext(rect.size);
302 | CGContextRef context = UIGraphicsGetCurrentContext();
303 | CGContextSetFillColorWithColor(context, [color CGColor]);
304 | CGContextFillRect(context, rect);
305 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
306 | UIGraphicsEndImageContext();
307 | return image;
308 | }
309 |
310 | @end
--------------------------------------------------------------------------------
/lefthook.yml:
--------------------------------------------------------------------------------
1 | # EXAMPLE USAGE:
2 | #
3 | # Refer for explanation to following link:
4 | # https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
5 | #
6 | # pre-push:
7 | # commands:
8 | # packages-audit:
9 | # tags: frontend security
10 | # run: yarn audit
11 | # gems-audit:
12 | # tags: backend security
13 | # run: bundle audit
14 | #
15 | # pre-commit:
16 | # parallel: true
17 | # commands:
18 | # eslint:
19 | # glob: "*.{js,ts,jsx,tsx}"
20 | # run: yarn eslint {staged_files}
21 | # rubocop:
22 | # tags: backend style
23 | # glob: "*.rb"
24 | # exclude: "application.rb|routes.rb"
25 | # run: bundle exec rubocop --force-exclusion {all_files}
26 | # govet:
27 | # tags: backend style
28 | # files: git ls-files -m
29 | # glob: "*.go"
30 | # run: go vet {files}
31 | # scripts:
32 | # "hello.js":
33 | # runner: node
34 | # "any.go":
35 | # runner: go run
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-screenshot-prevent",
3 | "version": "1.2.1",
4 | "description": "This fork contains fully working blank screenshot on IOS13+ including screen recording",
5 | "main": "lib/module/index",
6 | "module": "lib/module/index",
7 | "types": "./src/index.d.ts",
8 | "react-native": "lib/module/index",
9 | "source": "./src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "*.podspec",
17 | "!ios/build",
18 | "!android/build",
19 | "!android/gradle",
20 | "!android/gradlew",
21 | "!android/gradlew.bat",
22 | "!android/local.properties",
23 | "!**/__tests__",
24 | "!**/__fixtures__",
25 | "!**/__mocks__",
26 | "!**/.*"
27 | ],
28 | "scripts": {
29 | "test": "jest",
30 | "typecheck": "tsc --noEmit",
31 | "prepare": "bob build",
32 | "lint": "eslint \"**/*.{js,ts,tsx}\""
33 | },
34 | "keywords": [
35 | "react-native",
36 | "ios",
37 | "android"
38 | ],
39 | "repository": "https://github.com/killserver/react-native-screenshot-prevent",
40 | "author": "killserver",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/killserver/react-native-screenshot-prevent/issues"
44 | },
45 | "homepage": "https://github.com/killserver/react-native-screenshot-prevent#readme",
46 | "devDependencies": {
47 | "@react-native-community/bob": "^0.10.1",
48 | "@commitlint/config-conventional": "^17.0.2",
49 | "@evilmartians/lefthook": "^1.2.2",
50 | "@react-native-community/eslint-config": "^3.0.2",
51 | "@release-it/conventional-changelog": "^5.0.0",
52 | "@types/jest": "^28.1.2",
53 | "@types/node": "^20.3.1",
54 | "@types/react": "~17.0.21",
55 | "@types/react-native": "0.70.0",
56 | "del-cli": "^5.0.0",
57 | "eslint": "^8.4.1",
58 | "eslint-config-prettier": "^8.5.0",
59 | "eslint-plugin-prettier": "^4.0.0",
60 | "jest": "^28.1.1",
61 | "pod-install": "^0.1.0",
62 | "prettier": "^2.0.5",
63 | "react": "18.2.0",
64 | "react-native": "0.71.7",
65 | "typescript": "^4.5.2"
66 | },
67 | "resolutions": {
68 | "@types/react": "17.0.21"
69 | },
70 | "peerDependencies": {
71 | "react": "*",
72 | "react-native": "*"
73 | },
74 | "engines": {
75 | "node": ">= 16.0.0"
76 | },
77 | "packageManager": "yarn@1.22.22+sha256.c17d3797fb9a9115bf375e31bfd30058cac6bc9c3b8807a3d8cb2094794b51ca",
78 | "jest": {
79 | "preset": "react-native",
80 | "modulePathIgnorePatterns": [
81 | "/lib/"
82 | ]
83 | },
84 | "commitlint": {
85 | "extends": [
86 | "@commitlint/config-conventional"
87 | ]
88 | },
89 | "eslintConfig": {
90 | "root": true,
91 | "extends": [
92 | "@react-native-community",
93 | "prettier"
94 | ],
95 | "rules": {
96 | "prettier/prettier": [
97 | "error",
98 | {
99 | "quoteProps": "consistent",
100 | "singleQuote": true,
101 | "tabWidth": 4,
102 | "trailingComma": "es5",
103 | "useTabs": true
104 | }
105 | ]
106 | }
107 | },
108 | "eslintIgnore": [
109 | "node_modules/",
110 | "lib/"
111 | ],
112 | "prettier": {
113 | "quoteProps": "consistent",
114 | "singleQuote": true,
115 | "tabWidth": 4,
116 | "trailingComma": "es5",
117 | "useTabs": true
118 | },
119 | "@react-native-community/bob": {
120 | "source": "src",
121 | "output": "lib",
122 | "targets": [
123 | "module",
124 | "typescript"
125 | ]
126 | },
127 | "codegenConfig": {
128 | "name": "RNScreenshotPreventSpec",
129 | "type": "modules",
130 | "jsSrcsDir": "src"
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react-native-screenshot-prevent' {
2 | export function enabled(value: boolean): void
3 | export function enableSecureView(imagePath?: string): void;
4 | export function disableSecureView(): void;
5 | export function usePreventScreenshot(): void;
6 | export function useDisableSecureView(): void;
7 | export function addListener(fn: Function): void;
8 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
2 | import { useEffect } from 'react';
3 |
4 | type FN = (resp: any) => void
5 | type Return = {
6 | readonly remove: () => void
7 | }
8 | let addListen: any, RNScreenshotPrevent: any;
9 | if (Platform.OS !== "web") {
10 | const { RNScreenshotPrevent: RNScreenshotPreventNative } = NativeModules;
11 | RNScreenshotPrevent = {
12 | ...RNScreenshotPreventNative,
13 | enableSecureView: function enableSecureView(imagePath: string = "") {
14 | RNScreenshotPreventNative.enableSecureView(imagePath)
15 | }
16 | }
17 | const eventEmitter = new NativeEventEmitter(RNScreenshotPrevent);
18 |
19 | /**
20 | * subscribes to userDidTakeScreenshot event
21 | * @param {function} callback handler
22 | * @returns {function} unsubscribe fn
23 | */
24 | addListen = (fn: FN): Return => {
25 | if (typeof (fn) !== 'function') {
26 | console.error('RNScreenshotPrevent: addListener requires valid callback function');
27 | return {
28 | remove: (): void => {
29 | console.error("RNScreenshotPrevent: remove not work because addListener requires valid callback function");
30 | }
31 | };
32 | }
33 |
34 | return eventEmitter.addListener("userDidTakeScreenshot", fn);
35 | }
36 | } else {
37 | RNScreenshotPrevent = {
38 | enabled: (enabled: boolean): void => {
39 | console.warn("RNScreenshotPrevent: enabled not work in web. value: " + enabled);
40 | },
41 | enableSecureView: (imagePath: string = ""): void => {
42 | console.warn("RNScreenshotPrevent: enableSecureView not work in web."+(!!imagePath ? " send: "+imagePath : ""));
43 | },
44 | disableSecureView: (): void => {
45 | console.warn("RNScreenshotPrevent: disableSecureView not work in web");
46 | }
47 | }
48 | addListen = (fn: FN): Return => {
49 | if (typeof (fn) !== 'function') {
50 | console.error('RNScreenshotPrevent: addListener requires valid callback function');
51 | return {
52 | remove: (): void => {
53 | console.error("RNScreenshotPrevent: remove not work because addListener requires valid callback function");
54 | }
55 | };
56 | }
57 | console.warn("RNScreenshotPrevent: addListener not work in web");
58 | return {
59 | remove: (): void => {
60 | console.warn("RNScreenshotPrevent: remove addListener not work in web");
61 | }
62 | }
63 | }
64 | }
65 |
66 | export const usePreventScreenshot = () => {
67 | useEffect(() => {
68 | RNScreenshotPrevent.enabled(true);
69 | return () => {
70 | RNScreenshotPrevent.enabled(false);
71 | };
72 | }, []);
73 | }
74 |
75 | export const useDisableSecureView = (imagePath: string = "") => {
76 | useEffect(() => {
77 | RNScreenshotPrevent.enableSecureView(imagePath);
78 | return () => {
79 | RNScreenshotPrevent.disableSecureView();
80 | };
81 | }, []);
82 | }
83 |
84 | export const enabled: (enabled: boolean) => void = RNScreenshotPrevent.enabled
85 | export const enableSecureView: (imagePath?: string) => void = RNScreenshotPrevent.enableSecureView
86 | export const disableSecureView: () => void = RNScreenshotPrevent.disableSecureView
87 | export const addListener: (fn: FN) => void = addListen
88 | export default RNScreenshotPrevent;
89 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "./tsconfig",
4 | "exclude": ["example"]
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "rootDir": "./src",
5 | "paths": {
6 | "react-native-screenshot-prevent": ["./src/index"]
7 | },
8 | "ignoreDeprecations": "5.0",
9 | "allowUnreachableCode": false,
10 | "allowUnusedLabels": false,
11 | "allowJs": true,
12 | "esModuleInterop": true,
13 | "importsNotUsedAsValues": "error",
14 | "forceConsistentCasingInFileNames": true,
15 | "jsx": "react",
16 | "lib": ["esnext","ES6","DOM"],
17 | "types": ["react"],
18 | "module": "esnext",
19 | "moduleResolution": "node",
20 | "noFallthroughCasesInSwitch": true,
21 | "noImplicitReturns": true,
22 | "noImplicitUseStrict": false,
23 | "noStrictGenericChecks": false,
24 | "noUncheckedIndexedAccess": true,
25 | "noUnusedLocals": true,
26 | "noUnusedParameters": true,
27 | "resolveJsonModule": true,
28 | "skipLibCheck": true,
29 | "strict": true,
30 | "target": "esnext"
31 | },
32 | "include": ["src/**/*"],
33 | "exclude": ["lib"]
34 | }
35 |
--------------------------------------------------------------------------------