Как отследить процент прогресса загрузки каждого загружаемого файла в библиотеке exoplayer? - PullRequest
0 голосов
/ 16 марта 2019

Я работаю над приложением медиаплеера.Я использую библиотеку ExoPlayer.У меня есть плейлист видео, и я хочу загружать видео одновременно.Я сделал это с помощью доступного демонстрационного приложения библиотеки exoplayer на GitHub.Я хочу показать прогресс каждой загрузки в пользовательском интерфейсе.Для этой работы я получаю помощь от метода DownloadNotificationUtil.buildProgressNotification.

  @Override
protected Notification getForegroundNotification(TaskState[] taskStates) {

    float totalPercentage = 0;
    int downloadTaskCount = 0;
    boolean allDownloadPercentagesUnknown = true;
    boolean haveDownloadedBytes = false;
    boolean haveDownloadTasks = false;
    boolean haveRemoveTasks = false;
    Log.e(TAG,"size task state: "+taskStates.length);
    for (TaskState taskState : taskStates) {
        Log.e(TAG,"taskId= "+taskState.taskId);
        if (taskState.state != TaskState.STATE_STARTED
                && taskState.state != TaskState.STATE_COMPLETED) {
            continue;
        }
        if (taskState.action.isRemoveAction) {
            haveRemoveTasks = true;
            continue;
        }
        haveDownloadTasks = true;
        if (taskState.downloadPercentage != C.PERCENTAGE_UNSET) {
            allDownloadPercentagesUnknown = false;
            totalPercentage += taskState.downloadPercentage;
        }
        haveDownloadedBytes |= taskState.downloadedBytes > 0;
        downloadTaskCount++;
    }

    int progress = 0;
    boolean indeterminate = true;
    if (haveDownloadTasks) {
        progress = (int) (totalPercentage / downloadTaskCount);
        indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes;

        Log.e(TAG,"notifi "+progress);
    }
    return DownloadNotificationUtil.buildProgressNotification(
            this,
            R.drawable.exo_icon_play,
            DOWNLOAD_CHANNEL_ID,
            null,
            null,
            taskStates);
}

Теперь я могу отслеживать процесс загрузки.Но у меня все еще есть проблема.Я не могу понять, какой элемент загружается для обновления индикатора выполнения в пользовательском интерфейсе.Есть ли идентичный идентификатор каждой загрузки, чтобы распознать его?Например, Android Download Manager имеет идентификатор загрузки для каждого загружаемого файла.Но я не знаю, как справиться с этой проблемой.Это MediaDownloadService:

