Wczytywanie grafik z różnych źródeł w aplikacji mobilnej
Uprawnienia do odczytu plików
Od Androida 6.0 (API 23) musimy poprosić o zgodę również w kodzie (runtime permission), służy do tego metoda onRequestPermissionsResult(). Funkcja obsługuje odpowiedzi użytkownika na prośbę o uprawnienie (np. dostęp do pamięci urządzenia). W systemie Android od wersji 6.0 (API 23) niektóre uprawnienia zwane niebezpiecznymi (dangerous permissions) muszą być zatwierdzone przez użytkownika w czasie działania aplikacji, a nie tylko w AndroidManifest.xml.Dlaczego musimy prosić o uprawnienia?
Wprowadzony model uprawnień ?runtime permissions?, oznacza, że: aplikacje nie mogą domyślnie uzyskać dostępu do poufnych danych, takich jak zdjęcia, pliki, kontakty czy lokalizacja, użytkownik musi świadomie wyrazić zgodę, np. po kliknięciu przycisku w aplikacji.To podejście zwiększa bezpieczeństwo i prywatność użytkownika.
W pliku AndroidManifest.xml dodaj nad sekcją <application> te uprawnienia:
Wskazówka:
<!--Uprawnienie do odczytu plików-->
<!-- Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 12 i niżej -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Wskazówka:
android:requestLegacyExternalStorage="true"
Fragment pliku manifestu
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--Uprawnienie do odczytu plików-->
<!-- Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 12 i niżej -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--uprawnienie do internetu-->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:requestLegacyExternalStorage="true"
</application>
</manifest>
Przejdź do głównego pliku MainActivity.kt i zaimportuj te biblioteki
Wskazówka:
import android.os.Environment
import java.io.File
Grafika w emulatorze
Aby przetestować zachowanie budowanej aplikacji w emulatorze należy wgrać kilka dowolnych plików graficznych rozszerzeniami png, jpg, bmp, webp itp. W rzeczywistym urządzeniu pliki graficzne są przechowywane między innymi w folderze DCIM/ Camera. Postaramy się wgrać tam kilka plików z dysku komputera.Wgraj grafikę do emulatora
Wybierz podgląd dostępu do struktury katalogów uruchomionego emulatora View/ Tool Windows/ Device Explorer

Nie widac folderu Camera
Odśwież widok folderu, Wybierz Upload i skopiuj pliki jpg, png itp. Opcja Upload dostępna jest w menu prawego przycisku myszki po wskazaniu folderu.
Ścieżka w zależności od wersji emulatora może się nieznacznie różnić. Poniżej ścieżka na emulatorze Pixel 9 Pro API 36

Można też posłużyć się aparatem z emulatora. Otwórz na emulatorze aplikację Aparat zrób kilka zdjęć z emulatora (będą to te same widoki). Wyłącz emulator i ponownie uruchom. Grafika powinna się załadować do ponownie uruchomionego emulatora.
Układ widoku
Przygotuj widok w układzie LinearLayout (vertical)

