Примерный вид экрана профиля (взято из другого проекта). Вместо кнопок "Обсуждения", "История" и "Настройки" у вас должны быть "загрузить права" и "сфотографировать паспорт" с показом соответствующих картинок, если они уже загружены (есть в классе User)
Аватарка пользователя в профиле должна быть круглой. Фон "фигурой", как можно бы было надеятся тут не работает.
Можно использовать контейнер CardView: элемент ImageView заворачивается в контейнер androidx.cardview.widget.CardView, которому и задаётся радиус (половина размера). Кроме радиуса можно задать тень:
<androidx.cardview.widget.CardView
android:layout_width="100dp"
android:layout_height="100dp"
app:cardCornerRadius="50dp"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
/>
</androidx.cardview.widget.CardView>
Изображению не забываем настроить режим: масштабирование с обрезкой.
С диалоговыми окнами мы уже знакомы (мы их используем для вывода текста ошибок). У класса AlertDialog есть метод, позволяющий сделать выбор из массива:
// сначала объявляем массив строк для выбора
val choiceItems = arrayOf("Галерея","Камера")
// создаём и показываваем диалог
AlertDialog.Builder(this)
.setTitle("Выберите источник")
.setNegativeButton("Отмена", null)
.setSingleChoiceItems(choiceItems, -1){
dialog, index ->
Toast.makeText(this, "$index", Toast.LENGTH_LONG).show()
dialog.dismiss()
}
.create()
.show()
При создании диалога добавился вызов метода setSingleChoiceItems, который как раз и задаёт массив элементов для выбора. Первым параметром этого метода задаётся массив строк, вторым - активный по-умолчанию элемент (можно указать -1, если не нужно выбирать что-то по-умолчанию) и третьим параметром задаётся лямбда функция, которая вызывается при выборе элемента списка.
В лямбда функцию передаётся два параметра:
Я в примере выше просто вывожу на экран номер выбранного элемента и закрываю диалог (dialog.dismiss()).
Полноценный вариант читать тут
Здесь приведён только простой вариант, который получает от камеры миниатюру (thumbinail). Для получения полноценной фотографии нужно иметь права на запись, сформировать имя файла и передать "камере"...
private val REQUEST_IMAGE_CAPTURE = 1
...
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(
takePictureIntent,
REQUEST_IMAGE_CAPTURE)
} catch (e: ActivityNotFoundException) {
// показать ошибку
}
Получение результата от активности мы уже разбирали, когда делали выбор города в проекте "погода". Обработчик результата всего один для класса активности. Поэтому при запуске активности мы и передаём уникальный код запроса, чтобы при разборке ответа знать от кого он пришёл:
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?)
{
super.onActivityResult(requestCode, resultCode, intent)
when(requestCode){
REQUEST_IMAGE_CAPTURE -> {
if (resultCode == Activity.RESULT_OK &&
intent != null)
{
val thumbnailBitmap = intent?.extras?
.get("data") as Bitmap
avatar.setImageBitmap(thumbnailBitmap)
// отправка полученного файла на сервер
sendFile(
bitmap2InputStream(thumbnailBitmap),
"avatar.jpg" // название фиксировано, описано в swagger-e
)
}
}
}
}
// преобразует битмап в поток (stream)
private fun bitmap2InputStream(bm: Bitmap): InputStream? {
val baos = ByteArrayOutputStream()
bm.compress(Bitmap.CompressFormat.JPEG, 75, baos)
return ByteArrayInputStream(baos.toByteArray())
}
Данные на сервер отправляются потоком, поэтому все исходные варианты я привожу к этому типу. Реализация метода sendFile будет ниже.
Для получения каких-то данных из "галереи" используется тип намерения Intent.ACTION_PICK.
Для конкретизации типа данных указывается свойство type и уникальный код запроса (REQUEST_JPG_FROM_GALERY):
// константа для анализа результата объявляется на уровне класса
private val REQUEST_JPG_FROM_GALERY = 2
В обработчике клика на кнопку запустить выбор изображения из галереи:
val photoPickerIntent = Intent(Intent.ACTION_PICK)
// фильтр
photoPickerIntent.type = "image/jpg"
//запускаем запрос, указав что ждём результат
startActivityForResult(photoPickerIntent, REQUEST_JPG_FROM_GALERY)
Дописываем блок в метод onActivityResult:
REQUEST_JPG_FROM_GALERY -> {
// убеждаемся что выполнено успешно (пользователь мог ничего и не выбрать в галерее)
if (resultCode == Activity.RESULT_OK
&& intent != null)
{
// галерея возвращает URI картинки в свойстве data параметра intent
fileImageView.setImageURI( intent.data!! )
val fileStream = contentResolver
.openInputStream(intent.data!!)
sendFile(
fileStream,
"prava.jpg")
}
}
Класс StreamHelper лежит в файле
../data/StreamHelper.ktэтого репозитория.
private fun sendFile(
fileStream: InputStream?, // поток, который надо отправить
fileName: String // имя файла
)
{
if (fileStream != null)
{
// поток преобразуем в RequestBody
val fileBody: RequestBody = StreamHelper
.create(
"image/jpg".toMediaType(),
fileStream
)
// из токена и файла формируем MultipartBody запрос
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("token", app.user?.userId.toString())
.addFormDataPart(
"file", // название части запроса
fileName,
fileBody // тело файла
)
.build()
val request = Request.Builder()
.url("http://carsharing.kolei.ru/user/photo")
.post(requestBody)
.build()
Http.call(request) { response, error ->
try {
if (error != null) throw error
if (!response!!.isSuccessful)
throw Exception(response.message)
} catch (e: Exception) {
// вывести алерт
}
}
}
}