public class MediaDownloadService extends DownloadService {

public static String TAG="MediaDownloadService";

private static final int FOREGROUND_NOTIFICATION_ID = 1;
public MediaDownloadService() {
    super(
            DOWNLOAD_NOTIFICATION_ID,
            DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
            DOWNLOAD_CHANNEL_ID,
            R.string.download_channel_name);
}



@Override
protected DownloadManager getDownloadManager() {
    return ((MyApplication) getApplication()).getDownloadManager();
}

@Nullable
@Override
protected Scheduler getScheduler() {
    return null;
}

@Override
protected Notification getForegroundNotification(TaskState[] taskStates) {

    float totalPercentage = 0;
    int downloadTaskCount = 0;
    boolean allDownloadPercentagesUnknown = true;
    boolean haveDownloadedBytes = false;
    boolean haveDownloadTasks = false;
    boolean haveRemoveTasks = false;
    for (TaskState taskState : taskStates) {
        if (taskState.state != TaskState.STATE_STARTED
                && taskState.state != TaskState.STATE_COMPLETED) {
            continue;
        }
        if (taskState.action.isRemoveAction) {
            haveRemoveTasks = true;
            continue;
        }
        haveDownloadTasks = true;
        if (taskState.downloadPercentage != C.PERCENTAGE_UNSET) {
            allDownloadPercentagesUnknown = false;
            totalPercentage += taskState.downloadPercentage;
        }
        haveDownloadedBytes |= taskState.downloadedBytes > 0;
        downloadTaskCount++;
    }

    int progress = 0;
    boolean indeterminate = true;
    if (haveDownloadTasks) {
        progress = (int) (totalPercentage / downloadTaskCount);
        indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes;

        Log.e(TAG,"notifi "+progress);
        sendIntent(progress);
    }
    return DownloadNotificationUtil.buildProgressNotification(
            this,
            R.drawable.exo_icon_play,
            DOWNLOAD_CHANNEL_ID,
            null,
            null,
            taskStates);
}

private void sendIntent(int progress){
    Intent intent = new Intent(ConstantUtil.MESSAGE_PROGRESS);
    intent.putExtra("progress",progress);
    LocalBroadcastManager.getInstance(MediaDownloadService.this).sendBroadcast(intent);
}

@Override
protected void onTaskStateChanged(TaskState taskState) {
    if (taskState.action.isRemoveAction) {
        return;
    }

    Notification notification = null;
    if (taskState.state == TaskState.STATE_COMPLETED) {
        Log.e(TAG,"STATE_COMPLETED");
        notification =
                DownloadNotificationUtil.buildDownloadCompletedNotification(
                        /* context= */ this,
                        R.drawable.exo_controls_play,
                        DOWNLOAD_CHANNEL_ID,
                        /* contentIntent= */ null,
                        Util.fromUtf8Bytes(taskState.action.data));
    } else if (taskState.state == TaskState.STATE_FAILED) {
        Log.e(TAG,"STATE_FAILED");
        notification =
                DownloadNotificationUtil.buildDownloadFailedNotification(
                        /* context= */ this,
                        R.drawable.exo_controls_play,
                        DOWNLOAD_CHANNEL_ID,
                        /* contentIntent= */ null,
                        Util.fromUtf8Bytes(taskState.action.data));
    }
    int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + taskState.taskId;
    NotificationUtil.setNotification(this, notificationId, notification);
}

}