Kontrolkę ListView rozciągnij tak aby były widoczne co najemnie dwa elementy listy. Pozostałe parametry osadzanych kontrolek dobierz według własnego uznania.
Na tym etapie tworzenia projektu zdefiniujemy wszystkie zmienne wykorzystywane w rozwiązaniu
Wskazówka:
class MainActivity : AppCompatActivity() {
//flaga kodu żądania- dowolna stala unikatowa wartość
private val PROSBA_DOSTEPU_DCIM = 1001
private lateinit var imageView: ImageView
private lateinit var listView: ListView
private lateinit var imageHint: TextView
private lateinit var btZPrzegladarki: Button
private lateinit var btDostepneZasoby: Button
private val imagePaths = mutableListOf<String>()
private val imageNames = mutableListOf<String>()
W trakcie kodowania kompilator poprosi i zasugeruje o dołączenie odpowiednich bibliotek. Zgódź się.
Przechodzimy do funkcji onCreate() i dodajemy poniższe linie kodu
Wskazówka:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
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
}
imageView = findViewById(R.id.imageView)
listView = findViewById(R.id.listView)
imageHint= findViewById(R.id.imageHint)
btZPrzegladarki = findViewById(R.id.btZPrzegladarki)
btDostepneZasoby = findViewById(R.id.btDostepneZasoby)
Wczytanie zasobów graficznych do ListView aplikacji
Po przyznaniu praw dostępu do zasobów możemy wczytać pliki graficzne do kontrolki ListView. Pliki zostaną pobrane z folderu DCIM/ Camera. Tworzymy funkcję wczytującą nazwy plików do listy
Wskazówka:
//laduj nazwy plików graficznych do listy
private fun ladujImageDCIM() {
// Pobranie katalogu DCIM zgodnie z systemem Android
val dcimDir: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
val cameraFolder = File(dcimDir, "Camera") // DCIM/Camera
if (!cameraFolder.exists() || !cameraFolder.isDirectory) {
Toast.makeText(this, "Folder DCIM/Camera nie istnieje", Toast.LENGTH_SHORT).show()
return
}
val imageFiles = cameraFolder.listFiles { file ->
file.isFile && file.extension.lowercase() in listOf("jpg", "jpeg", "png", "gif")
}
if (imageFiles.isNullOrEmpty()) {
Toast.makeText(this, "Brak zdjęć w folderze DCIM/Camera", Toast.LENGTH_SHORT).show()
return
}
imagePaths.clear()
imageNames.clear()
for (file in imageFiles) {
imagePaths.add(file.absolutePath)
imageNames.add(file.name)
}
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, imageNames)
listView.adapter = adapter
}
Jej automatyczne wywołanie wykonamy w zdarzeniu obsługi prośby o przydzielenie uprawnień.
Wskazówka:
// Obsługa wyniku prośby o uprawnienia
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PROSBA_DOSTEPU_DCIM && grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
ladujImageDCIM()
} else {
Toast.makeText(this, "Brak zgody na odczyt plików", Toast.LENGTH_LONG).show()
}
}
Obsłużenie zgody należy wywołać. I tu możemy rozszerzyć uniwersalność aplikacji na różne wersje API Androida .W zależności od poziomu wersji API przydzielenie uprawnień nieznacznie się różni. W do funkcji onCreate() wprowadź poniższy kod:
Wskazówka:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_MEDIA_IMAGES),
PROSBA_DOSTEPU_DCIM
)
} else {
ladujImageDCIM()
}
} else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
PROSBA_DOSTEPU_DCIM
)
} else {
ladujImageDCIM()
}
}
Na tę chwilę aplikacja nic nie potrafi zrobić poza jednym. Po pierwszym uruchomieniu pojawi się monit z pytaniem o przydzielenie uprawnień. Wybierz: Zezwól na wszystko.

Jeżeli przez pomyłkę wybierzesz inną opcje, odinstaluj aplikacje z emulator i skompiluj program jeszcze raz.
Lista rozwijalna z dostępną grafiką powinna się wypełnić nazwami plików graficznych. Patrz poniższy zrzut ekranu.

Teraz zostaje obsłużyć kliknięcie w element listy w celu załadowania grafiki do podglądu (kontrolki ImageView).
Wskazówka:
listView.setOnItemClickListener { _, _, indeks, _ ->
val path = imagePaths[indeks]
val uri = Uri.fromFile(File(path))
imageView.setImageURI(uri)
}
Skompiluj aplikację i sprawdź działanie. Po kliknięciu w element listy wybrany obraz powinien zostać załadowany do podglądu.

