Skip to content

Commit ebebdb9

Browse files
committed
[pasteboard] feat: add Android support and update project configuration
1 parent af444b4 commit ebebdb9

File tree

19 files changed

+258
-258
lines changed

19 files changed

+258
-258
lines changed

packages/pasteboard/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.4.0
2+
3+
* introduce Android support. thanks to
4+
[@Seidko](https://github.com/MixinNetwork/flutter-plugins/pull/362)
5+
[@vicajilau](https://github.com/MixinNetwork/flutter-plugins/pull/383)
6+
17
## 0.3.0
28

39
* bump web version to 1.0.0

packages/pasteboard/android/src/main/kotlin/one/mixin/pasteboard/PasteboardPlugin.kt

Lines changed: 98 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,97 +15,112 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
1515
import io.flutter.plugin.common.MethodChannel.Result
1616
import java.io.ByteArrayOutputStream
1717
import java.io.File
18-
import java.io.FileInputStream
1918
import java.io.FileOutputStream
20-
import java.io.IOException
2119
import java.util.UUID
22-
import kotlin.concurrent.thread
20+
import java.util.concurrent.Executors
2321

2422
/** PasteboardPlugin */
25-
class PasteboardPlugin: FlutterPlugin, MethodCallHandler {
26-
/// The MethodChannel that will the communication between Flutter and native Android
27-
///
28-
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
29-
/// when the Flutter Engine is detached from the Activity
30-
private lateinit var context: Context
31-
private lateinit var channel : MethodChannel
23+
class PasteboardPlugin : FlutterPlugin, MethodCallHandler {
24+
/// The MethodChannel that will the communication between Flutter and native Android
25+
///
26+
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
27+
/// when the Flutter Engine is detached from the Activity
28+
private lateinit var context: Context
29+
private lateinit var channel: MethodChannel
3230

33-
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
34-
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "pasteboard")
35-
channel.setMethodCallHandler(this)
36-
context = flutterPluginBinding.applicationContext
37-
}
31+
private val executor = Executors.newSingleThreadExecutor()
3832

39-
override fun onMethodCall(call: MethodCall, result: Result) {
40-
val manager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
41-
val cr = context.contentResolver
42-
val first = manager.primaryClip?.getItemAt(0)
43-
when (call.method) {
44-
"image" -> {
45-
first?.uri?.let {
46-
val mime = cr.getType(it)
47-
if (mime == null || !mime.startsWith("image")) return result.success(null)
48-
result.success(cr.openInputStream(it).use { stream ->
49-
stream?.buffered()?.readBytes()
50-
})
51-
}
52-
result.success(null)
53-
}
54-
"files" -> {
55-
manager.primaryClip?.run {
56-
if (itemCount == 0) result.success(null)
57-
val files: MutableList<String> = mutableListOf()
58-
for (i in 0 until itemCount) {
59-
getItemAt(i).uri?.let {
60-
files.add(it.toString())
33+
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
34+
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "pasteboard")
35+
channel.setMethodCallHandler(this)
36+
context = flutterPluginBinding.applicationContext
37+
}
38+
39+
override fun onMethodCall(call: MethodCall, result: Result) {
40+
val manager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
41+
val cr = context.contentResolver
42+
val first = manager.primaryClip?.getItemAt(0)
43+
when (call.method) {
44+
"image" -> {
45+
executor.run {
46+
val uri = first?.uri ?: return@run result.success(null)
47+
val mime = cr.getType(uri)
48+
if (mime == null || !mime.startsWith("image")) return@run result.success(null)
49+
val bytes =
50+
cr.openInputStream(uri)?.readBytes() ?: return@run result.success(null)
51+
result.success(bytes)
52+
}
6153
}
62-
}
63-
result.success(files)
64-
}
65-
}
66-
"html" -> result.success(first?.htmlText)
67-
"writeFiles" -> {
68-
val args = call.arguments<List<String>>() ?: return result.error(
69-
"NoArgs",
70-
"Missing Arguments",
71-
null,
72-
)
73-
val clip: ClipData? = null
74-
for (i in args) {
75-
val uri = Uri.parse(i)
76-
clip ?: ClipData.newUri(cr, "files", uri)
77-
clip?.addItem(ClipData.Item(uri))
78-
}
79-
clip?.let {
80-
manager.setPrimaryClip(it)
81-
}
82-
result.success(null)
83-
}
84-
"writeImage" -> {
85-
val image = call.arguments<ByteArray>() ?: return result.error(
86-
"NoArgs",
87-
"Missing Arguments",
88-
null,
89-
)
90-
val out = ByteArrayOutputStream()
91-
thread {
92-
val bitmap = BitmapFactory.decodeByteArray(image, 0, image.size)
93-
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
94-
}
95-
val name = UUID.randomUUID().toString()
96-
val file = File(context.cacheDir, name)
97-
FileOutputStream(file).use {
98-
out.writeTo(it)
54+
55+
"files" -> {
56+
manager.primaryClip?.run {
57+
if (itemCount == 0) result.success(null)
58+
val files: MutableList<String> = mutableListOf()
59+
for (i in 0 until itemCount) {
60+
getItemAt(i).uri?.let {
61+
files.add(it.toString())
62+
}
63+
}
64+
result.success(files)
65+
}
66+
}
67+
68+
"html" -> result.success(first?.htmlText)
69+
"writeFiles" -> {
70+
val args = call.arguments<List<String>>() ?: return result.error(
71+
"NoArgs",
72+
"Missing Arguments",
73+
null,
74+
)
75+
val clip: ClipData? = null
76+
for (i in args) {
77+
val uri = Uri.parse(i)
78+
clip ?: ClipData.newUri(cr, "files", uri)
79+
clip?.addItem(ClipData.Item(uri))
80+
}
81+
clip?.let {
82+
manager.setPrimaryClip(it)
83+
}
84+
result.success(null)
85+
}
86+
87+
"writeImage" -> {
88+
val image = call.arguments<ByteArray>() ?: return result.error(
89+
"NoArgs",
90+
"Missing Arguments",
91+
null,
92+
)
93+
94+
val name = UUID.randomUUID().toString() + ".png"
95+
val file = File(context.cacheDir, name)
96+
97+
executor.execute {
98+
try {
99+
val bitmap = BitmapFactory.decodeByteArray(image, 0, image.size)
100+
val out = ByteArrayOutputStream()
101+
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
102+
103+
FileOutputStream(file).use {
104+
out.writeTo(it)
105+
}
106+
107+
val uri = FileProvider.getUriForFile(
108+
context, "${context.packageName}.provider", file
109+
)
110+
val clip = ClipData.newUri(cr, "image.png", uri)
111+
manager.setPrimaryClip(clip)
112+
result.success(null)
113+
} catch (e: Exception) {
114+
result.error("Error", "Failed to write image", e.message)
115+
}
116+
}
117+
}
118+
119+
else -> result.notImplemented()
99120
}
100-
val uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
101-
val clip = ClipData.newUri(cr, "image.png", uri)
102-
manager.setPrimaryClip(clip)
103-
}
104-
else -> result.notImplemented()
105121
}
106-
}
107122

108-
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
109-
channel.setMethodCallHandler(null)
110-
}
123+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
124+
channel.setMethodCallHandler(null)
125+
}
111126
}

