Embed activity inside jetpack composable - android

Can we embed activity inside composable, so for example, when navigate to that composable its shows the activity content inside it(and not just start activity outside the composable),
That what i succeed so far, but its just show the activity and not embed it
#Composable
fun MyContent() {
val context = LocalContext.current
val intent = Intent(context, MyActivity::class.java)
context.startActivity(intent)
}
The activity
class WebViews : ComponentActivity() {
var webView: WebView? = null
private var mUploadMessage: ValueCallback<Uri>? = null
var uploadMessage: ValueCallback<Array<Uri>>? = null
val REQUEST_SELECT_FILE = 100
private val FILECHOOSER_RESULTCODE = 1
var url = "http://example.com"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web_views)
webView = findViewById<View>(R.id.webView) as WebView
webView!!.settings.javaScriptEnabled = true
webView!!.loadUrl(url)
webView!!.webViewClient = xWebViewClient()
webView!!.webChromeClient = object : WebChromeClient() {
// For 3.0+ Devices (Start)
// onActivityResult attached before constructor
protected fun openFileChooser(
uploadMsg: ValueCallback<Uri>?, acceptType: String?
) {
mUploadMessage = uploadMsg
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "image/*"
startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE)
}
// For Lollipop 5.0+ Devices
override fun onShowFileChooser(
mWebView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: WebChromeClient.FileChooserParams
): Boolean {
if (uploadMessage != null) {
uploadMessage!!.onReceiveValue(null)
uploadMessage = null
}
uploadMessage = filePathCallback
var intent: Intent? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent = fileChooserParams.createIntent()
}
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
uploadMessage = null
return false
}
return true
}
//For Android 4.1 only
protected fun openFileChooser(
uploadMsg: ValueCallback<Uri>?,
acceptType: String?,
capture: String?
) {
mUploadMessage = uploadMsg
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE)
}
protected fun openFileChooser(uploadMsg: ValueCallback<Uri>?) {
mUploadMessage = uploadMsg
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "image/*"
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null) return
uploadMessage!!.onReceiveValue(
WebChromeClient.FileChooserParams.parseResult(
resultCode,
intent
)
)
uploadMessage = null
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage) return
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
// val result: Uri? =
// if (intent == null || resultCode != WebViews.RESULT_OK) null else intent.data
// mUploadMessage.onReceiveValue(result)
mUploadMessage = null
}
}
private class xWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
if (url != null) {
view.loadUrl(url)
}
return true
}
}
}
Is there a way to make it work as composable?
Thanks

Can we embed activity inside composable
Not really.
If the activity you are trying to start is from some third-party app, or is yours but is not written in Compose UI, the best that you can do is try activity embedding on Android 12L and higher.
If the activity that you are trying to start is your own, and it itself is built in Compose UI, you could get rid of that activity and navigate to its composable.

You can try to reference the activity through an extenstion function:
inline fun <reified Activity : ComponentActivity> Context.getActivity(): Activity? {
return when (this) {
is Activity -> this
else -> {
var context = this
while (context is ContextWrapper) {
context = context.baseContext
if (context is Activity) return context
}
null
}
}
}
Usage:
#Composable
fun YourComposable() {
val activity = LocalContext.current.getActivity<MainActivity>()
}

I think a good way of approaching your solution is just to hoist the open of the webview outside your composable
Example
class MainActivity() {
setContent {
YourComposable(param1, param2) {
openWebViewCallBack
}
}
}
#Composable
fun YourComposable(param1:Int, param2:String, onWebViewOpen: (url:String) -> Unit) { ... }

Related

Android OnActivityResult gave me a NULL value with getStringExtra()

