1818import static androidx .media .MediaBrowserServiceCompat .BrowserRoot .EXTRA_SUGGESTED ;
1919import static java .util .Arrays .asList ;
2020
21+ import android .Manifest ;
2122import android .app .Activity ;
2223import android .app .PendingIntent ;
2324import android .content .Context ;
6162import androidx .appcompat .app .ActionBar ;
6263import androidx .appcompat .app .AppCompatActivity ;
6364import androidx .appcompat .widget .Toolbar ;
65+ import androidx .core .app .ActivityCompat ;
6466import androidx .core .content .ContextCompat ;
6567import androidx .core .content .res .ResourcesCompat ;
6668import androidx .core .graphics .drawable .DrawableCompat ;
7375
7476import com .google .android .material .tabs .TabLayout ;
7577
78+ import java .io .FileNotFoundException ;
79+ import java .io .OutputStream ;
7680import java .util .ArrayList ;
7781import java .util .Collections ;
7882import java .util .Comparator ;
@@ -111,12 +115,16 @@ public class MediaAppControllerActivity extends AppCompatActivity {
111115 // Key name for Intent extras.
112116 private static final String APP_DETAILS_EXTRA =
113117 "com.example.android.mediacontroller.APP_DETAILS_EXTRA" ;
118+ private static final String DEFAULT_BROWSE_TREE_FILE_NAME = "_BrowseTreeContent.txt" ;
114119
115120 // Index values for spinner.
116121 private static final int SEARCH_INDEX = 0 ;
117122 private static final int MEDIA_ID_INDEX = 1 ;
118123 private static final int URI_INDEX = 2 ;
119124
125+ // Used for user storage permission request
126+ private static final int CREATE_DOCUMENT_REQUEST_FOR_SNAPSHOT = 1 ;
127+
120128 private MediaAppDetails mMediaAppDetails ;
121129 private MediaControllerCompat mController ;
122130 private MediaBrowserCompat mBrowser ;
@@ -143,6 +151,8 @@ public class MediaAppControllerActivity extends AppCompatActivity {
143151
144152 private ViewGroup mRatingViewGroup ;
145153
154+ private MediaBrowseTreeSnapshot mMediaBrowseTreeSnapshot ;
155+
146156 private final SparseArray <ImageButton > mActionButtonMap = new SparseArray <>();
147157
148158 /**
@@ -248,20 +258,20 @@ public Object instantiateItem(@NonNull ViewGroup container, int position) {
248258 browseTreeList .setHasFixedSize (true );
249259 browseTreeList .setAdapter (mBrowseMediaItemsAdapter );
250260 mBrowseMediaItemsAdapter .init (findViewById (R .id .media_browse_tree_top ),
251- findViewById (R .id .media_browse_tree_up ));
261+ findViewById (R .id .media_browse_tree_up ), findViewById ( R . id . media_browse_tree_save ) );
252262
253263 final RecyclerView browseTreeListExtraSuggested = findViewById (R .id .media_items_list_extra_suggested );
254264 browseTreeListExtraSuggested .setLayoutManager (new LinearLayoutManager (this ));
255265 browseTreeListExtraSuggested .setHasFixedSize (true );
256266 browseTreeListExtraSuggested .setAdapter (mBrowseMediaItemsExtraSuggestedAdapter );
257267 mBrowseMediaItemsExtraSuggestedAdapter .init (findViewById (R .id .media_browse_tree_top_extra_suggested ),
258- findViewById (R .id .media_browse_tree_up_extra_suggested ));
268+ findViewById (R .id .media_browse_tree_up_extra_suggested ), findViewById ( R . id . media_browse_tree_save ) );
259269
260270 final RecyclerView searchItemsList = findViewById (R .id .search_items_list );
261271 searchItemsList .setLayoutManager (new LinearLayoutManager (this ));
262272 searchItemsList .setHasFixedSize (true );
263273 searchItemsList .setAdapter (mSearchMediaItemsAdapter );
264- mSearchMediaItemsAdapter .init (null , null );
274+ mSearchMediaItemsAdapter .init (null , null , null );
265275
266276 findViewById (R .id .search_button ).setOnClickListener (v -> {
267277 CharSequence queryText = ((TextView ) findViewById (R .id .search_query )).getText ();
@@ -271,6 +281,28 @@ public Object instantiateItem(@NonNull ViewGroup container, int position) {
271281 });
272282 }
273283
284+ @ Override
285+ protected void onActivityResult (int requestCode , int resultCode , @ Nullable Intent data ) {
286+ super .onActivityResult (requestCode , resultCode , data );
287+ if (requestCode == CREATE_DOCUMENT_REQUEST_FOR_SNAPSHOT ) {
288+ if (resultCode == RESULT_OK && mMediaBrowseTreeSnapshot != null ) {
289+ Uri uri = data .getData ();
290+ OutputStream outputStream = null ;
291+ try {
292+ outputStream = getContentResolver ().openOutputStream (uri );
293+ } catch (FileNotFoundException e ) {
294+ e .printStackTrace ();
295+ }
296+ mMediaBrowseTreeSnapshot .takeBrowserSnapshot (outputStream );
297+ Toast .makeText (this , "Output file location: " + uri .getPath (), Toast .LENGTH_SHORT ).show ();
298+ } else {
299+ Toast .makeText (this , "File could not be saved." , Toast .LENGTH_SHORT ).show ();
300+ }
301+
302+
303+ }
304+ }
305+
274306 @ Override
275307 protected void onDestroy () {
276308 if (mController != null ) {
@@ -1102,7 +1134,7 @@ private class BrowseMediaItemsAdapter extends
11021134 RecyclerView .Adapter <BrowseMediaItemsAdapter .ViewHolder > {
11031135
11041136 private List <MediaBrowserCompat .MediaItem > mItems ;
1105- private Stack <String > mNodes = new Stack <>();
1137+ private final Stack <String > mNodes = new Stack <>();
11061138
11071139 MediaBrowserCompat .SubscriptionCallback callback =
11081140 new MediaBrowserCompat .SubscriptionCallback () {
@@ -1206,7 +1238,7 @@ void updateItems(List<MediaBrowserCompat.MediaItem> items) {
12061238 * Assigns click handlers to the buttons if provided for moving to the top of the tree or
12071239 * for moving up one level in the tree.
12081240 */
1209- void init (View topButtonView , View upButtonView ) {
1241+ void init (View topButtonView , View upButtonView , View saveButtonView ) {
12101242 if (topButtonView != null ) {
12111243 topButtonView .setOnClickListener (v -> {
12121244 if (mNodes .size () > 1 ) {
@@ -1228,6 +1260,38 @@ void init(View topButtonView, View upButtonView) {
12281260 }
12291261 });
12301262 }
1263+ if (saveButtonView != null ) {
1264+ saveButtonView .setOnClickListener (v -> {
1265+ takeMediaBrowseTreeSnapshot ();
1266+ });
1267+ }
1268+
1269+ }
1270+
1271+ private void takeMediaBrowseTreeSnapshot (){
1272+ if (mBrowser != null ) {
1273+ if (mMediaBrowseTreeSnapshot == null ) {
1274+ mMediaBrowseTreeSnapshot = new MediaBrowseTreeSnapshot (
1275+ MediaAppControllerActivity .this , mBrowser );
1276+ }
1277+ Intent saveTextFileIntent = new Intent (Intent .ACTION_CREATE_DOCUMENT );
1278+ saveTextFileIntent .addCategory (Intent .CATEGORY_OPENABLE );
1279+ saveTextFileIntent .setType ("text/plain" );
1280+ saveTextFileIntent .putExtra (
1281+ Intent .EXTRA_TITLE , DEFAULT_BROWSE_TREE_FILE_NAME );
1282+ MediaAppControllerActivity .this .startActivityForResult (saveTextFileIntent ,
1283+ CREATE_DOCUMENT_REQUEST_FOR_SNAPSHOT );
1284+
1285+ }else {
1286+ Log .e (TAG , "Media browser is null" );
1287+ runOnUiThread (new Runnable () {
1288+ @ Override
1289+ public void run () {
1290+ Toast .makeText (getApplicationContext (),"No media browser to snapshot" ,
1291+ Toast .LENGTH_SHORT ).show ();
1292+ }
1293+ });
1294+ }
12311295 }
12321296
12331297 protected void subscribe () {
@@ -1284,7 +1348,8 @@ private class SearchMediaItemsAdapter extends BrowseMediaItemsAdapter {
12841348 @ Override
12851349 protected void subscribe () {
12861350 if (treeDepth () == 1 ) {
1287- mBrowser .search (getCurrentNode (), null , new MediaBrowserCompat .SearchCallback () {
1351+ mBrowser .search (getCurrentNode (), null ,
1352+ new MediaBrowserCompat .SearchCallback () {
12881353 @ Override
12891354 public void onSearchResult (@ NonNull String query , Bundle extras ,
12901355 @ NonNull List <MediaBrowserCompat .MediaItem > items ) {
0 commit comments