Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProfileContractActivity, Profile model and FillFormActivity have been added #153

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: set up JDK 11
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: gradle

Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Activity #3

### Задание #1
В классе **EditProfileActivity** по клику на `ImageView` с `id=imageview_photo` покажите Alert Dialog с выбором действия. Используйте реализацию диалога указанную в [примере](https://material.io/components/dialogs/android#simple-dialog), в качестве элементов массива добавьте элементы “Сделать фото” и “Выбрать фото”

По клику на кнопку Сделать фото запросите у пользователя Runtime Permission на использование камеры. Обработайте следующие возможные сценарии:

1. Пользователя выдал разрешение на использование камеры → отобразите в ImageView ресурс `R.drawable.cat`
2. Пользователь не разрешил использовать камеру первый раз → ничего не делаем
3. Пользователь еще раз запросил разрешение на использование камеры после отмены → покажите Rationale Dialog, и объясните зачем вам камера

В качестве реализации Rationale Dialog используйте [простой Alert Dialog](https://material.io/components/dialogs/android#alert-dialog) с двумя кнопками: “Дать доступ” и “Отмена”. По клику на кнопку “Дать доступ” повторно запросите разрешение, по клику на кнопку “Отмена” закройте диалоговое окно

4. Пользователь повторно запретил использовать камеру → Покажите диалоговое окно с одной кнопкой → “Открыть настройки”. По клику на кнопку отправьте пользователя в настройки приложения, с возможностью поменять разрешение

> 💡 В этом задании вам не нужно использовать какое либо кеширование/флаги и прочее. Реализуйте все с использованием ResultApi и методов Activity

### Задание #2
По клику на кнопку “Выбрать фото” откройте экран выбора фото из галлереи, после того как вы получите URI фотографии в `ActivityResultCallback` вызовите метод `populateImage`, чтобы отобразить полученную фотографию в ImageView

> 💡 Используйте готовый контракт из класса `ActivityResultContracts` для открытия пикера медиафайлов

<img src="art/Untitled.png" width="720">

### Задание #3
1. Создайте класс-наследник Activity **FillFormActivity** и добавьте в нее 3 EditText для ввода имени, фамилии и возраста, и кнопку “Применить”
2. В **EditProfileActivity** по клику на кнопку “Редактировать профиль” откройте **FillFormActivity** с запросом результата, используя ResultApi
3. По нажатию на кнопку “Применить” на **FillFormActivity** заберите введенный текст из 3 полей и отправьте результат на запустившую ее Activity, тоесть на **EditProfileActivity**
4. В **EditProfileActivity** обработайте полученный результат и отобразите контент в соответствующих TextView
5. Реализуйте функцию `openSenderApp` так, чтобы она отправляла явный интент в Telegram, в качестве extras отправьте картинку полученную из галлереи и контент TextView

<img src="art/Untitled%201.png" width="720">
9 changes: 7 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id("io.gitlab.arturbosch.detekt")
id 'kotlin-parcelize'
}

android {
Expand Down Expand Up @@ -43,8 +44,8 @@ detekt {

tasks.named("detekt").configure {
reports {
txt.required.set(true)
html.required.set(false)
txt.required.set(false)
html.required.set(true)
md.required.set(false)
xml.required.set(false)
sarif.required.set(false)
Expand All @@ -56,4 +57,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.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'
}
29 changes: 28 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

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

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

<activity
android:name=".EditProfileActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".FillFormActivity"
android:exported="true">
<intent-filter>
<action android:name="FillFromFormIntent" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

</application>

</manifest>
167 changes: 167 additions & 0 deletions app/src/main/java/otus/gpb/homework/activities/EditProfileActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package otus.gpb.homework.activities

import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar

class EditProfileActivity : AppCompatActivity() {

private val requestCameraPermission = registerForActivityResult(
ActivityResultContracts
.RequestPermission()
)
{ granted ->
when {
granted -> imageView.setImageResource(R.drawable.cat)
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> showRationalDialog()
else -> showSettingsDialog()
}
}

private val imageContent = registerForActivityResult(ActivityResultContracts.GetContent())
{
it?.let {
profileImageUri = it
populateImage(it)
}
}

private val launcherProfileContract = registerForActivityResult(ProfileContractActivity())
{ result ->
profile = result
findViewById<TextView>(R.id.textview_name).text = result?.name
findViewById<TextView>(R.id.textview_surname).text = result?.surname
findViewById<TextView>(R.id.textview_age).text = result?.age
}

private var profile: Profile? = null
private var profileImageUri: Uri? = null
private lateinit var imageView: ImageView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_profile)
imageView = findViewById(R.id.imageview_photo)

findViewById<Toolbar>(R.id.toolbar).apply {
inflateMenu(R.menu.menu)
setOnMenuItemClickListener {
when (it.itemId) {
R.id.send_item -> {
openSenderApp()
true
}

else -> false
}
}
}

imageView.setOnClickListener {
showSelectPictureAlert()
}

findViewById<Button>(R.id.button4).setOnClickListener {
launcherProfileContract.launch(Unit)
}
}


private fun showSelectPictureAlert() = AlertDialog.Builder(this)
.setTitle(resources.getString(R.string.choose_image_title))
.setItems(
arrayOf(
resources.getString(R.string.take_photo_dialog_button),
resources.getString(R.string.choose_photo_dialog_button)
)
) { _, which ->
when (which) {
0 -> setPhoto()
1 -> getImage()
}
}
.create()
.show()

private fun showRationalDialog() = AlertDialog.Builder(this)
.setTitle(resources.getString(R.string.camera_permission_title))
.setMessage(resources.getString(R.string.rational_dialog_message))
.setPositiveButton(resources.getString(R.string.give_permission_dialog_button)) { _, _ ->
setPhoto()
}
.setNegativeButton(resources.getString(R.string.cancel_dialog_button)) { dialog, _ ->
dialog.cancel()
}
.create()
.show()

private fun showSettingsDialog() = AlertDialog.Builder(this)
.setTitle(resources.getString(R.string.go_to_settings_dialog_message))
.setPositiveButton(resources.getString(R.string.open_settings_screen_dialog_button)) { _, _ ->
val settingIntent = Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", packageName, null)
}
startActivity(settingIntent)
}
.create()
.show()

private fun setPhoto() {
requestCameraPermission.launch(Manifest.permission.CAMERA)
}

private fun getImage() {
imageContent.launch("image/*")
}

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

private fun openSenderApp() {

if (profile != null) {
val intent = Intent(Intent.ACTION_SEND).apply {
setPackage("org.telegram.messenger")
setType("image/*")
putExtra(
Intent.EXTRA_TEXT,
"${profile?.name}\n${profile?.surname}\n${profile?.age}"
)

profileImageUri?.let {
putExtra(Intent.EXTRA_STREAM, profileImageUri)
}
}
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(
this,
resources.getString(R.string.telegram_alert_message),
LENGTH_SHORT
).show()
}
}
else Toast.makeText(this, resources.getString(R.string.fill_profile_info_message), LENGTH_SHORT)
.show()
}
}
25 changes: 25 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,25 @@
package otus.gpb.homework.activities