I'm trying to get a response inside the onActivityResult from an uri intent. However, it is always -1 as resultCode and null as Intent data.
private fun startMyFunction(call: MethodCall) {
val app: String? = call.argument("app")
val url: String? = call.argument("url")
try {
val uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage(app)
if (activity?.let { intent.resolveActivity(it.packageManager) } == null) {
this.success("activity_unavailable")
return
}
activity?.startActivityForResult(intent, requestCodeNumber)
} catch (ex: Exception) {
Log.e("my_app", ex.toString())
this.success("failed_to_open_app")
}
}
private fun success(o: String) {
if (!hasResponded) {
hasResponded = true
result?.success(o)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
if (requestCodeNumber == requestCode && result != null) {
Log.d("onActivityResult - response - ", data.toString())
if (data != null) {
try {
val response = data.getStringExtra("response")!!
this.success(response)
} catch (ex: Exception) {
this.success("invalid_response - Exception")
}
} else {
this.success("user_cancelled - Null data")
}
}
return true
}
I have read many of the similar situations here on StackOverflow, as well as on Google, but none of them have helped me in my case
For ref -
https://github.com/GJJ2019/upi_pay/blob/master/android/src/main/kotlin/com/drenther/upi_pay/UpiPayPlugin.kt

Kotlin cast one AppCompatActivity into another one in Android

world! I am new to Android development. I am creating an app and I have a question, how can I cast one AppCompatActvity class into another?
This is my sample code:
class UploadActivity : AppCompatActivity() {
var sm = FirebaseStorageManager()
private var filePath: Uri? = null
private val btnSelectImage: AppCompatButton by lazy {
findViewById(R.id.select)
}
private val btnUploadImage: AppCompatButton by lazy {
findViewById(R.id.upload)
}
private val imgPost: AppCompatImageView by lazy {
findViewById(R.id.imageView)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_upload)
init()
}
private fun init() {
btnSelectImage.setOnClickListener {
imagePicker()
}
btnUploadImage.setOnClickListener {
filePath?.let {
it1 -> sm.uploadImage(this, it1)
thread {
Thread.sleep(1000)
//ParseUploadActivity().uploader(this, it1)
//(this#UploadActivity as ParseUploadActivity).uploader(this, it1)
(this as ParseUploadActivity).uploader(this, it1)
}
}
}
}
private fun imagePicker() {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select Picture"), 1)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == 1 && resultCode == Activity.RESULT_OK) {
if(data == null || data.data == null) {
return
}
filePath = data.data
try {
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, filePath)
imgPost?.setImageBitmap(bitmap)
} catch(e: IOException) {
e.printStackTrace()
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
var inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.filemenu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when(item.itemId) {
R.id.upload_menu -> {
true
}
R.id.show_menu -> {
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
The problem is in this line of code (this as ParseUploadActivity).uploader(this, it1). I tried different ways but still have an error and can't fix it.
P.S.
ParseUploadActivity is also inherited from AppCompatActivity.
I mean
class ParseUploadActivity : AppCompatActivity() {...}

Pick file with registerForActivityResult on Androidx

I need to implement the select file option in my webviewActivity and all the tutorials I found have only the example with the startActivityResult, but it is currently deprecated and so I would like some help on how to transform this code to the new templates of a register as in the documentation: https://developer.android.com/training/basics/intents/result.
WebviewActivity.kt
class WebviewActivity: AppCompatActivity() {
val REQUEST_SELECT_FILE = 1
val FILE_CHOOSER_RESULT = 2
var uploadMessage: ValueCallback<Array<Uri>>? = null
var uploaded: ValueCallback<Uri>? = null
private fun launchWebview(url: String): WebView =
webview_id.apply{
loadUrl(url)
webViewClient : object = WebViewClient(){
//...//
}
webChromeClient : object = WebChromeClient(){
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: WebChromeClient.FileChooserParams
): Boolean{
if (uploadMessage != null){
uploadMessage!!.onReceiveValue(null)
uploadMessage = null
}
uploadMessage = filePathCallback
val intent = fileChooserParams.createIntent()
startActivityForResult(intent, REQUEST_SELECT_FILE)
return true
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_SELECT_FILE){
uploadMessage!!.onReceiveValue(
WebChromeClient.FileChooserParams.parseResult(
resultCode, data
)
)
uploadMessage = null
} else if (requestCode == FILE_CHOOSER_RESULT){
val result = if (data == null || resultCode != RESULT_OK) null else data.data
uploaded!!.onReceiveValue(result)
uploaded = null
}
super.onActivityResult(requestCode, resultCode, data)
}
}
I used this link to make the code above: Android File Chooser not calling from Android Webview
You have to do sth. like that:
private fun createFile() {
getResult.launch("chartName.pdf")
}
private val getResult = registerForActivityResult(
CreateSpecificTypeDocument("application/pdf")
) { uri ->
if(uri != null){
contentResolver.openFileDescriptor(uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
//DO sth. with file
}
}
}
}
with:
class CreateSpecificTypeDocument(private val type: String) :
ActivityResultContracts.CreateDocument() {
override fun createIntent(context: Context, input: String): Intent {
return super.createIntent(context, input).setType(type)
}
}

Multiple results from activity to fragment

I've got a question about results/callback from activities to fragments.
Until now I have a fragment which calls a camera activity to scan QR Codes. So I start the activity from the fragment with startActivityForResult. If a QR Code is successfully scanned I get a callback Intent which is handled in onActivityResult.
This works perfectly.
Now I want to handle multiple scanns. In detail that means, that every successfully scan should call the onActivityResult function without closing the activity. The problem which I got at this point is, that onActivityResult is only called if I call finish() in the camera activity.
So my question is, how can I call onActivityResult multiple times with or without calling finish() but without closing the activity? Or is there another way to handle callbacks from activities to fragments?
This is my fragment code:
class ScanFragment : Fragment() {
private val CHECKIN_CODE = 0
private val CHECKOUT_CODE = 1
companion object {
fun newInstance(): LeadScanFragment = LeadScanFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_lead_scan, container, false)
view.checkin.setOnClickListener {view ->
val intent = Intent(activity, CodeScannerActivity::class.java)
startActivityForResult(intent, CHECKIN_CODE)
}
view.checkout.setOnClickListener {view ->
val intent = Intent(activity, CodeScannerActivity::class.java)
startActivityForResult(intent, CHECKOUT_CODE)
}
return view
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CHECKIN_CODE) {
if (resultCode == Activity.RESULT_OK) {
val returnString = data!!.getStringExtra("hash")
Log.d("scaned in", returnString)
}
}
if (requestCode == CHECKOUT_CODE) {
if (resultCode == Activity.RESULT_OK) {
val returnString = data!!.getStringExtra("hash")
Log.d("scaned out", returnString)
}
}
}
}
And this is the camera activity code:
class CodeScannerActivity : AppCompatActivity() {
private val requestCodeCameraPermission = 1001
private lateinit var cameraSource: CameraSource
private lateinit var detector: BarcodeDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_code_scanner)
if (ContextCompat.checkSelfPermission(this#CodeScannerActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
askForCameraPermission()
} else {
setup()
}
}
private fun setup() {
detector = BarcodeDetector.Builder(this#CodeScannerActivity).build()
cameraSource = CameraSource.Builder(this#CodeScannerActivity, detector).setAutoFocusEnabled(true).build()
cameraSurfaceView.holder.addCallback(surfaceCallback)
detector.setProcessor(processor)
}
private fun askForCameraPermission() {
ActivityCompat.requestPermissions(this#CodeScannerActivity, arrayOf(Manifest.permission.CAMERA), requestCodeCameraPermission)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == requestCodeCameraPermission && grantResults.isNotEmpty()) {
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setup()
} else {
Toast.makeText(applicationContext, "Permission denied!", Toast.LENGTH_SHORT).show()
}
}
}
private val surfaceCallback = object : SurfaceHolder.Callback {
override fun surfaceCreated(surfaceHolder: SurfaceHolder?) {
try {
cameraSource.start(surfaceHolder)
} catch (exception: Exception) {
Toast.makeText(applicationContext, "Something went wrong", Toast.LENGTH_SHORT).show()
}
}
override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {
}
override fun surfaceDestroyed(p0: SurfaceHolder?) {
cameraSource.stop()
}
}
private val processor = object : Detector.Processor<Barcode> {
override fun release() {
}
override fun receiveDetections(detections: Detector.Detections<Barcode>?) {
val intent = Intent()
if(detections != null && detections.detectedItems.isNotEmpty()) {
val qrCodes: SparseArray<Barcode> = detections.detectedItems
val code = qrCodes.valueAt(0)
intent.putExtra("hash", code.displayValue)
setResult(Activity.RESULT_OK, intent)
finish()
} else {
setResult(Activity.RESULT_CANCELED, intent)
finish()
}
}
}
}
receiveDetections inside the processor in the lower area of the camera activity code is where the callback Intent is send back to onActivityResult.
You could have the scanner Activity send a local broadcast Intent to forward "results" to the calling Fragment. The Fragment (or its hosting Activity) should set a listener to listen for the broadcast "results". In this way you could perform multiple scans and send each result back to the underlying Activity.

i can't upload image,video and files in kotlin webview

I can select a videos and images but i could not upload in my webview ,kotlin program.
webview.setWebChromeClient(object:WebChromeClient() {
override fun onShowFileChooser(webView:WebView, filePathCallback:ValueCallback<Array<Uri>>, fileChooserParams:FileChooserParams):Boolean {
var mFilePathCallback = filePathCallback
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("*/*")
val PICKFILE_REQUEST_CODE = 100
startActivityForResult(intent, PICKFILE_REQUEST_CODE)
return true
}
})
fun onActivityResult(requestCode: Int, resultCode: Int,
intent: Intent,
mFilePathCallback: Any): Boolean {
var PICKFILE_REQUEST_CODE = null
if (requestCode == PICKFILE_REQUEST_CODE)
{
val result = if (intent == null || resultCode != RESULT_OK)
null
else
intent.getData()
val resultsArray = arrayOfNulls<Uri>(1)
resultsArray[0] = result
mFilePathCallback.onReceiveValue(resultsArray)
}
return true
}
You need add to your #Override onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_SELECT_FILE) {
if (null == uploadMessage) {
return
}
uploadMessage?.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode,data))
uploadMessage = null
}
}
but not inside of your "webview.setWebChromeClient(object:WebChromeClient()" hier doesnt override.
class MainActivity : AppCompatActivity() {
val TAG = "hellow web view"
val webURL: String = "https://www.google.com"
override fun onCreate(savedInstanceState: Bundle?): Unit {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
webview.restoreState(savedInstanceState)
// Log.i(TAG,"onCreate")
webview.webViewClient = WebViewClient()
webview.loadUrl(webURL)
webview.settings.javaScriptEnabled = true
webview.settings.builtInZoomControls = true
webview.settings.displayZoomControls = true
webview.settings.allowFileAccess = true
webview.settings.allowFileAccessFromFileURLs = true
webview.setWebChromeClient(object:WebChromeClient() {
override fun onShowFileChooser(webView:WebView, filePathCallback:ValueCallback<Array<Uri>>, fileChooserParams:FileChooserParams):Boolean {
var mFilePathCallback = filePathCallback
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("*/*")
val PICKFILE_REQUEST_CODE = 100
startActivityForResult(intent, PICKFILE_REQUEST_CODE)
return true
}
})
fun onActivityResult(requestCode: Int, resultCode: Int,
intent: Intent,
mFilePathCallback: Any): Boolean {
var PICKFILE_REQUEST_CODE = null
if (requestCode == PICKFILE_REQUEST_CODE)
{
val result = if (intent == null || resultCode != RESULT_OK)
null
else
intent.getData()
val resultsArray = arrayOfNulls<Uri>(1)
resultsArray[0] = result
mFilePathCallback.onReceiveValue(resultsArray)
}
return true
}
webview.setDownloadListener(object : DownloadListener {
override fun onDownloadStart(url: String, userAgent: String,
contentDisposition: String, mimetype: String,
contentLength: Long) {
val request = DownloadManager.Request(Uri.parse(url))
request.allowScanningByMediaScanner()
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) //Notify client once download is completed!
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mimetype)
val webview = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
webview.enqueue(request)
Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show()
}
})
class webviewclient : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
webview.loadUrl("http://google.com")
return true
}
}
searchbtn.setOnClickListener({ (webview.loadUrl("https://www.google.com")) })
btn1.setOnClickListener({ (webview.goBack()) })
btn3.setOnClickListener({ (webview.goForward()) })
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack()) {
webview.goBack()
return true
}
return super.onKeyDown(keyCode, event)
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
webview.saveState(outState)
Log.i(TAG, "onSaveInstanceState")
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
webview.restoreState(savedInstanceState)
Log.i(TAG, "onRestoreInstanceState")
}
private fun Any.onReceiveValue(resultsArray: Array<Uri?>) {}
}

Categories

Resources