Powyższe rozwiązanie wynikające z dostępnych metod API Androida jest dość kłopotliwe. Dużo łatwiej jest to zrobić wykorzystując bibliotekę Glide. UWAGA! Użycie tej biblioteki nie zwalnia z przypisania uprawnień aplikacji i zgody użytkownika.
Wybór obrazu z mechanizmu udostępniania środowiska Android
W kolejnym kroku budowy aplikacji wykonamy metodę wczytywanie obrazu pobranego z dowolnego zasobu zapisanego w systemie urządzenia mobilnego z wykorzystaniem biblioteki Glide. Glide to biblioteka open-source dla Androida stworzona przez firmę Bump Technologies (obecnie rozwijana przez Google), która ułatwia ładowanie i wyświetlanie obrazów w aplikacjach. Jednym z pomysłów jest wczytani obrazu poprzez mechanizm udostępniania z jednej aplikacji do innej, w tym przypadku naszej.
Po co jest Glide?
Dlaczego używać Glide, a nie BitmapFactory lub HttpURLConnection?
Bez Glide musiałbyś:- Samodzielnie pobrać obraz z internetu (HttpURLConnection / OkHttp).
- Przekonwertować go na Bitmap.
- Zająć się wątkiem roboczym (żeby nie blokować UI).
- Zająć się buforowaniem i pamięcią.
Glide robi to wszystko za Ciebie jedną linią kodu:
Wskazówka:
Glide.with(context)
.load(url)
.into(imageView)
Główne funkcje Glide:
- load(url) ? ładuje obraz z internetu lub innego źródła.
- placeholder(R.drawable.xxx) ? obraz tymczasowy wyświetlany podczas ładowania.
- error(R.drawable.xxx) ? obraz, jeśli ładowanie się nie powiedzie.
- centerCrop(), fitCenter() ? skalowanie obrazu.
- Obsługa RecyclerView, ListView bez zacinania aplikacji.
Dodanie biblioteki Glide
Dodanie biblioteki Glide wymaga modyfikacji pliku bulid.gradle.kts (dla module). Otwórz ten plik i wprowadź poniższe zmiany
Wskazówka:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("kotlin-kapt") // <-- dodaj tę linię
}
dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0") // <-- dodaj tę linię
kapt("com.github.bumptech.glide:compiler:4.16.0") // <-- dodaj tę linię
implementation(libs.androidx.appcompat)
implementation(libs.material)
}
Po dodaniu wybierz synchronizację z projektem- kliknij Sync Now
A w pliku głównym MainActivity.kt zaimportuj bibliotekę
Wskazówka:
import com.bumptech.glide.Glide
Aplikację rozszerzymy o dwie ikony przedstawiające błąd ładowania zasobu graficznego oraz ikonę reprezentującą obraz. Pobranie darmowych ikon wspieranych przez Google możesz skorzystać z:
Google Material Icons darmowe ikony od Google
https://fonts.google.com/iconsPobrane ikony zaimportuj do folderu res/ drawable

Otwórz plik manifestu i dodaj dwa filtry w sekcji <activity></activity>
Wskazówka:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" /> <!-- udostępnianie linków -->
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" /> <!-- udostępnianie obrazów zapisanych lokalnie -->
</intent-filter>
Otwórz plik MainActivity.kt i dodaj poniższą funkcję
Wskazówka:
//wczytaj obraz poprzez mechanizm udostępnij np. z przeglądania galerii obrazów
private fun wczytaj_z_Udostepnij(intent: Intent?) {
if (intent == null) return
when (intent.action) {
Intent.ACTION_SEND -> {
when (intent.type) {
"text/plain" -> { // Link do obrazu
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
if (!sharedText.isNullOrEmpty()) {
Glide.with(this)
.load(sharedText)
.placeholder(R.drawable.image_256dp)
.error(R.drawable.error_256dp)
.into(imageView)
imageHint.visibility = View.GONE
}
}
else -> if (intent.type?.startsWith("image/") == true) { // Plik obrazu lokalny
val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
if (imageUri != null) {
Glide.with(this)
.load(imageUri)
.placeholder(R.drawable.image_256dp)
.error(R.drawable.error_256dp)
.into(imageView)
imageHint.visibility = View.GONE
}
}
}
}
}
}
Funkcję wywołaj w metodzie onCreate()
Wskazówka:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
listView.setOnItemClickListener { _, _, indeks, _ ->
val path = imagePaths[indeks]
val uri = Uri.fromFile(File(path))
imageView.setImageURI(uri)
// Po wczytaniu obrazu schowaj udawanego hinta
imageHint.visibility = View.GONE
}
// Sprawdzenie czy uruchomiono przez "Udostępnij"
wczytaj_z_Udostepnij(intent)
}
Działanie tej funkcji umożliwi wczytanie obraz na przykład z przeglądanie strony www. W uruchomionym emulatorze zamknij tworzona aplikację. Uruchom przeglądarkę i wczytaj dowolną stronę. Kliknij prawym przyciskiem myszy na dowolnej grafice i z menu prawego przycisku myszki wybierz jak poniżej.