packages/pasteboard/example/.metadata

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# This file should be version controlled and should not be manually edited.
55

66
version:
7-
revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3"
7+
revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
88
channel: "stable"
99

1010
project_type: app
@@ -13,26 +13,11 @@ project_type: app
1313
migration:
1414
platforms:
1515
- platform: root
16-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
17-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
16+
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
17+
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
1818
- platform: android
19-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
20-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
21-
- platform: ios
22-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
23-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
24-
- platform: linux
25-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
26-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
27-
- platform: macos
28-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
29-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
30-
- platform: web
31-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
32-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
33-
- platform: windows
34-
create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
35-
base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
19+
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
20+
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
3621

3722
# User provided section
3823

packages/pasteboard/example/android/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ gradle-wrapper.jar
55
/gradlew.bat
66
/local.properties
77
GeneratedPluginRegistrant.java
8+
.cxx/
89

910
# Remember to never publicly share your keystore.
10-
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11+
# See https://flutter.dev/to/reference-keystore
1112
key.properties
1213
**/*.keystore
1314
**/*.jks

packages/pasteboard/example/android/app/build.gradle

Lines changed: 0 additions & 58 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
plugins {
2+
id("com.android.application")
3+
id("kotlin-android")
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5+
id("dev.flutter.flutter-gradle-plugin")
6+
}
7+
8+
android {
9+
namespace = "com.example.pasteboard_example"
10+
compileSdk = flutter.compileSdkVersion
11+
ndkVersion = flutter.ndkVersion
12+
13+
compileOptions {
14+
sourceCompatibility = JavaVersion.VERSION_11
15+
targetCompatibility = JavaVersion.VERSION_11
16+
}
17+
18+
kotlinOptions {
19+
jvmTarget = JavaVersion.VERSION_11.toString()
20+
}
21+
22+
defaultConfig {
23+
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
24+
applicationId = "com.example.pasteboard_example"
25+
// You can update the following values to match your application needs.
26+
// For more information, see: https://flutter.dev/to/review-gradle-config.
27+
minSdk = flutter.minSdkVersion
28+
targetSdk = flutter.targetSdkVersion
29+
versionCode = flutter.versionCode
30+
versionName = flutter.versionName
31+
}
32+
33+
buildTypes {
34+
release {
35+
// TODO: Add your own signing config for the release build.
36+
// Signing with the debug keys for now, so `flutter run --release` works.
37+
signingConfig = signingConfigs.getByName("debug")
38+
}
39+
}
40+
}
41+
42+
flutter {
43+
source = "../.."
44+
}

packages/pasteboard/example/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22
<application
3-
android:label="example"
3+
android:label="pasteboard_example"
44
android:name="${applicationName}"
55
android:icon="@mipmap/ic_launcher">
66
<activity

packages/pasteboard/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.example.pasteboard_example
2+
3+
import io.flutter.embedding.android.FlutterActivity
4+
5+
class MainActivity : FlutterActivity()
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<paths xmlns:android="http://schemas.android.com/apk/res/android">
33
<external-path
4-
name="external_files"
5-
path="." />
4+
name="external_files"
5+
path="."/>
6+
<cache-path name="cache" path="."/>
7+
<files-path name="files" path="."/>
68
</paths>

0 commit comments

Comments
 (0)