Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit 940f959

Browse files
authored
Merge pull request #82 from android/add-photo-from-internet-demo
Add download image from internet demo
2 parents 550468a + 8461270 commit 940f959

7 files changed

Lines changed: 303 additions & 76 deletions

File tree

ScopedStorage/app/build.gradle

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ android {
2525

2626
defaultConfig {
2727
applicationId "com.samples.storage"
28-
minSdkVersion 19
28+
minSdkVersion 21
2929
targetSdkVersion 30
3030
versionCode 1
3131
versionName "1.0"
@@ -57,16 +57,18 @@ dependencies {
5757
implementation 'androidx.core:core-ktx:1.3.2'
5858
implementation 'androidx.appcompat:appcompat:1.2.0'
5959
implementation 'com.google.android.material:material:1.3.0'
60-
implementation 'androidx.activity:activity-ktx:1.3.0-alpha05'
60+
implementation 'androidx.activity:activity-ktx:1.3.0-alpha06'
6161
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
62-
implementation 'androidx.fragment:fragment-ktx:1.3.1'
62+
implementation 'androidx.fragment:fragment-ktx:1.3.2'
6363
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
6464
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
65-
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
66-
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'
67-
implementation 'androidx.recyclerview:recyclerview:1.1.0'
65+
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
66+
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
67+
implementation 'androidx.recyclerview:recyclerview:1.2.0'
6868
implementation 'androidx.documentfile:documentfile:1.0.1'
69-
implementation 'io.coil-kt:coil:1.1.1'
69+
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
70+
implementation 'com.github.bumptech.glide:glide:4.12.0'
71+
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
7072

7173
testImplementation 'junit:junit:4.13.2'
7274
androidTestImplementation 'androidx.test.ext:junit:1.1.2'

ScopedStorage/app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,20 @@
1818
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
1919
package="com.samples.storage">
2020

21+
<!-- Use to download images in the [AddMediaFragment] demo -->
22+
<uses-permission android:name="android.permission.INTERNET" />
23+
24+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
25+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
26+
2127
<application
2228
android:allowBackup="true"
2329
android:icon="@mipmap/ic_launcher"
2430
android:label="@string/app_name"
2531
android:roundIcon="@mipmap/ic_launcher_round"
2632
android:supportsRtl="true"
27-
android:theme="@style/Theme.StorageAPIs">
33+
android:theme="@style/Theme.StorageAPIs"
34+
android:requestLegacyExternalStorage="true">
2835
<activity android:name=".MainActivity">
2936
<intent-filter>
3037
<action android:name="android.intent.action.MAIN" />

ScopedStorage/app/src/main/java/com/samples/storage/mediastore/AddMediaFragment.kt

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,78 @@
1515
*/
1616
package com.samples.storage.mediastore
1717

18+
import android.Manifest.permission.READ_EXTERNAL_STORAGE
19+
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
1820
import android.os.Bundle
1921
import android.util.Log
2022
import android.view.LayoutInflater
2123
import android.view.View
2224
import android.view.ViewGroup
25+
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
2326
import androidx.activity.result.contract.ActivityResultContracts.TakePicture
2427
import androidx.fragment.app.Fragment
2528
import androidx.fragment.app.viewModels
26-
import coil.load
29+
import androidx.lifecycle.lifecycleScope
30+
import com.bumptech.glide.Glide
2731
import com.samples.storage.databinding.FragmentAddMediaBinding
32+
import kotlinx.coroutines.launch
2833

29-
// TODO(yrezgui): Add media from camera (video), from internet and generated one
3034
class AddMediaFragment : Fragment() {
3135
private var _binding: FragmentAddMediaBinding? = null
3236
private val binding get() = _binding!!
3337
private val viewModel: AddMediaViewModel by viewModels()
3438

35-
private val actionTakePicture = registerForActivityResult(TakePicture()) { success ->
36-
if (!success) {
37-
Log.d(tag, "Image taken FAIL: ${viewModel.temporaryMediaUri}")
38-
return@registerForActivityResult
39-
}
40-
41-
Log.d(tag, "Image taken SUCCESS: ${viewModel.temporaryMediaUri}")
42-
viewModel.loadMedia()
43-
}
44-
4539
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
4640
_binding = FragmentAddMediaBinding.inflate(inflater, container, false)
4741

48-
// Once we've added a media, we set its URI to the currentMediaUri property.
49-
// Every time
42+
// Every time currentMediaUri is changed, we update the ImageView
5043
viewModel.currentMediaUri.observe(viewLifecycleOwner) { uri ->
51-
binding.mediaThumbnail.load(uri) {
52-
crossfade(true)
53-
}
44+
Glide.with(this).load(uri).into(binding.mediaThumbnail)
45+
}
46+
47+
binding.requestPermissionButton.setOnClickListener {
48+
actionRequestPermission.launch(arrayOf(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE))
5449
}
5550

5651
binding.takePictureButton.setOnClickListener {
57-
viewModel.createPhotoUri(Source.CAMERA)?.let { uri ->
58-
actionTakePicture.launch(uri)
52+
viewLifecycleOwner.lifecycleScope.launch {
53+
54+
if(viewModel.canWriteInMediaStore) {
55+
viewModel.createPhotoUri(Source.CAMERA)?.let { uri ->
56+
viewModel.saveTemporarilyPhotoUri(uri)
57+
actionTakePicture.launch(uri)
58+
}
59+
} else {
60+
showPermissionSection()
61+
}
62+
}
63+
}
64+
65+
binding.takeVideoButton.setOnClickListener {
66+
viewLifecycleOwner.lifecycleScope.launch {
67+
68+
69+
if(viewModel.canWriteInMediaStore) {
70+
viewModel.createVideoUri(Source.CAMERA)?.let { uri ->
71+
actionTakeVideo.launch(uri)
72+
}
73+
} else {
74+
showPermissionSection()
75+
}
76+
}
77+
}
78+
79+
binding.downloadImageFromInternetButton.setOnClickListener {
80+
81+
if(viewModel.canWriteInMediaStore) {
82+
binding.downloadImageFromInternetButton.isEnabled = false
83+
viewModel.saveRandomImageFromInternet {
84+
// We re-enable the button once the download is done
85+
// Keep in mind the logic is basic as it doesn't handle errors
86+
binding.downloadImageFromInternetButton.isEnabled = true
87+
}
88+
} else {
89+
showPermissionSection()
5990
}
6091
}
6192

@@ -66,4 +97,56 @@ class AddMediaFragment : Fragment() {
6697
super.onDestroyView()
6798
_binding = null
6899
}
100+
101+
override fun onResume() {
102+
super.onResume()
103+
104+
handlePermissionSectionVisibility()
105+
}
106+
107+
private fun handlePermissionSectionVisibility() {
108+
if (viewModel.canWriteInMediaStore) {
109+
hidePermissionSection()
110+
} else {
111+
showPermissionSection()
112+
}
113+
}
114+
115+
private fun hidePermissionSection() {
116+
binding.permissionSection.visibility = View.GONE
117+
binding.actions.visibility = View.VISIBLE
118+
}
119+
120+
private fun showPermissionSection() {
121+
binding.permissionSection.visibility = View.VISIBLE
122+
binding.actions.visibility = View.GONE
123+
}
124+
125+
private val actionRequestPermission = registerForActivityResult(RequestMultiplePermissions()) {
126+
handlePermissionSectionVisibility()
127+
}
128+
129+
private val actionTakePicture = registerForActivityResult(TakePicture()) { success ->
130+
if (!success) {
131+
Log.d(tag, "Image taken FAIL")
132+
return@registerForActivityResult
133+
}
134+
135+
Log.d(tag, "Image taken SUCCESS")
136+
137+
viewModel.temporaryPhotoUri?.let {
138+
viewModel.loadCameraMedia(it)
139+
viewModel.saveTemporarilyPhotoUri(null)
140+
}
141+
}
142+
143+
private val actionTakeVideo = registerForActivityResult(CustomTakeVideo()) { uri ->
144+
if (uri == null) {
145+
Log.d(tag, "Video taken FAIL")
146+
return@registerForActivityResult
147+
}
148+
149+
Log.d(tag, "Video taken SUCCESS")
150+
viewModel.loadCameraMedia(uri)
151+
}
69152
}

0 commit comments

Comments
 (0)