-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add native ios code outside of ios project * helper script * going to be a lot of these commits to squash...backing up * save * start of an expo plugin * create info.plist * copy the view controller * maybe working * working * wait working now * working plugin * use current scheme * update intent path * use better params * support text in uri * build * use better encoding * handle images * cleanup ios plugin * android * move bash script to /scripts * handle cases where loaded data is uiimage rather than uri * remove unnecessary logic, allow more than 4 images and just take first 4 * android build plugin * limit images to four on android * use js for plugins, no need to build * revert changes to app config * use correct scheme on android * android readme * move ios extension to /modules * remove unnecessary event * revert typo * plugin readme * scripts readme * add configurable scheme to .env, default to `bluesky` * remove debug * revert .gitignore change * add comment about updating .env to app.config.js for those modifying scheme * modify .env * update android module to use the proper url * update ios extension * remove comment * parse and validate incoming image uris * fix types * rm oops * fix a few typos
- Loading branch information
Showing
27 changed files
with
860 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>NSExtension</key> | ||
<dict> | ||
<key>NSExtensionPrincipalClass</key> | ||
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string> | ||
<key>NSExtensionAttributes</key> | ||
<dict> | ||
<key>NSExtensionActivationRule</key> | ||
<dict> | ||
<key>NSExtensionActivationSupportsText</key> | ||
<true/> | ||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key> | ||
<integer>1</integer> | ||
<key>NSExtensionActivationSupportsImageWithMaxCount</key> | ||
<integer>10</integer> | ||
</dict> | ||
</dict> | ||
<key>NSExtensionPointIdentifier</key> | ||
<string>com.apple.share-services</string> | ||
</dict> | ||
<key>MainAppScheme</key> | ||
<string>bluesky</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundleDisplayName</key> | ||
<string>Extension</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>$(MARKETING_VERSION)</string> | ||
</dict> | ||
</plist> |
10 changes: 10 additions & 0 deletions
10
modules/Share-with-Bluesky/Share-with-Bluesky.entitlements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>com.apple.security.application-groups</key> | ||
<array> | ||
<string>group.xyz.blueskyweb.app</string> | ||
</array> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import UIKit | ||
|
||
class ShareViewController: UIViewController { | ||
// This allows other forks to use this extension while also changing their | ||
// scheme. | ||
let appScheme = Bundle.main.object(forInfoDictionaryKey: "MainAppScheme") as? String ?? "bluesky" | ||
|
||
// | ||
override func viewDidAppear(_ animated: Bool) { | ||
super.viewDidAppear(animated) | ||
|
||
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem, | ||
let attachments = extensionItem.attachments, | ||
let firstAttachment = extensionItem.attachments?.first | ||
else { | ||
self.completeRequest() | ||
return | ||
} | ||
|
||
Task { | ||
if firstAttachment.hasItemConformingToTypeIdentifier("public.text") { | ||
await self.handleText(item: firstAttachment) | ||
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.url") { | ||
await self.handleUrl(item: firstAttachment) | ||
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.image") { | ||
await self.handleImages(items: attachments) | ||
} else { | ||
self.completeRequest() | ||
} | ||
} | ||
} | ||
|
||
private func handleText(item: NSItemProvider) async -> Void { | ||
do { | ||
if let data = try await item.loadItem(forTypeIdentifier: "public.text") as? String { | ||
if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), | ||
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") | ||
{ | ||
_ = self.openURL(url) | ||
} | ||
} | ||
self.completeRequest() | ||
} catch { | ||
self.completeRequest() | ||
} | ||
} | ||
|
||
private func handleUrl(item: NSItemProvider) async -> Void { | ||
do { | ||
if let data = try await item.loadItem(forTypeIdentifier: "public.url") as? URL { | ||
if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), | ||
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)") | ||
{ | ||
_ = self.openURL(url) | ||
} | ||
} | ||
self.completeRequest() | ||
} catch { | ||
self.completeRequest() | ||
} | ||
} | ||
|
||
private func handleImages(items: [NSItemProvider]) async -> Void { | ||
let firstFourItems: [NSItemProvider] | ||
if items.count < 4 { | ||
firstFourItems = items | ||
} else { | ||
firstFourItems = Array(items[0...3]) | ||
} | ||
|
||
var valid = true | ||
var imageUris = "" | ||
|
||
for (index, item) in firstFourItems.enumerated() { | ||
var imageUriInfo: String? = nil | ||
|
||
do { | ||
if let dataUri = try await item.loadItem(forTypeIdentifier: "public.image") as? URL { | ||
// We need to duplicate this image, since we don't have access to the outgoing temp directory | ||
// We also will get the image dimensions here, sinze RN makes it difficult to get those dimensions for local files | ||
let data = try Data(contentsOf: dataUri) | ||
let image = UIImage(data: data) | ||
imageUriInfo = self.saveImageWithInfo(image) | ||
} else if let image = try await item.loadItem(forTypeIdentifier: "public.image") as? UIImage { | ||
imageUriInfo = self.saveImageWithInfo(image) | ||
} | ||
} catch { | ||
valid = false | ||
} | ||
|
||
if let imageUriInfo = imageUriInfo { | ||
imageUris.append(imageUriInfo) | ||
if index < items.count - 1 { | ||
imageUris.append(",") | ||
} | ||
} else { | ||
valid = false | ||
} | ||
} | ||
|
||
if valid, | ||
let encoded = imageUris.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), | ||
let url = URL(string: "\(self.appScheme)://intent/compose?imageUris=\(encoded)") | ||
{ | ||
_ = self.openURL(url) | ||
} | ||
|
||
self.completeRequest() | ||
} | ||
|
||
private func saveImageWithInfo(_ image: UIImage?) -> String? { | ||
guard let image = image else { | ||
return nil | ||
} | ||
|
||
do { | ||
// Saving this file to the bundle group's directory lets us access it from | ||
// inside of the app. Otherwise, we wouldn't have access even though the | ||
// extension does. | ||
if let dir = FileManager() | ||
.containerURL( | ||
forSecurityApplicationGroupIdentifier: "group.\(Bundle.main.bundleIdentifier?.replacingOccurrences(of: ".Share-with-Bluesky", with: "") ?? "")") | ||
{ | ||
let filePath = "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).jpeg" | ||
|
||
if let newUri = URL(string: filePath), | ||
let jpegData = image.jpegData(compressionQuality: 1) | ||
{ | ||
try jpegData.write(to: newUri) | ||
return "\(newUri.absoluteString)|\(image.size.width)|\(image.size.height)" | ||
} | ||
} | ||
return nil | ||
} catch { | ||
return nil | ||
} | ||
} | ||
|
||
private func completeRequest() -> Void { | ||
self.extensionContext?.completeRequest(returningItems: nil) | ||
} | ||
|
||
@objc func openURL(_ url: URL) -> Bool { | ||
var responder: UIResponder? = self | ||
while responder != nil { | ||
if let application = responder as? UIApplication { | ||
return application.perform(#selector(openURL(_:)), with: url) != nil | ||
} | ||
responder = responder?.next | ||
} | ||
return false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Expo Receive Android Intents | ||
|
||
This module handles incoming intents on Android. Handled intents are `text/plain` and `image/*` (single or multiple). | ||
The module handles saving images to the app's filesystem for access within the app, limiting the selection of images | ||
to a max of four, and handling intent types. No JS code is required for this module, and it is no-op on non-android | ||
platforms. | ||
|
||
No installation is required. Gradle will automatically add this module on build. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# OSX | ||
# | ||
.DS_Store | ||
|
||
# Android/IntelliJ | ||
# | ||
build/ | ||
.idea | ||
.gradle | ||
local.properties | ||
*.iml | ||
*.hprof | ||
|
||
# Bundle artifacts | ||
*.jsbundle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
apply plugin: 'com.android.library' | ||
apply plugin: 'kotlin-android' | ||
apply plugin: 'maven-publish' | ||
|
||
group = 'xyz.blueskyweb.app.exporeceiveandroidintents' | ||
version = '0.4.1' | ||
|
||
buildscript { | ||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") | ||
if (expoModulesCorePlugin.exists()) { | ||
apply from: expoModulesCorePlugin | ||
applyKotlinExpoModulesCorePlugin() | ||
} | ||
|
||
// Simple helper that allows the root project to override versions declared by this library. | ||
ext.safeExtGet = { prop, fallback -> | ||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback | ||
} | ||
|
||
// Ensures backward compatibility | ||
ext.getKotlinVersion = { | ||
if (ext.has("kotlinVersion")) { | ||
ext.kotlinVersion() | ||
} else { | ||
ext.safeExtGet("kotlinVersion", "1.8.10") | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}") | ||
} | ||
} | ||
|
||
afterEvaluate { | ||
publishing { | ||
publications { | ||
release(MavenPublication) { | ||
from components.release | ||
} | ||
} | ||
repositories { | ||
maven { | ||
url = mavenLocal().url | ||
} | ||
} | ||
} | ||
} | ||
|
||
android { | ||
compileSdkVersion safeExtGet("compileSdkVersion", 33) | ||
|
||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION | ||
if (agpVersion.tokenize('.')[0].toInteger() < 8) { | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_11 | ||
targetCompatibility JavaVersion.VERSION_11 | ||
} | ||
|
||
kotlinOptions { | ||
jvmTarget = JavaVersion.VERSION_11.majorVersion | ||
} | ||
} | ||
|
||
namespace "xyz.blueskyweb.app.exporeceiveandroidintents" | ||
defaultConfig { | ||
minSdkVersion safeExtGet("minSdkVersion", 21) | ||
targetSdkVersion safeExtGet("targetSdkVersion", 34) | ||
versionCode 1 | ||
versionName "0.4.1" | ||
} | ||
lintOptions { | ||
abortOnError false | ||
} | ||
publishing { | ||
singleVariant("release") { | ||
withSourcesJar() | ||
} | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
implementation project(':expo-modules-core') | ||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" | ||
} |
2 changes: 2 additions & 0 deletions
2
modules/expo-receive-android-intents/android/src/main/AndroidManifest.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<manifest> | ||
</manifest> |
Oops, something went wrong.