Это DownloadTracker класс:

    public class DownloadTracker implements DownloadManager.Listener {

    /** Listens for changes in the tracked downloads. */
    public interface Listener {

        /** Called when the tracked downloads changed. */
        void onDownloadsChanged();
    }

    private static final String TAG = "DownloadTracker";

    private final Context context;
    private final DataSource.Factory dataSourceFactory;
    private final TrackNameProvider trackNameProvider;
    private final CopyOnWriteArraySet<Listener> listeners;
    private  Listener onDownloadsChanged;
    private final HashMap<Uri, DownloadAction> trackedDownloadStates;
    private final ActionFile actionFile;
    private final Handler actionFileWriteHandler;

    public DownloadTracker(
            Context context,
            DataSource.Factory dataSourceFactory,
            File actionFile,
            DownloadAction.Deserializer... deserializers) {
        this.context = context.getApplicationContext();
        this.dataSourceFactory = dataSourceFactory;
        this.actionFile = new ActionFile(actionFile);
        trackNameProvider = new DefaultTrackNameProvider(context.getResources());
        listeners = new CopyOnWriteArraySet<>();
        trackedDownloadStates = new HashMap<>();
        HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker");
        actionFileWriteThread.start();
        actionFileWriteHandler = new Handler(actionFileWriteThread.getLooper());
        loadTrackedActions(
                deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers());
    }



    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    public boolean isDownloaded(Uri uri) {
        return trackedDownloadStates.containsKey(uri);
    }

    @SuppressWarnings("unchecked")
    public List<StreamKey> getOfflineStreamKeys(Uri uri) {
        if (!trackedDownloadStates.containsKey(uri)) {
            return Collections.emptyList();
        }
        return trackedDownloadStates.get(uri).getKeys();
    }

    public int toggleDownload(Activity activity, String name, Uri uri, String extension) {
        if (isDownloaded(uri)) {
            Log.e(TAG,"isDownloaded");
            DownloadAction removeAction =
                    getDownloadHelper(uri, extension).getRemoveAction(Util.getUtf8Bytes(name));
            startServiceWithAction(removeAction);
            return -1;
        } else {
            StartDownloadDialogHelper helper =
                    new StartDownloadDialogHelper(activity, getDownloadHelper(uri, extension), name);
            helper.prepare();
            return helper.getTaskId();
        }
    }

    @Override
    public void onInitialized(DownloadManager downloadManager) {
        // Do nothing.
    }

    @Override
    public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) {

        DownloadAction action = taskState.action;
        Uri uri = action.uri;
        if ((action.isRemoveAction && taskState.state == TaskState.STATE_COMPLETED)
                || (!action.isRemoveAction && taskState.state == TaskState.STATE_FAILED)) {
            // A download has been removed, or has failed. Stop tracking it.
            if (trackedDownloadStates.remove(uri) != null) {


                handleTrackedDownloadStatesChanged();
            }
        }   

    }

    @Override
    public void onIdle(DownloadManager downloadManager) {
        // Do nothing.
    }

    // Internal methods

    private void loadTrackedActions(DownloadAction.Deserializer[] deserializers) {
        try {
            DownloadAction[] allActions = actionFile.load(deserializers);
            for (DownloadAction action : allActions) {
                trackedDownloadStates.put(action.uri, action);
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to load tracked actions", e);
        }
    }

    private void handleTrackedDownloadStatesChanged() {
        for (Listener listener : listeners) {
            listener.onDownloadsChanged();
        }
        final DownloadAction[] actions = trackedDownloadStates.values().toArray(new DownloadAction[0]);
        Log.e(TAG,"actions: "+actions.toString());
        actionFileWriteHandler.post(
                () -> {
                    try {
                        actionFile.store(actions);
                    } catch (IOException e) {
                        Log.e(TAG, "Failed to store tracked actions", e);
                    }
                });
    }

    private void startDownload(DownloadAction action) {
        if (trackedDownloadStates.containsKey(action.uri)) {
            // This content is already being downloaded. Do nothing.
            Log.e(TAG,"download already exsit");
            return;
        }
        trackedDownloadStates.put(action.uri, action);
        handleTrackedDownloadStatesChanged();
        startServiceWithAction(action);
    }

    private void startServiceWithAction(DownloadAction action) {

        DownloadService.startWithAction(context, MediaDownloadService.class, action, false);
    }

    private DownloadHelper getDownloadHelper(Uri uri, String extension) {
        int type = Util.inferContentType(uri, extension);
        switch (type) {
            case C.TYPE_DASH:
                return new DashDownloadHelper(uri, dataSourceFactory);
            case C.TYPE_SS:
                return new SsDownloadHelper(uri, dataSourceFactory);
            case C.TYPE_HLS:
                return new HlsDownloadHelper(uri, dataSourceFactory);
            case C.TYPE_OTHER:
                return new ProgressiveDownloadHelper(uri);
            default:
                throw new IllegalStateException("Unsupported type: " + type);
        }
    }

    private final class StartDownloadDialogHelper
            implements DownloadHelper.Callback, DialogInterface.OnClickListener {

        private final DownloadHelper downloadHelper;
        private final String name;

        private final AlertDialog.Builder builder;
        private final View dialogView;
        private final List<TrackKey> trackKeys;
        private final ArrayAdapter<String> trackTitles;
        private final ListView representationList;
        private int taskId;

        public StartDownloadDialogHelper(
                Activity activity, DownloadHelper downloadHelper, String name) {
            this.downloadHelper = downloadHelper;
            this.name = name;
            builder =
                    new AlertDialog.Builder(activity)
                            .setTitle(R.string.exo_download_description)
                            .setPositiveButton(android.R.string.ok, this)
                            .setNegativeButton(android.R.string.cancel, null);

            // Inflate with the builder's context to ensure the correct style is used.
            LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
            dialogView = dialogInflater.inflate(R.layout.start_download_dialog, null);

            trackKeys = new ArrayList<>();
            trackTitles =
                    new ArrayAdapter<>(
                            builder.getContext(), android.R.layout.simple_list_item_multiple_choice);
            representationList = dialogView.findViewById(R.id.representation_list);
            representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
            representationList.setAdapter(trackTitles);
        }

        public void prepare() {
            downloadHelper.prepare(this);

        }

        @Override
        public void onPrepared(DownloadHelper helper) {

            for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
                TrackGroupArray trackGroups = downloadHelper.getTrackGroups(i);
                for (int j = 0; j < trackGroups.length; j++) {
                    TrackGroup trackGroup = trackGroups.get(j);
                    for (int k = 0; k < trackGroup.length; k++) {
                        trackKeys.add(new TrackKey(i, j, k));
                        trackTitles.add(trackNameProvider.getTrackName(trackGroup.getFormat(k)));
                    }
                }
            }
            if (!trackKeys.isEmpty()) {
                builder.setView(dialogView);
            }
            builder.create().show();
        }

        @Override
        public void onPrepareError(DownloadHelper helper, IOException e) {
            Toast.makeText(
                    context.getApplicationContext(), R.string.download_start_error, Toast.LENGTH_LONG)
                    .show();
            Log.e(TAG, "Failed to start download", e);
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            ArrayList<TrackKey> selectedTrackKeys = new ArrayList<>();
            for (int i = 0; i < representationList.getChildCount(); i++) {
                if (representationList.isItemChecked(i)) {
                    selectedTrackKeys.add(trackKeys.get(i));
                }
            }
            if (!selectedTrackKeys.isEmpty() || trackKeys.isEmpty()) {
                // We have selected keys, or we're dealing with single stream content.
                DownloadAction downloadAction =
                        downloadHelper.getDownloadAction(Util.getUtf8Bytes(name), selectedTrackKeys);
                taskId=MyApplication.getInstance().getDownloadManager().handleAction(downloadAction);

                startDownload(downloadAction);
            }
        }

    }
}

