Unable to Bring Image Captured (using camera) In Front of an ImageButton in Kotlin Android Studio - android-studio

I am developing a simple form in Kotlin Android Studio that includes taking a photo using the camera. After taking a photo using the camera, I want the image to be set in the camera button (which is an ImageButton).
Currently, it looks like this,
Current Result
How do I send the image to front?
I used a bringToFront() method (shown in the code snippet below) but to no avail.
Here are the relevant codes:
class FormActivity : AppCompatActivity() {
lateinit var currentPhotoPath: String
val REQUEST_IMAGE_CAPTURE = 1
var selectedPhotoUri : Uri? = null
companion object {
const val REQUEST_FROM_CAMERA = 1001
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_formactivity)
...
val imageButton = findViewById<ImageButton>(R.id.imageButton)
// to launch the camera
imageButton.setOnClickListener {
takePictureUsingCamera()
}
}
private fun takePictureUsingCamera(){
ImagePicker.with(this).cameraOnly()
.crop()
.start(REQUEST_FROM_CAMERA)
}
// to access the image captured
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
when (requestCode){
REQUEST_FROM_CAMERA -> {
selectedPhotoUri = data!!.data
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedPhotoUri)
val bitmapDrawable = BitmapDrawable(bitmap)
val imageButton = findViewById<ImageButton>(R.id.imageButton)
// add the image to the image button
imageButton.setImageDrawable(bitmapDrawable)
imageButton.bringToFront()
}
}
}
}
}
Here is my XML code:
<ImageButton
android:id="#+id/imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="#+id/submitButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toBottomOf="#+id/spinnerRslt"
android:adjustViewBounds="true"
android:scaleType="fitXY"
app:srcCompat="#mipmap/ic_camera_capture" />
Before: ImageButton - before clicking/taking a photo
After: After the image has been taken

Related

How to restrict input length using two-way data binding with live data the correct way?

I am building an app where users have to input some information. For that reason, I am using TextInputLayout
and TextInputEditText to have more interaction with them. For instance, when the user input more than 5 characters I would like to show an error message etc.
At the moment I am able to do the input restriction, however, most of the code is in the Activity and that is not what I like to have. Also, I have to assign the TextInputEditText in the activity to set a new string back to that field layout so I do not think that this is correct two-way data binding.
My main goal here is to restrict the user to input more than 5 characters and more importantly keep all the code inside the ViewModel.
Screen:
...
...
Layout:
...
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/til_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:gravity="center"
android:hint="#string/hint_order"
tools:hint="#string/hint_order"
app:errorEnabled="true"
app:errorTextColor="#color/red_currant_red"
app:passwordToggleEnabled="true"
app:passwordToggleTint="#color/row_text_brown"
app:boxBackgroundColor="#color/white"
app:counterEnabled="true"
app:counterMaxLength="5"
android:minHeight="20dp"
app:expandedHintEnabled="false"
app:hintAnimationEnabled="false"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_column="1">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/tiet_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:selectAllOnFocus="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#color/black_text"
android:textColorHighlight="#color/blue_onahau"
android:text="#={ myNewViewModel._inputWordOrderTextInt}"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
...
ViewModel:
...
val _inputWordOrderTextInt = MutableLiveData<String>()
val inputWordOrderTextInt: LiveData<String>
get() = _inputWordOrderTextInt
...
Activity:
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityNewWordBinding.inflate(layoutInflater)
binding.myNewViewModel = newWordViewModel
val view = binding.root
setContentView(view)
manageInputTypeForWordOrderTextInt()
}
...
private fun manageInputTypeForWordOrderTextInt() {
newWordViewModel.inputWordOrderTextInt.observe(this, Observer { input ->
if (input != null && input.isNotEmpty()) {
if (input.length > 5) {
val newInput = input.substring(0, 5)
binding.tiet2.setText(newInput)
binding.tiet2.setSelection(newInput.length)
}
}
})
}
...

E/RecyclerView: No adapter attached; skipping layout / Kotlin

