Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id("io.gitlab.arturbosch.detekt")
id("kotlin-parcelize")
}

android {
compileSdk 34
compileSdk 36

defaultConfig {
applicationId "otus.gpb.homework.activities"
minSdk 23
targetSdk 34
targetSdk 36
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -54,10 +55,12 @@ tasks.named("detekt").configure {

dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.activity:activity-ktx:1.9.0'
implementation 'androidx.fragment:fragment-ktx:1.7.1'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.google.android.material:material:1.14.0-alpha05'
implementation 'androidx.activity:activity:1.11.0'
}
13 changes: 11 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-feature
android:name="android.hardware.camera"
android:required="false" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -11,8 +18,10 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Activities"
tools:targetApi="31">

tools:targetApi="36">
<activity
android:name=".FillFormActivity"
android:exported="false" />
<activity
android:name=".EditProfileActivity"
android:exported="true">
Expand Down
168 changes: 166 additions & 2 deletions app/src/main/java/otus/gpb/homework/activities/EditProfileActivity.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,56 @@
package otus.gpb.homework.activities

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.launch
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import android.content.DialogInterface
import android.widget.Button
import androidx.core.content.res.ResourcesCompat
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.registerForActivityResult

class EditProfileActivity : AppCompatActivity() {

class EditProfileActivity : AppCompatActivity() {
private lateinit var imageView: ImageView
private lateinit var editProfileButton: Button
private var repeatCameraAccessAttempt: Boolean = false

private val ImageMethods by lazy {
arrayOf(
resources.getString(R.string.alert_dialog_create_foto),
resources.getString(R.string.alert_dialog_choose_foto)
)
}

private var selectedImageMethod = -1

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_profile)
imageView = findViewById(R.id.imageview_photo)
imageView.setOnClickListener {
Toast.makeText(this, "ФОТО!!!", Toast.LENGTH_SHORT).show()
dialogActionWithFoto.show()
}

editProfileButton = findViewById(R.id.editProfileButton)
editProfileButton.setOnClickListener {
launcherFillUserInfo.launch()
}


findViewById<Toolbar>(R.id.toolbar).apply {
inflateMenu(R.menu.menu)
Expand All @@ -30,15 +66,143 @@ class EditProfileActivity : AppCompatActivity() {
}
}

private val launcherFillUserInfo = registerForActivityResult(UserProfileContract()){ userInfo ->
userInfo?.name?.let { findViewById<TextView>(R.id.textview_name).text = it }
userInfo?.surName?.let { findViewById<TextView>(R.id.textview_surname).text = it }
userInfo?.age?.let { findViewById<TextView>(R.id.textview_age).text = it.toString() }
}
private val launcherPermissionCamera = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
when {
granted -> {
Toast.makeText(this, "Доступ к камере получен! Снимаем фото!", Toast.LENGTH_SHORT).show()
setImageToImageView()
}
!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
if( !repeatCameraAccessAttempt ){
Toast.makeText(this, "Очень нужен доступ к камере, чтобы сделать фото!", Toast.LENGTH_SHORT).show()
openSettingsDialog.show()
}
}
else -> {
Toast.makeText(this, "Потом попробуешь еще раз...", Toast.LENGTH_SHORT).show()
repeatCameraAccessAttempt = true
}
}
}

private val launcherGetPicture = registerForActivityResult(
ActivityResultContracts.TakePicturePreview()
) { result ->
findViewById<ImageView>(R.id.imageview_photo).setImageBitmap(result)
}

private fun setImageToImageView(){
imageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.cat, null))
}

private val dialogActionWithFoto
get()= MaterialAlertDialogBuilder(this)
.setTitle(R.string.alert_dialog_title)
.setSingleChoiceItems(ImageMethods, -1) { _, which -> selectedImageMethod = which}
.setPositiveButton(R.string.alert_dialog_ok, ::onPositiveButtonActionWithFotoClickListener)
.setNegativeButton(R.string.alert_dialog_canceled) { dialog, _ ->
dialog.dismiss()
selectedImageMethod = -1
}
.create()

fun onPositiveButtonActionWithFotoClickListener(dialog: DialogInterface?, which: Int) {
when(selectedImageMethod){
0 -> {
val isGrantedCamera = ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
if(isGrantedCamera){
Toast.makeText(this, "Доступ к камере есть! Снимаем фото!", Toast.LENGTH_SHORT).show()
launcherGetPicture.launch()
}
else{
Toast.makeText(this, "Доступа к камере нет! Запрашиваем!", Toast.LENGTH_SHORT).show()
if( repeatCameraAccessAttempt ){
repeatCameraPermissionDialog.show()
}
else{
launcherPermissionCamera.launch(Manifest.permission.CAMERA)
}
}
}
1 -> {
selectPictureLauncher.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.ImageOnly
))
}
}
dialog?.dismiss()
}