В моем фрагменте / задании:

 /* this method will be called when user click on download button of each item */
    @Override
            public void onDownloadClick(LectureList lecture) {
                Log.e(TAG,"onClickDownload");
downloadTracker.toggleDownload(this,lecture.getTitle_lecture(),
                        Uri.parse(lecture.getUrlPath()),lecture.getExtension());
            }

А вот мой приемник:

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG,"onRecive download");

        if(intent.getAction().equals(MESSAGE_PROGRESS)){
            int progress=intent.getLongExtra("progress",0);
        }
    }
};

1 Ответ

0 голосов
/ 07 мая 2019

В методе getForegroundNotification () вы получите список TaskState объектов, в котором есть члены downloadPercentage и загружаемый Uri taskState.action.uri , который уникален для каждой задачи загрузки.Сохраните эти переменные на карте и передайте карту.

override fun getForegroundNotification(taskStates: Array<TaskState>): Notification {

    var totalPercentage = 0f
    var downloadTaskCount = 0

    var progressMap : HashMap<Uri, Int> = HashMap()

    for (taskState in taskStates) {
        if (taskState.state != TaskState.STATE_STARTED && taskState.state != TaskState.STATE_COMPLETED) {
            continue
        }
        if (taskState.action.isRemoveAction) {

            continue
        }

        if (taskState.downloadPercentage != C.PERCENTAGE_UNSET.toFloat()) {

            totalPercentage += taskState.downloadPercentage
            progressMap.put(taskState.action.uri, taskState.downloadPercentage.toInt())
        }
        downloadTaskCount++
    }

    var progress = 0
    progress = (totalPercentage / downloadTaskCount).toInt()
    broadcastIndividualProgress(progressMap)
    return buildProgressNotification(progress)
}
...