I have been reading the different answers here on stackoverflow and on this blog post and tried to implement their solutions but I am still getting the error. The code is from a yt tutorial. I hope someone can help me. Thanks
Error E/RecyclerView: No adapter attached; skipping layout
My Adapter
This is my Main Activity
class MainActivity : AppCompatActivity(), IDrinkLoadListener {
lateinit var drinkLoadListener: IDrinkLoadListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
loadDrinkFromFirebase()
}
private fun loadDrinkFromFirebase() {
val drinkModels : MutableList<DrinkModel> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("Drink")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()) {
for (drinkSnapshot in snapshot.children) {
val drinkModel = drinkSnapshot.getValue(DrinkModel::class.java)
drinkModel!!.key = drinkSnapshot.key
drinkModels.add(drinkModel)
}
drinkLoadListener.onDrinkLoadSuccess(drinkModels)
} else
drinkLoadListener.onDrinkLoadFailed("Drink items not exist")
}
override fun onCancelled(error: DatabaseError) {
drinkLoadListener.onDrinkLoadFailed(error.message)
}
})
}
private fun init() {
drinkLoadListener = this
val gridLayoutManager = GridLayoutManager(this, 2)
recycler_drink.layoutManager = gridLayoutManager
recycler_drink.addItemDecoration(SpaceItemDecoration())
}
override fun onDrinkLoadSuccess(drinkModelList: List<DrinkModel>?) {
val adapter = MyDrinkAdapter(this,drinkModelList!!)
recycler_drink.adapter = adapter
}
override fun onDrinkLoadFailed(message: String?) {
Snackbar.make(mainLayout,message!!,Snackbar.LENGTH_LONG).show()
}
}
class SpaceItemDecoration : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
if(parent.getChildLayoutPosition(view) % 2 != 0)
{
outRect.top= 25
outRect.bottom= -25
}else outRect.top = 0
}
}
Thanks in advance!!!
That happens because the adapter is attached to the recycler after an asynchronous operation on onDrinkLoadSuccess
You can attach the adapter to the recycler during onCreate and then the onDrinkLoadSuccess update it with the data.
The ListAdapter class has a submit method for it. If you are using plain recycler is usually something like this:
Recycler ... {
private val dataList = mutableListOf()
fun update(list: List<YourType>) {
dataList.clear()
dataList.addAll(list)
notifyDataDetChanged() //important updates the UI
}
}
There are more specific updates methods like notifyItemRangeChanged, etc.
Also there are helpers for finding difference in the data sets DiffUtil.ItemCallback

Kotlin: LoadURL onReceivedError() not firing

Simple kotlin app under android studio that makes a loadURL to a local address:-
The function often fails, probably due to local net latency with:
Web Page not available
The web page at http://192.168.1.144/apikey/webcam could not be loaded because:
net: ERR_ADDRESS_UNREACHABLE
I have
android:usesCleartextTraffic="true"
<uses-permission android:name="android.permission.INTERNET"/>
in the manifest, and the loadurl often is fine
In order to capture the error and provide a message an
onReceivedError()
action is used.
It never fires
Is the syntax of the onReceivedError correct? It refers to WebView rather than my instance myWebview (which causes a reference error), and I've moved the scope around to no effect.
The Android Studio comment says that the function is never used. A big hint, but I can't see which scope to place it in.
Or is this type of error one of those not caught by OnReceivedError. If so, how which function would?
Ideally I'd like to increase the 'wait' time of the LoadUrl function so that the lazy local IP can respond.
I've copied this from other examples.
I'd really welcome some help please
Here is my class code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val CHANNEL_ID = "only_channel"
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create channel to show notifications.
val channelId = getString(R.string.default_notification_channel_id)
val channelName = getString(R.string.default_notification_channel_name)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager?.createNotificationChannel(NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_HIGH))
}
val myWebView: WebView = findViewById(R.id.webview)
/*myWebView.loadUrl("https://amazon.co.uk")*/
myWebView.webViewClient = WebViewClient()
myWebView.setWebViewClient(object : WebViewClient() {
fun onReceivedError(view: WebView , errorCode: Int, description: String, failingUrl: String, getContext: Context) {
Log.i("WEB_VIEW_TEST", "error code:$errorCode")
Toast.makeText(getContext, "Webcam not reachable",Toast.LENGTH_SHORT ).show()
}
})
WebView.setWebContentsDebuggingEnabled(true)
/*5 March 2021*/
myWebView.clearCache(true)
myWebView.loadUrl("http://192.168.1.144/apikey/webcam")
val disable_button: Button = findViewById(R.id.disable)
disable_button.setOnClickListener {
myWebView.loadUrl("http://192.168.1.144/apikey/disable")
}
fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
Toast.makeText(this, "Webcam not reachable", Toast.LENGTH_SHORT).show()
}
}
}