private val repeatCameraPermissionDialog
get() = MaterialAlertDialogBuilder(this)
.setTitle(R.string.repeat_alert_dialog_title)
.setMessage("Необходим доступ к камере для создания аватара!!!")
.setPositiveButton(R.string.repeat_alert_dialog_get_access, ::onPositiveButtonRepeatCameraPermissionDialog)
.setNegativeButton(R.string.repeat_alert_dialog_canceled) { dialog, _ ->
dialog.dismiss()
}
.create()

fun onPositiveButtonRepeatCameraPermissionDialog(dialog: DialogInterface?, which: Int) {
launcherPermissionCamera.launch(Manifest.permission.CAMERA)
repeatCameraAccessAttempt = false
}

private val openSettingsDialog
get() = MaterialAlertDialogBuilder(this)
.setTitle(R.string.open_settings_dialog_title)
.setPositiveButton(R.string.open_settings_dialog_button, ::onPositiveButtonOpenSettingsDialog)
.create()

fun onPositiveButtonOpenSettingsDialog(dialog: DialogInterface?, which: Int) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
startActivity(intent)
}

private val selectPictureLauncher = registerForActivityResult(
ActivityResultContracts.PickVisualMedia()
) { result ->
result?.let { populateImage(it) }
}

/**
* Используйте этот метод чтобы отобразить картинку полученную из медиатеки в ImageView
*/
private fun populateImage(uri: Uri) {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
imageView.setImageBitmap(bitmap)
imageView.tag = uri
}

private fun openSenderApp() {
TODO("В качестве реализации метода отправьте неявный Intent чтобы поделиться профилем. В качестве extras передайте заполненные строки и картинку")
val intent = Intent().apply {
action = Intent.ACTION_SEND
type = "image/*"
setPackage("org.telegram.messenger")

val imgUri = imageView.tag as? Uri

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Получаете uri, но не сохраняете его в tag. Из-за этого imgUri всегда будет null, и картинка не отправится в Telegram. Нужно добавить imageView.tag = uri в метод populateImage

if (imgUri != null) putExtra(Intent.EXTRA_STREAM, imgUri)
putExtra(Intent.EXTRA_TEXT,
"Имя : ${ findViewById<TextView>(R.id.textview_name).text}\nФамилия: ${ findViewById<TextView>(R.id.textview_surname).text}\nВозраст: ${ findViewById<TextView>(R.id.textview_age).text}")
}

runCatching {
startActivity(intent)
}.getOrElse {
Toast.makeText(this, "Ну удалось запустить Telegram!!!", Toast.LENGTH_SHORT).show()
}
}
}
42 changes: 42 additions & 0 deletions app/src/main/java/otus/gpb/homework/activities/FillFormActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package otus.gpb.homework.activities

import android.os.Bundle
import android.widget.EditText
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.widget.Button
import android.content.Intent

class FillFormActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_fill_form)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}

findViewById<Button>(R.id.button)?.setOnClickListener {
val firstName = findViewById<EditText>(R.id.editFirstName).text.toString()
val lastName = findViewById<EditText>(R.id.editLastName).text.toString()
val age = findViewById<EditText>(R.id.editAge).text.toString().toIntOrNull() ?: 0

if (firstName.isNotBlank() && lastName.isNotBlank() && age > 0) {
val user = UserInfo(firstName, lastName, age)
val intent = Intent().putExtra(USER_PROFILE_RESULT_KEY, user)
setResult(RESULT_OK, intent)
} else {
setResult(RESULT_CANCELED)
}
finish()
}
}

companion object {
const val USER_PROFILE_RESULT_KEY = "UserProfileResultKey"
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/otus/gpb/homework/activities/UserProfile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package otus.gpb.homework.activities
import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class UserInfo(
val name: String? = null,
val surName: String? = null,
val age: Int? = null
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package otus.gpb.homework.activities

import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract
import androidx.appcompat.app.AppCompatActivity.RESULT_CANCELED
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.core.content.IntentCompat

class UserProfileContract : ActivityResultContract<Unit, UserInfo?>() {

override fun createIntent(
context: Context,
input: Unit
): Intent {
return Intent(context, FillFormActivity::class.java)
}

override fun parseResult(
resultCode: Int,
intent: Intent?
): UserInfo? {
if (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можем упростить: if (resultCode != RESULT_OK || intent == null) return null

resultCode == RESULT_CANCELED
|| intent == null
|| resultCode != RESULT_OK
) return null

return IntentCompat.getParcelableExtra(
intent,
FillFormActivity.USER_PROFILE_RESULT_KEY,
UserInfo::class.java
)
}
}
4 changes: 3 additions & 1 deletion app/src/main/res/layout/activity_edit_profile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="72dp"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="Профиль"
Expand Down Expand Up @@ -61,7 +63,7 @@
app:layout_constraintTop_toBottomOf="@+id/textview_surname" />

<Button
android:id="@+id/button4"
android:id="@+id/editProfileButton"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Expand Down
Loading