30
30
import android .graphics .drawable .Drawable ;
31
31
import android .media .AudioManager ;
32
32
import android .net .Uri ;
33
+ import android .os .Build ;
33
34
import android .os .Bundle ;
35
+ import android .os .Handler ;
36
+ import android .os .HandlerThread ;
37
+ import android .os .Looper ;
34
38
import android .os .RemoteException ;
35
39
import android .support .v4 .media .MediaBrowserCompat ;
36
40
import android .support .v4 .media .MediaBrowserCompat .MediaItem ;
60
64
import androidx .annotation .IdRes ;
61
65
import androidx .annotation .NonNull ;
62
66
import androidx .annotation .Nullable ;
67
+ import androidx .annotation .RequiresApi ;
63
68
import androidx .appcompat .app .ActionBar ;
64
69
import androidx .appcompat .app .AppCompatActivity ;
65
70
import androidx .appcompat .widget .Toolbar ;
66
71
import androidx .core .app .ActivityCompat ;
67
72
import androidx .core .content .ContextCompat ;
68
73
import androidx .core .content .res .ResourcesCompat ;
69
74
import androidx .core .graphics .drawable .DrawableCompat ;
75
+ import androidx .core .os .HandlerCompat ;
70
76
import androidx .media .MediaBrowserServiceCompat ;
71
77
import androidx .recyclerview .widget .DiffUtil ;
72
78
import androidx .recyclerview .widget .LinearLayoutManager ;
91
97
import java .util .Map ;
92
98
import java .util .Set ;
93
99
import java .util .Stack ;
100
+ import java .util .concurrent .CompletableFuture ;
101
+ import java .util .concurrent .ExecutionException ;
102
+ import java .util .concurrent .ExecutorService ;
103
+ import java .util .concurrent .Executors ;
104
+ import java .util .concurrent .Semaphore ;
105
+ import java .util .concurrent .locks .Lock ;
94
106
95
107
/**
96
108
* This class connects to a {@link MediaBrowserServiceCompat}
@@ -1247,6 +1259,7 @@ void updateItems(List items) {
1247
1259
* Assigns click handlers to the buttons if provided for moving to the top of the tree or
1248
1260
* for moving up one level in the tree.
1249
1261
*/
1262
+ @ RequiresApi (api = Build .VERSION_CODES .N )
1250
1263
void init (View topButtonView , View upButtonView , View saveButtonView ) {
1251
1264
if (topButtonView != null ) {
1252
1265
topButtonView .setOnClickListener (v -> {
@@ -1271,6 +1284,14 @@ void init(View topButtonView, View upButtonView, View saveButtonView) {
1271
1284
}
1272
1285
if (saveButtonView != null ) {
1273
1286
notifyDataSetChanged ();
1287
+
1288
+ // Go to root of browse tree
1289
+ unsubscribe ();
1290
+ while (mNodes .size () > 1 ) {
1291
+ mNodes .pop ();
1292
+ }
1293
+ subscribe ();
1294
+
1274
1295
saveButtonView .setOnClickListener (
1275
1296
v -> {
1276
1297
@@ -1283,80 +1304,141 @@ void init(View topButtonView, View upButtonView, View saveButtonView) {
1283
1304
return ;
1284
1305
}
1285
1306
File root = android .os .Environment .getExternalStorageDirectory ();
1307
+ String dirs_path = root .getAbsolutePath () + "/Temp/" ;
1308
+ File dirs = new File (dirs_path );
1309
+ File file = new File (dirs .getAbsolutePath (), "_BrowseTreeContent.txt" );
1310
+ if (file .exists ()){
1311
+ file .delete ();
1312
+ }
1286
1313
try {
1287
- String dirs_path = root .getAbsolutePath () + "/Temp/" ;
1288
- File dirs = new File (dirs_path );
1289
- File file = new File (dirs .getAbsolutePath (), "_BrowseTreeContent.txt" );
1290
- FileOutputStream f = new FileOutputStream (file );
1291
- PrintWriter pw = new PrintWriter (f );
1292
- // We print the file path at the beginning of the file so that we can use it
1293
- // to pull the file from platform to local computer.
1294
-
1295
- pw .println (file .toString ());
1296
- if (mItems == null ){
1297
- Toast toast =
1298
- Toast .makeText (
1299
- getApplicationContext (),
1300
- "No media items found, could not save tree." ,
1301
- Toast .LENGTH_LONG );
1302
- toast .setMargin (50 , 50 );
1303
- toast .show ();
1304
- return ;
1305
- }
1314
+ final FileOutputStream f = new FileOutputStream (file );
1306
1315
1307
- for (MediaBrowserCompat .MediaItem item : mItems ) {
1308
- if (item != null ) {
1309
- Log .i (TAG , "Logging media item" );
1310
- MediaDescriptionCompat descriptionCompat = item .getDescription ();
1311
- if (descriptionCompat != null ) {
1312
- String infoStr = "Title:" ;
1313
- infoStr += descriptionCompat .getTitle () != null
1314
- ? descriptionCompat .getTitle ().toString ()
1315
- : "NAN" ;
1316
-
1317
- infoStr += ",Subtitle:" ;
1318
- infoStr += descriptionCompat .getSubtitle () != null
1319
- ? descriptionCompat .getSubtitle ().toString ()
1320
- : "NAN" ;
1321
-
1322
- infoStr += ",MediaId:" ;
1323
- infoStr += descriptionCompat .getMediaId () != null
1324
- ? descriptionCompat .getMediaId ().toString ()
1325
- : "NAN" ;
1326
- infoStr += ",Uri:" ;
1327
- infoStr += descriptionCompat .getMediaUri () != null
1328
- ? descriptionCompat .getMediaUri ().toString ()
1329
- : "NAN" ;
1330
- infoStr += ",Description:" ;
1331
- infoStr += descriptionCompat .getDescription () != null
1332
- ? descriptionCompat .getDescription ().toString ()
1333
- : "NAN" ;
1334
-
1335
- pw .println (infoStr );
1336
- }
1337
- }
1338
- }
1316
+ PrintWriter pw = new PrintWriter (f );
1317
+ // We print the file path at the beginning of the file so that we can use it
1318
+ // to pull the file from platform to local computer.
1339
1319
1340
- pw .flush ();
1341
- pw .close ();
1342
- f .close ();
1320
+ pw .println (file .toString ());
1321
+ if (mItems == null ){
1343
1322
Toast toast =
1344
1323
Toast .makeText (
1345
1324
getApplicationContext (),
1346
- "MediaItems saved to " + file . getAbsolutePath () ,
1325
+ "No media items found, could not save tree." ,
1347
1326
Toast .LENGTH_LONG );
1348
1327
toast .setMargin (50 , 50 );
1349
1328
toast .show ();
1329
+ return ;
1330
+ }
1331
+ pw .println ("Root:" );
1332
+ Semaphore writeCompleted = new Semaphore (1 );
1333
+ ExecutorService executorService = Executors .newFixedThreadPool (4 );
1334
+ executorService .execute (new Runnable () {
1335
+ @ Override
1336
+ public void run () {
1337
+ for (MediaBrowserCompat .MediaItem item : mItems ) {
1338
+ try {
1339
+ writeCompleted .acquire ();
1340
+ } catch (InterruptedException e ) {
1341
+ e .printStackTrace ();
1342
+ }
1343
+ writeMediaItemToFile (item , pw , 1 , executorService );
1344
+ writeCompleted .release ();
1345
+
1346
+ }
1347
+
1348
+ Log .i (TAG , "CLOSING FILE" );
1349
+ pw .flush ();
1350
+ pw .close ();
1351
+ try {
1352
+ f .close ();
1353
+ } catch (IOException e ) {
1354
+ e .printStackTrace ();
1355
+ }
1356
+ runOnUiThread (new Runnable () {
1357
+ public void run () {
1358
+ Toast toast =
1359
+ Toast .makeText (
1360
+ getApplicationContext (),
1361
+ "MediaItems saved to " + file .getAbsolutePath (),
1362
+ Toast .LENGTH_LONG );
1363
+ toast .setMargin (50 , 50 );
1364
+ toast .show ();
1365
+ }
1366
+ });
1367
+ }});
1350
1368
} catch (FileNotFoundException e ) {
1351
1369
e .printStackTrace ();
1352
- } catch (IOException e ) {
1353
- e .printStackTrace ();
1354
1370
}
1355
1371
});
1356
1372
}
1357
1373
1358
1374
}
1359
1375
1376
+ @ RequiresApi (api = Build .VERSION_CODES .N )
1377
+ private void writeMediaItemToFile (MediaItem mediaItem , PrintWriter printWriter , int depth , ExecutorService executorService ){
1378
+ if (mediaItem != null ) {
1379
+ MediaDescriptionCompat descriptionCompat = mediaItem .getDescription ();
1380
+ if (descriptionCompat != null ) {
1381
+
1382
+ // Tab the media item to the respective depth
1383
+ String tabStr = new String (new char [depth ]).replace ("\0 " , "\t " );
1384
+
1385
+ String titleStr = descriptionCompat .getTitle () != null
1386
+ ? descriptionCompat .getTitle ().toString ()
1387
+ : "NAN" ;
1388
+ String subTitleStr = descriptionCompat .getSubtitle () != null
1389
+ ? descriptionCompat .getSubtitle ().toString ()
1390
+ : "NAN" ;
1391
+ String mIDStr = descriptionCompat .getMediaId () != null
1392
+ ? descriptionCompat .getMediaId ().toString ()
1393
+ : "NAN" ;
1394
+ String uriStr = descriptionCompat .getMediaUri () != null
1395
+ ? descriptionCompat .getMediaUri ().toString ()
1396
+ : "NAN" ;
1397
+ String desStr = descriptionCompat .getDescription () != null
1398
+ ? descriptionCompat .getDescription ().toString ()
1399
+ : "NAN" ;
1400
+ String infoStr = String .format ("%sTitle:%s,Subtitle:%s,MediaId:%s,URI:%s,Description:%s" , tabStr , titleStr , subTitleStr , mIDStr , uriStr , desStr );
1401
+ Log .i (TAG , "Logging media item: " + infoStr + " at depth: " + depth );
1402
+ printWriter .println (infoStr );
1403
+ }
1404
+ if (mediaItem .isBrowsable ()) {
1405
+ Log .i (TAG , "Media Item is browseable" );
1406
+ Semaphore loaded = new Semaphore (1 );
1407
+ try {
1408
+ loaded .acquire ();
1409
+ } catch (InterruptedException e ) {
1410
+ e .printStackTrace ();
1411
+ }
1412
+ final List <MediaItem > mChildren = new ArrayList <MediaItem >();
1413
+ executorService .execute (new Runnable () {
1414
+ @ Override
1415
+ public void run () {
1416
+ mBrowser .subscribe (mediaItem .getMediaId (), new MediaBrowserCompat .SubscriptionCallback () {
1417
+ @ Override
1418
+ public void onChildrenLoaded (@ NonNull String parentId , @ NonNull List <MediaItem > children ) {
1419
+ Log .i (TAG , "Children loaded" );
1420
+ mChildren .addAll (children );
1421
+ loaded .release ();
1422
+ super .onChildrenLoaded (parentId , children );
1423
+ }
1424
+ });
1425
+ }
1426
+ });
1427
+
1428
+ try {
1429
+ loaded .acquire ();
1430
+ } catch (InterruptedException e ) {
1431
+ e .printStackTrace ();
1432
+ }
1433
+ Log .i (TAG , "Childeren finished loading" );
1434
+ for (MediaItem mediaItemChild : mChildren ) {
1435
+ writeMediaItemToFile (mediaItemChild , printWriter , depth + 1 , executorService );
1436
+ }
1437
+ }
1438
+ }
1439
+
1440
+ }
1441
+
1360
1442
protected void subscribe () {
1361
1443
if (mNodes .size () > 0 ) {
1362
1444
mBrowser .subscribe (mNodes .peek (), callback );
0 commit comments