Android Studio Room data without livedata

I got the following Room database and want to output the name of a random user in a textview. Unfortunately running the code yields the output: kotlin.unit inside the textview. My files look like that:
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var mNameViewModel: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mNameViewModel = ViewModelProvider(this).get(NameViewModel::class.java)
val btn = findViewById<Button>(R.id.btn_addName)
val tv = findViewById<TextView>(R.id.tv_showName)
btn.setOnClickListener {
val text = findViewById<EditText>(R.id.et_enterName)?.text.toString()
val name = Name(0, text)
// Add Data to Database
mNameViewModel.addName(name)
Toast.makeText(applicationContext, "Successfully added $text.", Toast.LENGTH_LONG).show()
val randomName = mNameViewModel.getRandomName()
// Without .toString() I get an error, with it it displays kotlin.unit
tv.text = randomName.toString()
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/tv_showName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.133" />
<EditText
android:id="#+id/et_enterName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:hint="Name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.244" />
<Button
android:id="#+id/btn_addName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.379" />
</androidx.constraintlayout.widget.ConstraintLayout>
Name.kt
#Entity(tableName = "name_data")
data class Name (
#PrimaryKey(autoGenerate = true) val id: Int,
#ColumnInfo(name = "name") val name: String
)
NameDao.kt
#Dao
interface NameDao {
#Insert
fun addName(name: Name)
#Query("SELECT name FROM name_data ORDER BY RANDOM() LIMIT 1")
fun getRandomName(): String
}
NameDatabase.kt
#Database(entities = [Name::class], version = 1, exportSchema = false)
abstract class NameDatabase: RoomDatabase() {
abstract fun nameDao(): NameDao
companion object{
#Volatile
private var INSTANCE: NameDatabase? = null
fun getDatabase(context: Context): NameDatabase{
val tempInstance = INSTANCE
if(tempInstance != null){
return tempInstance
}
synchronized(this){
val instance = databaseBuilder(
context.applicationContext,
NameDatabase::class.java,
"name_data"
).build()
INSTANCE = instance
return instance
}
}
}
}
NameRepository.kt
class NameRepository(private val nameDao: NameDao) {
fun getRandomName() { nameDao.getRandomName() }
fun addName(name: Name) { nameDao.addName(name) }
}
NameViewModel.kt
class NameViewModel(application: Application): AndroidViewModel(application) {
private val repository: NameRepository
init {
val nameDao = NameDatabase.getDatabase(application).nameDao()
repository = NameRepository(nameDao)
}
fun addName(name: Name) {
viewModelScope.launch(Dispatchers.IO){
repository.addName(name)
}
}
fun getRandomName() {
viewModelScope.launch(Dispatchers.IO){
repository.getRandomName()
}
}
}
This is how the output of textview when pressing the button.
The database gets populated though.
Apreciate any help to get the data displayed. Thank you!
The issue is that in your getRandomNumber method inside viewmodel you don't return anything that's why you get kotlin.unit . You should instead make sure to return a value
A possible solution would be the following.
Create a method inside your dao which takes a number and returns that row (this will be essentially the random name)
From your repo call that method with a random number you could use Random.getNextInt not sure how do you get a random
From your view model call that method
From your button onClick call the viewModel method
Make sure to use suspend where applicable in order to get a result. For the case that I showcased above that would be to launch a coroutine in view level and make the rest of the calls (vm,repo,dao) suspend
So after some testing and back and forth this is what I have and it seems to be working.
If anyone has some things to bring in I gladly accept them and a big thanks to georkost for all the usefull tips!
// No changes made to database
#Entity(tableName = "name_data")
data class Name (
#PrimaryKey(autoGenerate = true) val id: Int,
#ColumnInfo(name = "name") val name: String
)
//*********************************************************************************
// Changed return type from Query to LiveData<String> (was just String before)
#Dao
interface NameDao {
#Insert
fun addName(name: Name)
#Query("SELECT name FROM name_data ORDER BY RANDOM() LIMIT 1")
fun getRandomName(): LiveData<String> // HERE
}
//*********************************************************************************
// Changed getRandomMeal from fun to val
class NameRepository(private val nameDao: NameDao) {
val getRandomMeal: LiveData<String> = nameDao.getRandomName() // HERE
fun addName(name: Name) { nameDao.addName(name) }
}
//*********************************************************************************
// Added getRandomName val, initialized it and removed the fun
class NameViewModel(application: Application): AndroidViewModel(application) {
val getRandomName: LiveData<String> // HERE
private val repository: NameRepository
init {
val nameDao = NameDatabase.getDatabase(application).nameDao()
repository = NameRepository(nameDao)
getRandomName = repository.getRandomMeal // HERE
}
fun addName(name: Name) {
viewModelScope.launch(Dispatchers.IO){
repository.addName(name)
}
}
}
//*********************************************************************************
// Changed the last row to observe the LiveData and display it in the Text View
class MainActivity : AppCompatActivity() {
private lateinit var mNameViewModel: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mNameViewModel = ViewModelProvider(this).get(NameViewModel::class.java)
val btn = findViewById<Button>(R.id.btn_addName)
val tv = findViewById<TextView>(R.id.tv_showName)
btn.setOnClickListener {
val text = findViewById<EditText>(R.id.et_enterName)?.text.toString()
val name = Name(0, text)
// Add Data to Database
mNameViewModel.addName(name)
Toast.makeText(applicationContext, "Successfully added $text.", Toast.LENGTH_LONG).show()
// HERE
mNameViewModel.getRandomName.observe(this, Observer { String -> tv.text = String })
}
}
}

