diff --git a/.idea/misc.xml b/.idea/misc.xml index 1388f5f..67920b0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -15,6 +15,7 @@ + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f7c9770..dc41d57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,8 +20,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.QuestionForm"> - - + diff --git a/app/src/main/java/com/example/questionform/MainActivity.kt b/app/src/main/java/com/example/questionform/MainActivity.kt index 5fca0f4..7a7a1d1 100644 --- a/app/src/main/java/com/example/questionform/MainActivity.kt +++ b/app/src/main/java/com/example/questionform/MainActivity.kt @@ -10,6 +10,7 @@ import android.widget.ArrayAdapter import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.FileProvider.getUriForFile +import androidx.core.net.toFile import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import com.canhub.cropper.CropImage @@ -133,7 +134,13 @@ class MainActivity : AppCompatActivity() { ImageQuestion( faker.hobbit().quote(), id = faker.crypto().md5(), mandatory = faker.bool().bool(), - done = faker.bool().bool() + done = faker.bool().bool(), + value = if (faker.bool().bool()) listOf( + faker.avatar().image(), + faker.avatar().image(), + "https://storage.googleapis.com/gweb-uniblog-publish-prod/images/Chrome__logo.max-500x500.png", + "" + ).toMutableList() else mutableListOf() ) ) @@ -141,7 +148,8 @@ class MainActivity : AppCompatActivity() { VideoQuestion( faker.backToTheFuture().quote(), id = faker.crypto().md5(), mandatory = faker.bool().bool(), - done = faker.bool().bool() + done = faker.bool().bool(), + value = "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_1MB.mp4" ) ) @@ -149,7 +157,8 @@ class MainActivity : AppCompatActivity() { AudioQuestion( title = faker.gameOfThrones().quote(), id = faker.crypto().md5(), mandatory = faker.bool().bool(), - done = faker.bool().bool() + done = faker.bool().bool(), + value = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_2MG.mp3" ) ) @@ -247,7 +256,7 @@ class MainActivity : AppCompatActivity() { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 43) { if (resultCode == RESULT_OK) { - questionAdapter.updatePickedVideo(videoFile) + questionAdapter.updatePickedVideo(videoFile.path) } } if (requestCode == 42) { @@ -259,24 +268,11 @@ class MainActivity : AppCompatActivity() { if (resultCode == RESULT_OK) { val recordFile: Uri? = data?.getParcelableExtra("recordPath") val position = data?.getIntExtra("position", -1) ?: -1 - recordFile.log() - position.log() - questionAdapter.addRecordFile(recordFile, position) + questionAdapter.addRecordFile(recordFile?.path, position) } } - if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { - val result = CropImage.getActivityResult(data) - if (resultCode == Activity.RESULT_OK) { - result?.also { - questionAdapter.updateImageAdapterAtPosition( - it.originalUri.toString() - ) - } - } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) { - val error = result!!.error - } - } + } override fun onRequestPermissionsResult( diff --git a/form/build.gradle b/form/build.gradle index 8bc6f01..f15aeaf 100644 --- a/form/build.gradle +++ b/form/build.gradle @@ -45,6 +45,5 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation 'com.squareup.picasso:picasso:2.71828' - implementation ("com.github.CanHub:Android-Image-Cropper:3.1.1") } \ No newline at end of file diff --git a/form/src/main/java/com/m7mdra/questionForm/ImageAdapter.kt b/form/src/main/java/com/m7mdra/questionForm/ImageAdapter.kt index 9424890..ab9b96d 100644 --- a/form/src/main/java/com/m7mdra/questionForm/ImageAdapter.kt +++ b/form/src/main/java/com/m7mdra/questionForm/ImageAdapter.kt @@ -1,13 +1,22 @@ package com.m7mdra.questionForm +import android.content.Context +import android.graphics.Bitmap import android.view.LayoutInflater import android.view.ViewGroup +import android.webkit.URLUtil import androidx.recyclerview.widget.RecyclerView import com.m7mdra.questionForm.viewholder.RowImageViewHolder +import com.squareup.picasso.Cache +import com.squareup.picasso.LruCache +import com.squareup.picasso.OkHttp3Downloader import com.squareup.picasso.Picasso import java.io.File -class ImageAdapter(private val clickListener: (Int, String) -> Unit = { _, _ -> }) : +class ImageAdapter( + private val context: Context, + private val clickListener: (Int, String) -> Unit = { _, _ -> } +) : RecyclerView.Adapter() { private val list = mutableListOf() @@ -29,20 +38,43 @@ class ImageAdapter(private val clickListener: (Int, String) -> Unit = { _, _ -> return RowImageViewHolder(view) } + private val picasso: Picasso by lazy { + Picasso.Builder(context) + .memoryCache(LruCache(context)) + .loggingEnabled(BuildConfig.DEBUG) + .downloader(OkHttp3Downloader(context)) + .build() + } + + override fun onBindViewHolder(holder: RowImageViewHolder, position: Int) { val imageSource: String = list[position] holder.view.setOnClickListener { clickListener.invoke(position, imageSource) } - Picasso.get() - .load(File(imageSource)) - .fit() - .centerInside() - .into(holder.selectedImageView) + if (URLUtil.isHttpUrl(imageSource) || URLUtil.isHttpsUrl(imageSource)) { + picasso.load(imageSource) + .fit() + .centerInside() + .error(R.drawable.placeholder_image) + .placeholder(R.drawable.placeholder_image) + .into(holder.selectedImageView) + } else if(URLUtil.isFileUrl(imageSource)) { + picasso + .load(File(imageSource)) + .fit() + .error(R.drawable.placeholder_image) + .placeholder(R.drawable.placeholder_image) + .centerInside() + .into(holder.selectedImageView) + }else{ + return + } + } override fun getItemCount(): Int { return list.size } -} \ No newline at end of file +} diff --git a/form/src/main/java/com/m7mdra/questionForm/QuestionAdapter.kt b/form/src/main/java/com/m7mdra/questionForm/QuestionAdapter.kt index ded18bb..b278a78 100644 --- a/form/src/main/java/com/m7mdra/questionForm/QuestionAdapter.kt +++ b/form/src/main/java/com/m7mdra/questionForm/QuestionAdapter.kt @@ -6,6 +6,7 @@ import android.content.pm.PackageManager import android.graphics.Color import android.graphics.Typeface import android.media.MediaPlayer +import android.net.Uri import android.os.Handler import android.os.Looper import android.text.Editable @@ -22,7 +23,6 @@ import android.view.View import android.view.ViewGroup import android.widget.* import androidx.core.content.ContextCompat -import androidx.core.net.toUri import androidx.core.util.forEach import androidx.core.util.set import androidx.recyclerview.widget.LinearLayoutManager @@ -31,7 +31,6 @@ import androidx.recyclerview.widget.RecyclerView import com.m7mdra.questionForm.question.* import com.m7mdra.questionForm.question.QuestionType.* import com.m7mdra.questionForm.viewholder.* -import java.io.File class QuestionAdapter( @@ -155,14 +154,12 @@ class QuestionAdapter( holder.textInputEditText.addTextChangedListener(textWatcher) if (question.done) { - holder.rootView.disable() - holder.itemView.disable() - holder.rootView.disableChildren() + holder.textInputEditText.disable() holder.submittedTextView.show() } else { - holder.rootView.enable() - holder.itemView.enable() - holder.rootView.enableChildren() + + holder.textInputEditText.enable() + holder.submittedTextView.gone() } @@ -199,14 +196,11 @@ class QuestionAdapter( autoCompleteTextView.onItemClickListener = onItemClickListener dropDownListener[position] = onItemClickListener if (question.done) { - holder.rootView.disable() - holder.itemView.disable() - holder.rootView.disableChildren() + holder.autoCompleteTextView.disable() holder.submittedTextView.show() } else { - holder.rootView.enable() - holder.itemView.enable() - holder.rootView.enableChildren() + holder.autoCompleteTextView.enable() + holder.submittedTextView.gone() } } @@ -259,7 +253,6 @@ class QuestionAdapter( mediaViewHolderIndexes.append(position, position) val holder = viewHolder as VideoViewHolder val question = list[position] as VideoQuestion - question.log() val videoView = holder.videoView @@ -276,10 +269,10 @@ class QuestionAdapter( videoPickListener?.invoke(question, position) } - val file = question.value - if (file != null) { + val video = question.value + if (video != null && video.isNotEmpty()) { videoView.show() - videoView.setVideoURI(file.toUri()) + videoView.setVideoURI(Uri.parse(video)) videoView.setOnPreparedListener { holder.playOrStopButton.show() holder.playOrStopButton.setImageResource(R.drawable.ic_baseline_play_circle_filled_24) @@ -304,14 +297,11 @@ class QuestionAdapter( } } if (question.done) { - holder.rootView.disable() - holder.itemView.disable() - holder.rootView.disableChildren() + holder.captureOrPickVideoButton.disable() holder.submittedTextView.show() } else { - holder.rootView.enable() - holder.itemView.enable() - holder.rootView.enableChildren() + holder.captureOrPickVideoButton.enable() + holder.submittedTextView.gone() } @@ -339,7 +329,7 @@ class QuestionAdapter( holder.titleTextView.text = titleWithRedAsterisk(question.mandatory, question.title, position) - val imageAdapter = ImageAdapter { childPosition, image -> + val imageAdapter = ImageAdapter(context) { childPosition, image -> imageClickListener?.invoke(position, childPosition, image) } holder.imageButton.setOnClickListener { @@ -351,14 +341,10 @@ class QuestionAdapter( imageAdapter.addAll(question.value) holder.imagesRecyclerView.adapter = imageAdapter if (question.done) { - holder.rootView.disable() - holder.itemView.disable() - holder.rootView.disableChildren() + holder.imageButton.disable() holder.submittedTextView.show() } else { - holder.rootView.enable() - holder.itemView.enable() - holder.rootView.enableChildren() + holder.imageButton.disable() holder.submittedTextView.gone() } @@ -379,7 +365,8 @@ class QuestionAdapter( holder.titleTextView.text = titleWithRedAsterisk(question.required, question.title, position) - if (question.value != null) { + val audio = question.value + if (audio != null && audio.isNotEmpty()) { val mediaPlayer = MediaPlayer() val handler = Handler(Looper.getMainLooper()) @@ -404,22 +391,18 @@ class QuestionAdapter( audioHandlers[position] = handler audioHandlersCallback[position] = runnable holder.playOrStopButton.setOnClickListener { - val audio = question.value ?: return@setOnClickListener - try { - if (mediaPlayer.isPlaying) { - mediaPlayer.stop() - holder.recordDurationTextView.text = - context.getString(R.string.zero_zero) - holder.playOrStopButton.setImageResource(R.drawable.ic_baseline_play_arrow_24) - holder.recordProgress.progress = 0 - } else { - mediaPlayer.reset() - mediaPlayer.setDataSource(audio.path) - mediaPlayer.prepareAsync() - } - } catch (error: Exception) { - + if (mediaPlayer.isPlaying) { + mediaPlayer.stop() + holder.recordDurationTextView.text = + context.getString(R.string.zero_zero) + holder.playOrStopButton.setImageResource(R.drawable.ic_baseline_play_arrow_24) + holder.recordProgress.progress = 0 + } else { + mediaPlayer.reset() + mediaPlayer.setDataSource(audio) + mediaPlayer.prepareAsync() } + } mediaPlayer.setOnPreparedListener { mediaPlayer.start() @@ -435,7 +418,7 @@ class QuestionAdapter( } } - holder.playOrStopButton.isEnabled = question.value != null + holder.playOrStopButton.isEnabled = audio != null holder.recordAudioButton.text = if (isAudioPermissionGranted(holder.context) @@ -447,11 +430,11 @@ class QuestionAdapter( } if (question.done) { - holder.rootView.disable() - holder.itemView.disable() - holder.rootView.disableChildren() + holder.recordAudioButton.disable() holder.submittedTextView.show() } else { + holder.recordAudioButton.disable() + holder.rootView.enable() holder.itemView.enable() holder.rootView.enableChildren() @@ -498,7 +481,6 @@ class QuestionAdapter( } else { holder.rootView.enable() holder.itemView.enable() - holder.rootView.enableChildren() holder.submittedTextView.gone() } @@ -631,7 +613,7 @@ class QuestionAdapter( mediaPlayers.remove(adapterPosition) } - fun addRecordFile(recordFile: File?, position: Int) { + fun addRecordFile(recordFile: String?, position: Int) { if (recordFile == null) return if (position == -1) @@ -641,7 +623,7 @@ class QuestionAdapter( notifyItemChanged(position) } - fun updatePickedVideo(uri: File) { + fun updatePickedVideo(uri: String) { val question = list[lastImageVideoIndex] as VideoQuestion question.update(uri) notifyItemChanged(lastImageVideoIndex) diff --git a/form/src/main/java/com/m7mdra/questionForm/extensions.kt b/form/src/main/java/com/m7mdra/questionForm/extensions.kt index 2396474..358ef4b 100644 --- a/form/src/main/java/com/m7mdra/questionForm/extensions.kt +++ b/form/src/main/java/com/m7mdra/questionForm/extensions.kt @@ -11,6 +11,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.InputMethodManager +import android.webkit.URLUtil import android.widget.EditText import androidx.appcompat.app.AppCompatActivity import androidx.core.view.children @@ -90,6 +91,14 @@ fun ViewGroup.disableChildren() { } +fun String.isUrl(): Boolean { + return this.isNotEmpty() && URLUtil.isHttpUrl(this) || URLUtil.isHttpsUrl(this) +} + +fun String.isFile(): Boolean { + return this.isNotEmpty() && URLUtil.isFileUrl(this) +} + fun ViewGroup.enableChildren() { children.forEach { if (it is ViewGroup) { diff --git a/form/src/main/java/com/m7mdra/questionForm/question/AudioQuestion.kt b/form/src/main/java/com/m7mdra/questionForm/question/AudioQuestion.kt index 20be093..9a91f13 100644 --- a/form/src/main/java/com/m7mdra/questionForm/question/AudioQuestion.kt +++ b/form/src/main/java/com/m7mdra/questionForm/question/AudioQuestion.kt @@ -1,9 +1,8 @@ package com.m7mdra.questionForm.question -import java.io.File class AudioQuestion( - override var value: File? = null, + override var value: String? = null, title: String, id: String, private val mandatory: Boolean = false, @@ -11,7 +10,7 @@ class AudioQuestion( done: Boolean = false ) : - Question( + Question( title, questionType = QuestionType.Audio, id = id, @@ -38,7 +37,7 @@ class AudioQuestion( } - override fun update(value: File?) { + override fun update(value: String?) { this.value = value } diff --git a/form/src/main/java/com/m7mdra/questionForm/question/VideoQuestion.kt b/form/src/main/java/com/m7mdra/questionForm/question/VideoQuestion.kt index fa1c52c..9400b9f 100644 --- a/form/src/main/java/com/m7mdra/questionForm/question/VideoQuestion.kt +++ b/form/src/main/java/com/m7mdra/questionForm/question/VideoQuestion.kt @@ -6,10 +6,10 @@ class VideoQuestion( title: String, id: String, val mandatory: Boolean = false, private val params: Map = mapOf(), - done: Boolean = false, override var value: File? = null + done: Boolean = false, override var value: String? = null -) : Question( +) : Question( title, questionType = QuestionType.Video, id = id, @@ -35,7 +35,7 @@ class VideoQuestion( } - override fun update(value: File?) { + override fun update(value: String?) { this.value = value } diff --git a/form/src/main/res/drawable/placeholder_image.xml b/form/src/main/res/drawable/placeholder_image.xml new file mode 100644 index 0000000..1f87604 --- /dev/null +++ b/form/src/main/res/drawable/placeholder_image.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file