4 | * Copyright (c) 2014 Kenji Sasaki 5 | * Released under the MIT license. 6 | * https://github.com/npedotnet/TGAReader/blob/master/LICENSE 7 | *
8 | * English document 9 | * https://github.com/npedotnet/TGAReader/blob/master/README.md 10 | *
11 | * Japanese document
12 | * http://3dtech.jp/wiki/index.php?TGAReader
13 | */
14 |
15 | package su.xash.engine.util;
16 |
17 | import java.io.IOException;
18 |
19 | public final class TGAReader {
20 |
21 | public static final Order ARGB = new Order(16, 8, 0, 24);
22 | public static final Order ABGR = new Order(0, 8, 16, 24);
23 |
24 | public static int getWidth(byte[] buffer) {
25 | return (buffer[12] & 0xFF) | (buffer[13] & 0xFF) << 8;
26 | }
27 |
28 | public static int getHeight(byte[] buffer) {
29 | return (buffer[14] & 0xFF) | (buffer[15] & 0xFF) << 8;
30 | }
31 |
32 | public static int[] read(byte[] buffer, Order order) throws IOException {
33 |
34 | // header
35 | // int idFieldLength = buffer[0] & 0xFF;
36 | // int colormapType = buffer[1] & 0xFF;
37 | int type = buffer[2] & 0xFF;
38 | int colormapOrigin = (buffer[3] & 0xFF) | (buffer[4] & 0xFF) << 8;
39 | int colormapLength = (buffer[5] & 0xFF) | (buffer[6] & 0xFF) << 8;
40 | int colormapDepth = buffer[7] & 0xFF;
41 | // int originX = (buffer[8] & 0xFF) | (buffer[9] & 0xFF) << 8; // unsupported
42 | // int originY = (buffer[10] & 0xFF) | (buffer[11] & 0xFF) << 8; // unsupported
43 | int width = getWidth(buffer);
44 | int height = getHeight(buffer);
45 | int depth = buffer[16] & 0xFF;
46 | int descriptor = buffer[17] & 0xFF;
47 |
48 | int[] pixels;
49 |
50 | // data
51 | switch (type) {
52 | case COLORMAP: {
53 | int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
54 | pixels = createPixelsFromColormap(width, height, colormapDepth, buffer, imageDataOffset, buffer, colormapOrigin, descriptor, order);
55 | }
56 | break;
57 | case RGB:
58 | pixels = createPixelsFromRGB(width, height, depth, buffer, 18, descriptor, order);
59 | break;
60 | case GRAYSCALE:
61 | pixels = createPixelsFromGrayscale(width, height, depth, buffer, 18, descriptor, order);
62 | break;
63 | case COLORMAP_RLE: {
64 | int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
65 | byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, imageDataOffset);
66 | pixels = createPixelsFromColormap(width, height, colormapDepth, decodeBuffer, 0, buffer, colormapOrigin, descriptor, order);
67 | }
68 | break;
69 | case RGB_RLE: {
70 | byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
71 | pixels = createPixelsFromRGB(width, height, depth, decodeBuffer, 0, descriptor, order);
72 | }
73 | break;
74 | case GRAYSCALE_RLE: {
75 | byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
76 | pixels = createPixelsFromGrayscale(width, height, depth, decodeBuffer, 0, descriptor, order);
77 | }
78 | break;
79 | default:
80 | throw new IOException("Unsupported image type: " + type);
81 | }
82 |
83 | return pixels;
84 |
85 | }
86 |
87 | private static final int COLORMAP = 1;
88 | private static final int RGB = 2;
89 | private static final int GRAYSCALE = 3;
90 | private static final int COLORMAP_RLE = 9;
91 | private static final int RGB_RLE = 10;
92 | private static final int GRAYSCALE_RLE = 11;
93 |
94 | private static final int RIGHT_ORIGIN = 0x10;
95 | private static final int UPPER_ORIGIN = 0x20;
96 |
97 | private static byte[] decodeRLE(int width, int height, int depth, byte[] buffer, int offset) {
98 | int elementCount = depth / 8;
99 | byte[] elements = new byte[elementCount];
100 | int decodeBufferLength = elementCount * width * height;
101 | byte[] decodeBuffer = new byte[decodeBufferLength];
102 | int decoded = 0;
103 | while (decoded < decodeBufferLength) {
104 | int packet = buffer[offset++] & 0xFF;
105 | if ((packet & 0x80) != 0) { // RLE
106 | for (int i = 0; i < elementCount; i++) {
107 | elements[i] = buffer[offset++];
108 | }
109 | int count = (packet & 0x7F) + 1;
110 | for (int i = 0; i < count; i++) {
111 | for (int j = 0; j < elementCount; j++) {
112 | decodeBuffer[decoded++] = elements[j];
113 | }
114 | }
115 | } else { // RAW
116 | int count = (packet + 1) * elementCount;
117 | for (int i = 0; i < count; i++) {
118 | decodeBuffer[decoded++] = buffer[offset++];
119 | }
120 | }
121 | }
122 | return decodeBuffer;
123 | }
124 |
125 | private static int[] createPixelsFromColormap(int width, int height, int depth, byte[] bytes, int offset, byte[] palette, int colormapOrigin, int descriptor, Order order) throws IOException {
126 | int[] pixels;
127 | int rs = order.redShift;
128 | int gs = order.greenShift;
129 | int bs = order.blueShift;
130 | int as = order.alphaShift;
131 | switch (depth) {
132 | case 24:
133 | pixels = new int[width * height];
134 | if ((descriptor & RIGHT_ORIGIN) != 0) {
135 | if ((descriptor & UPPER_ORIGIN) != 0) {
136 | // UpperRight
137 | for (int i = 0; i < height; i++) {
138 | for (int j = 0; j < width; j++) {
139 | int colormapIndex = bytes[offset + width * i + j] &
140 | 0xFF - colormapOrigin;
141 | int color = 0xFFFFFFFF;
142 | if (colormapIndex >= 0) {
143 | int index = 3 * colormapIndex + 18;
144 | int b = palette[index] & 0xFF;
145 | int g = palette[index + 1] & 0xFF;
146 | int r = palette[index + 2] & 0xFF;
147 | int a = 0xFF;
148 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
149 | }
150 | pixels[width * i + (width - j - 1)] = color;
151 | }
152 | }
153 | } else {
154 | // LowerRight
155 | for (int i = 0; i < height; i++) {
156 | for (int j = 0; j < width; j++) {
157 | int colormapIndex = bytes[offset + width * i + j] &
158 | 0xFF - colormapOrigin;
159 | int color = 0xFFFFFFFF;
160 | if (colormapIndex >= 0) {
161 | int index = 3 * colormapIndex + 18;
162 | int b = palette[index] & 0xFF;
163 | int g = palette[index + 1] & 0xFF;
164 | int r = palette[index + 2] & 0xFF;
165 | int a = 0xFF;
166 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
167 | }
168 | pixels[width * (height - i - 1) + (width - j - 1)] = color;
169 | }
170 | }
171 | }
172 | } else {
173 | if ((descriptor & UPPER_ORIGIN) != 0) {
174 | // UpperLeft
175 | for (int i = 0; i < height; i++) {
176 | for (int j = 0; j < width; j++) {
177 | int colormapIndex = bytes[offset + width * i + j] &
178 | 0xFF - colormapOrigin;
179 | int color = 0xFFFFFFFF;
180 | if (colormapIndex >= 0) {
181 | int index = 3 * colormapIndex + 18;
182 | int b = palette[index] & 0xFF;
183 | int g = palette[index + 1] & 0xFF;
184 | int r = palette[index + 2] & 0xFF;
185 | int a = 0xFF;
186 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
187 | }
188 | pixels[width * i + j] = color;
189 | }
190 | }
191 | } else {
192 | // LowerLeft
193 | for (int i = 0; i < height; i++) {
194 | for (int j = 0; j < width; j++) {
195 | int colormapIndex = bytes[offset + width * i + j] &
196 | 0xFF - colormapOrigin;
197 | int color = 0xFFFFFFFF;
198 | if (colormapIndex >= 0) {
199 | int index = 3 * colormapIndex + 18;
200 | int b = palette[index] & 0xFF;
201 | int g = palette[index + 1] & 0xFF;
202 | int r = palette[index + 2] & 0xFF;
203 | int a = 0xFF;
204 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
205 | }
206 | pixels[width * (height - i - 1) + j] = color;
207 | }
208 | }
209 | }
210 | }
211 | break;
212 | case 32:
213 | pixels = new int[width * height];
214 | if ((descriptor & RIGHT_ORIGIN) != 0) {
215 | if ((descriptor & UPPER_ORIGIN) != 0) {
216 | // UpperRight
217 | for (int i = 0; i < height; i++) {
218 | for (int j = 0; j < width; j++) {
219 | int colormapIndex = bytes[offset + width * i + j] &
220 | 0xFF - colormapOrigin;
221 | int color = 0xFFFFFFFF;
222 | if (colormapIndex >= 0) {
223 | int index = 4 * colormapIndex + 18;
224 | int b = palette[index] & 0xFF;
225 | int g = palette[index + 1] & 0xFF;
226 | int r = palette[index + 2] & 0xFF;
227 | int a = palette[index + 3] & 0xFF;
228 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
229 | }
230 | pixels[width * i + (width - j - 1)] = color;
231 | }
232 | }
233 | } else {
234 | // LowerRight
235 | for (int i = 0; i < height; i++) {
236 | for (int j = 0; j < width; j++) {
237 | int colormapIndex = bytes[offset + width * i + j] &
238 | 0xFF - colormapOrigin;
239 | int color = 0xFFFFFFFF;
240 | if (colormapIndex >= 0) {
241 | int index = 4 * colormapIndex + 18;
242 | int b = palette[index] & 0xFF;
243 | int g = palette[index + 1] & 0xFF;
244 | int r = palette[index + 2] & 0xFF;
245 | int a = palette[index + 3] & 0xFF;
246 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
247 | }
248 | pixels[width * (height - i - 1) + (width - j - 1)] = color;
249 | }
250 | }
251 | }
252 | } else {
253 | if ((descriptor & UPPER_ORIGIN) != 0) {
254 | // UpperLeft
255 | for (int i = 0; i < height; i++) {
256 | for (int j = 0; j < width; j++) {
257 | int colormapIndex = bytes[offset + width * i + j] &
258 | 0xFF - colormapOrigin;
259 | int color = 0xFFFFFFFF;
260 | if (colormapIndex >= 0) {
261 | int index = 4 * colormapIndex + 18;
262 | int b = palette[index] & 0xFF;
263 | int g = palette[index + 1] & 0xFF;
264 | int r = palette[index + 2] & 0xFF;
265 | int a = palette[index + 3] & 0xFF;
266 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
267 | }
268 | pixels[width * i + j] = color;
269 | }
270 | }
271 | } else {
272 | // LowerLeft
273 | for (int i = 0; i < height; i++) {
274 | for (int j = 0; j < width; j++) {
275 | int colormapIndex = bytes[offset + width * i + j] &
276 | 0xFF - colormapOrigin;
277 | int color = 0xFFFFFFFF;
278 | if (colormapIndex >= 0) {
279 | int index = 4 * colormapIndex + 18;
280 | int b = palette[index] & 0xFF;
281 | int g = palette[index + 1] & 0xFF;
282 | int r = palette[index + 2] & 0xFF;
283 | int a = palette[index + 3] & 0xFF;
284 | color = (r << rs) | (g << gs) | (b << bs) | (a << as);
285 | }
286 | pixels[width * (height - i - 1) + j] = color;
287 | }
288 | }
289 | }
290 | }
291 | break;
292 | default:
293 | throw new IOException("Unsupported depth:" + depth);
294 | }
295 | return pixels;
296 | }
297 |
298 | private static int[] createPixelsFromRGB(int width, int height, int depth, byte[] bytes, int offset, int descriptor, Order order) throws IOException {
299 | int[] pixels;
300 | int rs = order.redShift;
301 | int gs = order.greenShift;
302 | int bs = order.blueShift;
303 | int as = order.alphaShift;
304 | switch (depth) {
305 | case 24:
306 | pixels = new int[width * height];
307 | if ((descriptor & RIGHT_ORIGIN) != 0) {
308 | if ((descriptor & UPPER_ORIGIN) != 0) {
309 | // UpperRight
310 | for (int i = 0; i < height; i++) {
311 | for (int j = 0; j < width; j++) {
312 | int index = offset + 3 * width * i + 3 * j;
313 | int b = bytes[index] & 0xFF;
314 | int g = bytes[index + 1] & 0xFF;
315 | int r = bytes[index + 2] & 0xFF;
316 | int a = 0xFF;
317 | pixels[width * i + (width - j - 1)] = (r << rs) |
318 | (g << gs) |
319 | (b << bs) |
320 | (a << as);
321 | }
322 | }
323 | } else {
324 | // LowerRight
325 | for (int i = 0; i < height; i++) {
326 | for (int j = 0; j < width; j++) {
327 | int index = offset + 3 * width * i + 3 * j;
328 | int b = bytes[index] & 0xFF;
329 | int g = bytes[index + 1] & 0xFF;
330 | int r = bytes[index + 2] & 0xFF;
331 | int a = 0xFF;
332 | pixels[width * (height - i - 1) + (width - j - 1)] = (r << rs) |
333 | (g << gs) |
334 | (b << bs) |
335 | (a << as);
336 | }
337 | }
338 | }
339 | } else {
340 | if ((descriptor & UPPER_ORIGIN) != 0) {
341 | // UpperLeft
342 | for (int i = 0; i < height; i++) {
343 | for (int j = 0; j < width; j++) {
344 | int index = offset + 3 * width * i + 3 * j;
345 | int b = bytes[index] & 0xFF;
346 | int g = bytes[index + 1] & 0xFF;
347 | int r = bytes[index + 2] & 0xFF;
348 | int a = 0xFF;
349 | pixels[width * i + j] = (r << rs) |
350 | (g << gs) |
351 | (b << bs) |
352 | (a << as);
353 | }
354 | }
355 | } else {
356 | // LowerLeft
357 | for (int i = 0; i < height; i++) {
358 | for (int j = 0; j < width; j++) {
359 | int index = offset + 3 * width * i + 3 * j;
360 | int b = bytes[index] & 0xFF;
361 | int g = bytes[index + 1] & 0xFF;
362 | int r = bytes[index + 2] & 0xFF;
363 | int a = 0xFF;
364 | pixels[width * (height - i - 1) + j] = (r << rs) |
365 | (g << gs) |
366 | (b << bs) |
367 | (a << as);
368 | }
369 | }
370 | }
371 | }
372 | break;
373 | case 32:
374 | pixels = new int[width * height];
375 | if ((descriptor & RIGHT_ORIGIN) != 0) {
376 | if ((descriptor & UPPER_ORIGIN) != 0) {
377 | // UpperRight
378 | for (int i = 0; i < height; i++) {
379 | for (int j = 0; j < width; j++) {
380 | int index = offset + 4 * width * i + 4 * j;
381 | int b = bytes[index] & 0xFF;
382 | int g = bytes[index + 1] & 0xFF;
383 | int r = bytes[index + 2] & 0xFF;
384 | int a = bytes[index + 3] & 0xFF;
385 | pixels[width * i + (width - j - 1)] = (r << rs) |
386 | (g << gs) |
387 | (b << bs) |
388 | (a << as);
389 | }
390 | }
391 | } else {
392 | // LowerRight
393 | for (int i = 0; i < height; i++) {
394 | for (int j = 0; j < width; j++) {
395 | int index = offset + 4 * width * i + 4 * j;
396 | int b = bytes[index] & 0xFF;
397 | int g = bytes[index + 1] & 0xFF;
398 | int r = bytes[index + 2] & 0xFF;
399 | int a = bytes[index + 3] & 0xFF;
400 | pixels[width * (height - i - 1) + (width - j - 1)] = (r << rs) |
401 | (g << gs) |
402 | (b << bs) |
403 | (a << as);
404 | }
405 | }
406 | }
407 | } else {
408 | if ((descriptor & UPPER_ORIGIN) != 0) {
409 | // UpperLeft
410 | for (int i = 0; i < height; i++) {
411 | for (int j = 0; j < width; j++) {
412 | int index = offset + 4 * width * i + 4 * j;
413 | int b = bytes[index] & 0xFF;
414 | int g = bytes[index + 1] & 0xFF;
415 | int r = bytes[index + 2] & 0xFF;
416 | int a = bytes[index + 3] & 0xFF;
417 | pixels[width * i + j] = (r << rs) |
418 | (g << gs) |
419 | (b << bs) |
420 | (a << as);
421 | }
422 | }
423 | } else {
424 | // LowerLeft
425 | for (int i = 0; i < height; i++) {
426 | for (int j = 0; j < width; j++) {
427 | int index = offset + 4 * width * i + 4 * j;
428 | int b = bytes[index] & 0xFF;
429 | int g = bytes[index + 1] & 0xFF;
430 | int r = bytes[index + 2] & 0xFF;
431 | int a = bytes[index + 3] & 0xFF;
432 | pixels[width * (height - i - 1) + j] = (r << rs) |
433 | (g << gs) |
434 | (b << bs) |
435 | (a << as);
436 | }
437 | }
438 | }
439 | }
440 | break;
441 | default:
442 | throw new IOException("Unsupported depth:" + depth);
443 | }
444 | return pixels;
445 | }
446 |
447 | private static int[] createPixelsFromGrayscale(int width, int height, int depth, byte[] bytes, int offset, int descriptor, Order order) throws IOException {
448 | int[] pixels;
449 | int rs = order.redShift;
450 | int gs = order.greenShift;
451 | int bs = order.blueShift;
452 | int as = order.alphaShift;
453 | switch (depth) {
454 | case 8:
455 | pixels = new int[width * height];
456 | if ((descriptor & RIGHT_ORIGIN) != 0) {
457 | if ((descriptor & UPPER_ORIGIN) != 0) {
458 | // UpperRight
459 | for (int i = 0; i < height; i++) {
460 | for (int j = 0; j < width; j++) {
461 | int e = bytes[offset + width * i + j] & 0xFF;
462 | int a = 0xFF;
463 | pixels[width * i + (width - j - 1)] = (e << rs) |
464 | (e << gs) |
465 | (e << bs) |
466 | (a << as);
467 | }
468 | }
469 | } else {
470 | // LowerRight
471 | for (int i = 0; i < height; i++) {
472 | for (int j = 0; j < width; j++) {
473 | int e = bytes[offset + width * i + j] & 0xFF;
474 | int a = 0xFF;
475 | pixels[width * (height - i - 1) + (width - j - 1)] = (e << rs) |
476 | (e << gs) |
477 | (e << bs) |
478 | (a << as);
479 | }
480 | }
481 | }
482 | } else {
483 | if ((descriptor & UPPER_ORIGIN) != 0) {
484 | // UpperLeft
485 | for (int i = 0; i < height; i++) {
486 | for (int j = 0; j < width; j++) {
487 | int e = bytes[offset + width * i + j] & 0xFF;
488 | int a = 0xFF;
489 | pixels[width * i + j] = (e << rs) |
490 | (e << gs) |
491 | (e << bs) |
492 | (a << as);
493 | }
494 | }
495 | } else {
496 | // LowerLeft
497 | for (int i = 0; i < height; i++) {
498 | for (int j = 0; j < width; j++) {
499 | int e = bytes[offset + width * i + j] & 0xFF;
500 | int a = 0xFF;
501 | pixels[width * (height - i - 1) + j] = (e << rs) |
502 | (e << gs) |
503 | (e << bs) |
504 | (a << as);
505 | }
506 | }
507 | }
508 | }
509 | break;
510 | case 16:
511 | pixels = new int[width * height];
512 | if ((descriptor & RIGHT_ORIGIN) != 0) {
513 | if ((descriptor & UPPER_ORIGIN) != 0) {
514 | // UpperRight
515 | for (int i = 0; i < height; i++) {
516 | for (int j = 0; j < width; j++) {
517 | int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
518 | int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
519 | pixels[width * i + (width - j - 1)] = (e << rs) |
520 | (e << gs) |
521 | (e << bs) |
522 | (a << as);
523 | }
524 | }
525 | } else {
526 | // LowerRight
527 | for (int i = 0; i < height; i++) {
528 | for (int j = 0; j < width; j++) {
529 | int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
530 | int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
531 | pixels[width * (height - i - 1) + (width - j - 1)] = (e << rs) |
532 | (e << gs) |
533 | (e << bs) |
534 | (a << as);
535 | }
536 | }
537 | }
538 | } else {
539 | if ((descriptor & UPPER_ORIGIN) != 0) {
540 | // UpperLeft
541 | for (int i = 0; i < height; i++) {
542 | for (int j = 0; j < width; j++) {
543 | int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
544 | int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
545 | pixels[width * i + j] = (e << rs) |
546 | (e << gs) |
547 | (e << bs) |
548 | (a << as);
549 | }
550 | }
551 | } else {
552 | // LowerLeft
553 | for (int i = 0; i < height; i++) {
554 | for (int j = 0; j < width; j++) {
555 | int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
556 | int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
557 | pixels[width * (height - i - 1) + j] = (e << rs) |
558 | (e << gs) |
559 | (e << bs) |
560 | (a << as);
561 | }
562 | }
563 | }
564 | }
565 | break;
566 | default:
567 | throw new IOException("Unsupported depth:" + depth);
568 | }
569 | return pixels;
570 | }
571 |
572 | private TGAReader() {
573 | }
574 |
575 | public static final class Order {
576 | Order(int redShift, int greenShift, int blueShift, int alphaShift) {
577 | this.redShift = redShift;
578 | this.greenShift = greenShift;
579 | this.blueShift = blueShift;
580 | this.alphaShift = alphaShift;
581 | }
582 |
583 | public int redShift;
584 | public int greenShift;
585 | public int blueShift;
586 | public int alphaShift;
587 | }
588 |
589 | }
--------------------------------------------------------------------------------
/app/src/main/java/su/xash/engine/workers/FileCopyWorker.kt:
--------------------------------------------------------------------------------
1 | package su.xash.engine.workers
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import android.provider.DocumentsContract
6 | import android.util.Log
7 | import androidx.documentfile.provider.DocumentFile
8 | import androidx.work.CoroutineWorker
9 | import androidx.work.Worker
10 | import androidx.work.WorkerParameters
11 | import androidx.work.workDataOf
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.withContext
14 | import su.xash.engine.model.Game
15 | import java.io.FileInputStream
16 |
17 | const val KEY_FILE_URI = "KEY_FILE_URI"
18 |
19 | class FileCopyWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
20 | companion object {
21 | const val Input = "Input"
22 | }
23 |
24 | override suspend fun doWork(): Result {
25 | withContext(Dispatchers.IO) {
26 | val fileUri = inputData.getString(KEY_FILE_URI)
27 | setProgress(workDataOf(Input to fileUri))
28 |
29 | val target = DocumentFile.fromFile(applicationContext.getExternalFilesDir(null)!!)
30 | val source = DocumentFile.fromTreeUri(applicationContext, Uri.parse(fileUri))
31 |
32 | source?.copyDirTo(applicationContext, target) ?: return@withContext Result.failure()
33 | }
34 | return Result.success()
35 | }
36 | }
37 |
38 | fun DocumentFile.copyFileTo(ctx: Context, file: DocumentFile) {
39 | val outFile = file.createFile("application", name!!)!!
40 |
41 | ctx.contentResolver.openOutputStream(outFile.uri).use { os ->
42 | ctx.contentResolver.openInputStream(uri).use {
43 | it?.copyTo(os!!)
44 | }
45 | }
46 | }
47 |
48 | fun DocumentFile.copyDirTo(ctx: Context, dir: DocumentFile) {
49 | val outDir = dir.createDirectory(name!!)!!
50 |
51 | listFiles().forEach {
52 | if (it.isDirectory) {
53 | it.copyDirTo(ctx, outDir)
54 | } else {
55 | it.copyFileTo(ctx, outDir)
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_add_24.xml:
--------------------------------------------------------------------------------
1 |