captured and displayed image on imageView, now i want to pass the imageView to another activity

This is my code on android studio using kotlin to capture and show image on main activity, I want that captured image to be displayed on my other activity. After image has been captured, the image will be displayed on the imageView on main Activity, now i want to pass that image to another activity using buttonClassify
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeTensorClassifier()
buttonRecognize.setOnClickListener {
setVisibilityOnCaptured(false)
cameraView.captureImage {
onImageCaptured(it)
}
buttonClassify.setOnClickListener{
val intent = Intent(this, classify::class.java)
startActivity(intent)
}
}
}
private fun onImageCaptured(it: CameraKitImage) {
val bitmap = Bitmap.createScaledBitmap(it.bitmap, INPUT_WIDTH, INPUT_HEIGHT, false)
showCapturedImage(bitmap)
classifier?.let {
try {
showRecognizedResult(it.recognizeImage(bitmap))
} catch (e: java.lang.RuntimeException) {
Log.e(TAG, "Crashing due to classification.closed() before the recognizer finishes!")
}
}
}
private fun showCapturedImage(bitmap: Bitmap?) {
runOnUiThread {
imageCaptured.visibility = View.VISIBLE
imageCaptured.setImageBitmap(bitmap)
}
}
If you store the image in a file, you can just pass the path to the file. If not, you can pass the Bitmap as a Parcelable in the Intent Extras.
private fun showCapturedImage(bitmap: Bitmap?) {
runOnUiThread {
imageCaptured.visibility = View.VISIBLE
imageCaptured.setImageBitmap(bitmap)
val nextActivityIntent = Intent(this, NextActivity::class.java).apply {
putExtra("captured_image", bitmap)
}
startActivity(nextActivityIntent)
}
}
Then in the next Activity you could retreive it like this:
override fun onCreate(savedInstance: Bundle?) {
...
val capturedImage = intent.extras.getParcelable("captured_image") as Bitmap?
/* Use bitmap as you wish */
}
Beware, large bitmaps can throw Exceptions when trying to be passed as Intent Extras, so consider saving the image and passing around the path.

Resources