Refactor: extract image processor
|
@ -121,6 +121,6 @@ dependencies {
|
||||||
// fix crash on sdk33, need investigation
|
// fix crash on sdk33, need investigation
|
||||||
implementation "androidx.window:window:1.0.0"
|
implementation "androidx.window:window:1.0.0"
|
||||||
implementation 'com.google.android.gms:play-services-maps:18.1.0'
|
implementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||||
implementation 'com.nkming.nc_photos.np_android_log:np_android_log'
|
implementation 'com.nkming.nc_photos.np_android_core:np_android_core'
|
||||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
|
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package com.nkming.nc_photos
|
package com.nkming.nc_photos
|
||||||
|
|
||||||
import com.nkming.nc_photos.np_android_log.LogConfig
|
import com.nkming.nc_photos.np_android_core.LogConfig
|
||||||
import io.flutter.BuildConfig
|
import io.flutter.BuildConfig
|
||||||
import io.flutter.app.FlutterApplication
|
import io.flutter.app.FlutterApplication
|
||||||
|
|
||||||
class App : FlutterApplication() {
|
class App : FlutterApplication() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
LogConfig.isShowInfo = BuildConfig.DEBUG
|
LogConfig.isShowInfo = BuildConfig.DEBUG
|
||||||
LogConfig.isShowDebug = BuildConfig.DEBUG
|
LogConfig.isShowDebug = BuildConfig.DEBUG
|
||||||
LogConfig.isShowVerbose = BuildConfig.DEBUG
|
LogConfig.isShowVerbose = BuildConfig.DEBUG
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,44 @@
|
||||||
package com.nkming.nc_photos
|
package com.nkming.nc_photos
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.nkming.nc_photos.np_android_log.logI
|
import com.nkming.nc_photos.np_android_core.logI
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.net.ssl.HostnameVerifier
|
import javax.net.ssl.HostnameVerifier
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import javax.net.ssl.SSLSession
|
import javax.net.ssl.SSLSession
|
||||||
|
|
||||||
class CustomHostnameVerifier(context: Context) : HostnameVerifier {
|
class CustomHostnameVerifier(context: Context) : HostnameVerifier {
|
||||||
/**
|
/**
|
||||||
* Allow host names allowed by user, for other hosts, revert to the default
|
* Allow host names allowed by user, for other hosts, revert to the default
|
||||||
* behavior
|
* behavior
|
||||||
*/
|
*/
|
||||||
override fun verify(hostname: String, session: SSLSession): Boolean {
|
override fun verify(hostname: String, session: SSLSession): Boolean {
|
||||||
return if (allowedHosts.contains(
|
return if (allowedHosts.contains(hostname.lowercase(Locale.getDefault()))) {
|
||||||
hostname.lowercase(Locale.getDefault())
|
// good
|
||||||
)) {
|
logI(
|
||||||
// good
|
"CustomHostnameVerifier::verify",
|
||||||
logI(
|
"Allowing registered host: $hostname"
|
||||||
"CustomHostnameVerifier::verify",
|
)
|
||||||
"Allowing registered host: $hostname"
|
true
|
||||||
)
|
} else {
|
||||||
true
|
defaultHostnameVerifier.verify(hostname, session)
|
||||||
} else {
|
}
|
||||||
defaultHostnameVerifier.verify(hostname, session)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reload(context: Context) {
|
fun reload(context: Context) {
|
||||||
val certManager = SelfSignedCertManager()
|
val certManager = SelfSignedCertManager()
|
||||||
val certs = certManager.readAllCerts(context)
|
val certs = certManager.readAllCerts(context)
|
||||||
allowedHosts.clear()
|
allowedHosts.clear()
|
||||||
for (c in certs) {
|
for (c in certs) {
|
||||||
allowedHosts.add(c.first.host.lowercase(Locale.getDefault()))
|
allowedHosts.add(c.first.host.lowercase(Locale.getDefault()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultHostnameVerifier: HostnameVerifier =
|
private val defaultHostnameVerifier: HostnameVerifier =
|
||||||
HttpsURLConnection.getDefaultHostnameVerifier()
|
HttpsURLConnection.getDefaultHostnameVerifier()
|
||||||
private val allowedHosts: MutableList<String> = ArrayList()
|
private val allowedHosts: MutableList<String> = ArrayList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
reload(context)
|
reload(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,80 +9,84 @@ import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
// See: https://stackoverflow.com/a/6378872
|
// See: https://stackoverflow.com/a/6378872
|
||||||
class CustomKeyStoresTrustManager(keyStore: KeyStore) : X509TrustManager {
|
class CustomKeyStoresTrustManager(keyStore: KeyStore) : X509TrustManager {
|
||||||
/*
|
/*
|
||||||
* Delegate to the default trust manager.
|
* Delegate to the default trust manager.
|
||||||
*/
|
*/
|
||||||
@Throws(CertificateException::class)
|
@Throws(CertificateException::class)
|
||||||
override fun checkClientTrusted(chain: Array<X509Certificate>,
|
override fun checkClientTrusted(
|
||||||
authType: String) {
|
chain: Array<X509Certificate>, authType: String
|
||||||
val defaultX509TrustManager = x509TrustManagers[0]
|
) {
|
||||||
defaultX509TrustManager.checkClientTrusted(chain, authType)
|
val defaultX509TrustManager = x509TrustManagers[0]
|
||||||
}
|
defaultX509TrustManager.checkClientTrusted(chain, authType)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop over the trustmanagers until we find one that accepts our server
|
* Loop over the trustmanagers until we find one that accepts our server
|
||||||
*/
|
*/
|
||||||
@Throws(CertificateException::class)
|
@Throws(CertificateException::class)
|
||||||
override fun checkServerTrusted(chain: Array<X509Certificate>,
|
override fun checkServerTrusted(
|
||||||
authType: String) {
|
chain: Array<X509Certificate>, authType: String
|
||||||
var defaultException: Exception? = null
|
) {
|
||||||
for (tm in x509TrustManagers) {
|
var defaultException: Exception? = null
|
||||||
try {
|
for (tm in x509TrustManagers) {
|
||||||
tm.checkServerTrusted(chain, authType)
|
try {
|
||||||
return
|
tm.checkServerTrusted(chain, authType)
|
||||||
} catch (e: CertificateException) {
|
return
|
||||||
// ignore
|
} catch (e: CertificateException) {
|
||||||
if (defaultException == null) {
|
// ignore
|
||||||
defaultException = e
|
if (defaultException == null) {
|
||||||
}
|
defaultException = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (defaultException != null) {
|
}
|
||||||
throw defaultException
|
if (defaultException != null) {
|
||||||
}
|
throw defaultException
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
override fun getAcceptedIssuers(): Array<X509Certificate> {
|
||||||
val list = ArrayList<X509Certificate>()
|
val list = ArrayList<X509Certificate>()
|
||||||
for (tm in x509TrustManagers) {
|
for (tm in x509TrustManagers) {
|
||||||
list.addAll(tm.acceptedIssuers.toList())
|
list.addAll(tm.acceptedIssuers.toList())
|
||||||
}
|
}
|
||||||
return list.toTypedArray()
|
return list.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCustomKeyStore(keyStore: KeyStore) {
|
fun setCustomKeyStore(keyStore: KeyStore) {
|
||||||
val factories = ArrayList<TrustManagerFactory>()
|
val factories = ArrayList<TrustManagerFactory>()
|
||||||
// The default Trustmanager with default keystore
|
// The default Trustmanager with default keystore
|
||||||
val original = TrustManagerFactory.getInstance(
|
val original = TrustManagerFactory.getInstance(
|
||||||
TrustManagerFactory.getDefaultAlgorithm())
|
TrustManagerFactory.getDefaultAlgorithm()
|
||||||
original.init(null as KeyStore?)
|
)
|
||||||
factories.add(original)
|
original.init(null as KeyStore?)
|
||||||
|
factories.add(original)
|
||||||
|
|
||||||
// with custom keystore
|
// with custom keystore
|
||||||
val custom = TrustManagerFactory.getInstance(
|
val custom = TrustManagerFactory.getInstance(
|
||||||
TrustManagerFactory.getDefaultAlgorithm())
|
TrustManagerFactory.getDefaultAlgorithm()
|
||||||
custom.init(keyStore)
|
)
|
||||||
factories.add(custom)
|
custom.init(keyStore)
|
||||||
|
factories.add(custom)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterate over the returned trustmanagers, and hold on
|
* Iterate over the returned trustmanagers, and hold on
|
||||||
* to any that are X509TrustManagers
|
* to any that are X509TrustManagers
|
||||||
*/
|
*/
|
||||||
for (tmf in factories) {
|
for (tmf in factories) {
|
||||||
for (tm in tmf.trustManagers) {
|
for (tm in tmf.trustManagers) {
|
||||||
if (tm is X509TrustManager) {
|
if (tm is X509TrustManager) {
|
||||||
x509TrustManagers.add(tm)
|
x509TrustManagers.add(tm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x509TrustManagers.isEmpty()) {
|
if (x509TrustManagers.isEmpty()) {
|
||||||
throw RuntimeException("Couldn't find any X509TrustManagers")
|
throw RuntimeException("Couldn't find any X509TrustManagers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val x509TrustManagers: MutableList<X509TrustManager> = ArrayList()
|
private val x509TrustManagers: MutableList<X509TrustManager> = ArrayList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setCustomKeyStore(keyStore)
|
setCustomKeyStore(keyStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,90 +15,100 @@ import javax.net.ssl.SSLSocketFactory
|
||||||
import javax.net.ssl.TrustManager
|
import javax.net.ssl.TrustManager
|
||||||
|
|
||||||
class CustomSSLSocketFactory(context: Context) : SSLSocketFactory() {
|
class CustomSSLSocketFactory(context: Context) : SSLSocketFactory() {
|
||||||
override fun getDefaultCipherSuites(): Array<String> {
|
override fun getDefaultCipherSuites(): Array<String> {
|
||||||
return sslSocketFactory.defaultCipherSuites
|
return sslSocketFactory.defaultCipherSuites
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSupportedCipherSuites(): Array<String> {
|
override fun getSupportedCipherSuites(): Array<String> {
|
||||||
return sslSocketFactory.supportedCipherSuites
|
return sslSocketFactory.supportedCipherSuites
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(): Socket {
|
override fun createSocket(): Socket {
|
||||||
return enableProtocols(sslSocketFactory.createSocket())
|
return enableProtocols(sslSocketFactory.createSocket())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(s: Socket, host: String, port: Int,
|
override fun createSocket(
|
||||||
autoClose: Boolean): Socket {
|
s: Socket, host: String, port: Int, autoClose: Boolean
|
||||||
return enableProtocols(
|
): Socket {
|
||||||
sslSocketFactory.createSocket(s, host, port, autoClose))
|
return enableProtocols(
|
||||||
}
|
sslSocketFactory.createSocket(s, host, port, autoClose)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(host: String, port: Int): Socket {
|
override fun createSocket(host: String, port: Int): Socket {
|
||||||
return enableProtocols(sslSocketFactory.createSocket(host, port))
|
return enableProtocols(sslSocketFactory.createSocket(host, port))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(host: String, port: Int, localHost: InetAddress,
|
override fun createSocket(
|
||||||
localPort: Int): Socket {
|
host: String, port: Int, localHost: InetAddress, localPort: Int
|
||||||
return enableProtocols(
|
): Socket {
|
||||||
sslSocketFactory.createSocket(host, port, localHost, localPort))
|
return enableProtocols(
|
||||||
}
|
sslSocketFactory.createSocket(host, port, localHost, localPort)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(host: InetAddress, port: Int): Socket {
|
override fun createSocket(host: InetAddress, port: Int): Socket {
|
||||||
return enableProtocols(sslSocketFactory.createSocket(host, port))
|
return enableProtocols(sslSocketFactory.createSocket(host, port))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun createSocket(address: InetAddress, port: Int,
|
override fun createSocket(
|
||||||
localAddress: InetAddress, localPort: Int): Socket {
|
address: InetAddress,
|
||||||
return enableProtocols(
|
port: Int,
|
||||||
sslSocketFactory.createSocket(address, port, localAddress,
|
localAddress: InetAddress,
|
||||||
localPort))
|
localPort: Int
|
||||||
}
|
): Socket {
|
||||||
|
return enableProtocols(
|
||||||
|
sslSocketFactory.createSocket(
|
||||||
|
address, port, localAddress, localPort
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun reload(context: Context) {
|
fun reload(context: Context) {
|
||||||
val keyStore = makeCustomKeyStore(context)
|
val keyStore = makeCustomKeyStore(context)
|
||||||
trustManager.setCustomKeyStore(keyStore)
|
trustManager.setCustomKeyStore(keyStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableProtocols(socket: Socket): Socket {
|
private fun enableProtocols(socket: Socket): Socket {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
// enable TLSv1.1 and TLSv1.2 Protocols for API level 19 and below
|
// enable TLSv1.1 and TLSv1.2 Protocols for API level 19 and below
|
||||||
if (socket is SSLSocket) {
|
if (socket is SSLSocket) {
|
||||||
socket.enabledProtocols = arrayOf("TLSv1.1", "TLSv1.2")
|
socket.enabledProtocols = arrayOf("TLSv1.1", "TLSv1.2")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return socket
|
return socket
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCustomKeyStore(context: Context): KeyStore {
|
private fun makeCustomKeyStore(context: Context): KeyStore {
|
||||||
// build key store with ca certificate
|
// build key store with ca certificate
|
||||||
val keyStoreType = KeyStore.getDefaultType()
|
val keyStoreType = KeyStore.getDefaultType()
|
||||||
val keyStore = KeyStore.getInstance(keyStoreType)
|
val keyStore = KeyStore.getInstance(keyStoreType)
|
||||||
keyStore.load(null, null)
|
keyStore.load(null, null)
|
||||||
|
|
||||||
val certManager = SelfSignedCertManager()
|
val certManager = SelfSignedCertManager()
|
||||||
val certs = certManager.readAllCerts(context)
|
val certs = certManager.readAllCerts(context)
|
||||||
for (c in certs) {
|
for (c in certs) {
|
||||||
keyStore.setCertificateEntry(c.first.host, c.second)
|
keyStore.setCertificateEntry(c.first.host, c.second)
|
||||||
}
|
}
|
||||||
return keyStore
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sslSocketFactory: SSLSocketFactory
|
private val sslSocketFactory: SSLSocketFactory
|
||||||
private val trustManager: CustomKeyStoresTrustManager
|
private val trustManager: CustomKeyStoresTrustManager
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val keyStore = makeCustomKeyStore(context)
|
val keyStore = makeCustomKeyStore(context)
|
||||||
trustManager = CustomKeyStoresTrustManager(keyStore)
|
trustManager = CustomKeyStoresTrustManager(keyStore)
|
||||||
|
|
||||||
// Create an SSLContext that uses our TrustManager
|
// Create an SSLContext that uses our TrustManager
|
||||||
val sslContext = SSLContext.getInstance("TLS")
|
val sslContext = SSLContext.getInstance("TLS")
|
||||||
sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
|
sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
|
||||||
sslSocketFactory = sslContext.socketFactory
|
sslSocketFactory = sslContext.socketFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,40 +8,40 @@ import com.nkming.nc_photos.plugin.NcPhotosPlugin
|
||||||
import io.flutter.plugin.common.EventChannel
|
import io.flutter.plugin.common.EventChannel
|
||||||
|
|
||||||
class DownloadEventCancelChannelHandler(context: Context) : BroadcastReceiver(),
|
class DownloadEventCancelChannelHandler(context: Context) : BroadcastReceiver(),
|
||||||
EventChannel.StreamHandler {
|
EventChannel.StreamHandler {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val CHANNEL =
|
val CHANNEL =
|
||||||
"com.nkming.nc_photos/download_event/action_download_cancel"
|
"com.nkming.nc_photos/download_event/action_download_cancel"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
if (intent?.action != NcPhotosPlugin.ACTION_DOWNLOAD_CANCEL || !intent.hasExtra(
|
if (intent?.action != NcPhotosPlugin.ACTION_DOWNLOAD_CANCEL || !intent.hasExtra(
|
||||||
NcPhotosPlugin.EXTRA_NOTIFICATION_ID
|
NcPhotosPlugin.EXTRA_NOTIFICATION_ID
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val id = intent.getIntExtra(NcPhotosPlugin.EXTRA_NOTIFICATION_ID, 0)
|
val id = intent.getIntExtra(NcPhotosPlugin.EXTRA_NOTIFICATION_ID, 0)
|
||||||
_eventSink?.success(
|
_eventSink?.success(
|
||||||
mapOf(
|
mapOf(
|
||||||
"notificationId" to id
|
"notificationId" to id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
|
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
|
||||||
_context.registerReceiver(
|
_context.registerReceiver(
|
||||||
this, IntentFilter(NcPhotosPlugin.ACTION_DOWNLOAD_CANCEL)
|
this, IntentFilter(NcPhotosPlugin.ACTION_DOWNLOAD_CANCEL)
|
||||||
)
|
)
|
||||||
_eventSink = events
|
_eventSink = events
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCancel(arguments: Any?) {
|
override fun onCancel(arguments: Any?) {
|
||||||
_context.unregisterReceiver(this)
|
_context.unregisterReceiver(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _context = context
|
private val _context = context
|
||||||
private var _eventSink: EventChannel.EventSink? = null
|
private var _eventSink: EventChannel.EventSink? = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.nkming.nc_photos
|
|
||||||
|
|
||||||
interface K {
|
|
||||||
companion object {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,11 +6,11 @@ import android.os.Bundle
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
import com.google.android.gms.maps.MapsInitializer
|
import com.google.android.gms.maps.MapsInitializer
|
||||||
import com.google.android.gms.maps.OnMapsSdkInitializedCallback
|
import com.google.android.gms.maps.OnMapsSdkInitializedCallback
|
||||||
import com.nkming.nc_photos.np_android_log.logD
|
import com.nkming.nc_photos.np_android_core.UriUtil
|
||||||
import com.nkming.nc_photos.np_android_log.logE
|
import com.nkming.nc_photos.np_android_core.logD
|
||||||
import com.nkming.nc_photos.np_android_log.logI
|
import com.nkming.nc_photos.np_android_core.logE
|
||||||
import com.nkming.nc_photos.plugin.NcPhotosPlugin
|
import com.nkming.nc_photos.np_android_core.logI
|
||||||
import com.nkming.nc_photos.plugin.UriUtil
|
import com.nkming.nc_photos.np_platform_image_processor.NpPlatformImageProcessorPlugin
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.plugin.common.EventChannel
|
import io.flutter.plugin.common.EventChannel
|
||||||
|
@ -19,117 +19,117 @@ import io.flutter.plugin.common.MethodChannel
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
|
||||||
class MainActivity : FlutterActivity(), MethodChannel.MethodCallHandler,
|
class MainActivity : FlutterActivity(), MethodChannel.MethodCallHandler,
|
||||||
OnMapsSdkInitializedCallback {
|
OnMapsSdkInitializedCallback {
|
||||||
companion object {
|
companion object {
|
||||||
private const val METHOD_CHANNEL = "com.nkming.nc_photos/activity"
|
private const val METHOD_CHANNEL = "com.nkming.nc_photos/activity"
|
||||||
|
|
||||||
private const val TAG = "MainActivity"
|
private const val TAG = "MainActivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (intent.action == NcPhotosPlugin.ACTION_SHOW_IMAGE_PROCESSOR_RESULT) {
|
if (intent.action == NpPlatformImageProcessorPlugin.ACTION_SHOW_IMAGE_PROCESSOR_RESULT) {
|
||||||
val route = getRouteFromImageProcessorResult(intent) ?: return
|
val route = getRouteFromImageProcessorResult(intent) ?: return
|
||||||
logI(TAG, "Initial route: $route")
|
logI(TAG, "Initial route: $route")
|
||||||
_initialRoute = route
|
_initialRoute = route
|
||||||
}
|
}
|
||||||
MapsInitializer.initialize(
|
MapsInitializer.initialize(
|
||||||
applicationContext, MapsInitializer.Renderer.LATEST, this
|
applicationContext, MapsInitializer.Renderer.LATEST, this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
MethodChannel(
|
MethodChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
SelfSignedCertChannelHandler.CHANNEL
|
SelfSignedCertChannelHandler.CHANNEL
|
||||||
).setMethodCallHandler(
|
).setMethodCallHandler(
|
||||||
SelfSignedCertChannelHandler(this)
|
SelfSignedCertChannelHandler(this)
|
||||||
)
|
)
|
||||||
MethodChannel(
|
MethodChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
ShareChannelHandler.CHANNEL
|
ShareChannelHandler.CHANNEL
|
||||||
).setMethodCallHandler(
|
).setMethodCallHandler(
|
||||||
ShareChannelHandler(this)
|
ShareChannelHandler(this)
|
||||||
)
|
)
|
||||||
MethodChannel(
|
MethodChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL
|
flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL
|
||||||
).setMethodCallHandler(this)
|
).setMethodCallHandler(this)
|
||||||
|
|
||||||
EventChannel(
|
EventChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
DownloadEventCancelChannelHandler.CHANNEL
|
DownloadEventCancelChannelHandler.CHANNEL
|
||||||
).setStreamHandler(
|
).setStreamHandler(
|
||||||
DownloadEventCancelChannelHandler(this)
|
DownloadEventCancelChannelHandler(this)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
NcPhotosPlugin.ACTION_SHOW_IMAGE_PROCESSOR_RESULT -> {
|
NpPlatformImageProcessorPlugin.ACTION_SHOW_IMAGE_PROCESSOR_RESULT -> {
|
||||||
val route = getRouteFromImageProcessorResult(intent) ?: return
|
val route = getRouteFromImageProcessorResult(intent) ?: return
|
||||||
logI(TAG, "Navigate to route: $route")
|
logI(TAG, "Navigate to route: $route")
|
||||||
flutterEngine?.navigationChannel?.pushRoute(route)
|
flutterEngine?.navigationChannel?.pushRoute(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"consumeInitialRoute" -> {
|
"consumeInitialRoute" -> {
|
||||||
result.success(_initialRoute)
|
result.success(_initialRoute)
|
||||||
_initialRoute = null
|
_initialRoute = null
|
||||||
}
|
}
|
||||||
|
|
||||||
"isNewGMapsRenderer" -> {
|
"isNewGMapsRenderer" -> {
|
||||||
result.success(_isNewGMapsRenderer)
|
result.success(_isNewGMapsRenderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMapsSdkInitialized(renderer: MapsInitializer.Renderer) {
|
override fun onMapsSdkInitialized(renderer: MapsInitializer.Renderer) {
|
||||||
_isNewGMapsRenderer = when (renderer) {
|
_isNewGMapsRenderer = when (renderer) {
|
||||||
MapsInitializer.Renderer.LATEST -> {
|
MapsInitializer.Renderer.LATEST -> {
|
||||||
logD(TAG, "Using new map renderer")
|
logD(TAG, "Using new map renderer")
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
MapsInitializer.Renderer.LEGACY -> {
|
MapsInitializer.Renderer.LEGACY -> {
|
||||||
logD(TAG, "Using legacy map renderer")
|
logD(TAG, "Using legacy map renderer")
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRouteFromImageProcessorResult(intent: Intent): String? {
|
private fun getRouteFromImageProcessorResult(intent: Intent): String? {
|
||||||
val resultUri = intent.getParcelableExtra<Uri>(
|
val resultUri = intent.getParcelableExtra<Uri>(
|
||||||
NcPhotosPlugin.EXTRA_IMAGE_RESULT_URI
|
NpPlatformImageProcessorPlugin.EXTRA_IMAGE_RESULT_URI
|
||||||
)
|
)
|
||||||
if (resultUri == null) {
|
if (resultUri == null) {
|
||||||
logE(TAG, "Image result uri == null")
|
logE(TAG, "Image result uri == null")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return if (resultUri.scheme?.startsWith("http") == true) {
|
return if (resultUri.scheme?.startsWith("http") == true) {
|
||||||
// remote uri
|
// remote uri
|
||||||
val encodedUrl = URLEncoder.encode(resultUri.toString(), "utf-8")
|
val encodedUrl = URLEncoder.encode(resultUri.toString(), "utf-8")
|
||||||
"/result-viewer?url=$encodedUrl"
|
"/result-viewer?url=$encodedUrl"
|
||||||
} else {
|
} else {
|
||||||
val filename = UriUtil.resolveFilename(this, resultUri)?.let {
|
val filename = UriUtil.resolveFilename(this, resultUri)?.let {
|
||||||
URLEncoder.encode(it, Charsets.UTF_8.toString())
|
URLEncoder.encode(it, Charsets.UTF_8.toString())
|
||||||
}
|
}
|
||||||
StringBuilder().apply {
|
StringBuilder().apply {
|
||||||
append("/enhanced-photo-browser?")
|
append("/enhanced-photo-browser?")
|
||||||
if (filename != null) append("filename=$filename")
|
if (filename != null) append("filename=$filename")
|
||||||
}.toString()
|
}.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _initialRoute: String? = null
|
private var _initialRoute: String? = null
|
||||||
private var _isNewGMapsRenderer = false
|
private var _isNewGMapsRenderer = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.nkming.nc_photos
|
package com.nkming.nc_photos
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import com.nkming.nc_photos.np_android_log.logE
|
import com.nkming.nc_photos.np_android_core.logE
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
|
@ -2,8 +2,8 @@ package com.nkming.nc_photos
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import com.nkming.nc_photos.np_android_log.logE
|
import com.nkming.nc_photos.np_android_core.logE
|
||||||
import com.nkming.nc_photos.np_android_log.logI
|
import com.nkming.nc_photos.np_android_core.logI
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
@ -13,57 +13,63 @@ import java.time.OffsetDateTime
|
||||||
|
|
||||||
// Modifications to this class must also reflect on dart side
|
// Modifications to this class must also reflect on dart side
|
||||||
data class CertInfo(
|
data class CertInfo(
|
||||||
val host: String, val sha1: String, val subject: String,
|
val host: String,
|
||||||
val issuer: String, val startValidity: OffsetDateTime,
|
val sha1: String,
|
||||||
val endValidity: OffsetDateTime
|
val subject: String,
|
||||||
|
val issuer: String,
|
||||||
|
val startValidity: OffsetDateTime,
|
||||||
|
val endValidity: OffsetDateTime
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromJson(json: JSONObject): CertInfo {
|
fun fromJson(json: JSONObject): CertInfo {
|
||||||
return CertInfo(
|
return CertInfo(
|
||||||
json.getString("host"), json.getString("sha1"),
|
json.getString("host"),
|
||||||
json.getString("subject"), json.getString("issuer"),
|
json.getString("sha1"),
|
||||||
OffsetDateTime.parse(json.getString("startValidity")),
|
json.getString("subject"),
|
||||||
OffsetDateTime.parse(json.getString("endValidity"))
|
json.getString("issuer"),
|
||||||
)
|
OffsetDateTime.parse(json.getString("startValidity")),
|
||||||
}
|
OffsetDateTime.parse(json.getString("endValidity"))
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelfSignedCertManager {
|
class SelfSignedCertManager {
|
||||||
/**
|
/**
|
||||||
* Read and return all persisted certificates
|
* Read and return all persisted certificates
|
||||||
*
|
*
|
||||||
* @return List of certificates with the corresponding info
|
* @return List of certificates with the corresponding info
|
||||||
*/
|
*/
|
||||||
fun readAllCerts(context: Context): List<Pair<CertInfo, Certificate>> {
|
fun readAllCerts(context: Context): List<Pair<CertInfo, Certificate>> {
|
||||||
val products = ArrayList<Pair<CertInfo, Certificate>>()
|
val products = ArrayList<Pair<CertInfo, Certificate>>()
|
||||||
val certDir = openCertsDir(context)
|
val certDir = openCertsDir(context)
|
||||||
val certFiles = certDir.listFiles()!!
|
val certFiles = certDir.listFiles()!!
|
||||||
val factory = CertificateFactory.getInstance("X.509")
|
val factory = CertificateFactory.getInstance("X.509")
|
||||||
for (f in certFiles) {
|
for (f in certFiles) {
|
||||||
if (f.name.endsWith(".json")) {
|
if (f.name.endsWith(".json")) {
|
||||||
// companion file
|
// companion file
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val c = factory.generateCertificate(FileInputStream(f))
|
val c = factory.generateCertificate(FileInputStream(f))
|
||||||
val jsonFile = File(certDir, f.name + ".json")
|
val jsonFile = File(certDir, f.name + ".json")
|
||||||
val jsonStr = jsonFile.bufferedReader().use { it.readText() }
|
val jsonStr = jsonFile.bufferedReader().use { it.readText() }
|
||||||
val info = CertInfo.fromJson(JSONObject(jsonStr))
|
val info = CertInfo.fromJson(JSONObject(jsonStr))
|
||||||
logI(
|
logI(
|
||||||
"SelfSignedCertManager::readAllCerts",
|
"SelfSignedCertManager::readAllCerts",
|
||||||
"Found certificate: ${f.name} for host: ${info.host}"
|
"Found certificate: ${f.name} for host: ${info.host}"
|
||||||
)
|
)
|
||||||
products.add(Pair(info, c))
|
products.add(Pair(info, c))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logE(
|
logE(
|
||||||
"SelfSignedCertManager::readAllCerts",
|
"SelfSignedCertManager::readAllCerts",
|
||||||
"Failed to read certificate file: ${f.name}", e
|
"Failed to read certificate file: ${f.name}",
|
||||||
)
|
e
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
return products
|
}
|
||||||
}
|
return products
|
||||||
|
}
|
||||||
|
|
||||||
// Outdated, don't use
|
// Outdated, don't use
|
||||||
// /**
|
// /**
|
||||||
|
@ -88,21 +94,21 @@ class SelfSignedCertManager {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private fun openCertsDir(context: Context): File {
|
private fun openCertsDir(context: Context): File {
|
||||||
val certDir = File(context.filesDir, "certs")
|
val certDir = File(context.filesDir, "certs")
|
||||||
return if (!certDir.exists()) {
|
return if (!certDir.exists()) {
|
||||||
certDir.mkdir()
|
certDir.mkdir()
|
||||||
certDir
|
certDir
|
||||||
} else if (!certDir.isDirectory) {
|
} else if (!certDir.isDirectory) {
|
||||||
logE(
|
logE(
|
||||||
"SelfSignedCertManager::openCertsDir",
|
"SelfSignedCertManager::openCertsDir",
|
||||||
"Removing certs file to make way for the directory"
|
"Removing certs file to make way for the directory"
|
||||||
)
|
)
|
||||||
certDir.delete()
|
certDir.delete()
|
||||||
certDir.mkdir()
|
certDir.mkdir()
|
||||||
certDir
|
certDir
|
||||||
} else {
|
} else {
|
||||||
certDir
|
certDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,130 +8,134 @@ import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
class ShareChannelHandler(activity: Activity) :
|
class ShareChannelHandler(activity: Activity) :
|
||||||
MethodChannel.MethodCallHandler {
|
MethodChannel.MethodCallHandler {
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL = "com.nkming.nc_photos/share"
|
const val CHANNEL = "com.nkming.nc_photos/share"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"shareItems" -> {
|
"shareItems" -> {
|
||||||
try {
|
try {
|
||||||
shareItems(
|
shareItems(
|
||||||
call.argument("fileUris")!!,
|
call.argument("fileUris")!!,
|
||||||
call.argument("mimeTypes")!!, result
|
call.argument("mimeTypes")!!,
|
||||||
)
|
result
|
||||||
} catch (e: Throwable) {
|
)
|
||||||
result.error("systemException", e.toString(), null)
|
} catch (e: Throwable) {
|
||||||
}
|
result.error("systemException", e.toString(), null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"shareText" -> {
|
"shareText" -> {
|
||||||
try {
|
try {
|
||||||
shareText(
|
shareText(
|
||||||
call.argument("text")!!, call.argument("mimeType"),
|
call.argument("text")!!,
|
||||||
result
|
call.argument("mimeType"),
|
||||||
)
|
result
|
||||||
} catch (e: Throwable) {
|
)
|
||||||
result.error("systemException", e.toString(), null)
|
} catch (e: Throwable) {
|
||||||
}
|
result.error("systemException", e.toString(), null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"shareAsAttachData" -> {
|
"shareAsAttachData" -> {
|
||||||
try {
|
try {
|
||||||
shareAsAttachData(
|
shareAsAttachData(
|
||||||
call.argument("fileUri")!!, call.argument("mimeType"),
|
call.argument("fileUri")!!,
|
||||||
result
|
call.argument("mimeType"),
|
||||||
)
|
result
|
||||||
} catch (e: Throwable) {
|
)
|
||||||
result.error("systemException", e.toString(), null)
|
} catch (e: Throwable) {
|
||||||
}
|
result.error("systemException", e.toString(), null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
result.notImplemented()
|
result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareItems(
|
private fun shareItems(
|
||||||
fileUris: List<String>, mimeTypes: List<String?>,
|
fileUris: List<String>,
|
||||||
result: MethodChannel.Result
|
mimeTypes: List<String?>,
|
||||||
) {
|
result: MethodChannel.Result
|
||||||
assert(fileUris.isNotEmpty())
|
) {
|
||||||
assert(fileUris.size == mimeTypes.size)
|
assert(fileUris.isNotEmpty())
|
||||||
val uris = fileUris.map { Uri.parse(it) }
|
assert(fileUris.size == mimeTypes.size)
|
||||||
|
val uris = fileUris.map { Uri.parse(it) }
|
||||||
|
|
||||||
val shareIntent = if (uris.size == 1) Intent().apply {
|
val shareIntent = if (uris.size == 1) Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
putExtra(Intent.EXTRA_STREAM, uris[0])
|
putExtra(Intent.EXTRA_STREAM, uris[0])
|
||||||
// setting clipdata is needed for FLAG_GRANT_READ_URI_PERMISSION to
|
// setting clipdata is needed for FLAG_GRANT_READ_URI_PERMISSION to
|
||||||
// work, see: https://developer.android.com/reference/android/content/Intent#ACTION_SEND
|
// work, see: https://developer.android.com/reference/android/content/Intent#ACTION_SEND
|
||||||
clipData =
|
clipData =
|
||||||
ClipData.newUri(_context.contentResolver, "Share", uris[0])
|
ClipData.newUri(_context.contentResolver, "Share", uris[0])
|
||||||
type = mimeTypes[0] ?: "*/*"
|
type = mimeTypes[0] ?: "*/*"
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
} else Intent().apply {
|
} else Intent().apply {
|
||||||
action = Intent.ACTION_SEND_MULTIPLE
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||||
clipData =
|
clipData =
|
||||||
ClipData.newUri(_context.contentResolver, "Share", uris[0])
|
ClipData.newUri(_context.contentResolver, "Share", uris[0])
|
||||||
.apply {
|
.apply {
|
||||||
for (uri in uris.subList(1, uris.size)) {
|
for (uri in uris.subList(1, uris.size)) {
|
||||||
addItem(ClipData.Item(uri))
|
addItem(ClipData.Item(uri))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type = if (mimeTypes.all {
|
type = if (mimeTypes.all {
|
||||||
it?.startsWith("image/") == true
|
it?.startsWith("image/") == true
|
||||||
}) "image/*" else "*/*"
|
}) "image/*" else "*/*"
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
val shareChooser = Intent.createChooser(
|
val shareChooser = Intent.createChooser(
|
||||||
shareIntent, _context.getString(
|
shareIntent, _context.getString(
|
||||||
R.string.download_successful_notification_action_share_chooser
|
R.string.download_successful_notification_action_share_chooser
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_context.startActivity(shareChooser)
|
_context.startActivity(shareChooser)
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareText(
|
private fun shareText(
|
||||||
text: String, mimeType: String?, result: MethodChannel.Result
|
text: String, mimeType: String?, result: MethodChannel.Result
|
||||||
) {
|
) {
|
||||||
val shareIntent = Intent().apply {
|
val shareIntent = Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
type = mimeType ?: "text/plain"
|
type = mimeType ?: "text/plain"
|
||||||
putExtra(Intent.EXTRA_TEXT, text)
|
putExtra(Intent.EXTRA_TEXT, text)
|
||||||
}
|
}
|
||||||
val shareChooser = Intent.createChooser(
|
val shareChooser = Intent.createChooser(
|
||||||
shareIntent, _context.getString(
|
shareIntent, _context.getString(
|
||||||
R.string.download_successful_notification_action_share_chooser
|
R.string.download_successful_notification_action_share_chooser
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_context.startActivity(shareChooser)
|
_context.startActivity(shareChooser)
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareAsAttachData(
|
private fun shareAsAttachData(
|
||||||
fileUri: String, mimeType: String?, result: MethodChannel.Result
|
fileUri: String, mimeType: String?, result: MethodChannel.Result
|
||||||
) {
|
) {
|
||||||
val intent = Intent().apply {
|
val intent = Intent().apply {
|
||||||
action = Intent.ACTION_ATTACH_DATA
|
action = Intent.ACTION_ATTACH_DATA
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
data = Uri.parse(fileUri)
|
data = Uri.parse(fileUri)
|
||||||
} else {
|
} else {
|
||||||
setDataAndType(Uri.parse(fileUri), mimeType)
|
setDataAndType(Uri.parse(fileUri), mimeType)
|
||||||
}
|
}
|
||||||
addCategory(Intent.CATEGORY_DEFAULT)
|
addCategory(Intent.CATEGORY_DEFAULT)
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
val chooser = Intent.createChooser(
|
val chooser = Intent.createChooser(
|
||||||
intent, _context.getString(R.string.attach_data_chooser_title)
|
intent, _context.getString(R.string.attach_data_chooser_title)
|
||||||
)
|
)
|
||||||
_context.startActivity(chooser)
|
_context.startActivity(chooser)
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _activity = activity
|
private val _activity = activity
|
||||||
private val _context get() = _activity
|
private val _context get() = _activity
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
include ':app'
|
include ':app'
|
||||||
includeBuild '../../np_android_log'
|
includeBuild '../../np_android_core'
|
||||||
|
|
||||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
def properties = new Properties()
|
def properties = new Properties()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/stream_extension.dart';
|
import 'package:nc_photos/stream_extension.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
import 'package:np_platform_permission/np_platform_permission.dart';
|
||||||
|
|
||||||
Future<Map<String, int>> requestPermissionsForResult(
|
Future<Map<String, int>> requestPermissionsForResult(
|
||||||
List<String> permissions) async {
|
List<String> permissions) async {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||||
import 'package:np_async/np_async.dart';
|
import 'package:np_async/np_async.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
|
import 'package:np_platform_permission/np_platform_permission.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
|
|
||||||
part 'enhanced_photo_browser.g.dart';
|
part 'enhanced_photo_browser.g.dart';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:nc_photos/k.dart' as k;
|
||||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||||
import 'package:nc_photos/mobile/android/permission_util.dart';
|
import 'package:nc_photos/mobile/android/permission_util.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
import 'package:np_platform_permission/np_platform_permission.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
|
|
||||||
/// Handle platform permissions
|
/// Handle platform permissions
|
||||||
|
|
|
@ -23,7 +23,8 @@ import 'package:nc_photos/widget/image_editor/color_toolbar.dart';
|
||||||
import 'package:nc_photos/widget/image_editor/crop_controller.dart';
|
import 'package:nc_photos/widget/image_editor/crop_controller.dart';
|
||||||
import 'package:nc_photos/widget/image_editor/transform_toolbar.dart';
|
import 'package:nc_photos/widget/image_editor/transform_toolbar.dart';
|
||||||
import 'package:nc_photos/widget/image_editor_persist_option_dialog.dart';
|
import 'package:nc_photos/widget/image_editor_persist_option_dialog.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
import 'package:np_platform_image_processor/np_platform_image_processor.dart';
|
||||||
|
import 'package:np_platform_raw_image/np_platform_raw_image.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
|
|
||||||
class ImageEditorArguments {
|
class ImageEditorArguments {
|
||||||
|
|
|
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:nc_photos/app_localizations.dart';
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
import 'package:nc_photos/object_extension.dart';
|
import 'package:nc_photos/object_extension.dart';
|
||||||
import 'package:nc_photos/widget/image_editor/toolbar_button.dart';
|
import 'package:nc_photos/widget/image_editor/toolbar_button.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
|
||||||
import 'package:np_collection/np_collection.dart';
|
import 'package:np_collection/np_collection.dart';
|
||||||
|
import 'package:np_platform_image_processor/np_platform_image_processor.dart';
|
||||||
import 'package:np_string/np_string.dart';
|
import 'package:np_string/np_string.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,9 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/pixel_image_provider.dart';
|
import 'package:nc_photos/pixel_image_provider.dart';
|
||||||
import 'package:nc_photos/widget/image_editor/transform_toolbar.dart';
|
import 'package:nc_photos/widget/image_editor/transform_toolbar.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
import 'package:np_platform_image_processor/np_platform_image_processor.dart';
|
||||||
|
import 'package:np_platform_raw_image/np_platform_raw_image.dart';
|
||||||
|
|
||||||
part 'crop_controller.g.dart';
|
part 'crop_controller.g.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:nc_photos/app_localizations.dart';
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
import 'package:nc_photos/widget/image_editor/toolbar_button.dart';
|
import 'package:nc_photos/widget/image_editor/toolbar_button.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
import 'package:np_platform_image_processor/np_platform_image_processor.dart';
|
||||||
|
|
||||||
enum TransformToolType {
|
enum TransformToolType {
|
||||||
crop,
|
crop,
|
||||||
|
|
|
@ -28,8 +28,8 @@ import 'package:nc_photos/widget/handler/permission_handler.dart';
|
||||||
import 'package:nc_photos/widget/image_editor_persist_option_dialog.dart';
|
import 'package:nc_photos/widget/image_editor_persist_option_dialog.dart';
|
||||||
import 'package:nc_photos/widget/selectable.dart';
|
import 'package:nc_photos/widget/selectable.dart';
|
||||||
import 'package:nc_photos/widget/settings/enhancement_settings.dart';
|
import 'package:nc_photos/widget/settings/enhancement_settings.dart';
|
||||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
import 'package:np_platform_image_processor/np_platform_image_processor.dart';
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:np_ui/np_ui.dart';
|
import 'package:np_ui/np_ui.dart';
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import 'package:nc_photos/mobile/self_signed_cert_manager.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:np_platform_lock/np_platform_lock.dart';
|
|
||||||
import 'package:np_platform_util/np_platform_util.dart';
|
import 'package:np_platform_util/np_platform_util.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
|
||||||
|
|
|
@ -1011,6 +1011,13 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
np_platform_image_processor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../np_platform_image_processor"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
np_platform_lock:
|
np_platform_lock:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1018,6 +1025,20 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
np_platform_permission:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../np_platform_permission"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
|
np_platform_raw_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../np_platform_raw_image"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
np_platform_util:
|
np_platform_util:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -109,8 +109,14 @@ dependencies:
|
||||||
path: ../np_log
|
path: ../np_log
|
||||||
np_math:
|
np_math:
|
||||||
path: ../np_math
|
path: ../np_math
|
||||||
|
np_platform_image_processor:
|
||||||
|
path: ../np_platform_image_processor
|
||||||
np_platform_lock:
|
np_platform_lock:
|
||||||
path: ../np_platform_lock
|
path: ../np_platform_lock
|
||||||
|
np_platform_permission:
|
||||||
|
path: ../np_platform_permission
|
||||||
|
np_platform_raw_image:
|
||||||
|
path: ../np_platform_raw_image
|
||||||
np_platform_util:
|
np_platform_util:
|
||||||
path: ../np_platform_util
|
path: ../np_platform_util
|
||||||
np_string:
|
np_string:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
group 'com.nkming.nc_photos.np_android_log'
|
group 'com.nkming.nc_photos.np_android_core'
|
||||||
version '1.0-SNAPSHOT'
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -26,7 +26,7 @@ apply plugin: 'com.android.library'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.nkming.nc_photos.np_android_log'
|
namespace 'com.nkming.nc_photos.np_android_core'
|
||||||
compileSdk 31
|
compileSdk 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -51,4 +51,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation "androidx.annotation:annotation:1.6.0"
|
||||||
|
implementation "androidx.core:core-ktx:1.10.1"
|
||||||
|
implementation "androidx.exifinterface:exifinterface:1.3.6"
|
||||||
}
|
}
|
1
np_android_core/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = "np_android_core"
|
|
@ -0,0 +1,244 @@
|
||||||
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.exifinterface.media.ExifInterface
|
||||||
|
import java.io.InputStream
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
fun Bitmap.aspectRatio() = width / height.toFloat()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recycle the bitmap after @c block returns.
|
||||||
|
*
|
||||||
|
* @param block
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
inline fun <T> Bitmap.use(block: (Bitmap) -> T): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return block(this)
|
||||||
|
} finally {
|
||||||
|
recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BitmapResizeMethod {
|
||||||
|
FIT, FILL,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BitmapUtil {
|
||||||
|
companion object {
|
||||||
|
fun loadImageFixed(
|
||||||
|
context: Context, uri: Uri, targetW: Int, targetH: Int
|
||||||
|
): Bitmap {
|
||||||
|
val opt = loadImageBounds(context, uri)
|
||||||
|
val subsample = calcBitmapSubsample(
|
||||||
|
opt.outWidth,
|
||||||
|
opt.outHeight,
|
||||||
|
targetW,
|
||||||
|
targetH,
|
||||||
|
BitmapResizeMethod.FILL
|
||||||
|
)
|
||||||
|
if (subsample > 1) {
|
||||||
|
logD(
|
||||||
|
TAG,
|
||||||
|
"Subsample image to fixed: $subsample ${opt.outWidth}x${opt.outHeight} -> ${targetW}x$targetH"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val outOpt = BitmapFactory.Options().apply {
|
||||||
|
inSampleSize = subsample
|
||||||
|
}
|
||||||
|
val bitmap = loadImage(context, uri, outOpt)
|
||||||
|
if (subsample > 1) {
|
||||||
|
logD(TAG, "Bitmap subsampled: ${bitmap.width}x${bitmap.height}")
|
||||||
|
}
|
||||||
|
return Bitmap.createScaledBitmap(bitmap, targetW, targetH, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a bitmap
|
||||||
|
*
|
||||||
|
* If @c resizeMethod == FIT, make sure the size of the bitmap can fit
|
||||||
|
* inside the bound defined by @c targetW and @c targetH, i.e.,
|
||||||
|
* bitmap.w <= @c targetW and bitmap.h <= @c targetH
|
||||||
|
*
|
||||||
|
* If @c resizeMethod == FILL, make sure the size of the bitmap can
|
||||||
|
* completely fill the bound defined by @c targetW and @c targetH, i.e.,
|
||||||
|
* bitmap.w >= @c targetW and bitmap.h >= @c targetH
|
||||||
|
*
|
||||||
|
* If bitmap is smaller than the bound and @c shouldUpscale == true, it
|
||||||
|
* will be upscaled
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param uri
|
||||||
|
* @param targetW
|
||||||
|
* @param targetH
|
||||||
|
* @param resizeMethod
|
||||||
|
* @param isAllowSwapSide
|
||||||
|
* @param shouldUpscale
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun loadImage(
|
||||||
|
context: Context,
|
||||||
|
uri: Uri,
|
||||||
|
targetW: Int,
|
||||||
|
targetH: Int,
|
||||||
|
resizeMethod: BitmapResizeMethod,
|
||||||
|
isAllowSwapSide: Boolean = false,
|
||||||
|
shouldUpscale: Boolean = true,
|
||||||
|
shouldFixOrientation: Boolean = false,
|
||||||
|
): Bitmap {
|
||||||
|
val opt = loadImageBounds(context, uri)
|
||||||
|
val shouldSwapSide =
|
||||||
|
isAllowSwapSide && opt.outWidth != opt.outHeight && (opt.outWidth >= opt.outHeight) != (targetW >= targetH)
|
||||||
|
val dstW = if (shouldSwapSide) targetH else targetW
|
||||||
|
val dstH = if (shouldSwapSide) targetW else targetH
|
||||||
|
val subsample = calcBitmapSubsample(
|
||||||
|
opt.outWidth, opt.outHeight, dstW, dstH, resizeMethod
|
||||||
|
)
|
||||||
|
if (subsample > 1) {
|
||||||
|
logD(
|
||||||
|
TAG,
|
||||||
|
"Subsample image to ${resizeMethod.name}: $subsample ${opt.outWidth}x${opt.outHeight} -> ${dstW}x$dstH" + (if (shouldSwapSide) " (swapped)" else "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val outOpt = BitmapFactory.Options().apply {
|
||||||
|
inSampleSize = subsample
|
||||||
|
}
|
||||||
|
val bitmap = loadImage(context, uri, outOpt)
|
||||||
|
if (subsample > 1) {
|
||||||
|
logD(TAG, "Bitmap subsampled: ${bitmap.width}x${bitmap.height}")
|
||||||
|
}
|
||||||
|
if (bitmap.width < dstW && bitmap.height < dstH && !shouldUpscale) {
|
||||||
|
return if (shouldFixOrientation) {
|
||||||
|
fixOrientation(context, uri, bitmap)
|
||||||
|
} else {
|
||||||
|
bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val result = when (resizeMethod) {
|
||||||
|
BitmapResizeMethod.FIT -> Bitmap.createScaledBitmap(
|
||||||
|
bitmap,
|
||||||
|
minOf(dstW, (dstH * bitmap.aspectRatio()).toInt()),
|
||||||
|
minOf(dstH, (dstW / bitmap.aspectRatio()).toInt()),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
BitmapResizeMethod.FILL -> Bitmap.createScaledBitmap(
|
||||||
|
bitmap,
|
||||||
|
maxOf(dstW, (dstH * bitmap.aspectRatio()).toInt()),
|
||||||
|
maxOf(dstH, (dstW / bitmap.aspectRatio()).toInt()),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return if (shouldFixOrientation) {
|
||||||
|
fixOrientation(context, uri, result)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate the image to its visible orientation
|
||||||
|
*/
|
||||||
|
private fun fixOrientation(
|
||||||
|
context: Context, uri: Uri, bitmap: Bitmap
|
||||||
|
): Bitmap {
|
||||||
|
return try {
|
||||||
|
openUriInputStream(context, uri)!!.use {
|
||||||
|
val iExif = ExifInterface(it)
|
||||||
|
val orientation =
|
||||||
|
iExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1)
|
||||||
|
logI(
|
||||||
|
TAG,
|
||||||
|
"[fixOrientation] Input file orientation: $orientation"
|
||||||
|
)
|
||||||
|
val rotate = when (orientation) {
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_90, ExifInterface.ORIENTATION_TRANSPOSE -> 90f
|
||||||
|
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_180, ExifInterface.ORIENTATION_FLIP_VERTICAL -> 180f
|
||||||
|
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_270, ExifInterface.ORIENTATION_TRANSVERSE -> 270f
|
||||||
|
|
||||||
|
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> 0f
|
||||||
|
else -> return bitmap
|
||||||
|
}
|
||||||
|
val mat = Matrix()
|
||||||
|
mat.postRotate(rotate)
|
||||||
|
if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL || orientation == ExifInterface.ORIENTATION_TRANSVERSE || orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL || orientation == ExifInterface.ORIENTATION_TRANSPOSE) {
|
||||||
|
mat.postScale(-1f, 1f)
|
||||||
|
}
|
||||||
|
Bitmap.createBitmap(
|
||||||
|
bitmap, 0, 0, bitmap.width, bitmap.height, mat, true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
logE(
|
||||||
|
TAG,
|
||||||
|
"[fixOrientation] Failed fixing, assume normal orientation",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openUriInputStream(
|
||||||
|
context: Context, uri: Uri
|
||||||
|
): InputStream? {
|
||||||
|
return if (UriUtil.isAssetUri(uri)) {
|
||||||
|
context.assets.open(UriUtil.getAssetUriPath(uri))
|
||||||
|
} else {
|
||||||
|
context.contentResolver.openInputStream(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadImageBounds(
|
||||||
|
context: Context, uri: Uri
|
||||||
|
): BitmapFactory.Options {
|
||||||
|
openUriInputStream(context, uri)!!.use {
|
||||||
|
val opt = BitmapFactory.Options().apply {
|
||||||
|
inJustDecodeBounds = true
|
||||||
|
}
|
||||||
|
BitmapFactory.decodeStream(it, null, opt)
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadImage(
|
||||||
|
context: Context, uri: Uri, opt: BitmapFactory.Options
|
||||||
|
): Bitmap {
|
||||||
|
openUriInputStream(context, uri)!!.use {
|
||||||
|
return BitmapFactory.decodeStream(it, null, opt)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calcBitmapSubsample(
|
||||||
|
originalW: Int,
|
||||||
|
originalH: Int,
|
||||||
|
targetW: Int,
|
||||||
|
targetH: Int,
|
||||||
|
resizeMethod: BitmapResizeMethod
|
||||||
|
): Int {
|
||||||
|
return when (resizeMethod) {
|
||||||
|
BitmapResizeMethod.FIT -> maxOf(
|
||||||
|
originalW / targetW, originalH / targetH
|
||||||
|
)
|
||||||
|
|
||||||
|
BitmapResizeMethod.FILL -> minOf(
|
||||||
|
originalW / targetW, originalH / targetH
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val TAG = "BitmapUtil"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
|
class PermissionException(message: String) : Exception(message)
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
|
internal interface K {
|
||||||
|
companion object {
|
||||||
|
const val PERMISSION_REQUEST_CODE = 11011
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.nkming.nc_photos.np_android_log
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
class LogConfig {
|
class LogConfig {
|
||||||
companion object {
|
companion object {
|
|
@ -1,4 +1,4 @@
|
||||||
package com.nkming.nc_photos.plugin
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
interface PermissionUtil {
|
||||||
|
companion object {
|
||||||
|
const val REQUEST_CODE = K.PERMISSION_REQUEST_CODE
|
||||||
|
|
||||||
|
fun request(
|
||||||
|
activity: Activity, vararg permissions: String
|
||||||
|
) {
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity, permissions, REQUEST_CODE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasReadExternalStorage(context: Context): Boolean {
|
||||||
|
return ContextCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestReadExternalStorage(activity: Activity) = request(
|
||||||
|
activity, Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
fun hasWriteExternalStorage(context: Context): Boolean {
|
||||||
|
return ContextCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestWriteExternalStorage(activity: Activity) = request(
|
||||||
|
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container of pixel data stored in RGBA format
|
||||||
|
*/
|
||||||
|
class Rgba8Image(
|
||||||
|
val pixel: ByteArray, val width: Int, val height: Int
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun fromJson(json: Map<String, Any>) = Rgba8Image(
|
||||||
|
json["pixel"] as ByteArray,
|
||||||
|
json["width"] as Int,
|
||||||
|
json["height"] as Int
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fromBitmap(src: Bitmap): Rgba8Image {
|
||||||
|
assert(src.config == Bitmap.Config.ARGB_8888)
|
||||||
|
val buffer = ByteBuffer.allocate(src.width * src.height * 4).also {
|
||||||
|
src.copyPixelsToBuffer(it)
|
||||||
|
}
|
||||||
|
return Rgba8Image(buffer.array(), src.width, src.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJson() = mapOf<String, Any>(
|
||||||
|
"pixel" to pixel,
|
||||||
|
"width" to width,
|
||||||
|
"height" to height,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toBitmap(): Bitmap {
|
||||||
|
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
.apply {
|
||||||
|
copyPixelsFromBuffer(ByteBuffer.wrap(pixel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
assert(pixel.size == width * height * 4)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
package com.nkming.nc_photos.plugin
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import com.nkming.nc_photos.np_android_log.logI
|
|
||||||
|
|
||||||
interface UriUtil {
|
interface UriUtil {
|
||||||
companion object {
|
companion object {
|
|
@ -1,9 +1,8 @@
|
||||||
package com.nkming.nc_photos.plugin
|
package com.nkming.nc_photos.np_android_core
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.nkming.nc_photos.np_android_log.logI
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
|
|
21
np_android_log/proguard-rules.pro
vendored
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
|
@ -1 +0,0 @@
|
||||||
rootProject.name = "np_android_log"
|
|
30
np_platform_image_processor/.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
33
np_platform_image_processor/.metadata
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: plugin
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
- platform: android
|
||||||
|
create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
- platform: ios
|
||||||
|
create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
1
np_platform_image_processor/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include: package:np_lints/np.yaml
|
9
np_platform_image_processor/android/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.cxx
|
76
np_platform_image_processor/android/build.gradle
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
group 'com.nkming.nc_photos.np_platform_image_processor'
|
||||||
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.8.20'
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.nkming.nc_photos.np_platform_image_processor'
|
||||||
|
compileSdk 31
|
||||||
|
ndkVersion "23.2.8568313"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
ndk {
|
||||||
|
abiFilters "armeabi-v7a", "arm64-v8a", "x86_64"
|
||||||
|
}
|
||||||
|
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path file('src/main/cpp/CMakeLists.txt')
|
||||||
|
version '3.18.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'LongLogTag'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
|
||||||
|
implementation "androidx.annotation:annotation:1.6.0"
|
||||||
|
implementation "androidx.core:core-ktx:1.10.1"
|
||||||
|
implementation "androidx.exifinterface:exifinterface:1.3.6"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
implementation 'com.nkming.nc_photos.np_android_core:np_android_core'
|
||||||
|
}
|
3
np_platform_image_processor/android/gradle.properties
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
5
np_platform_image_processor/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
|
2
np_platform_image_processor/android/settings.gradle
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
rootProject.name = 'np_platform_image_processor'
|
||||||
|
includeBuild '../../np_android_core'
|
|
@ -0,0 +1,11 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.nkming.nc_photos.np_platform_image_processor">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<service
|
||||||
|
android:name=".ImageProcessorService"
|
||||||
|
android:exported="false" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.18.1)
|
||||||
|
|
||||||
# Declares and names the project.
|
# Declares and names the project.
|
||||||
|
|
||||||
project("plugin")
|
project("np_platform_image_processor")
|
||||||
|
|
||||||
# Creates and names a library, sets it as either STATIC
|
# Creates and names a library, sets it as either STATIC
|
||||||
# or SHARED, and provides the relative paths to its source code.
|
# or SHARED, and provides the relative paths to its source code.
|
||||||
|
@ -27,7 +27,7 @@ set_target_properties( renderscript-intrinsics-replacement-toolkit PROPERTIES IM
|
||||||
${dependency_DIR}/renderscript-intrinsics-replacement-toolkit/jni/${ANDROID_ABI}/librenderscript-toolkit.so )
|
${dependency_DIR}/renderscript-intrinsics-replacement-toolkit/jni/${ANDROID_ABI}/librenderscript-toolkit.so )
|
||||||
|
|
||||||
add_library( # Sets the name of the library.
|
add_library( # Sets the name of the library.
|
||||||
plugin
|
np_platform_image_processor
|
||||||
|
|
||||||
# Sets the library as a shared library.
|
# Sets the library as a shared library.
|
||||||
SHARED
|
SHARED
|
||||||
|
@ -62,7 +62,7 @@ add_library( # Sets the name of the library.
|
||||||
util.cpp
|
util.cpp
|
||||||
zero_dce.cpp
|
zero_dce.cpp
|
||||||
)
|
)
|
||||||
set_target_properties( plugin PROPERTIES COMPILE_OPTIONS -fopenmp )
|
set_target_properties( np_platform_image_processor PROPERTIES COMPILE_OPTIONS -fopenmp )
|
||||||
|
|
||||||
# Searches for a specified prebuilt library and stores the path as a
|
# Searches for a specified prebuilt library and stores the path as a
|
||||||
# variable. Because CMake includes system libraries in the search path by
|
# variable. Because CMake includes system libraries in the search path by
|
||||||
|
@ -79,7 +79,7 @@ find_library( # Sets the name of the path variable.
|
||||||
|
|
||||||
find_library( android-lib android )
|
find_library( android-lib android )
|
||||||
|
|
||||||
target_include_directories( plugin PRIVATE
|
target_include_directories( np_platform_image_processor PRIVATE
|
||||||
${dependency_DIR}/tensorflowlite/headers
|
${dependency_DIR}/tensorflowlite/headers
|
||||||
${dependency_DIR}/renderscript-intrinsics-replacement-toolkit/headers )
|
${dependency_DIR}/renderscript-intrinsics-replacement-toolkit/headers )
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ target_include_directories( plugin PRIVATE
|
||||||
# build script, prebuilt third-party libraries, or system libraries.
|
# build script, prebuilt third-party libraries, or system libraries.
|
||||||
|
|
||||||
target_link_libraries( # Specifies the target library.
|
target_link_libraries( # Specifies the target library.
|
||||||
plugin
|
np_platform_image_processor
|
||||||
|
|
||||||
# Links the target library to the log library
|
# Links the target library to the log library
|
||||||
# included in the NDK.
|
# included in the NDK.
|
|
@ -15,7 +15,7 @@
|
||||||
#include "tflite_wrapper.h"
|
#include "tflite_wrapper.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using namespace plugin;
|
using namespace im_proc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace tflite;
|
using namespace tflite;
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ private:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_ArbitraryStyleTransfer_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_ArbitraryStyleTransfer_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jbyteArray style, jfloat weight) {
|
jint width, jint height, jbyteArray style, jfloat weight) {
|
||||||
try {
|
try {
|
|
@ -7,7 +7,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_ArbitraryStyleTransfer_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_ArbitraryStyleTransfer_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jbyteArray style, jfloat weight);
|
jint width, jint height, jbyteArray style, jfloat weight);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using namespace core;
|
using namespace core;
|
||||||
using namespace plugin;
|
using namespace im_proc;
|
||||||
using namespace renderscript;
|
using namespace renderscript;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace tflite;
|
using namespace tflite;
|
||||||
|
@ -151,7 +151,7 @@ private:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3Portrait_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_DeepLab3Portrait_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jint radius) {
|
jint width, jint height, jint radius) {
|
||||||
try {
|
try {
|
||||||
|
@ -176,7 +176,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3Portrait_inferNative(
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3ColorPop_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_DeepLab3ColorPop_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jfloat weight) {
|
jint width, jint height, jfloat weight) {
|
||||||
try {
|
try {
|
|
@ -7,12 +7,12 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3Portrait_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_DeepLab3Portrait_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jint radius);
|
jint width, jint height, jint radius);
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3ColorPop_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_DeepLab3ColorPop_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height, jfloat weight);
|
jint width, jint height, jfloat weight);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "tflite_wrapper.h"
|
#include "tflite_wrapper.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using namespace plugin;
|
using namespace im_proc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace tflite;
|
using namespace tflite;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ private:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||||
Java_com_nkming_nc_1photos_plugin_image_1processor_Esrgan_inferNative(
|
Java_com_nkming_nc_1photos_np_1platform_1image_1processor_processor_Esrgan_inferNative(
|
||||||
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image,
|
||||||
jint width, jint height) {
|
jint width, jint height) {
|
||||||
try {
|
try {
|