├── example ├── .watchmanconfig ├── tsconfig.json ├── babel.config.js ├── android │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── index.js ├── .gitignore ├── ios │ └── Podfile ├── macos │ └── Podfile ├── visionos │ └── Podfile ├── app.json ├── windows │ └── .gitignore ├── package.json └── App.tsx ├── windows ├── Clipboard │ ├── pch.cpp │ ├── codegen │ │ ├── .clang-format │ │ └── NativeClipboardModuleSpec.g.h │ ├── Clipboard.rc │ ├── resource.h │ ├── Clipboard.def │ ├── packages.config │ ├── ReactPackageProvider.idl │ ├── targetver.h │ ├── PropertySheet.props │ ├── ReactPackageProvider.cpp │ ├── ReactPackageProvider.h │ ├── pch.h │ ├── readme.txt │ ├── Clipboard.h │ ├── Clipboard.cpp │ ├── Utilities.h │ ├── Clipboard.vcxproj.filters │ ├── Unicode.h │ ├── Unicode.cpp │ └── Clipboard.vcxproj ├── .gitignore ├── README.md └── Clipboard.sln ├── .yarnrc.yml ├── .gitattributes ├── android ├── gradle │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ └── .project ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── reactnativecommunity │ │ │ └── clipboard │ │ │ ├── BUCK │ │ │ ├── ClipboardPackage.java │ │ │ └── ClipboardModule.java │ └── paper │ │ └── java │ │ └── com │ │ └── reactnativecommunity │ │ └── clipboard │ │ └── NativeClipboardModuleSpec.java ├── gradle.properties ├── .classpath ├── .settings │ └── org.eclipse.buildship.core.prefs ├── .project └── build.gradle ├── src ├── index.ts ├── Clipboard.web.ts ├── useClipboard.ts ├── Clipboard.ts └── NativeClipboardModule.ts ├── macos ├── config │ ├── RNCClipboard.release.xcconfig │ ├── RNCClipboard.debug.xcconfig │ └── RNCClipboard.shared.xcconfig ├── RNCClipboard.h ├── RNCClipboard.m └── RNCClipboard.xcodeproj │ └── project.pbxproj ├── .github ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .releaserc ├── NuGet.config ├── ios ├── RNCClipboard.h ├── RNCClipboard.mm └── RNCClipboard.xcodeproj │ └── project.pbxproj ├── __tests__ └── mock.test.js ├── biome.json ├── metro.config.js ├── jest └── clipboard-mock.js ├── tsconfig.json ├── LICENSE ├── react-native.config.js ├── .gitignore ├── RNCClipboard.podspec ├── CONTRIBUTING.md ├── package.json └── README.md /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /windows/Clipboard/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /windows/Clipboard/codegen/.clang-format: -------------------------------------------------------------------------------- 1 | DisableFormat: true 2 | SortIncludes: false -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.1.1.cjs 4 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf 4 | 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["module:@react-native/babel-preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /android/gradle/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /windows/Clipboard/Clipboard.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-clipboard/clipboard/HEAD/windows/Clipboard/Clipboard.rc -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Clipboard } from "./Clipboard"; 2 | export { useClipboard } from "./useClipboard"; 3 | export default Clipboard; 4 | -------------------------------------------------------------------------------- /windows/Clipboard/resource.h: -------------------------------------------------------------------------------- 1 | // 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Clipboard.rc 4 | 5 | #pragma once 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-clipboard/clipboard/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /macos/config/RNCClipboard.release.xcconfig: -------------------------------------------------------------------------------- 1 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym" 2 | ENABLE_NS_ASSERTIONS = NO 3 | MTL_ENABLE_DEBUG_INFO = NO 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/react-native-clipboard/clipboard/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /windows/Clipboard/Clipboard.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE 3 | DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💬 Question 3 | about: You need help with the library. 4 | labels: 'question' 5 | --- 6 | 7 | ## Ask your Question 8 | 9 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | ReactNativeClipBoard_compileSdkVersion=30 2 | ReactNativeClipBoard_buildToolsVersion=30.0.2 3 | ReactNativeClipBoard_targetSdkVersion=30 4 | ReactNativeClipBoard_minSdkVersion=16 -------------------------------------------------------------------------------- /windows/Clipboard/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import { AppRegistry } from "react-native"; 6 | import App from "./App"; 7 | import { name as appName } from "./app.json"; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | *.binlog 2 | *.hprof 3 | *.xcworkspace/ 4 | *.zip 5 | .DS_Store 6 | .gradle/ 7 | .idea/ 8 | .vs/ 9 | .xcode.env 10 | Pods/ 11 | build/ 12 | dist/* 13 | !dist/.gitignore 14 | local.properties 15 | msbuild.binlog 16 | node_modules/ 17 | -------------------------------------------------------------------------------- /windows/Clipboard/ReactPackageProvider.idl: -------------------------------------------------------------------------------- 1 | namespace Clipboard 2 | { 3 | [webhosthidden] 4 | [default_interface] 5 | runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider 6 | { 7 | ReactPackageProvider(); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /macos/config/RNCClipboard.debug.xcconfig: -------------------------------------------------------------------------------- 1 | DEBUG_INFORMATION_FORMAT = dwarf 2 | ENABLE_TESTABILITY = YES 3 | GCC_DYNAMIC_NO_PIC = NO 4 | GCC_OPTIMIZATION_LEVEL = 0 5 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1 6 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE 7 | ONLY_ACTIVE_ARCH = YES 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 08 22:19:11 CET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/test_app.rb" 6 | 7 | workspace 'Example.xcworkspace' 8 | 9 | use_test_app! 10 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb" 6 | 7 | workspace 'Example.xcworkspace' 8 | 9 | use_test_app! 10 | -------------------------------------------------------------------------------- /macos/RNCClipboard.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | @interface RNCClipboard : NSObject 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /windows/Clipboard/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /example/visionos/Podfile: -------------------------------------------------------------------------------- 1 | ws_dir = Pathname.new(__dir__) 2 | ws_dir = ws_dir.parent until 3 | File.exist?("#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb") || 4 | ws_dir.expand_path.to_s == '/' 5 | require "#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb" 6 | 7 | workspace 'Example.xcworkspace' 8 | 9 | use_test_app! 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ✨ Feature request 3 | about: Suggest an idea. 4 | labels: 'enhancement' 5 | --- 6 | 7 | ## Describe the Feature 8 | 9 | 10 | ## Possible Implementations 11 | 12 | 13 | ## Related Issues 14 | -------------------------------------------------------------------------------- /android/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | "@semantic-release/npm", 6 | "@semantic-release/github", 7 | [ 8 | "@semantic-release/git", 9 | { 10 | "assets": "package.json", 11 | "message": "chore(release): ${nextRelease.version} [skip ci] \n\n${nextRelease.notes}" 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | 4 | 5 | 6 | # Test Plan 7 | 8 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments= 2 | auto.sync=false 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2)) 5 | connection.project.dir=../example/android 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /ios/RNCClipboard.h: -------------------------------------------------------------------------------- 1 | #ifndef RNCClipboard_h 2 | #define RNCClipboard_h 3 | 4 | #ifdef RCT_NEW_ARCH_ENABLED 5 | #import 6 | #else 7 | #import 8 | #endif 9 | 10 | #import 11 | 12 | @interface RNCClipboard : RCTEventEmitter 13 | 14 | #ifdef RCT_NEW_ARCH_ENABLED 15 | 16 | #else 17 | 18 | #endif 19 | @end 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example", 3 | "displayName": "Example", 4 | "components": [ 5 | { 6 | "appKey": "Example", 7 | "displayName": "Example" 8 | } 9 | ], 10 | "resources": { 11 | "android": ["dist/res", "dist/main.android.jsbundle"], 12 | "ios": ["dist/assets", "dist/main.ios.jsbundle"], 13 | "macos": ["dist/assets", "dist/main.macos.jsbundle"], 14 | "visionos": ["dist/assets", "dist/main.visionos.jsbundle"], 15 | "windows": ["dist/assets", "dist/main.windows.bundle"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | 3 | # User-specific files 4 | *.suo 5 | *.user 6 | *.sln.docstates 7 | 8 | # Build results 9 | ARM64/ 10 | AppPackages/ 11 | [Bb]in/ 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Oo]bj/ 15 | [Rr]elease/ 16 | [Rr]eleases/ 17 | bld/ 18 | build/ 19 | x64/ 20 | x86/ 21 | 22 | # NuGet Packages Directory 23 | packages/ 24 | 25 | **/Generated Files/** 26 | *.binlog 27 | *.hprof 28 | *.sln 29 | ExperimentalFeatures.props 30 | NuGet.Config 31 | dist/ 32 | msbuild.binlog 33 | node_modules/ 34 | -------------------------------------------------------------------------------- /android/gradle/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | gradle 4 | Project gradle created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /__tests__/mock.test.js: -------------------------------------------------------------------------------- 1 | const mockClipboard = require("../jest/clipboard-mock"); 2 | 3 | jest.mock("../dist/index.js", () => mockClipboard); 4 | 5 | describe("mockClipboard", () => { 6 | const { getString, useClipboard } = require("../dist/index.js"); 7 | 8 | it("can get mock string", async () => { 9 | const result = await getString(); 10 | const expected = "mockString"; 11 | expect(result).toBe(expected); 12 | }); 13 | 14 | it("can get useClipboardHook", async () => { 15 | const [result] = useClipboard(); 16 | const expected = "mockString"; 17 | expect(result).toBe(expected); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": ["dist", "example/ios", "example/android"] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "tab" 15 | }, 16 | "organizeImports": { 17 | "enabled": true 18 | }, 19 | "linter": { 20 | "enabled": true, 21 | "rules": { 22 | "recommended": true 23 | } 24 | }, 25 | "javascript": { 26 | "formatter": { 27 | "quoteStyle": "double" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | 4 | #OS junk files 5 | [Tt]humbs.db 6 | *.DS_Store 7 | 8 | #Visual Studio files 9 | *.[Oo]bj 10 | *.user 11 | *.aps 12 | *.pch 13 | *.vspscc 14 | *.vssscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.[Cc]ache 23 | *.ilk 24 | *.log 25 | *.lib 26 | *.sbr 27 | *.sdf 28 | *.opensdf 29 | *.opendb 30 | *.unsuccessfulbuild 31 | ipch/ 32 | [Oo]bj/ 33 | [Bb]in 34 | [Dd]ebug*/ 35 | [Rr]elease*/ 36 | Ankh.NoLoad 37 | .vs/ 38 | # Visual C++ cache files 39 | 40 | #Files generated by the VS build 41 | **/Generated Files/** 42 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | google() 6 | } 7 | } 8 | 9 | rootProject.name = "Example" 10 | 11 | apply(from: { 12 | def searchDir = rootDir.toPath() 13 | do { 14 | def p = searchDir.resolve("node_modules/react-native-test-app/test-app.gradle") 15 | if (p.toFile().exists()) { 16 | return p.toRealPath().toString() 17 | } 18 | } while (searchDir = searchDir.getParent()) 19 | throw new GradleException("Could not find `react-native-test-app`"); 20 | }()) 21 | applyTestAppSettings(settings) 22 | -------------------------------------------------------------------------------- /windows/Clipboard/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /windows/Clipboard/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "ReactPackageProvider.h" 4 | #if __has_include("ReactPackageProvider.g.cpp") 5 | #include "ReactPackageProvider.g.cpp" 6 | #endif 7 | 8 | #include "Clipboard.h" 9 | 10 | using namespace winrt::Microsoft::ReactNative; 11 | 12 | namespace winrt::Clipboard::implementation 13 | { 14 | 15 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept 16 | { 17 | #ifdef USE_FABRIC 18 | AddAttributedModules(packageBuilder, true); 19 | #else 20 | AddAttributedModules(packageBuilder); 21 | #endif 22 | } 23 | 24 | } // namespace winrt::Clipboard::implementation 25 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require("node:path"); 2 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 3 | 4 | const { makeMetroConfig } = require("@rnx-kit/metro-config"); 5 | module.exports = mergeConfig( 6 | getDefaultConfig(__dirname), 7 | makeMetroConfig({ 8 | projectRoot: path.join(__dirname, "example"), 9 | watchFolders: [__dirname], 10 | resolver: { 11 | extraNodeModules: { 12 | "@react-native-clipboard/clipboard": __dirname, 13 | }, 14 | }, 15 | transformer: { 16 | getTransformOptions: async () => ({ 17 | transform: { 18 | experimentalImportSupport: false, 19 | inlineRequires: false, 20 | }, 21 | }), 22 | }, 23 | }), 24 | ); 25 | -------------------------------------------------------------------------------- /jest/clipboard-mock.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | /* eslint-env jest */ 5 | 6 | const ClipboardMock = { 7 | getString: jest.fn().mockResolvedValue("mockString"), 8 | getImagePNG: jest.fn(), 9 | getImageJPG: jest.fn(), 10 | setImage: jest.fn(), 11 | setString: jest.fn(), 12 | hasString: jest.fn().mockResolvedValue(true), 13 | hasImage: jest.fn().mockResolvedValue(true), 14 | hasURL: jest.fn().mockResolvedValue(true), 15 | addListener: jest.fn(), 16 | removeAllListeners: jest.fn(), 17 | getEnforcing: jest.fn(), 18 | }; 19 | 20 | const useClipboard = jest.fn(() => ["mockString", jest.fn()]); 21 | 22 | const RNCClipboardMock = { 23 | ...ClipboardMock, 24 | useClipboard, 25 | }; 26 | 27 | module.exports = RNCClipboardMock; 28 | -------------------------------------------------------------------------------- /windows/Clipboard/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ReactPackageProvider.g.h" 4 | 5 | using namespace winrt::Microsoft::ReactNative; 6 | 7 | namespace winrt::Clipboard::implementation 8 | { 9 | 10 | struct ReactPackageProvider : ReactPackageProviderT 11 | { 12 | ReactPackageProvider() = default; 13 | 14 | void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; 15 | }; 16 | 17 | } // namespace winrt::Clipboard::implementation 18 | 19 | namespace winrt::Clipboard::factory_implementation 20 | { 21 | 22 | struct ReactPackageProvider : ReactPackageProviderT {}; 23 | 24 | } // namespace winrt::Clipboard::factory_implementation 25 | -------------------------------------------------------------------------------- /windows/Clipboard/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define NOMINMAX 1 11 | #define WIN32_LEAN_AND_MEAN 1 12 | #define WINRT_LEAN_AND_MEAN 1 13 | 14 | // Windows Header Files 15 | #include 16 | #undef GetCurrentTime 17 | #include 18 | 19 | // WinRT Header Files 20 | #include 21 | #include 22 | #include 23 | 24 | // C RunTime Header Files 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | // Reference additional headers your project requires here 31 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativecommunity/clipboard/BUCK: -------------------------------------------------------------------------------- 1 | load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") 2 | 3 | rn_android_library( 4 | name = "clipboard", 5 | srcs = glob(["**/*.java"]), 6 | visibility = [ 7 | "PUBLIC", 8 | ], 9 | deps = [ 10 | react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), 11 | react_native_dep("third-party/java/infer-annotations:infer-annotations"), 12 | react_native_dep("third-party/java/jsr-305:jsr-305"), 13 | react_native_target("java/com/facebook/react/bridge:bridge"), 14 | react_native_target("java/com/facebook/react/common:common"), 15 | react_native_target("java/com/facebook/react/module/annotations:annotations"), 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Report a bug 3 | about: Report a reproducible or regression bug.' 4 | labels: 'bug' 5 | --- 6 | 7 | ## Environment 8 | 9 | 10 | 11 | ## Platforms 12 | 13 | 14 | 15 | ## Versions 16 | 17 | - Android: 18 | - iOS: 19 | - react-native-netinfo: 20 | - react-native: 21 | - react: 22 | 23 | ## Description 24 | 25 | 26 | 27 | ## Reproducible Demo 28 | 29 | 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, 5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 6 | "rootDir": "src", 7 | "outDir": "dist", 8 | "lib": ["es2016", "dom"], 9 | "jsx": "react", 10 | "declaration": true, 11 | /* Strict Type-Checking Options */ 12 | "strict": true /* Enable all strict type-checking options. */, 13 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 14 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 15 | }, 16 | "exclude": ["example", "dist"], 17 | "include": ["src"] 18 | } 19 | -------------------------------------------------------------------------------- /src/Clipboard.web.ts: -------------------------------------------------------------------------------- 1 | declare let navigator: { 2 | clipboard: { 3 | readText(): Promise; 4 | writeText(data: string): Promise; 5 | }; 6 | }; 7 | 8 | export const Clipboard = { 9 | getString(): Promise { 10 | if (navigator?.clipboard) { 11 | return navigator.clipboard.readText(); 12 | } 13 | const el = document.createElement("textarea"); 14 | document.body.appendChild(el); 15 | el.select(); 16 | document.execCommand("paste"); 17 | const value = el.innerText; 18 | document.body.removeChild(el); 19 | return Promise.resolve(value); 20 | }, 21 | 22 | setString(content: string) { 23 | if (navigator?.clipboard) { 24 | navigator.clipboard.writeText(content); 25 | } else { 26 | const el = document.createElement("textarea"); 27 | el.value = content; 28 | document.body.appendChild(el); 29 | el.select(); 30 | document.execCommand("copy"); 31 | document.body.removeChild(el); 32 | } 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /windows/README.md: -------------------------------------------------------------------------------- 1 | # React Native Clipboard (Windows) 2 | 3 | React Native Clipboard is currently maintained for React Native Windows (RNW) >= 0.62. 4 | 5 | # Local Development Setup (RNW >= 0.62) 6 | 7 | In order to work on _Clipboard_, you'll need to install the [Windows Development Dependencies](https://aka.ms/rnw-deps). 8 | 9 | In addition, `clipboard` targets React Native 0.61.5 and doesn't include React Native Windows as a dependency. So in order to build `Clipboard` locally you'll need to temporarily upgrade the development dependencies: 10 | 11 | ## RNW >= 0.63 12 | 13 | ``` 14 | yarn upgrade react-native@^0.63 15 | yarn add react-native-windows@^0.63 --dev 16 | ``` 17 | 18 | Now you should be able to open `Clipboard.sln` in Visual Studio and build the project. 19 | 20 | ## RNW 0.62 21 | 22 | ``` 23 | yarn upgrade react-native@^0.62 24 | yarn add react-native-windows@^0.62 --dev 25 | ``` 26 | 27 | Now you should be able to open `Clipboard.sln` in Visual Studio and build the project. 28 | -------------------------------------------------------------------------------- /src/useClipboard.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * useClipboard.ts 3 | * This code is inspired from the @react-native-community/hooks package 4 | * All credit goes to author of the useClipboard custom hooks. 5 | * https://github.com/react-native-community/hooks 6 | */ 7 | 8 | import { useEffect, useState } from "react"; 9 | import { Clipboard } from "./Clipboard"; 10 | 11 | type Listener = (content: string) => void; 12 | const listeners = new Set(); 13 | 14 | function setString(content: string) { 15 | Clipboard.setString(content); 16 | for (const listener of listeners) { 17 | listener(content); 18 | } 19 | } 20 | 21 | export const useClipboard = (): [string, (content: string) => void] => { 22 | const [data, updateClipboardData] = useState(""); 23 | 24 | useEffect(() => { 25 | Clipboard.getString().then(updateClipboardData); 26 | }, []); 27 | 28 | useEffect(() => { 29 | listeners.add(updateClipboardData); 30 | 31 | return () => { 32 | listeners.delete(updateClipboardData); 33 | }; 34 | }, []); 35 | 36 | return [data, setString]; 37 | }; 38 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | @react-native-community_clipboard 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | 25 | 0 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-present, Facebook, Inc. 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. -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | const project = (() => { 2 | const fs = require("node:fs"); 3 | const path = require("node:path"); 4 | try { 5 | const { configureProjects } = require("react-native-test-app"); 6 | 7 | return configureProjects({ 8 | android: { 9 | sourceDir: path.join("example", "android"), 10 | }, 11 | ios: { 12 | sourceDir: "example/ios", 13 | }, 14 | windows: { 15 | sourceDir: path.join("example", "windows"), 16 | solutionFile: path.join("example", "windows", "Example.sln"), 17 | }, 18 | }); 19 | } catch (e) { 20 | return undefined; 21 | } 22 | })(); 23 | 24 | module.exports = { 25 | dependencies: { 26 | // Help rn-cli find and autolink this library 27 | "@react-native-clipboard/clipboard": { 28 | root: __dirname, 29 | }, 30 | }, 31 | dependency: { 32 | platforms: { 33 | windows: { 34 | sourceDir: "windows", 35 | solutionFile: "Clipboard.sln", 36 | projects: [ 37 | { 38 | projectFile: "Clipboard/Clipboard.vcxproj", 39 | directDependency: true, 40 | }, 41 | ], 42 | }, 43 | }, 44 | }, 45 | ...(project ? { project } : undefined), 46 | }; 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # typescript build file 59 | dist 60 | 61 | # eslint cache 62 | .eslintcache 63 | 64 | # yarn 65 | .yarn/install-state.gz -------------------------------------------------------------------------------- /macos/RNCClipboard.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "RNCClipboard.h" 9 | 10 | @implementation RNCClipboard 11 | 12 | RCT_EXPORT_MODULE() 13 | 14 | - (dispatch_queue_t)methodQueue 15 | { 16 | return dispatch_get_main_queue(); 17 | } 18 | 19 | 20 | RCT_EXPORT_METHOD(setString:(NSString *)content) 21 | { 22 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 23 | [pasteboard clearContents]; 24 | [pasteboard setString:(content ? : @"") forType:NSPasteboardTypeString]; 25 | } 26 | 27 | RCT_EXPORT_METHOD(getString:(RCTPromiseResolveBlock)resolve 28 | rejecter:(__unused RCTPromiseRejectBlock)reject) 29 | { 30 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 31 | resolve(([pasteboard stringForType:NSPasteboardTypeString] ? : @"")); 32 | } 33 | 34 | RCT_EXPORT_METHOD(addListener : (NSString *)eventName) { 35 | // Keep: Required for RN built in Event Emitter Calls. 36 | } 37 | 38 | RCT_EXPORT_METHOD(removeListeners : (double)count) { 39 | // Keep: Required for RN built in Event Emitter Calls. 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /windows/Clipboard/readme.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | C++/WinRT Clipboard Project Overview 3 | ======================================================================== 4 | 5 | This project demonstrates how to get started authoring Windows Runtime 6 | classes directly with standard C++, using the C++/WinRT SDK component 7 | to generate implementation headers from interface (IDL) files. The 8 | generated Windows Runtime component binary and WinMD files should then 9 | be bundled with the Universal Windows Platform (UWP) app consuming them. 10 | 11 | Steps: 12 | 1. Create an interface (IDL) file to define your Windows Runtime class, 13 | its default interface, and any other interfaces it implements. 14 | 2. Build the project once to generate module.g.cpp, module.h.cpp, and 15 | implementation templates under the "Generated Files" folder, as 16 | well as skeleton class definitions under "Generated Files\sources". 17 | 3. Use the skeleton class definitions for reference to implement your 18 | Windows Runtime classes. 19 | 20 | ======================================================================== 21 | Learn more about C++/WinRT here: 22 | http://aka.ms/cppwinrt/ 23 | ======================================================================== 24 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | apply(from: { 3 | def searchDir = rootDir.toPath() 4 | do { 5 | def p = searchDir.resolve("node_modules/react-native-test-app/android/dependencies.gradle") 6 | if (p.toFile().exists()) { 7 | return p.toRealPath().toString() 8 | } 9 | } while (searchDir = searchDir.getParent()) 10 | throw new GradleException("Could not find `react-native-test-app`"); 11 | }()) 12 | 13 | repositories { 14 | mavenCentral() 15 | google() 16 | } 17 | 18 | dependencies { 19 | getReactNativeDependencies().each { dependency -> 20 | classpath(dependency) 21 | } 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | maven { 28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 29 | url({ 30 | def searchDir = rootDir.toPath() 31 | do { 32 | def p = searchDir.resolve("node_modules/react-native/android") 33 | if (p.toFile().exists()) { 34 | return p.toRealPath().toString() 35 | } 36 | } while (searchDir = searchDir.getParent()) 37 | throw new GradleException("Could not find `react-native`"); 38 | }()) 39 | } 40 | mavenCentral() 41 | google() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RNCClipboard.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' 6 | 7 | Pod::Spec.new do |s| 8 | s.name = "RNCClipboard" 9 | s.version = package['version'] 10 | s.summary = package['description'] 11 | s.license = package['license'] 12 | 13 | s.authors = package['author'] 14 | s.homepage = package['homepage'] 15 | 16 | s.source = { :git => "https://github.com/react-native-clipboard/clipboard", :tag => "v#{s.version}" } 17 | s.ios.source_files = "ios/**/*.{h,m,mm}" 18 | s.osx.source_files = "macos/**/*.{h,m,mm}" 19 | s.visionos.source_files = "ios/**/*.{h,m,mm}" 20 | s.frameworks = 'CoreServices' 21 | 22 | if fabric_enabled 23 | folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' 24 | 25 | s.pod_target_xcconfig = { 26 | 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"', 27 | 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', 28 | } 29 | s.platforms = { ios: '11.0', tvos: '11.0', :osx => "10.14", :visionos => "1.0" } 30 | s.compiler_flags = folly_compiler_flags + ' -DRCT_NEW_ARCH_ENABLED' 31 | 32 | install_modules_dependencies(s) 33 | else 34 | s.platforms = { :ios => "9.0", :tvos => "9.0", :osx => "10.14", :visionos => "1.0" } 35 | 36 | s.dependency "React-Core" 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /windows/Clipboard/Clipboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pch.h" 4 | #include "resource.h" 5 | 6 | #if __has_include("codegen/NativeClipboardDataTypes.g.h") 7 | #include "codegen/NativeClipboardDataTypes.g.h" 8 | #endif 9 | #include "codegen/NativeClipboardModuleSpec.g.h" 10 | 11 | #include 12 | #include 13 | #include "NativeModules.h" 14 | 15 | #include 16 | #include 17 | 18 | using namespace winrt::Microsoft::ReactNative; 19 | using namespace winrt::Windows::Foundation; 20 | namespace datatransfer = winrt::Windows::ApplicationModel::DataTransfer; 21 | 22 | namespace winrt::Clipboard 23 | { 24 | 25 | REACT_MODULE(ClipboardModule, L"RNCClipboard"); 26 | struct ClipboardModule 27 | { 28 | REACT_INIT(Initialize); 29 | void Initialize(const winrt::Microsoft::ReactNative::ReactContext& reactContext) noexcept { 30 | m_context = reactContext; 31 | } 32 | 33 | REACT_METHOD(GetString, L"getString"); 34 | void GetString(React::ReactPromise&& result) noexcept; 35 | 36 | REACT_METHOD(SetString, L"setString"); 37 | void SetString(std::string const& str) noexcept; 38 | 39 | REACT_METHOD(AddListener, L"addListener"); 40 | void AddListener(std::string const& event) noexcept; 41 | 42 | REACT_METHOD(RemoveListeners, L"removeListeners"); 43 | void RemoveListeners(int count) noexcept; 44 | 45 | private: 46 | int _listenerCount = 0; 47 | winrt::Microsoft::ReactNative::ReactContext m_context; 48 | }; 49 | 50 | } // namespace winrt::Clipboard -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "build:android": "npm run mkdist && react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res", 8 | "build:ios": "npm run mkdist && react-native bundle --entry-file index.js --platform ios --dev true --bundle-output dist/main.ios.jsbundle --assets-dest dist", 9 | "ios": "react-native run-ios", 10 | "mkdist": "node -e \"require('node:fs').mkdirSync('dist', { recursive: true, mode: 0o755 })\"", 11 | "start": "react-native start", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "@react-native-clipboard/clipboard": "1.15.0", 16 | "react": "18.3.1", 17 | "react-native": "^0.75.3", 18 | "react-native-windows": "^0.75.0" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.25.2", 22 | "@babel/preset-env": "^7.25.3", 23 | "@babel/runtime": "^7.25.0", 24 | "@react-native-community/cli": "^15.0.1", 25 | "@react-native-community/cli-platform-android": "^15.0.1", 26 | "@react-native-community/cli-platform-ios": "^15.0.1", 27 | "@react-native/babel-preset": "^0.75.3", 28 | "@react-native/metro-config": "^0.75.3", 29 | "@react-native/typescript-config": "^0.75.3", 30 | "@rnx-kit/metro-config": "^2.0.0", 31 | "@types/react": "^18.2.6", 32 | "@types/react-test-renderer": "^18.0.0", 33 | "babel-jest": "^29.6.3", 34 | "jest": "^29.6.3", 35 | "react-native-test-app": "^4.0.5", 36 | "react-test-renderer": "^18.3.1", 37 | "typescript": "5.0.4" 38 | }, 39 | "engines": { 40 | "node": ">=18" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /windows/Clipboard/Clipboard.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "Clipboard.h" 4 | #include "Unicode.h" 5 | 6 | namespace winrt::Clipboard 7 | { 8 | 9 | // See https://microsoft.github.io/react-native-windows/docs/native-modules for details on writing native modules 10 | 11 | void ClipboardModule::GetString(React::ReactPromise&& promise) noexcept { 12 | auto dataPackageView = datatransfer::Clipboard::GetContent(); 13 | if (dataPackageView.Contains(datatransfer::StandardDataFormats::Text())) { 14 | dataPackageView.GetTextAsync().Completed([promise, dataPackageView](IAsyncOperation info, AsyncStatus status) { 15 | if (status == AsyncStatus::Completed) { 16 | auto text = winrt::to_string(info.GetResults()); 17 | promise.Resolve(text); 18 | } 19 | else { 20 | promise.Reject("Failure"); 21 | } 22 | }); 23 | return; 24 | } 25 | promise.Resolve(""); 26 | } 27 | 28 | void ClipboardModule::SetString(std::string const& str) noexcept 29 | { 30 | m_context.UIDispatcher().Post([str](){ 31 | datatransfer::DataPackage dataPackage{}; 32 | dataPackage.SetText(winrt::to_hstring(str)); 33 | datatransfer::Clipboard::SetContent(dataPackage); 34 | }); 35 | } 36 | 37 | void ClipboardModule::AddListener(std::string const& event) noexcept 38 | { 39 | _listenerCount++; 40 | } 41 | 42 | void ClipboardModule::RemoveListeners(int count) noexcept 43 | { 44 | _listenerCount--; 45 | if (_listenerCount == 0) { 46 | // Disconnect any native eventing here 47 | } 48 | } 49 | 50 | } // namespace winrt::Clipboard -------------------------------------------------------------------------------- /macos/config/RNCClipboard.shared.xcconfig: -------------------------------------------------------------------------------- 1 | PRODUCT_NAME = $(TARGET_NAME) 2 | 3 | ALWAYS_SEARCH_USER_PATHS = NO 4 | CLANG_ANALYZER_NONNULL = YES 5 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE 6 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14" 7 | CLANG_CXX_LIBRARY = "libc++" 8 | CLANG_ENABLE_MODULES = YES 9 | CLANG_ENABLE_OBJC_ARC = YES 10 | CLANG_ENABLE_OBJC_WEAK = YES 11 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES 12 | CLANG_WARN_BOOL_CONVERSION = YES 13 | CLANG_WARN_COMMA = YES 14 | CLANG_WARN_CONSTANT_CONVERSION = YES 15 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES 16 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR 17 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES 18 | CLANG_WARN_EMPTY_BODY = YES 19 | CLANG_WARN_ENUM_CONVERSION = YES 20 | CLANG_WARN_INFINITE_RECURSION = YES 21 | CLANG_WARN_INT_CONVERSION = YES 22 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES 23 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 24 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES 25 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR 26 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES 27 | CLANG_WARN_STRICT_PROTOTYPES = YES 28 | CLANG_WARN_SUSPICIOUS_MOVE = YES 29 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 30 | CLANG_WARN_UNREACHABLE_CODE = YES 31 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 32 | COPY_PHASE_STRIP = NO 33 | ENABLE_STRICT_OBJC_MSGSEND = YES 34 | GCC_C_LANGUAGE_STANDARD = gnu11 35 | GCC_NO_COMMON_BLOCKS = YES 36 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES 37 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR 38 | GCC_WARN_UNDECLARED_SELECTOR = YES 39 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE 40 | GCC_WARN_UNUSED_FUNCTION = YES 41 | GCC_WARN_UNUSED_VARIABLE = YES 42 | MACOSX_DEPLOYMENT_TARGET = 10.15 43 | MTL_FAST_MATH = YES 44 | SDKROOT = macosx 45 | -------------------------------------------------------------------------------- /windows/Clipboard/Utilities.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | namespace Microsoft::Common::Utilities { 9 | 10 | // A simple wrapper for reinterpret_casting from TOriginalTypePtr to 11 | // TResultTypePtr that does a static_assert before performing the cast. The 12 | // usage syntax for this function is the same as the syntax for doing a regular 13 | // reinterpret_cast. For example: 14 | // 15 | // uint32_t* i; 16 | // auto p = CheckedReinterpretCast(i); 17 | // 18 | template 19 | inline TResultTypePtr CheckedReinterpretCast(TOriginalTypePtr p) noexcept { 20 | using TResultType = typename std::remove_pointer::type; 21 | using TOriginalType = typename std::remove_pointer::type; 22 | 23 | static_assert( 24 | std::is_pointer::value && std::is_pointer::value && 25 | std::is_integral::value && std::is_integral::value && 26 | sizeof(TResultType) == sizeof(TOriginalType), 27 | "CheckedReinterpretCast can only be used to cast from T1* to T2*, where " 28 | "T1 and T2 are integral types of the same size."); 29 | 30 | return reinterpret_cast(p); 31 | } 32 | 33 | // A compile time function that deduces the size of an array. 34 | template 35 | constexpr std::size_t ArraySize(T(&)[N]) noexcept { 36 | return N; 37 | } 38 | 39 | } // namespace Microsoft::Common::Utilities 40 | -------------------------------------------------------------------------------- /windows/Clipboard/Clipboard.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | Resource Files 42 | 43 | 44 | -------------------------------------------------------------------------------- /windows/Clipboard.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32929.385 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clipboard", "Clipboard\Clipboard.vcxproj", "{90BFF18B-474B-445D-9847-B065853288D8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Debug|ARM64 = Debug|ARM64 13 | Release|x64 = Release|x64 14 | Release|x86 = Release|x86 15 | Release|ARM64 = Release|ARM64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x64.ActiveCfg = Debug|x64 19 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x64.Build.0 = Debug|x64 20 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x64.Deploy.0 = Debug|x64 21 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x86.ActiveCfg = Debug|Win32 22 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x86.Build.0 = Debug|Win32 23 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x86.Deploy.0 = Debug|Win32 24 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.ActiveCfg = Debug|ARM64 25 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.Build.0 = Debug|ARM64 26 | {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.Deploy.0 = Debug|ARM64 27 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x64.ActiveCfg = Release|x64 28 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x64.Build.0 = Release|x64 29 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x64.Deploy.0 = Release|x64 30 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x86.ActiveCfg = Release|Win32 31 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x86.Build.0 = Release|Win32 32 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|x86.Deploy.0 = Release|Win32 33 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.ActiveCfg = Release|ARM64 34 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.Build.0 = Release|ARM64 35 | {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.Deploy.0 = Release|ARM64 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the Gradle Daemon. The setting is 11 | # particularly useful for configuring JVM memory settings for build performance. 12 | # This does not affect the JVM settings for the Gradle client VM. 13 | # The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`. 14 | org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 15 | 16 | # When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute 17 | # projects in parallel. To learn more about parallel task execution, see the 18 | # section on Gradle build performance: 19 | # https://docs.gradle.org/current/userguide/performance.html#parallel_execution. 20 | # Default is `false`. 21 | #org.gradle.parallel=true 22 | 23 | # AndroidX package structure to make it clearer which packages are bundled with the 24 | # Android operating system, and which are packaged with your app's APK 25 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 26 | android.useAndroidX=true 27 | # Automatically convert third-party libraries to use AndroidX 28 | android.enableJetifier=true 29 | # Jetifier randomly fails on these libraries 30 | android.jetifier.ignorelist=hermes-android 31 | 32 | # Use this property to specify which architecture you want to build. 33 | # You can also override it from the CLI using 34 | # ./gradlew -PreactNativeArchitectures=x86_64 35 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 36 | 37 | # Use this property to enable support to the new architecture. 38 | # This will allow you to use TurboModules and the Fabric render in 39 | # your application. You should enable this flag either if you want 40 | # to write custom TurboModules/Fabric components OR use libraries that 41 | # are providing them. 42 | # Note that this is incompatible with web debugging. 43 | #newArchEnabled=true 44 | #bridgelessEnabled=true 45 | 46 | # Uncomment the line below to build React Native from source. 47 | #react.buildFromSource=true 48 | 49 | # Version of Android NDK to build against. 50 | #ANDROID_NDK_VERSION=26.1.10909125 51 | 52 | # Version of Kotlin to build against. 53 | #KOTLIN_VERSION=1.8.22 54 | -------------------------------------------------------------------------------- /android/src/paper/java/com/reactnativecommunity/clipboard/NativeClipboardModuleSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Do not edit this file as changes may cause incorrect behavior and will be lost 6 | * once the code is regenerated. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.reactnativecommunity.clipboard; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.Promise; 17 | import com.facebook.react.bridge.ReactApplicationContext; 18 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 19 | import com.facebook.react.bridge.ReactMethod; 20 | import com.facebook.react.bridge.ReadableArray; 21 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 22 | 23 | public abstract class NativeClipboardModuleSpec extends ReactContextBaseJavaModule implements TurboModule { 24 | public NativeClipboardModuleSpec(ReactApplicationContext reactContext) { 25 | super(reactContext); 26 | } 27 | 28 | @ReactMethod 29 | @DoNotStrip 30 | public abstract void getString(Promise promise); 31 | 32 | @ReactMethod 33 | @DoNotStrip 34 | public abstract void getStrings(Promise promise); 35 | 36 | @ReactMethod 37 | @DoNotStrip 38 | public abstract void getImagePNG(Promise promise); 39 | 40 | @ReactMethod 41 | @DoNotStrip 42 | public abstract void getImageJPG(Promise promise); 43 | 44 | @ReactMethod 45 | @DoNotStrip 46 | public abstract void setImage(String content, Promise promise); 47 | 48 | @ReactMethod 49 | @DoNotStrip 50 | public abstract void getImage(Promise promise); 51 | 52 | @ReactMethod 53 | @DoNotStrip 54 | public abstract void setString(String content); 55 | 56 | @ReactMethod 57 | @DoNotStrip 58 | public abstract void setStrings(ReadableArray content); 59 | 60 | @ReactMethod 61 | @DoNotStrip 62 | public abstract void hasString(Promise promise); 63 | 64 | @ReactMethod 65 | @DoNotStrip 66 | public abstract void hasImage(Promise promise); 67 | 68 | @ReactMethod 69 | @DoNotStrip 70 | public abstract void hasURL(Promise promise); 71 | 72 | @ReactMethod 73 | @DoNotStrip 74 | public abstract void hasNumber(Promise promise); 75 | 76 | @ReactMethod 77 | @DoNotStrip 78 | public abstract void hasWebURL(Promise promise); 79 | 80 | @ReactMethod 81 | @DoNotStrip 82 | public abstract void setListener(); 83 | 84 | @ReactMethod 85 | @DoNotStrip 86 | public abstract void removeListener(); 87 | 88 | @ReactMethod 89 | @DoNotStrip 90 | public abstract void addListener(String eventName); 91 | 92 | @ReactMethod 93 | @DoNotStrip 94 | public abstract void removeListeners(double count); 95 | } 96 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to React Native Clipboard 2 | 3 | ## Development Process 4 | All work on React Native Clipboard happens directly on GitHub. Contributors send pull requests which go through a review process. 5 | 6 | > **Working on your first pull request?** You can learn how from this *free* series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 7 | 8 | 1. Fork the repo and create your branch from `master` (a guide on [how to fork a repository](https://help.github.com/articles/fork-a-repo/)). 9 | 2. Run `yarn` or `npm install` to install all required dependencies. 10 | 3. Now you are ready to make your changes! 11 | 12 | ## Tests & Verifications 13 | Currently we use `flow` for typechecking, `eslint` with `prettier` for linting and formatting the code, and `jest` for unit testing. We also use `detox` for end-to-end testing. All of these are run on CircleCI for all opened pull requests, but you should use them locally when making changes. 14 | 15 | * `yarn test`: Run all tests and validations. 16 | 17 | 18 | * `yarn flow`: Run `flow` typechecking. 19 | 20 | 21 | 22 | 23 | 24 | ## Sending a pull request 25 | When you're sending a pull request: 26 | 27 | * Prefer small pull requests focused on one change. 28 | * Verify that all tests and validations are passing. 29 | * Follow the pull request template when opening a pull request. 30 | 31 | ## Commit message convention 32 | We prefix our commit messages with one of the following to signify the kind of change: 33 | 34 | * **build**: Changes that affect the build system or external dependencies. 35 | * **ci**, **chore**: Changes to our CI configuration files and scripts. 36 | * **docs**: Documentation only changes. 37 | * **feat**: A new feature. 38 | * **fix**: A bug fix. 39 | * **perf**: A code change that improves performance. 40 | * **refactor**: A code change that neither fixes a bug nor adds a feature. 41 | * **style**: Changes that do not affect the meaning of the code. 42 | * **test**: Adding missing tests or correcting existing tests. 43 | 44 | ## Release process 45 | We use [Semantic Release](http://semantic-release.org) to automatically release new versions of the library when changes are merged into master. Using the commit message convention described above, it will detect if we need to release a patch, minor, or major version of the library. 46 | 47 | ## Reporting issues 48 | You can report issues on our [bug tracker](https://github.com/react-native-clipboard/clipboard/issues). Please search for existing issues and follow the issue template when opening an issue. 49 | 50 | ## License 51 | By contributing to React Native Clipboard, you agree that your contributions will be licensed under the **MIT** license. 52 | -------------------------------------------------------------------------------- /windows/Clipboard/Unicode.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | #if _HAS_CXX17 9 | #include 10 | #endif 11 | 12 | namespace Microsoft::Common::Unicode { 13 | 14 | // All functions in this header offer the strong exception safety guarantee and 15 | // may throw the following exceptions: 16 | // - std::bad_alloc, 17 | // - std::overflow_error, and 18 | // - UnicodeConversionException (defined below). 19 | // 20 | class UnicodeConversionException : public std::runtime_error { 21 | public: 22 | UnicodeConversionException(const char* const message, uint32_t errorCode) 23 | : std::runtime_error(message), m_errorCode(errorCode) {} 24 | 25 | inline uint32_t ErrorCode() const { 26 | return m_errorCode; 27 | } 28 | 29 | private: 30 | // The error code returned by GetLastError(). 31 | uint32_t m_errorCode; 32 | }; 33 | 34 | // The following functions convert UTF-8 strings to UTF-16BE strings. 35 | // 36 | // If the input UTF-8 string begins with the UTF-8 Byte Order Mark (BOM) (0xef 37 | // 0xbb 0xbf), then the output UTF-16BE string will begin with the UTF-16BE BOM 38 | // (0xfeff). For example, "abc" (0xef 0xbb 0xbf 0x61 0x62 0x63) is 39 | // converted to "abc" (0xfeff 0x0061 0x0062 0x0063). 40 | // 41 | // If the input UTF-8 string omits the UTF-8 BOM, then the output UTF-16BE 42 | // string will also omit the UTF16-BE BOM. For example, "abc" (0x61 0x62 0x63) 43 | // is converted to "abc" (0x0061 0x0062 0x0063). 44 | // 45 | // For (1), utf8 does not have to be null terminated, and utf8Len must reflect 46 | // the length of utf8, without the null terminator if it has one. The behavior 47 | // is undefined otherwise. 48 | // 49 | // For (2), utf8 must be null terminated. The behavior is undefined otherwise. 50 | // 51 | /* (1) */ std::wstring Utf8ToUtf16(const char* utf8, size_t utf8Len); 52 | /* (2) */ std::wstring Utf8ToUtf16(const char* utf8); 53 | /* (3) */ std::wstring Utf8ToUtf16(const std::string& utf8); 54 | #if _HAS_CXX17 55 | /* (4) */ std::wstring Utf8ToUtf16(const std::string_view& utf8); 56 | #endif 57 | 58 | // The following functions convert UTF-16BE strings to UTF-8 strings. Their 59 | // behaviors mirror those of the above Utf8ToUtf16 functions. 60 | // 61 | // For (1) and (2), utf16 does not have to be null terminated, and utf16Len must 62 | // reflect the length of utf16, without the null terminator if it has one. The 63 | // behavior is undefined otherwise. 64 | // 65 | // For (3) and (4), utf16 must be null terminated. The behavior is undefined 66 | // otherwise. 67 | // 68 | /* (1) */ std::string Utf16ToUtf8(const wchar_t* utf16, size_t utf16Len); 69 | /* (2) */ std::string Utf16ToUtf8(const char16_t* utf16, size_t utf16Len); 70 | /* (3) */ std::string Utf16ToUtf8(const wchar_t* utf16); 71 | /* (4) */ std::string Utf16ToUtf8(const char16_t* utf16); 72 | /* (5) */ std::string Utf16ToUtf8(const std::wstring& utf16); 73 | /* (6) */ std::string Utf16ToUtf8(const std::u16string& utf16); 74 | #if _HAS_CXX17 75 | /* (7) */ std::string Utf16ToUtf8(const std::wstring_view& utf16); 76 | /* (8) */ std::string Utf16ToUtf8(const std::u16string_view& utf16); 77 | #endif 78 | 79 | } // namespace Microsoft::Common::Unicode 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-clipboard/clipboard", 3 | "version": "1.16.3", 4 | "description": "React Native Clipboard API for macOS, iOS, Android, and Windows", 5 | "keywords": [ 6 | "Clipboard", 7 | "getString", 8 | "react-native", 9 | "setString" 10 | ], 11 | "homepage": "https://github.com/react-native-clipboard/clipboard#readme", 12 | "bugs": { 13 | "url": "https://github.com/react-native-clipboard/clipboard/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/react-native-clipboard/clipboard.git" 18 | }, 19 | "license": "MIT", 20 | "author": "M.Haris Baig ", 21 | "main": "dist/index.js", 22 | "types": "dist/index.d.ts", 23 | "files": [ 24 | "dist", 25 | "RNCClipboard.podspec", 26 | "ios", 27 | "macos", 28 | "android", 29 | "windows", 30 | "jest", 31 | "src", 32 | "!android/**/build/*" 33 | ], 34 | "scripts": { 35 | "ios": "react-native run-ios", 36 | "android": "react-native run-android --root example", 37 | "windows": "cd example && react-native run-windows", 38 | "start:windows": "install-windows-test-app -p example/windows && react-native run-windows --root example --logging", 39 | "start:windows:fabric": "install-windows-test-app -p example/windows --use-fabric && react-native run-windows --root example --logging", 40 | "start": "react-native start", 41 | "build": "tsc", 42 | "format": "biome format --write .", 43 | "lint": "biome lint --write .", 44 | "prepare": "npm run build", 45 | "test": "jest", 46 | "type-check": "tsc --noEmit" 47 | }, 48 | "jest": { 49 | "preset": "react-native" 50 | }, 51 | "devDependencies": { 52 | "@babel/core": "^7.12.9", 53 | "@biomejs/biome": "1.9.4", 54 | "@callstack/react-native-visionos": "^0.76.0", 55 | "@react-native/babel-preset": "^0.75.0", 56 | "@react-native/metro-config": "^0.75.0", 57 | "@rnx-kit/align-deps": "^2.4.1", 58 | "@rnx-kit/metro-config": "^2.0.0", 59 | "@types/react": "^18.2.0", 60 | "@typescript-eslint/eslint-plugin": "^8.16.0", 61 | "babel-jest": "^26.1.0", 62 | "jest": "^29.2.1", 63 | "react": "^18.3.1", 64 | "react-native": "^0.75.3", 65 | "react-native-macos": "0.76.0", 66 | "react-native-test-app": "^4.0.5", 67 | "react-native-windows": "^0.75.0", 68 | "react-test-renderer": "^18.2.0", 69 | "typescript": "^4.4.3" 70 | }, 71 | "peerDependencies": { 72 | "react": ">= 16.9.0", 73 | "react-native": ">= 0.61.5", 74 | "react-native-macos": ">= 0.61.0", 75 | "react-native-windows": ">= 0.61.0" 76 | }, 77 | "peerDependenciesMeta": { 78 | "react-native-macos": { 79 | "optional": true 80 | }, 81 | "react-native-windows": { 82 | "optional": true 83 | } 84 | }, 85 | "publishConfig": { 86 | "access": "public" 87 | }, 88 | "codegenConfig": { 89 | "name": "rnclipboard", 90 | "type": "modules", 91 | "jsSrcsDir": "./src", 92 | "android": { 93 | "javaPackageName": "com.reactnativecommunity.clipboard" 94 | }, 95 | "windows": { 96 | "namespace": "ClipboardCodegen", 97 | "outputDirectory": "windows/Clipboard/codegen", 98 | "separateDataTypes": true 99 | } 100 | }, 101 | "packageManager": "yarn@4.1.1", 102 | "workspaces": [ 103 | "example" 104 | ], 105 | "react-native-windows": { 106 | "init-windows": { 107 | "name": "Clipboard", 108 | "namespace": "Clipboard", 109 | "template": "cpp-lib" 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: push 3 | 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | node-version: [18, 20] 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: ${{ matrix.node-version }} 15 | - name: Get yarn cache 16 | id: yarn-cache 17 | run: echo "::set-output name=dir::$(yarn cache dir)" 18 | - uses: actions/cache@v3 19 | with: 20 | path: ${{ steps.yarn-cache.outputs.dir }} 21 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 22 | - name: Install Dependencies 23 | run: yarn 24 | - name: ESLint Checks 25 | run: yarn lint 26 | tsc: 27 | runs-on: ubuntu-latest 28 | strategy: 29 | matrix: 30 | node-version: [18, 20] 31 | steps: 32 | - uses: actions/checkout@v2 33 | - uses: actions/setup-node@v3 34 | with: 35 | node-version: ${{ matrix.node-version }} 36 | - name: Get yarn cache 37 | id: yarn-cache 38 | run: echo "::set-output name=dir::$(yarn cache dir)" 39 | - uses: actions/cache@v3 40 | with: 41 | path: ${{ steps.yarn-cache.outputs.dir }} 42 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 43 | - name: Install Dependencies 44 | run: yarn 45 | - name: TypeScript type check 46 | run: yarn type-check 47 | android: 48 | runs-on: ubuntu-latest 49 | strategy: 50 | matrix: 51 | node-version: [18, 20] 52 | steps: 53 | - uses: actions/checkout@v2 54 | - uses: actions/setup-node@v3 55 | with: 56 | node-version: ${{ matrix.node-version }} 57 | - name: Set up JDK 58 | uses: actions/setup-java@v4 59 | with: 60 | distribution: temurin 61 | java-version: 17 62 | - name: Get yarn cache 63 | id: yarn-cache 64 | run: echo "::set-output name=dir::$(yarn cache dir)" 65 | - uses: actions/cache@v3 66 | with: 67 | path: ${{ steps.yarn-cache.outputs.dir }} 68 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 69 | - name: Install Dependencies 70 | run: yarn 71 | - name: Build Android Example App and Library 72 | run: cd example/android && ./gradlew clean assembleDebug 73 | ios: 74 | runs-on: macos-latest 75 | strategy: 76 | matrix: 77 | node-version: [18, 20] 78 | steps: 79 | - uses: actions/checkout@v2 80 | - uses: actions/setup-node@v3 81 | with: 82 | node-version: ${{ matrix.node-version }} 83 | - name: Get yarn cache 84 | id: yarn-cache 85 | run: echo "::set-output name=dir::$(yarn cache dir)" 86 | - uses: actions/cache@v3 87 | with: 88 | path: ${{ steps.yarn-cache.outputs.dir }} 89 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 90 | - name: Install Dependencies 91 | run: yarn 92 | - name: Install Podfiles 93 | run: cd example && npx pod-install 94 | - name: Build example app 95 | run: yarn ios --no-packager 96 | # windows: 97 | # runs-on: windows-latest 98 | # strategy: 99 | # matrix: 100 | # node-version: [18, 20] 101 | # steps: 102 | # - uses: actions/checkout@v2 103 | # - uses: actions/setup-node@v3 104 | # with: 105 | # node-version: ${{ matrix.node-version }} 106 | # - name: Get yarn cache 107 | # id: yarn-cache 108 | # run: echo "::set-output name=dir::$(yarn cache dir)" 109 | # - uses: actions/cache@v3 110 | # with: 111 | # path: ${{ steps.yarn-cache.outputs.dir }} 112 | # key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 113 | # - name: Install Dependencies 114 | # run: yarn 115 | # - name: Build example app 116 | # run: yarn windows 117 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | def getExtOrDefault(name) { 13 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeClipBoard_' + name] 14 | } 15 | 16 | def getExtOrIntegerDefault(name) { 17 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeClipBoard_' + name]).toInteger() 18 | } 19 | 20 | apply plugin: 'com.android.library' 21 | 22 | def resolveReactNativeDirectory() { 23 | // monorepo workaround 24 | // react-native can be hoisted or in project's own node_modules 25 | def reactNativeFromProjectNodeModules = file("${rootProject.projectDir}/../node_modules/react-native") 26 | if (reactNativeFromProjectNodeModules.exists()) { 27 | return reactNativeFromProjectNodeModules 28 | } 29 | 30 | // This code is taken from react-native-reanimated, but with an extra "../" due to @react-native-cliploard/clipboard being in a subdirectory 31 | def reactNativeFromNodeModulesWithReactNativeClipboard = file("${projectDir}/../../../react-native") 32 | if (reactNativeFromNodeModulesWithReactNativeClipboard.exists()) { 33 | return reactNativeFromNodeModulesWithReactNativeClipboard 34 | } 35 | 36 | throw new Exception( 37 | "[react-native-clipboard] Unable to resolve react-native location in " + 38 | "node_modules. You should add project extension property (in app/build.gradle) " + 39 | "`REACT_NATIVE_NODE_MODULES_DIR` with path to react-native." 40 | ) 41 | } 42 | 43 | def REACT_NATIVE_DIR = resolveReactNativeDirectory() 44 | 45 | def reactProperties = new Properties() 46 | file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } 47 | 48 | def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME") 49 | def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger() 50 | 51 | def isNewArchitectureEnabled() { 52 | // To opt-in for the New Architecture, you can either: 53 | // - Set `newArchEnabled` to true inside the `gradle.properties` file 54 | // - Invoke gradle with `-newArchEnabled=true` 55 | // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` 56 | return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" 57 | } 58 | 59 | if (isNewArchitectureEnabled()) { 60 | apply plugin: "com.facebook.react" 61 | } 62 | 63 | android { 64 | def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION 65 | if (agpVersion.tokenize('.')[0].toInteger() >= 7) { 66 | namespace "com.reactnativecommunity.clipboard" 67 | } 68 | 69 | compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') 70 | 71 | // Used to override the NDK path/version on internal CI or by allowing 72 | // users to customize the NDK path/version from their root project (e.g. for M1 support) 73 | if (rootProject.hasProperty("ndkPath")) { 74 | ndkPath rootProject.ext.ndkPath 75 | } 76 | if (rootProject.hasProperty("ndkVersion")) { 77 | ndkVersion rootProject.ext.ndkVersion 78 | } 79 | 80 | defaultConfig { 81 | minSdkVersion getExtOrIntegerDefault('minSdkVersion') 82 | targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') 83 | } 84 | 85 | sourceSets.main { 86 | java { 87 | if (!isNewArchitectureEnabled()) { 88 | srcDirs += 'src/paper/java' 89 | } 90 | } 91 | } 92 | } 93 | 94 | repositories { 95 | google() 96 | mavenCentral() 97 | } 98 | 99 | dependencies { 100 | //noinspection GradleDynamicVersion 101 | if (isNewArchitectureEnabled() && REACT_NATIVE_MINOR_VERSION < 71) { 102 | implementation project(":ReactAndroid") 103 | } else { 104 | implementation 'com.facebook.react:react-native:+' 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativecommunity/clipboard/ClipboardPackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativecommunity.clipboard; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.facebook.react.TurboReactPackage; 6 | import com.facebook.react.ViewManagerOnDemandReactPackage; 7 | import com.facebook.react.bridge.ModuleSpec; 8 | import com.facebook.react.bridge.NativeModule; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.module.annotations.ReactModule; 11 | import com.facebook.react.module.annotations.ReactModuleList; 12 | import com.facebook.react.module.model.ReactModuleInfo; 13 | import com.facebook.react.module.model.ReactModuleInfoProvider; 14 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 15 | import com.facebook.react.uimanager.ViewManager; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import javax.annotation.Nonnull; 24 | 25 | @ReactModuleList( 26 | nativeModules = { 27 | ClipboardModule.class, 28 | }) 29 | public class ClipboardPackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage { 30 | 31 | /** {@inheritDoc} */ 32 | @Override 33 | public List getViewManagerNames(ReactApplicationContext reactContext) { 34 | return Collections.emptyList(); 35 | } 36 | 37 | @Override 38 | protected List getViewManagers(ReactApplicationContext reactContext) { 39 | return null; 40 | } 41 | 42 | /** {@inheritDoc} */ 43 | @Override 44 | public @Nullable ViewManager createViewManager( 45 | ReactApplicationContext reactContext, String viewManagerName) { 46 | return null; 47 | } 48 | 49 | @Override 50 | public NativeModule getModule(String name, @Nonnull ReactApplicationContext reactContext) { 51 | switch (name) { 52 | case ClipboardModule.NAME: 53 | return new ClipboardModule(reactContext); 54 | default: 55 | return null; 56 | } 57 | } 58 | 59 | @Override 60 | public ReactModuleInfoProvider getReactModuleInfoProvider() { 61 | try { 62 | Class reactModuleInfoProviderClass = 63 | Class.forName("com.reactnativecommunity.clipboard.ClipboardPackage$$ReactModuleInfoProvider"); 64 | return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance(); 65 | } catch (ClassNotFoundException e) { 66 | // ReactModuleSpecProcessor does not run at build-time. Create this ReactModuleInfoProvider by 67 | // hand. 68 | return new ReactModuleInfoProvider() { 69 | @Override 70 | public Map getReactModuleInfos() { 71 | final Map reactModuleInfoMap = new HashMap<>(); 72 | 73 | Class[] moduleList = 74 | new Class[] { 75 | ClipboardModule.class, 76 | }; 77 | 78 | for (Class moduleClass : moduleList) { 79 | ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class); 80 | 81 | reactModuleInfoMap.put( 82 | reactModule.name(), 83 | new ReactModuleInfo( 84 | reactModule.name(), 85 | moduleClass.getName(), 86 | reactModule.canOverrideExistingModule(), 87 | reactModule.needsEagerInit(), 88 | reactModule.hasConstants(), 89 | reactModule.isCxxModule(), 90 | TurboModule.class.isAssignableFrom(moduleClass))); 91 | } 92 | 93 | return reactModuleInfoMap; 94 | } 95 | }; 96 | } catch (InstantiationException | IllegalAccessException e) { 97 | throw new RuntimeException( 98 | "No ReactModuleInfoProvider for com.reactnativecommunity.clipboard.ClipboardPackage$$ReactModuleInfoProvider", e); 99 | } 100 | } 101 | @Override 102 | public List createViewManagers(ReactApplicationContext reactContext) { 103 | return Collections.emptyList(); 104 | } 105 | 106 | @Override 107 | public List createNativeModules(ReactApplicationContext reactContext) { 108 | List modules = new ArrayList<>(); 109 | 110 | modules.add(new ClipboardModule(reactContext)); 111 | 112 | return modules; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react"; 2 | import { useCallback, useEffect, useState } from "react"; 3 | import { 4 | StyleSheet, 5 | Text, 6 | View, 7 | Button, 8 | TextInput, 9 | Alert, 10 | SafeAreaView, 11 | Platform, 12 | Image, 13 | } from "react-native"; 14 | import Clipboard, { useClipboard } from "../src"; 15 | 16 | // Small icon of a plus for demo purposes 17 | const TEST_IMAGE = 18 | "iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=="; 19 | 20 | const changeListener = () => { 21 | console.warn("Clipboard changed!"); 22 | }; 23 | 24 | const App: React.FC = () => { 25 | const [text, setText] = useState(""); 26 | const [isURL, setIsURL] = useState(false); 27 | const [data, setString] = useClipboard(); 28 | const [image, setImage] = useState(null); 29 | const [imageString, setImageString] = useState(""); 30 | 31 | const checkStringType = useCallback(async () => { 32 | const checkClipboard = await Clipboard.hasURL(); 33 | setIsURL(checkClipboard); 34 | }, []); 35 | 36 | const pasteImageAndroid = async () => { 37 | const base64 = await Clipboard.getImage(); 38 | setImageString(base64); 39 | }; 40 | 41 | useEffect(() => { 42 | checkStringType(); 43 | }, [checkStringType]); 44 | 45 | useEffect(() => { 46 | if (Platform.OS === "ios" || Platform.OS === "android") { 47 | const listener = Clipboard.addListener(changeListener); 48 | 49 | return () => { 50 | listener.remove(); 51 | }; 52 | } 53 | }, []); 54 | 55 | const writeToClipboard = async () => { 56 | setString(text); 57 | Alert.alert(`Copied to clipboard: ${text}`); 58 | }; 59 | 60 | const writeImageToClipboard = async () => { 61 | Clipboard.setImage(TEST_IMAGE); 62 | Alert.alert("Copied Image to clipboard"); 63 | }; 64 | 65 | const getImage = async () => { 66 | if (await Clipboard.hasImage()) { 67 | const image = await Clipboard.getImagePNG(); 68 | setImage(image); 69 | } else { 70 | console.warn("No image in clipboard"); 71 | } 72 | }; 73 | 74 | return ( 75 | 76 | Clipboard Module 77 | 78 | Clipboard Contents: 79 | {data} 80 | Content is URL: 81 | {JSON.stringify(isURL)} 82 | Content is IMAGE: 83 | {image && } 84 | 85 | setText(input)} 91 | value={text} 92 | placeholder="Type here..." 93 | /> 94 |