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

Commit 907554a

Browse files
authored
Merge pull request #110 from android/add-document-demo
Add document in the Download folder demo
2 parents 442bde9 + f65eb3f commit 907554a

37 files changed

Lines changed: 783 additions & 378 deletions

ScopedStorage/app/build.gradle

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
plugins {
1818
id 'com.android.application'
1919
id 'kotlin-android'
20+
id 'kotlin-parcelize'
2021
}
2122

2223
android {
2324
compileSdkVersion 30
24-
buildToolsVersion "30.0.2"
25+
buildToolsVersion "30.0.3"
2526

2627
defaultConfig {
2728
applicationId "com.samples.storage"
@@ -54,17 +55,17 @@ android {
5455

5556
dependencies {
5657
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
57-
implementation 'androidx.core:core-ktx:1.3.2'
58-
implementation 'androidx.appcompat:appcompat:1.2.0'
58+
implementation 'androidx.core:core-ktx:1.5.0'
59+
implementation 'androidx.appcompat:appcompat:1.3.0'
5960
implementation 'com.google.android.material:material:1.3.0'
60-
implementation 'androidx.activity:activity-ktx:1.3.0-alpha07'
61+
implementation 'androidx.activity:activity-ktx:1.3.0-beta01'
6162
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
62-
implementation 'androidx.fragment:fragment-ktx:1.3.3'
63+
implementation 'androidx.fragment:fragment-ktx:1.3.4'
6364
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
6465
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
6566
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
6667
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
67-
implementation 'androidx.recyclerview:recyclerview:1.2.0'
68+
implementation 'androidx.recyclerview:recyclerview:1.2.1'
6869
implementation 'androidx.documentfile:documentfile:1.0.1'
6970
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
7071
implementation 'com.github.bumptech.glide:glide:4.12.0'

ScopedStorage/app/src/androidTest/java/com/samples/storage/ExampleInstrumentedTest.kt

Lines changed: 0 additions & 37 deletions
This file was deleted.

ScopedStorage/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
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 -->
21+
<!-- Use to download content in the MediaStore demo -->
2222
<uses-permission android:name="android.permission.INTERNET" />
2323

2424
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

ScopedStorage/app/src/main/java/com/samples/storage/ui/ActionListAdapter.kt renamed to ScopedStorage/app/src/main/java/com/samples/storage/ActionListAdapter.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
/*
2-
* Copyright (C) 2020 The Android Open Source Project
2+
* Copyright 2021 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.samples.storage.ui
16+
package com.samples.storage
1717

1818
import android.view.LayoutInflater
1919
import android.view.View
@@ -23,7 +23,6 @@ import androidx.annotation.IdRes
2323
import androidx.annotation.StringRes
2424
import androidx.navigation.findNavController
2525
import androidx.recyclerview.widget.RecyclerView
26-
import com.samples.storage.R
2726

2827
data class Action(@StringRes val nameRes: Int, @IdRes val actionRes: Int)
2928

ScopedStorage/app/src/main/java/com/samples/storage/MainActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright (C) 2020 The Android Open Source Project
2+
* Copyright 2020 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,

ScopedStorage/app/src/main/java/com/samples/storage/MainFragment.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright (C) 2020 The Android Open Source Project
2+
* Copyright 2020 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -22,8 +22,6 @@ import android.view.ViewGroup
2222
import androidx.fragment.app.Fragment
2323
import androidx.recyclerview.widget.LinearLayoutManager
2424
import com.samples.storage.databinding.FragmentListBinding
25-
import com.samples.storage.ui.Action
26-
import com.samples.storage.ui.ActionListAdapter
2725

2826
private val apiList = arrayOf(
2927
Action(R.string.demo_mediastore, R.id.action_mainFragment_to_mediaStoreFragment),
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.samples.storage.data
17+
18+
/**
19+
* List of remote sample files to be used in the different samples
20+
*/
21+
object SampleFiles {
22+
val images = listOf(
23+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Balcony%20Toss/card.jpg",
24+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Dance%20Search/card.jpg",
25+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Extra%20Spicy/card.jpg",
26+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Get%20Your%20Money's%20Worth/card.jpg"
27+
)
28+
29+
val video = listOf(
30+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Balcony%20Toss.mp4",
31+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Dance%20Search.mp4",
32+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Extra%20Spicy.mp4",
33+
"https://storage.googleapis.com/android-tv/Sample%20videos/Demo%20Slam/Google%20Demo%20Slam_%20Get%20Your%20Money's%20Worth.mp4"
34+
)
35+
36+
val media = images + video
37+
38+
val texts = listOf(
39+
"https://raw.githubusercontent.com/android/storage-samples/main/README.md",
40+
"https://raw.githubusercontent.com/android/security-samples/main/README.md"
41+
)
42+
43+
val documents = listOf(
44+
"https://developer.android.com/images/jetpack/compose/compose-testing-cheatsheet.pdf",
45+
"https://developer.android.com/images/training/dependency-injection/hilt-annotations.pdf",
46+
"https://android.github.io/android-test/downloads/espresso-cheat-sheet-2.1.0.pdf"
47+
)
48+
49+
val archives = listOf(
50+
"https://github.com/android/storage-samples/archive/refs/heads/main.zip",
51+
"https://github.com/android/security-samples/archive/refs/heads/main.zip"
52+
)
53+
54+
val nonMedia = texts + documents + archives
55+
}
Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright (C) 2020 The Android Open Source Project
2+
* Copyright 2020 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* https://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,30 +15,120 @@
1515
*/
1616
package com.samples.storage.mediastore
1717

18+
import android.Manifest
1819
import android.os.Bundle
20+
import android.text.format.DateUtils
21+
import android.text.format.Formatter
1922
import android.view.LayoutInflater
2023
import android.view.View
2124
import android.view.ViewGroup
25+
import androidx.activity.result.contract.ActivityResultContracts
2226
import androidx.fragment.app.Fragment
23-
import com.samples.storage.databinding.FragmentDemoBinding
27+
import androidx.fragment.app.viewModels
28+
import androidx.lifecycle.lifecycleScope
29+
import com.samples.storage.R
30+
import com.samples.storage.databinding.FragmentAddDocumentBinding
31+
import kotlinx.coroutines.launch
2432

25-
// TODO(yrezgui): Finish this demo
2633
class AddDocumentFragment : Fragment() {
27-
private var _binding: FragmentDemoBinding? = null
34+
private var _binding: FragmentAddDocumentBinding? = null
2835
private val binding get() = _binding!!
36+
private val viewModel: AddDocumentViewModel by viewModels()
2937

3038
override fun onCreateView(
3139
inflater: LayoutInflater,
3240
container: ViewGroup?,
3341
savedInstanceState: Bundle?
3442
): View {
35-
_binding = FragmentDemoBinding.inflate(inflater, container, false)
36-
val view = binding.root
37-
return view
43+
_binding = FragmentAddDocumentBinding.inflate(inflater, container, false)
44+
45+
// Every time currentFileEntry is changed, we update the file details
46+
viewModel.currentFileEntry.observe(viewLifecycleOwner) { fileDetails ->
47+
if (fileDetails == null) {
48+
binding.fileDetails.visibility = View.GONE
49+
return@observe
50+
}
51+
52+
binding.filename.text = fileDetails.filename
53+
binding.filePath.text = fileDetails.path
54+
binding.fileSizeAndMimeType.text = getString(
55+
R.string.mediastore_file_size_and_mimetype,
56+
Formatter.formatShortFileSize(context, fileDetails.size),
57+
fileDetails.mimeType
58+
)
59+
binding.fileAddedAt.text = getString(
60+
R.string.mediastore_file_added_at,
61+
DateUtils.formatDateTime(
62+
context,
63+
fileDetails.addedAt,
64+
DateUtils.FORMAT_SHOW_TIME or
65+
DateUtils.FORMAT_SHOW_DATE or
66+
DateUtils.FORMAT_SHOW_YEAR or
67+
DateUtils.FORMAT_SHOW_WEEKDAY or
68+
DateUtils.FORMAT_ABBREV_ALL
69+
)
70+
)
71+
binding.fileDetails.visibility = View.VISIBLE
72+
}
73+
74+
// Every time isDownloading is changed, we toggle the download button
75+
viewModel.isDownloading.observe(viewLifecycleOwner) { isDownloading ->
76+
binding.downloadRandomFileFromInternet.isEnabled = !isDownloading
77+
}
78+
79+
binding.requestPermissionButton.setOnClickListener {
80+
actionRequestPermission.launch(
81+
arrayOf(
82+
Manifest.permission.READ_EXTERNAL_STORAGE,
83+
Manifest.permission.WRITE_EXTERNAL_STORAGE
84+
)
85+
)
86+
}
87+
88+
binding.downloadRandomFileFromInternet.setOnClickListener {
89+
viewLifecycleOwner.lifecycleScope.launch {
90+
91+
if (viewModel.canAddDocument) {
92+
viewModel.addRandomFile()
93+
} else {
94+
showPermissionSection()
95+
}
96+
}
97+
}
98+
99+
return binding.root
38100
}
39101

40102
override fun onDestroyView() {
41103
super.onDestroyView()
42104
_binding = null
43105
}
106+
107+
override fun onResume() {
108+
super.onResume()
109+
handlePermissionSectionVisibility()
110+
}
111+
112+
private val actionRequestPermission =
113+
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
114+
handlePermissionSectionVisibility()
115+
}
116+
117+
private fun handlePermissionSectionVisibility() {
118+
if (viewModel.canAddDocument) {
119+
hidePermissionSection()
120+
} else {
121+
showPermissionSection()
122+
}
123+
}
124+
125+
private fun hidePermissionSection() {
126+
binding.permissionSection.visibility = View.GONE
127+
binding.actions.visibility = View.VISIBLE
128+
}
129+
130+
private fun showPermissionSection() {
131+
binding.permissionSection.visibility = View.VISIBLE
132+
binding.actions.visibility = View.GONE
133+
}
44134
}

0 commit comments

Comments
 (0)