import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity

class FillFormActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fill_form)

findViewById<Button>(R.id.apply_btn).setOnClickListener {
val profile = Profile(
findViewById<EditText>(R.id.username_et).text.toString(),
findViewById<EditText>(R.id.surname_et).text.toString(),
findViewById<EditText>(R.id.age_et).text.toString()
)
val intent = Intent().putExtra(RESULT_KEY, profile)
setResult(RESULT_OK, intent)
finish()
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/otus/gpb/homework/activities/Profile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package otus.gpb.homework.activities

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class Profile(
val name: String,
val surname: String,
val age: String
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package otus.gpb.homework.activities

import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract

const val RESULT_KEY = "result_key"

class ProfileContractActivity : ActivityResultContract<Unit, Profile?>() {
override fun createIntent(context: Context, input: Unit): Intent {
val intent = Intent(context, FillFormActivity::class.java)
return intent
}

override fun parseResult(resultCode: Int, intent: Intent?): Profile? {
if (resultCode != RESULT_OK)
return null
return intent?.extras?.getParcelable(RESULT_KEY)
}
}
Binary file added app/src/main/res/drawable/cat.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_baseline_add_photo_alternate_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:tint="#33000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,7v2.99s-1.99,0.01 -2,0L17,7h-3s0.01,-1.99 0,-2h3L17,2h2v3h3v2h-3zM16,11L16,8h-3L13,5L5,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-8h-3zM5,19l3,-4 2,3 3,-4 4,5L5,19z" />
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_baseline_send_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#FFFFFF" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
Loading