Skip to content

Commit

Permalink
Updated
Browse files Browse the repository at this point in the history
  • Loading branch information
ravindran-a committed Dec 14, 2023
1 parent 330beac commit ea41be1
Show file tree
Hide file tree
Showing 27 changed files with 305 additions and 217 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ Widget build (BuildContext context) {
);
}
```
- wesetup new entry point for secondary display so we can decided what we need to call for initialization. Works only for android for now
```dart
@pragma('vm:entry-point')
void secondaryDisplayMain() {
/// do something that don't break plugin registration here.
}
```
- Able to package android release build. Works fine in example app.

- Tested example app in android tab and ios tab and things work as expected. Ensure the devices have USB C 3.0 and above else HDMI out is not supported.

- In case of iOS, please refer to example app app delegate. There are few lines of code which needs to be added to your app's app delegate as well for this to work fine in iOS.

- Updated optional issues and null checks

- Added option to hide second display from the first

- WIP support second main in iOS for extended display

- WIP Send data back from 2nd to 1st display

You can take a look at our example to learn more about how the plugin works

Expand Down
12 changes: 7 additions & 5 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,36 @@ group 'com.namit.presentation_displays'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.5.20'
ext.kotlin_version = '1.7.20'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 31
compileSdkVersion 33

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 19
minSdkVersion 21
}
lintOptions {
disable 'InvalidPackage'
Expand All @@ -39,5 +40,6 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.android.support:support-annotations:28.0.0'
}
1 change: 0 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ data class DisplayJson(
val rotation: Int,
@SerializedName("name")
val name: String
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.util.Log
import android.view.Display
import androidx.annotation.NonNull
import com.google.gson.Gson
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutor
Expand All @@ -24,38 +25,41 @@ import org.json.JSONObject
/** PresentationDisplaysPlugin */
class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler {

private lateinit var channel : MethodChannel
private lateinit var eventChannel : EventChannel
private lateinit var channel: MethodChannel
private lateinit var eventChannel: EventChannel
private var flutterEngineChannel: MethodChannel? = null
private var context:Context?=null
private var presentation:PresentationDisplay?=null
private var context: Context? = null
private var presentation: PresentationDisplay? = null

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
override fun onAttachedToEngine(
@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, viewTypeId)
channel.setMethodCallHandler(this)

eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, viewTypeEventsId)
displayManager = flutterPluginBinding.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
displayManager =
flutterPluginBinding.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as
DisplayManager
val displayConnectedStreamHandler = DisplayConnectedStreamHandler(displayManager)
eventChannel.setStreamHandler(displayConnectedStreamHandler)
}

companion object{
private const val viewTypeId = "presentation_displays_plugin"
private const val viewTypeEventsId = "presentation_displays_plugin_events"
private var displayManager: DisplayManager? = null
companion object {
private const val viewTypeId = "presentation_displays_plugin"
private const val viewTypeEventsId = "presentation_displays_plugin_events"
private var displayManager: DisplayManager? = null

/**
* @hide
*/
/** @hide */
@Suppress("unused", "DEPRECATION")
@JvmStatic
fun registerWith(registrar: PluginRegistry.Registrar) {
val channel = MethodChannel(registrar.messenger(), viewTypeId)
channel.setMethodCallHandler(PresentationDisplaysPlugin())

val eventChannel = EventChannel(registrar.messenger(), viewTypeEventsId)
displayManager = registrar.activity()!!.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
displayManager =
registrar.activity()!!.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displayConnectedStreamHandler = DisplayConnectedStreamHandler(displayManager)
eventChannel.setStreamHandler(displayConnectedStreamHandler)
}
Expand All @@ -73,8 +77,8 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
try {
val obj = JSONObject(call.arguments as String)
Log.i(
TAG,
"Channel: method: ${call.method} | displayId: ${obj.getInt("displayId")} | routerName: ${
TAG,
"Channel: method: ${call.method} | displayId: ${obj.getInt("displayId")} | routerName: ${
obj.getString("routerName")
}"
)
Expand All @@ -84,16 +88,15 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
if (display != null) {
val flutterEngine = createFlutterEngine(tag)
flutterEngine?.let {
flutterEngineChannel = MethodChannel(
it.dartExecutor.binaryMessenger,
"${viewTypeId}_engine"
)
presentation =
context?.let { it1 -> PresentationDisplay(it1, tag, display) }
flutterEngineChannel =
MethodChannel(it.dartExecutor.binaryMessenger, "${viewTypeId}_engine")
presentation = context?.let { it1 -> PresentationDisplay(it1, tag, display) }
Log.i(TAG, "presentation: $presentation")
presentation?.show()

result.success(true)
} ?: result.error("404", "Can't find FlutterEngine", null)
}
?: result.error("404", "Can't find FlutterEngine", null)
} else {
result.error("404", "Can't find display with displayId is $displayId", null)
}
Expand All @@ -120,12 +123,7 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
if (displays != null) {
for (display: Display in displays) {
Log.i(TAG, "display: $display")
val d = DisplayJson(
display.displayId,
display.flags,
display.rotation,
display.name
)
val d = DisplayJson(display.displayId, display.flags, display.rotation, display.name)
listJson.add(d)
}
}
Expand All @@ -143,51 +141,50 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
}

private fun createFlutterEngine(tag: String): FlutterEngine? {
if (context == null)
return null
if (context == null) return null
if (FlutterEngineCache.getInstance().get(tag) == null) {
val flutterEngine = FlutterEngine(context!!)
flutterEngine.navigationChannel.setInitialRoute(tag)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterInjector.instance().flutterLoader().startInitialization(context!!)
val path = FlutterInjector.instance().flutterLoader().findAppBundlePath()
val entrypoint = DartExecutor.DartEntrypoint(path, "secondaryDisplayMain")
flutterEngine.dartExecutor.executeDartEntrypoint(entrypoint)
flutterEngine.lifecycleChannel.appIsResumed()
// Cache the FlutterEngine to be used by FlutterActivity.
FlutterEngineCache.getInstance().put(tag, flutterEngine)
}
return FlutterEngineCache.getInstance().get(tag)
}

override fun onDetachedFromActivity() {
}
override fun onDetachedFromActivity() {}

override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
this.context = binding.activity
displayManager = context?.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
}

override fun onDetachedFromActivityForConfigChanges() {
}
override fun onDetachedFromActivityForConfigChanges() {}
}

class DisplayConnectedStreamHandler(private var displayManager: DisplayManager?) : EventChannel.StreamHandler {
class DisplayConnectedStreamHandler(private var displayManager: DisplayManager?) :
EventChannel.StreamHandler {
private var sink: EventChannel.EventSink? = null
private var handler: Handler? = null

private val displayListener = object : DisplayManager.DisplayListener {
override fun onDisplayAdded(displayId: Int) {
sink?.success(1)
}
private val displayListener =
object : DisplayManager.DisplayListener {
override fun onDisplayAdded(displayId: Int) {
sink?.success(1)
}

override fun onDisplayRemoved(displayId: Int) {
sink?.success(0)
}
override fun onDisplayRemoved(displayId: Int) {
sink?.success(0)
}

override fun onDisplayChanged(p0: Int) {}
}
override fun onDisplayChanged(p0: Int) {}
}

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
sink = events
Expand Down
10 changes: 7 additions & 3 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
Expand All @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 31
compileSdkVersion 33

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
Expand All @@ -39,7 +39,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.namit.presentation_displays_example"
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand All @@ -52,6 +52,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
namespace 'com.namit.presentation_displays_example'
lint {
disable 'InvalidPackage'
}
}

flutter {
Expand Down
3 changes: 1 addition & 2 deletions example/android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.namit.presentation_displays_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
Expand Down
3 changes: 1 addition & 2 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.namit.presentation_displays_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
Expand Down
3 changes: 1 addition & 2 deletions example/android/app/src/profile/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.namit.presentation_displays_example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
Expand Down
6 changes: 3 additions & 3 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.20'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:7.0.0'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand All @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
1 change: 0 additions & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '11.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/presentation_displays/ios"

SPEC CHECKSUMS:
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
presentation_displays: 02a9b05f708afe31d23564988f285b4d8019ddcd

PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3

COCOAPODS: 1.11.2
COCOAPODS: 1.14.3
Loading

0 comments on commit ea41be1

Please sign in to comment.