W kolejnym oknie dialogowym wybierz tworzoną aplikację

System uruchomi tworzoną aplikację z wczytanym obrazem

Wczytanie obrazu z uruchomienia zewnętrznej aplikacji- wyszukiwarki Google Chrome
Kolejna metoda wczytania obrazu będzie polegać na uruchomieniu wyszukiwarki Google Chrome z poziomu aplikacji, wyszukaniu obrazu i wybraniu metody Udostępnij.
Do pliku MainActivity.kt dodajemy funkcję która uruchomi przeglądarkę Google Chrome
Wskazówka:
//uruchom przegladarke google chrom
private fun otworzGoogleChrom() {
val uri = Uri.parse("https://images.google.com/")
val chromeIntent = Intent(Intent.ACTION_VIEW, uri).apply {
setPackage("com.android.chrome")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(chromeIntent)
}
Funkcję wywołamy w metodzie onCreate() poprzez obsługe zdarzenia klikniecia w kontrolce Button
Wskazówka:
// Kliknięcie w przycisk wybierz z przegladarki
btZPrzegladarki.setOnClickListener {
otworzGoogleChrom()
}
Skompiluj program. Uruchom przeglądarkę z poziomu aplikacji. W pisz w pasek wyszukiwania : obrazy png. Przejdź na zakładkę Grafika.

Wskaż plik obrazu i wybierz Udostępnij

W oknie dialogowym wybierz tworzoną aplikację

Obraz zostanie wczytany do naszej aplikacji

Wczytanie grafiki z dostępnych zasobów urządzenia mobilnego
To rozwiązanie jest analogiczne do pierwszej metody, różni się szerszym dostępem do zasobów urządzenia mobilnego. Skorzystamy to nowego sposobu uruchamiania zewnętrznej akcji i odbierania jej wyniku w Androidzie, który zastępuje stare startActivityForResult() i onActivityResult().
Utwórz poniższą funkcję
Wskazówka:
//laduj obraz z zasobów urządzenia mobilnego
private val LadujObraz = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
if (uri != null) {
// bezpośrednio wczytujemy do ImageView
imageView.setImageURI(uri)
} else {
Toast.makeText(this, "Nie wybrano pliku", Toast.LENGTH_SHORT).show()
}
}
Co oznacza = registerForActivityResult(...)
registerForActivityResult rejestruje w Activity (lub Fragmencie) specjalny obiekt, który potrafi:- Uruchomić zewnętrzną akcję (np. systemowy selektor plików, aparat, wybór kontaktu),
- Odebrać wynik tej akcji w przekazanym callbacku ({ uri -> ... } w tym przypadku).
W tym kodzie kontraktem jest:
ActivityResultContracts.GetContent()co oznacza: ?Otwórz systemowy selektor plików i pozwól użytkownikowi wybrać pojedynczy plik określonego typu MIME?.
- Drugi argument ({ uri -> ... }) to lambda, która wykona się po zakończeniu wyboru:
- uri ? adres wybranego pliku (np. content://...),
- null ? jeśli użytkownik anulował wybór.
Dlaczego wywołuje się to tak:
- LadujObraz to obiekt utworzony przez registerForActivityResult.
- Metoda .launch("image/*") startuje akcję przypisaną do kontraktu:
- "image/*" ? filtr MIME otworzy selektor tylko dla plików graficznych.
- Po zakończeniu wyboru (lub anulowaniu) Android automatycznie wywoła nasz callback { uri -> ... }.
W skrócie:
- registerForActivityResult(...) tworzy ?launcher? do wykonania akcji.
- .launch("image/*") uruchamia akcję (tu: wybór obrazu).
- Callback { uri -> ... } odbiera wynik i przetwarza go w aplikacji.
Funkcje wywołaj w zdarzeniu kliknięcia w kontrolkę Button
Wskazówka:
// Kliknięcie w przycisk wczytanie obrazu z dostepnych zasobów
btDostepneZasoby.setOnClickListener {
LadujObraz.launch("image/*")
}
Skompiluj aplikację i sprawdź efekt. Po wybraniu klawisza tej metody w emulatorze otwiera się okno podglądu zasobów graficznych

Teraz można wskazać obraz i załadować do aplikacji
Pełny kod rozwiązania
W kodzie źródłowym znajdziesz nieomawiany hint podpowiedzi widoczny na środku kontrolki ImageView. Ten fragment został dodany od tak sobie aby było ładniej. Nie wpływa na funkcjonowanie ładowania grafiki jest jedynie dodatkowym środkiem wizualnym. Hint jest utworzony w dodatkowym widoku (layout) zapisanym w pliku XML
Plik res/ layout/ img_tekst_hint.xml
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="320dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/imageHint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33FFFFFF"
android:gravity="center"
android:text="@string/podglad_obrazu"
android:textColor="#888888"
android:textSize="18sp" />
</FrameLayout>
Plik res/ layout/ activity_main.xml
Wskazówka:
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="320dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:background="#C5EB98"
android:contentDescription="Obraz" />
<TextView
android:id="@+id/imageHint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Tutaj pojawi się obraz"
android:textSize="18sp"
android:textColor="#888888"
android:background="@android:color/transparent" />
</FrameLayout>
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/kolekcja_grafiki"
android:textSize="20sp" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="174dp"
android:background="#DCE0C2"
android:dividerHeight="1dp"
android:padding="20dp" />
<Button
android:id="@+id/btZPrzegladarki"
android:layout_width="249dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:text="@string/wybierz_z_przegladarki" />
<Button
android:id="@+id/btDostepneZasoby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:text="@string/wybierz_z_zasobów" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Plik manifestu AndroidManifest.xml
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--Uprawnienie do odczytu plików-->
<!-- Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android 12 i niżej -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--uprawnienie do internetu-->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WczytywanieGrafiki"
android:requestLegacyExternalStorage="true"
>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" /> <!-- udostępnianie linków -->
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" /> <!-- udostępnianie obrazów zapisanych lokalnie -->
</intent-filter>
</activity>
</application>
</manifest>
Plik MainActivity.kt
Wskazówka:
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.widget.*
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.os.Environment
import android.view.View
import com.bumptech.glide.Glide
import java.io.File
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
//flaga kodu żądania- dowolna stala unikatowa wartość
private val PROSBA_DOSTEPU_DCIM = 1001
private lateinit var imageView: ImageView
private lateinit var listView: ListView
private lateinit var imageHint: TextView
private lateinit var btZPrzegladarki: Button
private lateinit var btDostepneZasoby: Button
private val imagePaths = mutableListOf<String>()
private val imageNames = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
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
}
imageView = findViewById(R.id.imageView)
listView = findViewById(R.id.listView)
imageHint= findViewById(R.id.imageHint)
btZPrzegladarki = findViewById(R.id.btZPrzegladarki)
btDostepneZasoby = findViewById(R.id.btDostepneZasoby)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_MEDIA_IMAGES),
PROSBA_DOSTEPU_DCIM
)
} else {
ladujImageDCIM()
}
} else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
PROSBA_DOSTEPU_DCIM
)
} else {
ladujImageDCIM()
}
}
listView.setOnItemClickListener { _, _, indeks, _ ->
val path = imagePaths[indeks]
val uri = Uri.fromFile(File(path))
imageView.setImageURI(uri)
// Po wczytaniu obrazu schowaj udawanego hinta
imageHint.visibility = View.GONE
}
// Kliknięcie w przycisk wybierz z przegladarki
btZPrzegladarki.setOnClickListener {
otworzGoogleChrom()
}
// Kliknięcie w przycisk wczytanie obrazu z dostepnych zasobów
btDostepneZasoby.setOnClickListener {
LadujObraz.launch("image/*")
}
// Sprawdzenie czy uruchomiono przez "Udostępnij"
wczytaj_z_Udostepnij(intent)
}
//laduj nazwy plików graficznych do listy
private fun ladujImageDCIM() {
// Pobranie katalogu DCIM zgodnie z systemem Android
val dcimDir: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
val cameraFolder = File(dcimDir, "Camera") // DCIM/Camera
if (!cameraFolder.exists() || !cameraFolder.isDirectory) {
Toast.makeText(this, "Folder DCIM/Camera nie istnieje", Toast.LENGTH_SHORT).show()
return
}
val imageFiles = cameraFolder.listFiles { file ->
file.isFile && file.extension.lowercase() in listOf("jpg", "jpeg", "png", "gif")
}
if (imageFiles.isNullOrEmpty()) {
Toast.makeText(this, "Brak zdjęć w folderze DCIM/Camera", Toast.LENGTH_SHORT).show()
return
}
imagePaths.clear()
imageNames.clear()
for (file in imageFiles) {
imagePaths.add(file.absolutePath)
imageNames.add(file.name)
}
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, imageNames)
listView.adapter = adapter
}
//laduj obraz z zasobów urządzenia mobilnego
private val LadujObraz = registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
if (uri != null) {
// bezpośrednio wczytujemy do ImageView
imageView.setImageURI(uri)
imageHint.visibility = View.GONE
} else {
Toast.makeText(this, "Nie wybrano pliku", Toast.LENGTH_SHORT).show()
}
}
//uruchom przegladarke google chrom
private fun otworzGoogleChrom() {
val uri = Uri.parse("https://images.google.com/")
val chromeIntent = Intent(Intent.ACTION_VIEW, uri).apply {
setPackage("com.android.chrome")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(chromeIntent)
}
//wczytaj obraz poprzez mechanizm udostępnij np. z przeglądania galerii obrazów
private fun wczytaj_z_Udostepnij(intent: Intent?) {
if (intent == null) return
when (intent.action) {
Intent.ACTION_SEND -> {
when (intent.type) {
"text/plain" -> { // Link do obrazu
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
if (!sharedText.isNullOrEmpty()) {
Glide.with(this)
.load(sharedText)
.placeholder(R.drawable.image_256dp)
.error(R.drawable.error_256dp)
.into(imageView)
imageHint.visibility = View.GONE
}
}
else -> if (intent.type?.startsWith("image/") == true) { // Plik obrazu lokalny
val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
if (imageUri != null) {
Glide.with(this)
.load(imageUri)
.placeholder(R.drawable.image_256dp)
.error(R.drawable.error_256dp)
.into(imageView)
imageHint.visibility = View.GONE
}
}
}
}
}
}
// Obsługa wyniku prośby o uprawnienia
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PROSBA_DOSTEPU_DCIM && grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
ladujImageDCIM()
} else {
Toast.makeText(this, "Brak zgody na odczyt plików", Toast.LENGTH_LONG).show()
}
}
}
Plik bulid.gradle.kts
Wskazówka:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("kotlin-kapt") // <-- dodaj tę linię
}
android {
namespace = "pl.afizyka.wczytywaniegrafiki"
compileSdk = 36
defaultConfig {
applicationId = "pl.afizyka.wczytywaniegrafiki"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
kapt("com.github.bumptech.glide:compiler:4.16.0")
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}