├── 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 extends NativeModule>[] moduleList =
74 | new Class[] {
75 | ClipboardModule.class,
76 | };
77 |
78 | for (Class extends NativeModule> 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 |
95 |
99 |
100 | {Platform.OS === "android" && (
101 |
102 |
106 |
107 | )}
108 |
109 | {imageString === "" ? null : (
110 |
111 | )}
112 |
113 | );
114 | };
115 |
116 | const styles = StyleSheet.create({
117 | container: {
118 | flex: 1,
119 | alignItems: "center",
120 | },
121 | main: {
122 | flex: 1,
123 | alignItems: "center",
124 | justifyContent: "center",
125 | },
126 | header: {
127 | fontWeight: "700",
128 | fontSize: 30,
129 | marginBottom: 10,
130 | },
131 | boldText: {
132 | fontWeight: "600",
133 | marginBottom: 10,
134 | },
135 | separator: {
136 | height: StyleSheet.hairlineWidth,
137 | backgroundColor: "gray",
138 | width: "80%",
139 | marginVertical: 20,
140 | },
141 | textInput: {
142 | borderColor: "gray",
143 | borderWidth: 1,
144 | width: "80%",
145 | paddingHorizontal: 80,
146 | paddingVertical: 8,
147 | marginBottom: 16,
148 | },
149 | textInputMacOS: {
150 | borderColor: "gray",
151 | borderWidth: StyleSheet.hairlineWidth,
152 | width: 300,
153 | padding: 4,
154 | marginBottom: 16,
155 | },
156 | clipboardContent: {
157 | marginBottom: 20,
158 | },
159 | imageContent: {
160 | width: 40,
161 | height: 40,
162 | },
163 | imageAndroid: {
164 | height: 160,
165 | width: 160,
166 | },
167 | imageButtonAndroid: {
168 | marginTop: 10,
169 | },
170 | });
171 |
172 | export default App;
173 |
--------------------------------------------------------------------------------
/src/Clipboard.ts:
--------------------------------------------------------------------------------
1 | import { type EmitterSubscription, Platform } from "react-native";
2 | import NativeClipboard, {
3 | addListener,
4 | removeAllListeners,
5 | } from "./NativeClipboardModule";
6 |
7 | /**
8 | * `Clipboard` gives you an interface for setting and getting content from Clipboard on both iOS and Android
9 | */
10 | export const Clipboard = {
11 | /**
12 | * Get content of string type, this method returns a `Promise`, so you can use following code to get clipboard content
13 | * ```javascript
14 | * async _getContent() {
15 | * var content = await Clipboard.getString();
16 | * }
17 | * ```
18 | */
19 | getString(): Promise {
20 | return NativeClipboard.getString();
21 | },
22 | /**
23 | * (iOS Only)
24 | * Get contents of string array type, this method returns a `Promise`, so you can use following code to get clipboard content
25 | * ```javascript
26 | * async _getContent() {
27 | * var content = await Clipboard.getStrings();
28 | * }
29 | * ```
30 | */
31 | getStrings(): Promise {
32 | return NativeClipboard.getStrings();
33 | },
34 | /**
35 | * Get clipboard image as PNG in base64, this method returns a `Promise`, so you can use following code to get clipboard content
36 | * ```javascript
37 | * async _getContent() {
38 | * var content = await Clipboard.getImagePNG();
39 | * }
40 | * ```
41 | */
42 | getImagePNG(): Promise {
43 | return NativeClipboard.getImagePNG();
44 | },
45 | /**
46 | * Get clipboard image as JPG in base64, this method returns a `Promise`, so you can use following code to get clipboard content
47 | * ```javascript
48 | * async _getContent() {
49 | * var content = await Clipboard.getImageJPG();
50 | * }
51 | * ```
52 | */
53 | getImageJPG(): Promise {
54 | return NativeClipboard.getImageJPG();
55 | },
56 | /**
57 | * (iOS Only)
58 | * Set content of base64 image type. You can use following code to set clipboard content
59 | * ```javascript
60 | * _setContent() {
61 | * Clipboard.setImage(...);
62 | * }
63 | * ```
64 | * @param the content to be stored in the clipboard.
65 | */
66 | setImage(content: string) {
67 | if (Platform.OS !== "ios") {
68 | return;
69 | }
70 |
71 | NativeClipboard.setImage(content);
72 | },
73 | /**
74 | * (iOS and Android Only)
75 | * Get clipboard image in base64, this method returns a `Promise`, so you can use following code to get clipboard content
76 | * ```javascript
77 | * async _getContent() {
78 | * var content = await Clipboard.getImage();
79 | * }
80 | * ```
81 | */
82 | getImage(): Promise {
83 | return NativeClipboard.getImage();
84 | },
85 | /**
86 | * Set content of string type. You can use following code to set clipboard content
87 | * ```javascript
88 | * _setContent() {
89 | * Clipboard.setString('hello world');
90 | * }
91 | * ```
92 | * @param the content to be stored in the clipboard.
93 | */
94 | setString(content: string) {
95 | NativeClipboard.setString(content);
96 | },
97 | /**
98 | * Set content of string array type. You can use following code to set clipboard content
99 | * ```javascript
100 | * _setContent() {
101 | * Clipboard.setStrings(['hello world', 'second string']);
102 | * }
103 | * ```
104 | * @param the content to be stored in the clipboard.
105 | */
106 | setStrings(content: string[]) {
107 | NativeClipboard.setStrings(content);
108 | },
109 | /**
110 | * Returns whether the clipboard has content or is empty.
111 | * This method returns a `Promise`, so you can use following code to get clipboard content
112 | * ```javascript
113 | * async _hasContent() {
114 | * var hasContent = await Clipboard.hasString();
115 | * }
116 | * ```
117 | */
118 | hasString() {
119 | return NativeClipboard.hasString();
120 | },
121 | /**
122 | * Returns whether the clipboard has an image or is empty.
123 | * This method returns a `Promise`, so you can use following code to check clipboard content
124 | * ```javascript
125 | * async _hasContent() {
126 | * var hasContent = await Clipboard.hasImage();
127 | * }
128 | * ```
129 | */
130 | hasImage() {
131 | return NativeClipboard.hasImage();
132 | },
133 | /**
134 | * (iOS Only)
135 | * Returns whether the clipboard has a URL content. Can check
136 | * if there is a URL content in clipboard without triggering PasteBoard notification for iOS 14+
137 | * This method returns a `Promise`, so you can use following code to check for url content in clipboard.
138 | * ```javascript
139 | * async _hasURL() {
140 | * var hasURL = await Clipboard.hasURL();
141 | * }
142 | * ```
143 | */
144 | hasURL() {
145 | if (Platform.OS !== "ios") {
146 | return;
147 | }
148 | return NativeClipboard.hasURL();
149 | },
150 | /**
151 | * (iOS 14+ Only)
152 | * Returns whether the clipboard has a Number(UIPasteboardDetectionPatternNumber) content. Can check
153 | * if there is a Number content in clipboard without triggering PasteBoard notification for iOS 14+
154 | * This method returns a `Promise`, so you can use following code to check for Number content in clipboard.
155 | * ```javascript
156 | * async _hasNumber() {
157 | * var hasNumber = await Clipboard.hasNumber();
158 | * }
159 | * ```
160 | */
161 | hasNumber() {
162 | if (Platform.OS !== "ios") {
163 | return;
164 | }
165 | return NativeClipboard.hasNumber();
166 | },
167 | /**
168 | * (iOS 14+ Only)
169 | * Returns whether the clipboard has a WebURL(UIPasteboardDetectionPatternProbableWebURL) content. Can check
170 | * if there is a WebURL content in clipboard without triggering PasteBoard notification for iOS 14+
171 | * This method returns a `Promise`, so you can use following code to check for WebURL content in clipboard.
172 | * ```javascript
173 | * async _hasWebURL() {
174 | * var hasWebURL = await Clipboard.hasWebURL();
175 | * }
176 | * ```
177 | */
178 | hasWebURL() {
179 | if (Platform.OS !== "ios") {
180 | return;
181 | }
182 | return NativeClipboard.hasWebURL();
183 | },
184 | /**
185 | * (iOS and Android Only)
186 | * Adds a listener to get notifications when the clipboard has changed.
187 | * If this is the first listener, turns on clipboard notifications on the native side.
188 | * It returns EmitterSubscription where you can call "remove" to remove listener
189 | * ```javascript
190 | * const listener = () => console.log("changed!");
191 | * Clipboard.addListener(listener);
192 | * ```
193 | */
194 | addListener(callback: () => void): EmitterSubscription {
195 | return addListener(callback);
196 | },
197 |
198 | /**
199 | * (iOS and Android Only)
200 | * Removes all previously registered listeners and turns off notifications on the native side.
201 | * ```javascript
202 | * Clipboard.removeAllListeners();
203 | * ```
204 | */
205 | removeAllListeners() {
206 | removeAllListeners();
207 | },
208 | };
209 |
--------------------------------------------------------------------------------
/src/NativeClipboardModule.ts:
--------------------------------------------------------------------------------
1 | import {
2 | // @ts-ignore - remove this comment when RN in the repo & example app is upgraded
3 | TurboModuleRegistry,
4 | // @ts-ignore - remove this comment when RN in the repo & example app is upgraded
5 | type TurboModule,
6 | type EmitterSubscription,
7 | NativeEventEmitter,
8 | } from "react-native";
9 | // @ts-ignore - remove this comment when RN in the repo & example app is upgraded
10 | import type { Int32 } from "react-native/Libraries/Types/CodegenTypes";
11 |
12 | export interface Spec extends TurboModule {
13 | /**
14 | * Get content of string type, this method returns a `Promise`, so you can use following code to get clipboard content
15 | * ```javascript
16 | * async _getContent() {
17 | * var content = await Clipboard.getString();
18 | * }
19 | * ```
20 | */
21 | getString(): Promise;
22 | /**
23 | * (iOS Only)
24 | * Get contents of string array type, this method returns a `Promise`, so you can use following code to get clipboard content
25 | * ```javascript
26 | * async _getContent() {
27 | * var content = await Clipboard.getStrings();
28 | * }
29 | * ```
30 | */
31 | getStrings(): Promise;
32 | /**
33 | * Get clipboard image as PNG in base64, this method returns a `Promise`, so you can use following code to get clipboard content
34 | * ```javascript
35 | * async _getContent() {
36 | * var content = await Clipboard.getImagePNG();
37 | * }
38 | * ```
39 | */
40 | getImagePNG(): Promise;
41 | /**
42 | * Get clipboard image as JPG in base64, this method returns a `Promise`, so you can use following code to get clipboard content
43 | * ```javascript
44 | * async _getContent() {
45 | * var content = await Clipboard.getImageJPG();
46 | * }
47 | * ```
48 | */
49 | getImageJPG(): Promise;
50 | /**
51 | * (iOS Only)
52 | * Set content of base64 image type. You can use following code to set clipboard content
53 | * ```javascript
54 | * _setContent() {
55 | * Clipboard.setImage(...);
56 | * }
57 | * ```
58 | * @param the content to be stored in the clipboard.
59 | */
60 | setImage(content: string): Promise;
61 | /**
62 | * (iOS and Android Only)
63 | * Get clipboard image in base64, this method returns a `Promise`, so you can use following code to get clipboard content
64 | * ```javascript
65 | * async _getContent() {
66 | * var content = await Clipboard.getImage();
67 | * }
68 | * ```
69 | */
70 | getImage(): Promise;
71 | /**
72 | * Set content of string type. You can use following code to set clipboard content
73 | * ```javascript
74 | * _setContent() {
75 | * Clipboard.setString('hello world');
76 | * }
77 | * ```
78 | * @param the content to be stored in the clipboard.
79 | */
80 | setString(content: string): void;
81 | /**
82 | * Set content of string array type. You can use following code to set clipboard content
83 | * ```javascript
84 | * _setContent() {
85 | * Clipboard.setStrings(['hello world', 'second string']);
86 | * }
87 | * ```
88 | * @param the content to be stored in the clipboard.
89 | */
90 | setStrings(content: string[]): void;
91 | /**
92 | * Returns whether the clipboard has content or is empty.
93 | * This method returns a `Promise`, so you can use following code to get clipboard content
94 | * ```javascript
95 | * async _hasContent() {
96 | * var hasContent = await Clipboard.hasString();
97 | * }
98 | * ```
99 | */
100 | hasString(): Promise;
101 | /**
102 | * Returns whether the clipboard has an image or is empty.
103 | * This method returns a `Promise`, so you can use following code to check clipboard content
104 | * ```javascript
105 | * async _hasContent() {
106 | * var hasContent = await Clipboard.hasImage();
107 | * }
108 | * ```
109 | */
110 | hasImage(): Promise;
111 | /**
112 | * (iOS Only)
113 | * Returns whether the clipboard has a URL content. Can check
114 | * if there is a URL content in clipboard without triggering PasteBoard notification for iOS 14+
115 | * This method returns a `Promise`, so you can use following code to check for url content in clipboard.
116 | * ```javascript
117 | * async _hasURL() {
118 | * var hasURL = await Clipboard.hasURL();
119 | * }
120 | * ```
121 | */
122 | hasURL(): Promise;
123 | /**
124 | * (iOS 14+ Only)
125 | * Returns whether the clipboard has a Number(UIPasteboardDetectionPatternNumber) content. Can check
126 | * if there is a Number content in clipboard without triggering PasteBoard notification for iOS 14+
127 | * This method returns a `Promise`, so you can use following code to check for Number content in clipboard.
128 | * ```javascript
129 | * async _hasNumber() {
130 | * var hasNumber = await Clipboard.hasNumber();
131 | * }
132 | * ```
133 | */
134 | hasNumber(): Promise;
135 | /**
136 | * (iOS 14+ Only)
137 | * Returns whether the clipboard has a WebURL(UIPasteboardDetectionPatternProbableWebURL) content. Can check
138 | * if there is a WebURL content in clipboard without triggering PasteBoard notification for iOS 14+
139 | * This method returns a `Promise`, so you can use following code to check for WebURL content in clipboard.
140 | * ```javascript
141 | * async _hasWebURL() {
142 | * var hasWebURL = await Clipboard.hasWebURL();
143 | * }
144 | * ```
145 | */
146 | hasWebURL(): Promise;
147 | setListener(): void;
148 | removeListener(): void;
149 | addListener(eventName: string): void;
150 | removeListeners(count: Int32): void;
151 | }
152 |
153 | const ClipboardTurboModule =
154 | TurboModuleRegistry.getEnforcing("RNCClipboard");
155 |
156 | export default ClipboardTurboModule;
157 |
158 | const EVENT_NAME = "RNCClipboard_TEXT_CHANGED";
159 | const eventEmitter = new NativeEventEmitter(ClipboardTurboModule);
160 |
161 | let listenerCount = eventEmitter.listenerCount;
162 |
163 | // listenerCount is only available from RN 0.64
164 | // Older versions only have `listeners`
165 | if (!listenerCount) {
166 | listenerCount = (eventType: string) => {
167 | // @ts-ignore
168 | return eventEmitter.listeners(eventType).length;
169 | };
170 | } else {
171 | listenerCount = eventEmitter.listenerCount.bind(eventEmitter);
172 | }
173 |
174 | const addListener = (callback: () => void): EmitterSubscription => {
175 | if (listenerCount(EVENT_NAME) === 0) {
176 | ClipboardTurboModule.setListener();
177 | }
178 |
179 | const res = eventEmitter.addListener(EVENT_NAME, callback);
180 |
181 | // Path the remove call to also remove the native listener
182 | // if we no longer have listeners
183 | // @ts-ignore
184 | res._remove = res.remove;
185 | res.remove = function () {
186 | // @ts-ignore
187 | this._remove();
188 | if (listenerCount(EVENT_NAME) === 0) {
189 | ClipboardTurboModule.removeListener();
190 | }
191 | };
192 |
193 | return res;
194 | };
195 |
196 | const removeAllListeners = () => {
197 | eventEmitter.removeAllListeners(EVENT_NAME);
198 | ClipboardTurboModule.removeListener();
199 | };
200 |
201 | export { addListener, removeAllListeners };
202 |
--------------------------------------------------------------------------------
/macos/RNCClipboard.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0948FC9C24DCC218003F28CC /* RNCClipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 0948FC9B24DCC218003F28CC /* RNCClipboard.h */; };
11 | 0948FC9E24DCC218003F28CC /* RNCClipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 0948FC9D24DCC218003F28CC /* RNCClipboard.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXFileReference section */
15 | 0948FC9824DCC218003F28CC /* libRNCClipboard.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCClipboard.a; sourceTree = BUILT_PRODUCTS_DIR; };
16 | 0948FC9B24DCC218003F28CC /* RNCClipboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCClipboard.h; sourceTree = ""; };
17 | 0948FC9D24DCC218003F28CC /* RNCClipboard.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCClipboard.m; sourceTree = ""; };
18 | 09E83AD424EDCEB900627E7F /* RNCClipboard.shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = RNCClipboard.shared.xcconfig; sourceTree = ""; };
19 | 09E83AD524EDD01200627E7F /* RNCClipboard.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = RNCClipboard.release.xcconfig; sourceTree = ""; };
20 | 09E83AD624EDD01300627E7F /* RNCClipboard.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = RNCClipboard.debug.xcconfig; sourceTree = ""; };
21 | /* End PBXFileReference section */
22 |
23 | /* Begin PBXFrameworksBuildPhase section */
24 | 0948FC9624DCC218003F28CC /* Frameworks */ = {
25 | isa = PBXFrameworksBuildPhase;
26 | buildActionMask = 2147483647;
27 | files = (
28 | );
29 | runOnlyForDeploymentPostprocessing = 0;
30 | };
31 | /* End PBXFrameworksBuildPhase section */
32 |
33 | /* Begin PBXGroup section */
34 | 0948FC8F24DCC218003F28CC = {
35 | isa = PBXGroup;
36 | children = (
37 | 09E83AD324EDCEB900627E7F /* config */,
38 | 0948FC9924DCC218003F28CC /* Products */,
39 | 0948FC9B24DCC218003F28CC /* RNCClipboard.h */,
40 | 0948FC9D24DCC218003F28CC /* RNCClipboard.m */,
41 | );
42 | sourceTree = "";
43 | };
44 | 0948FC9924DCC218003F28CC /* Products */ = {
45 | isa = PBXGroup;
46 | children = (
47 | 0948FC9824DCC218003F28CC /* libRNCClipboard.a */,
48 | );
49 | name = Products;
50 | sourceTree = "";
51 | };
52 | 09E83AD324EDCEB900627E7F /* config */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 09E83AD624EDD01300627E7F /* RNCClipboard.debug.xcconfig */,
56 | 09E83AD524EDD01200627E7F /* RNCClipboard.release.xcconfig */,
57 | 09E83AD424EDCEB900627E7F /* RNCClipboard.shared.xcconfig */,
58 | );
59 | path = config;
60 | sourceTree = "";
61 | };
62 | /* End PBXGroup section */
63 |
64 | /* Begin PBXHeadersBuildPhase section */
65 | 0948FC9424DCC218003F28CC /* Headers */ = {
66 | isa = PBXHeadersBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | 0948FC9C24DCC218003F28CC /* RNCClipboard.h in Headers */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | /* End PBXHeadersBuildPhase section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | 0948FC9724DCC218003F28CC /* RNCClipboard */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = 0948FCA124DCC218003F28CC /* Build configuration list for PBXNativeTarget "RNCClipboard" */;
79 | buildPhases = (
80 | 0948FC9424DCC218003F28CC /* Headers */,
81 | 0948FC9524DCC218003F28CC /* Sources */,
82 | 0948FC9624DCC218003F28CC /* Frameworks */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = RNCClipboard;
89 | productName = RNCClipboard;
90 | productReference = 0948FC9824DCC218003F28CC /* libRNCClipboard.a */;
91 | productType = "com.apple.product-type.library.static";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | 0948FC9024DCC218003F28CC /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastUpgradeCheck = 1160;
100 | TargetAttributes = {
101 | 0948FC9724DCC218003F28CC = {
102 | CreatedOnToolsVersion = 11.6;
103 | };
104 | };
105 | };
106 | buildConfigurationList = 0948FC9324DCC218003F28CC /* Build configuration list for PBXProject "RNCClipboard" */;
107 | compatibilityVersion = "Xcode 9.3";
108 | developmentRegion = en;
109 | hasScannedForEncodings = 0;
110 | knownRegions = (
111 | en,
112 | Base,
113 | );
114 | mainGroup = 0948FC8F24DCC218003F28CC;
115 | productRefGroup = 0948FC9924DCC218003F28CC /* Products */;
116 | projectDirPath = "";
117 | projectRoot = "";
118 | targets = (
119 | 0948FC9724DCC218003F28CC /* RNCClipboard */,
120 | );
121 | };
122 | /* End PBXProject section */
123 |
124 | /* Begin PBXSourcesBuildPhase section */
125 | 0948FC9524DCC218003F28CC /* Sources */ = {
126 | isa = PBXSourcesBuildPhase;
127 | buildActionMask = 2147483647;
128 | files = (
129 | 0948FC9E24DCC218003F28CC /* RNCClipboard.m in Sources */,
130 | );
131 | runOnlyForDeploymentPostprocessing = 0;
132 | };
133 | /* End PBXSourcesBuildPhase section */
134 |
135 | /* Begin XCBuildConfiguration section */
136 | 0948FC9F24DCC218003F28CC /* Debug */ = {
137 | isa = XCBuildConfiguration;
138 | baseConfigurationReference = 09E83AD624EDD01300627E7F /* RNCClipboard.debug.xcconfig */;
139 | buildSettings = {
140 | };
141 | name = Debug;
142 | };
143 | 0948FCA024DCC218003F28CC /* Release */ = {
144 | isa = XCBuildConfiguration;
145 | baseConfigurationReference = 09E83AD524EDD01200627E7F /* RNCClipboard.release.xcconfig */;
146 | buildSettings = {
147 | };
148 | name = Release;
149 | };
150 | 0948FCA224DCC218003F28CC /* Debug */ = {
151 | isa = XCBuildConfiguration;
152 | baseConfigurationReference = 09E83AD424EDCEB900627E7F /* RNCClipboard.shared.xcconfig */;
153 | buildSettings = {
154 | };
155 | name = Debug;
156 | };
157 | 0948FCA324DCC218003F28CC /* Release */ = {
158 | isa = XCBuildConfiguration;
159 | baseConfigurationReference = 09E83AD424EDCEB900627E7F /* RNCClipboard.shared.xcconfig */;
160 | buildSettings = {
161 | };
162 | name = Release;
163 | };
164 | /* End XCBuildConfiguration section */
165 |
166 | /* Begin XCConfigurationList section */
167 | 0948FC9324DCC218003F28CC /* Build configuration list for PBXProject "RNCClipboard" */ = {
168 | isa = XCConfigurationList;
169 | buildConfigurations = (
170 | 0948FC9F24DCC218003F28CC /* Debug */,
171 | 0948FCA024DCC218003F28CC /* Release */,
172 | );
173 | defaultConfigurationIsVisible = 0;
174 | defaultConfigurationName = Release;
175 | };
176 | 0948FCA124DCC218003F28CC /* Build configuration list for PBXNativeTarget "RNCClipboard" */ = {
177 | isa = XCConfigurationList;
178 | buildConfigurations = (
179 | 0948FCA224DCC218003F28CC /* Debug */,
180 | 0948FCA324DCC218003F28CC /* Release */,
181 | );
182 | defaultConfigurationIsVisible = 0;
183 | defaultConfigurationName = Release;
184 | };
185 | /* End XCConfigurationList section */
186 | };
187 | rootObject = 0948FC9024DCC218003F28CC /* Project object */;
188 | }
189 |
--------------------------------------------------------------------------------
/windows/Clipboard/codegen/NativeClipboardModuleSpec.g.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * This file is auto-generated from a NativeModule spec file in js.
4 | *
5 | * This is a C++ Spec class that should be used with MakeTurboModuleProvider to register native modules
6 | * in a way that also verifies at compile time that the native module matches the interface required
7 | * by the TurboModule JS spec.
8 | */
9 | #pragma once
10 |
11 |
12 | #include
13 | #include
14 |
15 | namespace ClipboardCodegen {
16 |
17 | struct ClipboardModuleSpec : winrt::Microsoft::ReactNative::TurboModuleSpec {
18 | static constexpr auto methods = std::tuple{
19 | Method) noexcept>{0, L"getString"},
20 | Method>) noexcept>{1, L"getStrings"},
21 | Method) noexcept>{2, L"getImagePNG"},
22 | Method) noexcept>{3, L"getImageJPG"},
23 | Method) noexcept>{4, L"setImage"},
24 | Method) noexcept>{5, L"getImage"},
25 | Method{6, L"setString"},
26 | Method) noexcept>{7, L"setStrings"},
27 | Method) noexcept>{8, L"hasString"},
28 | Method) noexcept>{9, L"hasImage"},
29 | Method) noexcept>{10, L"hasURL"},
30 | Method) noexcept>{11, L"hasNumber"},
31 | Method) noexcept>{12, L"hasWebURL"},
32 | Method{13, L"setListener"},
33 | Method{14, L"removeListener"},
34 | Method{15, L"addListener"},
35 | Method{16, L"removeListeners"},
36 | };
37 |
38 | template
39 | static constexpr void ValidateModule() noexcept {
40 | constexpr auto methodCheckResults = CheckMethods();
41 |
42 | REACT_SHOW_METHOD_SPEC_ERRORS(
43 | 0,
44 | "getString",
45 | " REACT_METHOD(getString) void getString(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
46 | " REACT_METHOD(getString) static void getString(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
47 | REACT_SHOW_METHOD_SPEC_ERRORS(
48 | 1,
49 | "getStrings",
50 | " REACT_METHOD(getStrings) void getStrings(::React::ReactPromise> &&result) noexcept { /* implementation */ }\n"
51 | " REACT_METHOD(getStrings) static void getStrings(::React::ReactPromise> &&result) noexcept { /* implementation */ }\n");
52 | REACT_SHOW_METHOD_SPEC_ERRORS(
53 | 2,
54 | "getImagePNG",
55 | " REACT_METHOD(getImagePNG) void getImagePNG(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
56 | " REACT_METHOD(getImagePNG) static void getImagePNG(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
57 | REACT_SHOW_METHOD_SPEC_ERRORS(
58 | 3,
59 | "getImageJPG",
60 | " REACT_METHOD(getImageJPG) void getImageJPG(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
61 | " REACT_METHOD(getImageJPG) static void getImageJPG(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
62 | REACT_SHOW_METHOD_SPEC_ERRORS(
63 | 4,
64 | "setImage",
65 | " REACT_METHOD(setImage) void setImage(std::string content, ::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
66 | " REACT_METHOD(setImage) static void setImage(std::string content, ::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
67 | REACT_SHOW_METHOD_SPEC_ERRORS(
68 | 5,
69 | "getImage",
70 | " REACT_METHOD(getImage) void getImage(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
71 | " REACT_METHOD(getImage) static void getImage(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
72 | REACT_SHOW_METHOD_SPEC_ERRORS(
73 | 6,
74 | "setString",
75 | " REACT_METHOD(setString) void setString(std::string content) noexcept { /* implementation */ }\n"
76 | " REACT_METHOD(setString) static void setString(std::string content) noexcept { /* implementation */ }\n");
77 | REACT_SHOW_METHOD_SPEC_ERRORS(
78 | 7,
79 | "setStrings",
80 | " REACT_METHOD(setStrings) void setStrings(std::vector const & content) noexcept { /* implementation */ }\n"
81 | " REACT_METHOD(setStrings) static void setStrings(std::vector const & content) noexcept { /* implementation */ }\n");
82 | REACT_SHOW_METHOD_SPEC_ERRORS(
83 | 8,
84 | "hasString",
85 | " REACT_METHOD(hasString) void hasString(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
86 | " REACT_METHOD(hasString) static void hasString(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
87 | REACT_SHOW_METHOD_SPEC_ERRORS(
88 | 9,
89 | "hasImage",
90 | " REACT_METHOD(hasImage) void hasImage(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
91 | " REACT_METHOD(hasImage) static void hasImage(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
92 | REACT_SHOW_METHOD_SPEC_ERRORS(
93 | 10,
94 | "hasURL",
95 | " REACT_METHOD(hasURL) void hasURL(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
96 | " REACT_METHOD(hasURL) static void hasURL(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
97 | REACT_SHOW_METHOD_SPEC_ERRORS(
98 | 11,
99 | "hasNumber",
100 | " REACT_METHOD(hasNumber) void hasNumber(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
101 | " REACT_METHOD(hasNumber) static void hasNumber(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
102 | REACT_SHOW_METHOD_SPEC_ERRORS(
103 | 12,
104 | "hasWebURL",
105 | " REACT_METHOD(hasWebURL) void hasWebURL(::React::ReactPromise &&result) noexcept { /* implementation */ }\n"
106 | " REACT_METHOD(hasWebURL) static void hasWebURL(::React::ReactPromise &&result) noexcept { /* implementation */ }\n");
107 | REACT_SHOW_METHOD_SPEC_ERRORS(
108 | 13,
109 | "setListener",
110 | " REACT_METHOD(setListener) void setListener() noexcept { /* implementation */ }\n"
111 | " REACT_METHOD(setListener) static void setListener() noexcept { /* implementation */ }\n");
112 | REACT_SHOW_METHOD_SPEC_ERRORS(
113 | 14,
114 | "removeListener",
115 | " REACT_METHOD(removeListener) void removeListener() noexcept { /* implementation */ }\n"
116 | " REACT_METHOD(removeListener) static void removeListener() noexcept { /* implementation */ }\n");
117 | REACT_SHOW_METHOD_SPEC_ERRORS(
118 | 15,
119 | "addListener",
120 | " REACT_METHOD(addListener) void addListener(std::string eventName) noexcept { /* implementation */ }\n"
121 | " REACT_METHOD(addListener) static void addListener(std::string eventName) noexcept { /* implementation */ }\n");
122 | REACT_SHOW_METHOD_SPEC_ERRORS(
123 | 16,
124 | "removeListeners",
125 | " REACT_METHOD(removeListeners) void removeListeners(int count) noexcept { /* implementation */ }\n"
126 | " REACT_METHOD(removeListeners) static void removeListeners(int count) noexcept { /* implementation */ }\n");
127 | }
128 | };
129 |
130 | } // namespace ClipboardCodegen
131 |
--------------------------------------------------------------------------------
/windows/Clipboard/Unicode.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 | #include "pch.h"
4 | #include "Unicode.h"
5 | #include "Utilities.h"
6 |
7 | #include "windows.h"
8 |
9 | #include "stringapiset.h"
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | namespace Microsoft::Common::Unicode {
17 |
18 | // The implementations of the following functions heavily reference the MSDN
19 | // article at https://msdn.microsoft.com/en-us/magazine/mt763237.aspx.
20 |
21 | std::wstring Utf8ToUtf16(const char* utf8, size_t utf8Len) {
22 | std::wstring utf16{};
23 |
24 | // A small optimization.
25 | if (utf8Len == 0) {
26 | return utf16;
27 | }
28 |
29 | // Extra parentheses needed here to prevent expanding max as a
30 | // Windows-specific preprocessor macro.
31 | if (utf8Len > static_cast((std::numeric_limits::max)())) {
32 | throw std::overflow_error("Length of input string to Utf8ToUtf16() must fit into an int.");
33 | }
34 |
35 | const int utf8Length = static_cast(utf8Len);
36 |
37 | // We do not specify MB_ERR_INVALID_CHARS here, which means that invalid UTF-8
38 | // characters are replaced with U+FFFD.
39 | constexpr DWORD flags = 0;
40 |
41 | const int utf16Length = ::MultiByteToWideChar(
42 | CP_UTF8, // Source string is in UTF-8.
43 | flags, // Conversion flags.
44 | utf8, // Source UTF-8 string pointer.
45 | utf8Length, // Length of the source UTF-8 string, in chars.
46 | nullptr, // Do not convert during this step, instead, request the size
47 | 0 // of the destination buffer, in wchar_ts, excluding the
48 | // null termination character.
49 | );
50 |
51 | if (utf16Length == 0) {
52 | throw UnicodeConversionException(
53 | "Cannot get result string length when converting from UTF-8 to UTF-16 "
54 | "(MultiByteToWideChar failed).",
55 | GetLastError());
56 | }
57 |
58 | // Note that because the length of the input UTF-8 string was explicitly
59 | // passed to MultiByteToWideChar (instead of just passing -1 and asking
60 | // MultiByteToWideChar to scan the whole input string until a null terminator
61 | // is found), MultiByteToWideChar won't add an additional null terminator to
62 | // the result string. Therefore, there's no need to invoke
63 | // std::wstring::resize with a "utf16Length + 1" value.
64 | utf16.resize(utf16Length);
65 |
66 | // Convert from UTF-8 to UTF-16
67 | // Note that MultiByteToWideChar converts the UTF-8 BOM into the UTF-16BE BOM.
68 | // So we do not have to do anything extra here to ensure correct BOM behavior.
69 | int result = ::MultiByteToWideChar(
70 | CP_UTF8, // Source string is in UTF-8.
71 | flags, // Conversion flags.
72 | utf8, // Source UTF-8 string pointer.
73 | utf8Length, // Length of source UTF-8 string, in chars.
74 | &utf16[0], // Pointer to destination buffer. This is fine because the
75 | // the C++11 standard specifies that the elements of a
76 | // std::basic_string are stored continuously.
77 | utf16Length // Size of destination buffer, in wchar_ts.
78 | );
79 |
80 | if (result == 0) {
81 | throw UnicodeConversionException(
82 | "Cannot convert from UTF-8 to UTF-16 (MultiByteToWideChar failed).", GetLastError());
83 | }
84 |
85 | return utf16;
86 | }
87 |
88 | std::wstring Utf8ToUtf16(const char* utf8) {
89 | return Utf8ToUtf16(utf8, strlen(utf8));
90 | }
91 |
92 | std::wstring Utf8ToUtf16(const std::string& utf8) {
93 | return Utf8ToUtf16(utf8.c_str(), utf8.length());
94 | }
95 |
96 | #if _HAS_CXX17
97 | std::wstring Utf8ToUtf16(const std::string_view& utf8) {
98 | return Utf8ToUtf16(utf8.data(), utf8.length());
99 | }
100 | #endif
101 |
102 | std::string Utf16ToUtf8(const wchar_t* utf16, size_t utf16Len) {
103 | std::string utf8{};
104 |
105 | // A small optimization.
106 | if (utf16Len == 0) {
107 | return utf8;
108 | }
109 |
110 | // Extra parentheses needed here to prevent expanding max as a
111 | // Windows-specific preprocessor macro.
112 | if (utf16Len > static_cast((std::numeric_limits::max)())) {
113 | throw std::overflow_error("Length of input string to Utf16ToUtf8() must fit into an int.");
114 | }
115 |
116 | const int utf16Length = static_cast(utf16Len);
117 |
118 | // We do not specify WC_ERR_INVALID_CHARS here, which means that invalid
119 | // UTF-16 characters are replaced with U+FFFD.
120 | constexpr DWORD flags = 0;
121 |
122 | const int utf8Length = ::WideCharToMultiByte(
123 | CP_UTF8, // Destination string is in UTF-8.
124 | flags, // Conversion flags.
125 | utf16, // Source UTF-16 string pointer.
126 | utf16Length, // Length of the source UTF-16 string, in wchar_ts.
127 | nullptr, // Do not convert during this step, instead, request the size
128 | 0, // of the destination buffer, in chars, excluding the
129 | // null termination character.
130 | nullptr, // WideCharToMultiByte requires the last two parameters to be
131 | nullptr // nullptrs when converting to UTF-8.
132 | );
133 |
134 | if (utf8Length == 0) {
135 | throw UnicodeConversionException(
136 | "Cannot get result string length when converting from UTF-16 to UTF-8 "
137 | "(WideCharToMultiByte failed).",
138 | GetLastError());
139 | }
140 |
141 | // Note that because the length of the input UTF-16 string was explicitly
142 | // passed to WideCharToMultiByte (instead of just passing -1 and asking
143 | // WideCharToMultiByte to scan the whole input string until a null terminator
144 | // is found), WideCharToMultiByte won't add an additional null terminator to
145 | // the result string. Therefore, there's no need to invoke
146 | // std::string::resize with a "utf8Length + 1" value.
147 | utf8.resize(utf8Length);
148 |
149 | // Convert from UTF-8 to UTF-16
150 | // Note that MultiByteToWideChar converts the UTF-8 BOM into the UTF-16BE BOM.
151 | // So we do not have to do anything extra here to ensure correct BOM behavior.
152 | int result = ::WideCharToMultiByte(
153 | CP_UTF8, // Destination string is in UTF-8.
154 | flags, // Conversion flags.
155 | utf16, // Source UTF-16 string pointer.
156 | utf16Length, // Length of the source UTF-16 string, in wchar_ts.
157 | &utf8[0], // Pointer to destination buffer. This is fine because the
158 | // the C++11 standard specifies that the elements of a
159 | // std::basic_string are stored continuously.
160 | utf8Length, // Size of destination buffer, in chars.
161 | nullptr, // WideCharToMultiByte requires the last two parameters to be
162 | nullptr // nullptrs when converting to UTF-8.
163 | );
164 |
165 | if (result == 0) {
166 | throw UnicodeConversionException(
167 | "Cannot convert from UTF-16 to UTF-8 (WideCharToMultiByte failed).", GetLastError());
168 | }
169 |
170 | return utf8;
171 | }
172 |
173 | std::string Utf16ToUtf8(const char16_t* utf16, size_t utf16Len) {
174 | return Utf16ToUtf8(Utilities::CheckedReinterpretCast(utf16), utf16Len);
175 | }
176 |
177 | std::string Utf16ToUtf8(const wchar_t* utf16) {
178 | return Utf16ToUtf8(utf16, wcslen(utf16));
179 | }
180 |
181 | std::string Utf16ToUtf8(const char16_t* utf16) {
182 | return Utf16ToUtf8(utf16, std::char_traits::length(utf16));
183 | }
184 |
185 | std::string Utf16ToUtf8(const std::wstring& utf16) {
186 | return Utf16ToUtf8(utf16.c_str(), utf16.length());
187 | }
188 |
189 | std::string Utf16ToUtf8(const std::u16string& utf16) {
190 | return Utf16ToUtf8(Utilities::CheckedReinterpretCast(utf16.c_str()), utf16.length());
191 | }
192 |
193 | #if _HAS_CXX17
194 | std::string Utf16ToUtf8(const std::wstring_view& utf16) {
195 | return Utf16ToUtf8(utf16.data(), utf16.length());
196 | }
197 |
198 | std::string Utf16ToUtf8(const std::u16string_view& utf16) {
199 | return Utf16ToUtf8(Utilities::CheckedReinterpretCast(utf16.data()), utf16.length());
200 | }
201 | #endif
202 |
203 | } // namespace Microsoft::Common::Unicode
204 |
--------------------------------------------------------------------------------
/android/src/main/java/com/reactnativecommunity/clipboard/ClipboardModule.java:
--------------------------------------------------------------------------------
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 | package com.reactnativecommunity.clipboard;
9 |
10 | import android.annotation.TargetApi;
11 | import android.content.ClipDescription;
12 | import android.content.ClipboardManager;
13 | import android.content.ClipData;
14 | import android.content.ContentResolver;
15 | import android.content.Context;
16 | import android.graphics.Bitmap;
17 | import android.net.Uri;
18 | import android.os.Build;
19 | import android.provider.MediaStore;
20 | import android.util.Base64;
21 | import android.util.Log;
22 |
23 | import com.facebook.react.bridge.ReactApplicationContext;
24 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
25 | import com.facebook.react.bridge.ReactMethod;
26 | import com.facebook.react.bridge.Promise;
27 | import com.facebook.react.bridge.ReadableArray;
28 | import com.facebook.react.module.annotations.ReactModule;
29 | import com.facebook.react.modules.core.DeviceEventManagerModule;
30 |
31 | import java.io.ByteArrayOutputStream;
32 | import java.io.IOException;
33 | import java.nio.ByteBuffer;
34 |
35 | /**
36 | * A module that allows JS to get/set clipboard contents.
37 | */
38 | @ReactModule(name = ClipboardModule.NAME)
39 | public class ClipboardModule extends NativeClipboardModuleSpec {
40 |
41 | public static final String CLIPBOARD_TEXT_CHANGED = "RNCClipboard_TEXT_CHANGED";
42 | private ReactApplicationContext reactContext;
43 | private ClipboardManager.OnPrimaryClipChangedListener listener = null;
44 |
45 | public ClipboardModule(ReactApplicationContext reactContext) {
46 | super(reactContext);
47 | this.reactContext = reactContext;
48 | }
49 |
50 | public static final String NAME = "RNCClipboard";
51 | public static final String MIMETYPE_JPEG = "image/jpeg";
52 | public static final String MIMETYPE_JPG = "image/jpg";
53 | public static final String MIMETYPE_PNG = "image/png";
54 | public static final String MIMETYPE_WEBP = "image/webp";
55 | public static final String MIMETYPE_HEIC = "image/heic";
56 | public static final String MIMETYPE_HEIF = "image/heif";
57 |
58 | @Override
59 | public String getName() {
60 | return ClipboardModule.NAME;
61 | }
62 |
63 | private ClipboardManager getClipboardService() {
64 | return (ClipboardManager) reactContext.getSystemService(Context.CLIPBOARD_SERVICE);
65 | }
66 |
67 | @ReactMethod
68 | public void getString(Promise promise) {
69 | try {
70 | ClipboardManager clipboard = getClipboardService();
71 | ClipData clipData = clipboard.getPrimaryClip();
72 | if (clipData != null && clipData.getItemCount() >= 1) {
73 | ClipData.Item firstItem = clipData.getItemAt(0);
74 | promise.resolve("" + firstItem.getText());
75 | } else {
76 | promise.resolve("");
77 | }
78 | } catch (Exception e) {
79 | promise.reject(e);
80 | }
81 | }
82 |
83 | @Override
84 | public void getStrings(Promise promise) {
85 | promise.reject("Clipboard:getStrings", "getStrings is not supported on Android");
86 | }
87 |
88 | @Override
89 | public void getImagePNG(Promise promise) {
90 | promise.reject("Clipboard:getImagePNG", "getImagePNG is not supported on Android");
91 | }
92 |
93 | @Override
94 | public void getImageJPG(Promise promise) {
95 | promise.reject("Clipboard:getImageJPG", "getImageJPG is not supported on Android");
96 | }
97 |
98 | @Override
99 | public void setImage(String content, Promise promise) {
100 | promise.reject("Clipboard:setImage", "setImage is not supported on Android");
101 | }
102 |
103 | @ReactMethod
104 | public void setString(String text) {
105 | try {
106 | ClipData clipdata = ClipData.newPlainText(null, text);
107 | ClipboardManager clipboard = getClipboardService();
108 | clipboard.setPrimaryClip(clipdata);
109 | } catch (Exception e) {
110 | e.printStackTrace();
111 | }
112 | }
113 |
114 | @Override
115 | public void setStrings(ReadableArray content) {
116 |
117 | }
118 |
119 | @ReactMethod
120 | public void hasString(Promise promise) {
121 | try {
122 | ClipboardManager clipboard = getClipboardService();
123 | ClipData clipData = clipboard.getPrimaryClip();
124 | promise.resolve(clipData != null && clipData.getItemCount() >= 1);
125 | } catch (Exception e) {
126 | promise.reject(e);
127 | }
128 | }
129 |
130 | @Override
131 | public void hasImage(Promise promise) {
132 | promise.reject("Clipboard:hasImage", "hasImage is not supported on Android");
133 | }
134 |
135 | @Override
136 | public void hasURL(Promise promise) {
137 | promise.reject("Clipboard:hasURL", "hasURL is not supported on Android");
138 | }
139 |
140 | @Override
141 | public void hasNumber(Promise promise) {
142 | promise.reject("Clipboard:hasNumber", "hasNumber is not supported on Android");
143 | }
144 |
145 | @Override
146 | public void hasWebURL(Promise promise) {
147 | promise.reject("Clipboard:hasWebURL", "hasWebURL is not supported on Android");
148 | }
149 |
150 | @ReactMethod
151 | public void getImage(Promise promise){
152 | ClipboardManager clipboardManager = getClipboardService();
153 | if (!(clipboardManager.hasPrimaryClip())){
154 | promise.resolve("");
155 | }
156 | else if (clipboardManager.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)){
157 | promise.resolve("");
158 | }
159 | else {
160 | ClipData clipData = clipboardManager.getPrimaryClip();
161 | if(clipData != null){
162 | ClipData.Item item = clipData.getItemAt(0);
163 | Uri pasteUri = item.getUri();
164 | if (pasteUri != null){
165 | ContentResolver cr = reactContext.getContentResolver();
166 | String mimeType = cr.getType(pasteUri);
167 | if (mimeType != null){
168 | try {
169 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(cr, pasteUri);
170 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
171 | switch(mimeType){
172 | case MIMETYPE_JPEG:
173 | case MIMETYPE_JPG:
174 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
175 | break;
176 | case MIMETYPE_PNG:
177 | case MIMETYPE_HEIC:
178 | case MIMETYPE_HEIF:
179 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
180 | break;
181 | case MIMETYPE_WEBP:
182 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q){
183 | bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, outputStream);
184 | break;
185 | }
186 | bitmap.compress(Bitmap.CompressFormat.WEBP, 100, outputStream);
187 | break;
188 | default:
189 | return;
190 | }
191 | byte[] byteArray = outputStream.toByteArray();
192 | String encodedString = Base64.encodeToString(byteArray, Base64.DEFAULT);
193 | promise.resolve("data:" + mimeType + ";base64," + encodedString);
194 | } catch (IOException e) {
195 | promise.reject(e);
196 | e.printStackTrace();
197 | }
198 | }
199 | }
200 | }
201 | promise.resolve("");
202 | }
203 | }
204 |
205 | @ReactMethod
206 | public void setListener() {
207 | try {
208 | ClipboardManager clipboard = getClipboardService();
209 | listener = new ClipboardManager.OnPrimaryClipChangedListener() {
210 | @Override
211 | public void onPrimaryClipChanged() {
212 | reactContext
213 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
214 | .emit(CLIPBOARD_TEXT_CHANGED, null);
215 | }
216 | };
217 | clipboard.addPrimaryClipChangedListener(listener);
218 | } catch (Exception e) {
219 | e.printStackTrace();
220 | }
221 | }
222 |
223 | @ReactMethod
224 | public void removeListener() {
225 | if(listener != null){
226 | try{
227 | ClipboardManager clipboard = getClipboardService();
228 | clipboard.removePrimaryClipChangedListener(listener);
229 | } catch (Exception e) {
230 | e.printStackTrace();
231 | }
232 | }
233 | }
234 |
235 | @ReactMethod
236 | public void addListener(String eventName) {
237 | // Keep: Required for RN built in Event Emitter Calls.
238 | }
239 |
240 | @Override
241 | public void removeListeners(double count) {
242 |
243 | }
244 |
245 | @ReactMethod
246 | public void removeListeners(Integer count) {
247 | // Keep: Required for RN built in Event Emitter Calls.
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/ios/RNCClipboard.mm:
--------------------------------------------------------------------------------
1 | #import "RNCClipboard.h"
2 |
3 | #import
4 | #import
5 | #import
6 | #import
7 | #import
8 |
9 |
10 | @implementation RNCClipboard {
11 | BOOL isObserving;
12 | }
13 |
14 | RCT_EXPORT_MODULE();
15 |
16 | NSString *const CLIPBOARD_TEXT_CHANGED = @"RNCClipboard_TEXT_CHANGED";
17 |
18 | + (BOOL)requiresMainQueueSetup {
19 | return YES;
20 | }
21 |
22 | -(id) init {
23 | if (self = [super init]) {
24 | isObserving = NO;
25 | }
26 | return self;
27 | }
28 |
29 | - (dispatch_queue_t)methodQueue
30 | {
31 | return dispatch_get_main_queue();
32 | }
33 |
34 | - (NSArray *)supportedEvents
35 | {
36 | return @[CLIPBOARD_TEXT_CHANGED];
37 | }
38 |
39 | - (void)startObserving {
40 | isObserving = YES;
41 | }
42 |
43 | -(void)stopObserving {
44 | isObserving = NO;
45 | }
46 |
47 | - (void) listener:(NSNotification *) notification
48 | {
49 | if (isObserving) {
50 | [self sendEventWithName:CLIPBOARD_TEXT_CHANGED body:nil];
51 | }
52 | }
53 |
54 | RCT_EXPORT_METHOD(setListener)
55 | {
56 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listener:) name:UIPasteboardChangedNotification object:nil];
57 | }
58 |
59 | RCT_EXPORT_METHOD(removeListener)
60 | {
61 | [[NSNotificationCenter defaultCenter] removeObserver:self];
62 | }
63 |
64 | RCT_EXPORT_METHOD(setString:(NSString *)content)
65 | {
66 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
67 | clipboard.string = (content ? : @"");
68 | }
69 |
70 | RCT_EXPORT_METHOD(getString:(RCTPromiseResolveBlock)resolve
71 | reject:(__unused RCTPromiseRejectBlock)reject)
72 | {
73 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
74 | resolve((clipboard.string ? : @""));
75 | }
76 |
77 | RCT_EXPORT_METHOD(setStrings:(NSArray *)array)
78 | {
79 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
80 | clipboard.strings = (array ? : @[]);
81 | }
82 |
83 | RCT_EXPORT_METHOD(getStrings:(RCTPromiseResolveBlock)resolve
84 | reject:(__unused RCTPromiseRejectBlock)reject)
85 | {
86 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
87 | resolve((clipboard.strings ? : @[]));
88 | }
89 |
90 | RCT_EXPORT_METHOD(setImage:(NSString *)content
91 | resolve:(RCTPromiseResolveBlock) resolve
92 | reject:(RCTPromiseRejectBlock) reject
93 | ) {
94 | @try {
95 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
96 | NSData *imageData = [[NSData alloc]initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
97 | [pasteboard setImage:[UIImage imageWithData:imageData]];
98 | resolve(nil);
99 | }
100 | @catch (NSException *exception) {
101 | reject(@"Clipboard:setImage", exception.reason, nil);
102 | }
103 | }
104 |
105 |
106 | RCT_EXPORT_METHOD(getImagePNG:(RCTPromiseResolveBlock)resolve
107 | reject:(__unused RCTPromiseRejectBlock)reject)
108 | {
109 | NSString *pngPrefix = @"data:image/png;base64,";
110 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
111 | UIImage *clipboardImage = clipboard.image;
112 | if (!clipboardImage) {
113 | resolve(NULL);
114 | } else {
115 | NSString *imageDataBase64 = [UIImagePNGRepresentation(clipboardImage) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
116 | NSString *withPrefix = [pngPrefix stringByAppendingString:imageDataBase64];
117 | resolve((withPrefix ? : NULL));
118 | }
119 | }
120 |
121 | RCT_EXPORT_METHOD(getImageJPG:(RCTPromiseResolveBlock)resolve
122 | reject:(__unused RCTPromiseRejectBlock)reject)
123 | {
124 | NSString *jpgPrefix = @"data:image/jpeg;base64,";
125 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
126 | UIImage *clipboardImage = clipboard.image;
127 | if (!clipboardImage) {
128 | resolve(NULL);
129 | } else {
130 | NSString *imageDataBase64 = [UIImageJPEGRepresentation(clipboardImage, 1.0) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
131 | NSString *withPrefix = [jpgPrefix stringByAppendingString:imageDataBase64];
132 | resolve((withPrefix ? : NULL));
133 | }
134 | }
135 |
136 | RCT_EXPORT_METHOD(hasImage:(RCTPromiseResolveBlock)resolve
137 | reject:(__unused RCTPromiseRejectBlock)reject)
138 | {
139 | BOOL imagePresent = YES;
140 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
141 | if (@available(iOS 10, *)) {
142 | imagePresent = clipboard.hasImages;
143 | } else {
144 | UIImage *imageInPasteboard = clipboard.image;
145 | imagePresent = imageInPasteboard != nil;
146 | }
147 | resolve([NSNumber numberWithBool: imagePresent]);
148 | }
149 |
150 | RCT_EXPORT_METHOD(hasString:(RCTPromiseResolveBlock)resolve
151 | reject:(__unused RCTPromiseRejectBlock)reject)
152 | {
153 | BOOL stringPresent = YES;
154 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
155 | if (@available(iOS 10, *)) {
156 | stringPresent = clipboard.hasStrings;
157 | } else {
158 | NSString* stringInPasteboard = clipboard.string;
159 | stringPresent = [stringInPasteboard length] == 0;
160 | }
161 |
162 | resolve([NSNumber numberWithBool: stringPresent]);
163 | }
164 |
165 | RCT_EXPORT_METHOD(hasURL:(RCTPromiseResolveBlock)resolve
166 | reject:(__unused RCTPromiseRejectBlock)reject)
167 | {
168 | BOOL urlPresent = NO;
169 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
170 | if (@available(iOS 10, *)) {
171 | urlPresent = clipboard.hasURLs;
172 | }
173 | resolve([NSNumber numberWithBool: urlPresent]);
174 | }
175 |
176 | RCT_EXPORT_METHOD(hasNumber:(RCTPromiseResolveBlock)resolve
177 | reject:(__unused RCTPromiseRejectBlock)reject)
178 | {
179 | if (@available(iOS 14, *)) {
180 | UIPasteboard *board = [UIPasteboard generalPasteboard];
181 | [board detectPatternsForPatterns:[NSSet setWithObjects:UIPasteboardDetectionPatternProbableWebURL, UIPasteboardDetectionPatternNumber, UIPasteboardDetectionPatternProbableWebSearch, nil]
182 | completionHandler:^(NSSet * _Nullable set, __unused NSError * _Nullable error) {
183 | BOOL numberPresent = NO;
184 | for (NSString *type in set) {
185 | if ([type isEqualToString:UIPasteboardDetectionPatternNumber]) {
186 | numberPresent = YES;
187 | }
188 | }
189 | resolve([NSNumber numberWithBool: numberPresent]);
190 | }];
191 | } else {
192 | resolve([NSNumber numberWithBool: NO]);
193 | }
194 | }
195 |
196 | RCT_EXPORT_METHOD(hasWebURL:(RCTPromiseResolveBlock)resolve
197 | reject:(__unused RCTPromiseRejectBlock)reject)
198 | {
199 | if (@available(iOS 14, *)) {
200 | UIPasteboard *board = [UIPasteboard generalPasteboard];
201 | [board detectPatternsForPatterns:[NSSet setWithObjects:UIPasteboardDetectionPatternProbableWebURL, UIPasteboardDetectionPatternNumber, UIPasteboardDetectionPatternProbableWebSearch, nil]
202 | completionHandler:^(NSSet * _Nullable set, __unused NSError * _Nullable error) {
203 | BOOL webURLPresent = NO;
204 | for (NSString *type in set) {
205 | if ([type isEqualToString:UIPasteboardDetectionPatternProbableWebURL]) {
206 | webURLPresent = YES;
207 | }
208 | }
209 | resolve([NSNumber numberWithBool: webURLPresent]);
210 | }];
211 | } else {
212 | resolve([NSNumber numberWithBool: NO]);
213 | }
214 |
215 | }
216 |
217 | RCT_EXPORT_METHOD(getImage:(RCTPromiseResolveBlock)resolve
218 | reject:(__unused RCTPromiseRejectBlock)reject){
219 | UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
220 | NSString *withPrefix;
221 | for (NSItemProvider *itemProvider in clipboard.itemProviders) {
222 | if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
223 | for (NSString *identifier in itemProvider.registeredTypeIdentifiers) {
224 | if (UTTypeConformsTo((__bridge CFStringRef)identifier, kUTTypeImage)) {
225 | NSString *MIMEType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)identifier, kUTTagClassMIMEType);
226 | NSString *imageDataBase64 = [[clipboard dataForPasteboardType:identifier] base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
227 | withPrefix = [NSString stringWithFormat:@"data:%@;base64,%@", MIMEType, imageDataBase64];
228 | break;
229 | }
230 | }
231 | break;
232 | }
233 | }
234 | resolve((withPrefix ? : NULL));
235 | }
236 |
237 | RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
238 | // Keep: Required for RN built in Event Emitter Calls.
239 | }
240 |
241 | RCT_EXPORT_METHOD(removeListeners : (double)count) {
242 | // Keep: Required for RN built in Event Emitter Calls.
243 | }
244 |
245 | #if RCT_NEW_ARCH_ENABLED
246 | - (std::shared_ptr)getTurboModule:
247 | (const facebook::react::ObjCTurboModule::InitParams &)params
248 | {
249 | return std::make_shared(params);
250 | }
251 | #endif
252 |
253 |
254 | @end
255 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/ios/RNCClipboard.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 95AF303423BEF6A2001AC7C4 /* RNCClipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = 95AF303323BEF6A2001AC7C4 /* RNCClipboard.m */; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXCopyFilesBuildPhase section */
14 | 95AF302423BEF5FD001AC7C4 /* CopyFiles */ = {
15 | isa = PBXCopyFilesBuildPhase;
16 | buildActionMask = 2147483647;
17 | dstPath = "include/$(PRODUCT_NAME)";
18 | dstSubfolderSpec = 16;
19 | files = (
20 | );
21 | runOnlyForDeploymentPostprocessing = 0;
22 | };
23 | /* End PBXCopyFilesBuildPhase section */
24 |
25 | /* Begin PBXFileReference section */
26 | 95AF302623BEF5FD001AC7C4 /* libRNCClipboard.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCClipboard.a; sourceTree = BUILT_PRODUCTS_DIR; };
27 | 95AF303223BEF6A1001AC7C4 /* RNCClipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCClipboard.h; sourceTree = ""; };
28 | 95AF303323BEF6A2001AC7C4 /* RNCClipboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCClipboard.m; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 95AF302323BEF5FD001AC7C4 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 95AF301D23BEF5FD001AC7C4 = {
43 | isa = PBXGroup;
44 | children = (
45 | 95AF302723BEF5FD001AC7C4 /* Products */,
46 | 95AF303223BEF6A1001AC7C4 /* RNCClipboard.h */,
47 | 95AF303323BEF6A2001AC7C4 /* RNCClipboard.m */,
48 | );
49 | sourceTree = "";
50 | };
51 | 95AF302723BEF5FD001AC7C4 /* Products */ = {
52 | isa = PBXGroup;
53 | children = (
54 | 95AF302623BEF5FD001AC7C4 /* libRNCClipboard.a */,
55 | );
56 | name = Products;
57 | sourceTree = "";
58 | };
59 | /* End PBXGroup section */
60 |
61 | /* Begin PBXNativeTarget section */
62 | 95AF302523BEF5FD001AC7C4 /* RNCClipboard */ = {
63 | isa = PBXNativeTarget;
64 | buildConfigurationList = 95AF302F23BEF5FD001AC7C4 /* Build configuration list for PBXNativeTarget "RNCClipboard" */;
65 | buildPhases = (
66 | 95AF302223BEF5FD001AC7C4 /* Sources */,
67 | 95AF302323BEF5FD001AC7C4 /* Frameworks */,
68 | 95AF302423BEF5FD001AC7C4 /* CopyFiles */,
69 | );
70 | buildRules = (
71 | );
72 | dependencies = (
73 | );
74 | name = RNCClipboard;
75 | productName = RNCClipboard;
76 | productReference = 95AF302623BEF5FD001AC7C4 /* libRNCClipboard.a */;
77 | productType = "com.apple.product-type.library.static";
78 | };
79 | /* End PBXNativeTarget section */
80 |
81 | /* Begin PBXProject section */
82 | 95AF301E23BEF5FD001AC7C4 /* Project object */ = {
83 | isa = PBXProject;
84 | attributes = {
85 | LastUpgradeCheck = 1130;
86 | ORGANIZATIONNAME = "Jesse Katsumata";
87 | TargetAttributes = {
88 | 95AF302523BEF5FD001AC7C4 = {
89 | CreatedOnToolsVersion = 11.3;
90 | };
91 | };
92 | };
93 | buildConfigurationList = 95AF302123BEF5FD001AC7C4 /* Build configuration list for PBXProject "RNCClipboard" */;
94 | compatibilityVersion = "Xcode 9.3";
95 | developmentRegion = en;
96 | hasScannedForEncodings = 0;
97 | knownRegions = (
98 | en,
99 | Base,
100 | );
101 | mainGroup = 95AF301D23BEF5FD001AC7C4;
102 | productRefGroup = 95AF302723BEF5FD001AC7C4 /* Products */;
103 | projectDirPath = "";
104 | projectRoot = "";
105 | targets = (
106 | 95AF302523BEF5FD001AC7C4 /* RNCClipboard */,
107 | );
108 | };
109 | /* End PBXProject section */
110 |
111 | /* Begin PBXSourcesBuildPhase section */
112 | 95AF302223BEF5FD001AC7C4 /* Sources */ = {
113 | isa = PBXSourcesBuildPhase;
114 | buildActionMask = 2147483647;
115 | files = (
116 | 95AF303423BEF6A2001AC7C4 /* RNCClipboard.m in Sources */,
117 | );
118 | runOnlyForDeploymentPostprocessing = 0;
119 | };
120 | /* End PBXSourcesBuildPhase section */
121 |
122 | /* Begin XCBuildConfiguration section */
123 | 95AF302D23BEF5FD001AC7C4 /* Debug */ = {
124 | isa = XCBuildConfiguration;
125 | buildSettings = {
126 | ALWAYS_SEARCH_USER_PATHS = NO;
127 | CLANG_ANALYZER_NONNULL = YES;
128 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
129 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
130 | CLANG_CXX_LIBRARY = "libc++";
131 | CLANG_ENABLE_MODULES = YES;
132 | CLANG_ENABLE_OBJC_ARC = YES;
133 | CLANG_ENABLE_OBJC_WEAK = YES;
134 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
135 | CLANG_WARN_BOOL_CONVERSION = YES;
136 | CLANG_WARN_COMMA = YES;
137 | CLANG_WARN_CONSTANT_CONVERSION = YES;
138 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
139 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
140 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
141 | CLANG_WARN_EMPTY_BODY = YES;
142 | CLANG_WARN_ENUM_CONVERSION = YES;
143 | CLANG_WARN_INFINITE_RECURSION = YES;
144 | CLANG_WARN_INT_CONVERSION = YES;
145 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
146 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
147 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
148 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
149 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
150 | CLANG_WARN_STRICT_PROTOTYPES = YES;
151 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
152 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
153 | CLANG_WARN_UNREACHABLE_CODE = YES;
154 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
155 | COPY_PHASE_STRIP = NO;
156 | DEBUG_INFORMATION_FORMAT = dwarf;
157 | ENABLE_STRICT_OBJC_MSGSEND = YES;
158 | ENABLE_TESTABILITY = YES;
159 | GCC_C_LANGUAGE_STANDARD = gnu11;
160 | GCC_DYNAMIC_NO_PIC = NO;
161 | GCC_NO_COMMON_BLOCKS = YES;
162 | GCC_OPTIMIZATION_LEVEL = 0;
163 | GCC_PREPROCESSOR_DEFINITIONS = (
164 | "DEBUG=1",
165 | "$(inherited)",
166 | );
167 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
168 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
169 | GCC_WARN_UNDECLARED_SELECTOR = YES;
170 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
171 | GCC_WARN_UNUSED_FUNCTION = YES;
172 | GCC_WARN_UNUSED_VARIABLE = YES;
173 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
174 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
175 | MTL_FAST_MATH = YES;
176 | ONLY_ACTIVE_ARCH = YES;
177 | SDKROOT = iphoneos;
178 | };
179 | name = Debug;
180 | };
181 | 95AF302E23BEF5FD001AC7C4 /* Release */ = {
182 | isa = XCBuildConfiguration;
183 | buildSettings = {
184 | ALWAYS_SEARCH_USER_PATHS = NO;
185 | CLANG_ANALYZER_NONNULL = YES;
186 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
188 | CLANG_CXX_LIBRARY = "libc++";
189 | CLANG_ENABLE_MODULES = YES;
190 | CLANG_ENABLE_OBJC_ARC = YES;
191 | CLANG_ENABLE_OBJC_WEAK = YES;
192 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
193 | CLANG_WARN_BOOL_CONVERSION = YES;
194 | CLANG_WARN_COMMA = YES;
195 | CLANG_WARN_CONSTANT_CONVERSION = YES;
196 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
197 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
198 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
199 | CLANG_WARN_EMPTY_BODY = YES;
200 | CLANG_WARN_ENUM_CONVERSION = YES;
201 | CLANG_WARN_INFINITE_RECURSION = YES;
202 | CLANG_WARN_INT_CONVERSION = YES;
203 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
204 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
205 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
206 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
207 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
208 | CLANG_WARN_STRICT_PROTOTYPES = YES;
209 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
210 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
211 | CLANG_WARN_UNREACHABLE_CODE = YES;
212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
213 | COPY_PHASE_STRIP = NO;
214 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
215 | ENABLE_NS_ASSERTIONS = NO;
216 | ENABLE_STRICT_OBJC_MSGSEND = YES;
217 | GCC_C_LANGUAGE_STANDARD = gnu11;
218 | GCC_NO_COMMON_BLOCKS = YES;
219 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
220 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
221 | GCC_WARN_UNDECLARED_SELECTOR = YES;
222 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
223 | GCC_WARN_UNUSED_FUNCTION = YES;
224 | GCC_WARN_UNUSED_VARIABLE = YES;
225 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
226 | MTL_ENABLE_DEBUG_INFO = NO;
227 | MTL_FAST_MATH = YES;
228 | SDKROOT = iphoneos;
229 | VALIDATE_PRODUCT = YES;
230 | };
231 | name = Release;
232 | };
233 | 95AF303023BEF5FD001AC7C4 /* Debug */ = {
234 | isa = XCBuildConfiguration;
235 | buildSettings = {
236 | CODE_SIGN_STYLE = Automatic;
237 | OTHER_LDFLAGS = "-ObjC";
238 | PRODUCT_NAME = "$(TARGET_NAME)";
239 | SKIP_INSTALL = YES;
240 | TARGETED_DEVICE_FAMILY = "1,2";
241 | };
242 | name = Debug;
243 | };
244 | 95AF303123BEF5FD001AC7C4 /* Release */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | CODE_SIGN_STYLE = Automatic;
248 | OTHER_LDFLAGS = "-ObjC";
249 | PRODUCT_NAME = "$(TARGET_NAME)";
250 | SKIP_INSTALL = YES;
251 | TARGETED_DEVICE_FAMILY = "1,2";
252 | };
253 | name = Release;
254 | };
255 | /* End XCBuildConfiguration section */
256 |
257 | /* Begin XCConfigurationList section */
258 | 95AF302123BEF5FD001AC7C4 /* Build configuration list for PBXProject "RNCClipboard" */ = {
259 | isa = XCConfigurationList;
260 | buildConfigurations = (
261 | 95AF302D23BEF5FD001AC7C4 /* Debug */,
262 | 95AF302E23BEF5FD001AC7C4 /* Release */,
263 | );
264 | defaultConfigurationIsVisible = 0;
265 | defaultConfigurationName = Release;
266 | };
267 | 95AF302F23BEF5FD001AC7C4 /* Build configuration list for PBXNativeTarget "RNCClipboard" */ = {
268 | isa = XCConfigurationList;
269 | buildConfigurations = (
270 | 95AF303023BEF5FD001AC7C4 /* Debug */,
271 | 95AF303123BEF5FD001AC7C4 /* Release */,
272 | );
273 | defaultConfigurationIsVisible = 0;
274 | defaultConfigurationName = Release;
275 | };
276 | /* End XCConfigurationList section */
277 | };
278 | rootObject = 95AF301E23BEF5FD001AC7C4 /* Project object */;
279 | }
280 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @react-native-clipboard/clipboard
2 |
3 | [![Build Status][build-badge]][build]
4 | [![Version][version-badge]][package]
5 | ![Supports macOS, iOS, Android, and Windows][support-badge]
6 | [![MIT License][license-badge]][license]
7 | [![Lean Core Badge][lean-core-badge]][lean-core-issue]
8 |
9 | React Native Clipboard API for macOS, iOS, Android, and Windows.
10 |
11 | | macOS | iOS | Android | Windows |
12 | | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
13 | |
|
|
|
|
14 |
15 | ## Getting started
16 |
17 | Install the library using either Yarn:
18 |
19 | ```
20 | yarn add @react-native-clipboard/clipboard
21 | ```
22 |
23 | or npm:
24 |
25 | ```
26 | npm install --save @react-native-clipboard/clipboard
27 | ```
28 |
29 | ## Link
30 |
31 | ### React Native v0.60+
32 |
33 | For iOS, use `cocoapods` to link the package.
34 |
35 | run the following command:
36 |
37 | ```
38 | $ npx pod-install
39 | ```
40 |
41 | For android, the package will be linked automatically on build.
42 |
43 |
44 | For React Native version 0.59 or older
45 |
46 | ### React Native <= 0.59
47 |
48 | run the following command to link the package:
49 |
50 | ```
51 | $ react-native link @react-native-clipboard/clipboard
52 | ```
53 |
54 | For iOS, make sure you install the pod file.
55 |
56 | ```
57 | cd ios && pod install && cd ..
58 | ```
59 |
60 | or you could follow the instructions to [manually link the project](https://reactnative.dev/docs/linking-libraries-ios#manual-linking)
61 |
62 | ## Upgrading to React Native 0.60+
63 |
64 | New React Native comes with `autolinking` feature, which automatically links Native Modules in your project. In order to get it to work, make sure you unlink `Clipboard` first:
65 |
66 | ```
67 | $ react-native unlink @react-native-clipboard/clipboard
68 | ```
69 |
70 |
71 |
72 | ## Migrating from the core `react-native` module
73 |
74 | This module was created when the Clipboard API was split out from the core of React Native. To migrate to this module you need to follow the installation instructions above and then change you imports from:
75 |
76 | ```javascript
77 | import {Clipboard} from 'react-native';
78 | ```
79 |
80 | to:
81 |
82 | ```javascript
83 | import Clipboard from '@react-native-clipboard/clipboard';
84 | ```
85 |
86 | ## Example
87 |
88 | ```jsx
89 | import React, {useState} from 'react';
90 | import {
91 | SafeAreaView,
92 | View,
93 | Text,
94 | TouchableOpacity,
95 | StyleSheet,
96 | } from 'react-native';
97 | import Clipboard from '@react-native-clipboard/clipboard';
98 |
99 | const App = () => {
100 | const [copiedText, setCopiedText] = useState('');
101 |
102 | const copyToClipboard = () => {
103 | Clipboard.setString('hello world');
104 | };
105 |
106 | const fetchCopiedText = async () => {
107 | const text = await Clipboard.getString();
108 | setCopiedText(text);
109 | };
110 |
111 | return (
112 |
113 |
114 |
115 | Click here to copy to Clipboard
116 |
117 |
118 | View copied text
119 |
120 |
121 | {copiedText}
122 |
123 |
124 | );
125 | };
126 |
127 | const styles = StyleSheet.create({
128 | container: {
129 | flex: 1,
130 | justifyContent: 'center',
131 | alignItems: 'center',
132 | },
133 | copiedText: {
134 | marginTop: 10,
135 | color: 'red',
136 | },
137 | });
138 |
139 | export default App;
140 | ```
141 |
142 | # Reference
143 |
144 | ## Methods
145 |
146 | ### Clipboard
147 |
148 | #### `getString()`
149 |
150 | ```jsx
151 | static getString()
152 | ```
153 |
154 | Get content of string type, this method returns a `Promise`, so you can use following code to get clipboard content
155 |
156 | ```jsx
157 | async _getContent() {
158 | var content = await Clipboard.getString();
159 | }
160 | ```
161 |
162 | #### `getStrings()`
163 |
164 | ```jsx
165 | static getStrings()
166 | ```
167 |
168 | (iOS only)
169 | Get contents of string array type, this method returns a `Promise`, so you can use following code to get clipboard content
170 |
171 | ```jsx
172 | async _getContent() {
173 | var content = await Clipboard.getStrings();
174 | }
175 | ```
176 |
177 | #### `setString()`
178 |
179 | ```jsx
180 | static setString(content)
181 | ```
182 |
183 | Set content of string type. You can use following code to set clipboard content
184 |
185 | ```jsx
186 | _setContent() {
187 | Clipboard.setString('hello world');
188 | }
189 | ```
190 |
191 | #### Parameters
192 |
193 | | Name | Type | Required | Description |
194 | | ------- | ------ | -------- | ----------------------------------------- |
195 | | content | string | Yes | The content to be stored in the clipboard |
196 |
197 | #### `setStrings()`
198 |
199 | ```jsx
200 | static setStrings(content)
201 | ```
202 |
203 | (iOS only)
204 | Set content of string array type. You can use following code to set clipboard content
205 |
206 | ```jsx
207 | _setContent() {
208 | Clipboard.setStrings(['hello world', 'second string']);
209 | }
210 | ```
211 |
212 | #### Parameters
213 |
214 | | Name | Type | Required | Description |
215 | | ------- | -------- | -------- | ----------------------------------------- |
216 | | content | string[] | Yes | The content to be stored in the clipboard |
217 |
218 | #### `hasString()`
219 |
220 | ```jsx
221 | static hasString()
222 | ```
223 |
224 | Returns whether the clipboard has content or is empty.
225 | Can check if there is a content in clipboard without triggering PasteBoard notification for iOS 14+
226 |
227 | #### `hasURL()`
228 |
229 | ```jsx
230 | static hasURL()
231 | ```
232 |
233 | (iOS only)
234 | Returns whether the clipboard has a URL content.
235 | Can check if there is a URL content in clipboard without triggering PasteBoard notification for iOS 14+
236 |
237 | #### `hasNumber()`
238 |
239 | ```jsx
240 | static hasNumber()
241 | ```
242 |
243 | (iOS 14+ only)
244 | Returns whether the clipboard has a Number(UIPasteboardDetectionPatternNumber) content.
245 | Can check if there is a Number content in clipboard without triggering PasteBoard notification for iOS 14+
246 |
247 | #### `hasWebURL()`
248 |
249 | ```jsx
250 | static hasWebURL()
251 | ```
252 |
253 | (iOS 14+ only)
254 | Returns whether the clipboard has a WebURL(UIPasteboardDetectionPatternProbableWebURL) content.
255 | Can check if there is a WebURL content in clipboard without triggering PasteBoard notification for iOS 14+
256 |
257 | ### useClipboard
258 |
259 | `useClipboard` is a utility hooks for the `Clipboard` module. `data` contains the content stored in the clipboard.
260 |
261 | ```jsx
262 | import React, {useEffect} from 'react';
263 | import {Text} from 'react-native';
264 | import {useClipboard} from '@react-native-clipboard/clipboard';
265 |
266 | export const HooksSample = () => {
267 | const [data, setString] = useClipboard();
268 |
269 | useEffect(() => {
270 | setString('hello world');
271 | }, []);
272 |
273 | return {data};
274 | };
275 | ```
276 |
277 | ### `getImage()`
278 |
279 | ```jsx
280 | static getImage()
281 | ```
282 |
283 | Get content of image in base64 string type, this method returns a `Promise`, so you can use following code to get clipboard content (iOS and Android Only)
284 |
285 | ```jsx
286 | async _getContent() {
287 | var content = await Clipboard.getImage();
288 | }
289 | ```
290 |
291 | ## Mocking Clipboard
292 |
293 | If you're using `jest` as a test runner, you will need to setup a mock for clipboard, as NativeModules will be undefined when testing with Jest.
294 |
295 | Create a `jest.setup.js` in your project root,and set up the following to have Clipboard mocked:
296 |
297 | ```js
298 | import mockClipboard from '@react-native-clipboard/clipboard/jest/clipboard-mock.js';
299 |
300 | jest.mock('@react-native-clipboard/clipboard', () => mockClipboard);
301 | ```
302 |
303 | ## Maintainers
304 |
305 | - [M.Haris Baig](https://github.com/harisbaig100)
306 | - [Jesse Katsumata](https://github.com/Naturalclar)
307 |
308 | ## Contributing
309 |
310 | Please see the [`contributing guide`](/CONTRIBUTING.md).
311 |
312 | ## License
313 |
314 | The library is released under the MIT licence. For more information see [`LICENSE`](/LICENSE).
315 |
316 | [build-badge]: https://github.com/react-native-clipboard/clipboard/workflows/Build/badge.svg
317 | [build]: https://github.com/react-native-clipboard/clipboard/actions
318 | [version-badge]: https://img.shields.io/npm/v/@react-native-clipboard/clipboard.svg?style=flat-square
319 | [package]: https://www.npmjs.com/package/@react-native-clipboard/clipboard
320 | [support-badge]: https://img.shields.io/badge/platforms-android%20|%20ios%20|%20macos%20|%20windows-lightgrey.svg?style=flat-square
321 | [license-badge]: https://img.shields.io/npm/l/@react-native-clipboard/clipboard.svg?style=flat-square
322 | [license]: https://opensource.org/licenses/MIT
323 | [lean-core-badge]: https://img.shields.io/badge/Lean%20Core-Extracted-brightgreen.svg?style=flat-square
324 | [lean-core-issue]: https://github.com/facebook/react-native/issues/23313
325 |
--------------------------------------------------------------------------------
/windows/Clipboard/Clipboard.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | true
9 | true
10 | {90BFF18B-474B-445D-9847-B065853288D8}
11 | Clipboard
12 | Win32Proj
13 | Clipboard
14 | 10.0
15 | en-US
16 | 17.0
17 | false
18 |
19 |
20 |
21 |
22 | true
23 | true
24 | {90BFF18B-474B-445D-9847-B065853288D8}
25 | Clipboard
26 | Win32Proj
27 | Clipboard
28 | en-US
29 | 16.0
30 | true
31 | Windows Store
32 | 10.0
33 |
34 |
35 |
36 |
37 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\
38 | false
39 |
40 |
41 |
42 |
43 |
44 |
45 | Debug
46 | Win32
47 |
48 |
49 | Release
50 | Win32
51 |
52 |
53 | Debug
54 | x64
55 |
56 |
57 | Release
58 | x64
59 |
60 |
61 | Debug
62 | ARM64
63 |
64 |
65 | Release
66 | ARM64
67 |
68 |
69 |
70 | DynamicLibrary
71 | Unicode
72 | v143
73 | false
74 |
75 |
76 | true
77 |
78 |
79 | false
80 | true
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | Use
98 | pch.h
99 | $(IntDir)pch.pch
100 | Level4
101 | true
102 | %(AdditionalOptions) /bigobj
103 | 4453;28204
104 | _WINRT_DLL;%(PreprocessorDefinitions)
105 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
106 | stdcpp20
107 |
108 |
109 | shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)
110 | Console
111 | true
112 | Clipboard.def
113 |
114 |
115 |
116 |
117 | _DEBUG;%(PreprocessorDefinitions)
118 |
119 |
120 |
121 |
122 | NDEBUG;%(PreprocessorDefinitions)
123 |
124 |
125 |
126 |
127 | USE_FABRIC;%(PreprocessorDefinitions)
128 |
129 |
130 |
131 |
132 |
133 |
134 | ReactPackageProvider.idl
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Create
146 |
147 |
148 | ReactPackageProvider.idl
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | This project references targets in your node_modules\react-native-windows folder. The missing file is {0}.
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------