mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	resbase (08/11/2018)
This commit is contained in:
		| @@ -105,13 +105,13 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader { | |||||||
|      * but set the HTTP header field "Accept-Language" to the supplied string. |      * but set the HTTP header field "Accept-Language" to the supplied string. | ||||||
|      * |      * | ||||||
|      * @param siteUrl  the URL of the text file to return the contents of |      * @param siteUrl  the URL of the text file to return the contents of | ||||||
|      * @param localisation the language and country (usually a 2-character code) to set |      * @param localization the language and country (usually a 2-character code) to set | ||||||
|      * @return the contents of the specified text file |      * @return the contents of the specified text file | ||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     public String download(String siteUrl, Localization localisation) throws IOException, ReCaptchaException { |     public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException { | ||||||
|         Map<String, String> requestProperties = new HashMap<>(); |         Map<String, String> requestProperties = new HashMap<>(); | ||||||
|         requestProperties.put("Accept-Language", localisation.getLanguage()); |         requestProperties.put("Accept-Language", localization.getLanguage()); | ||||||
|         return download(siteUrl, requestProperties); |         return download(siteUrl, requestProperties); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,163 +0,0 @@ | |||||||
| package org.schabi.newpipe.download; |  | ||||||
|  |  | ||||||
| import android.app.Activity; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.support.annotation.NonNull; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.support.design.widget.BaseTransientBottomBar; |  | ||||||
| import android.support.design.widget.Snackbar; |  | ||||||
| import android.view.View; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.R; |  | ||||||
|  |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.HashSet; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
|  |  | ||||||
| import io.reactivex.Completable; |  | ||||||
| import io.reactivex.Observable; |  | ||||||
| import io.reactivex.android.schedulers.AndroidSchedulers; |  | ||||||
| import io.reactivex.disposables.Disposable; |  | ||||||
| import io.reactivex.schedulers.Schedulers; |  | ||||||
| import io.reactivex.subjects.PublishSubject; |  | ||||||
| import us.shandian.giga.get.DownloadManager; |  | ||||||
| import us.shandian.giga.get.DownloadMission; |  | ||||||
|  |  | ||||||
| public class DeleteDownloadManager { |  | ||||||
|  |  | ||||||
|     private static final String KEY_STATE = "delete_manager_state"; |  | ||||||
|  |  | ||||||
|     private View mView; |  | ||||||
|     private ArrayList<Long> mPendingMap; |  | ||||||
|     private List<Disposable> mDisposableList; |  | ||||||
|     private DownloadManager mDownloadManager; |  | ||||||
|     private final PublishSubject<DownloadMission> publishSubject = PublishSubject.create(); |  | ||||||
|  |  | ||||||
|     DeleteDownloadManager(Activity activity) { |  | ||||||
|         mPendingMap = new ArrayList<>(); |  | ||||||
|         mDisposableList = new ArrayList<>(); |  | ||||||
|         mView = activity.findViewById(android.R.id.content); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Observable<DownloadMission> getUndoObservable() { |  | ||||||
|         return publishSubject; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public boolean contains(@NonNull DownloadMission mission) { |  | ||||||
|         return mPendingMap.contains(mission.timestamp); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void add(@NonNull DownloadMission mission) { |  | ||||||
|         mPendingMap.add(mission.timestamp); |  | ||||||
|  |  | ||||||
|         if (mPendingMap.size() == 1) { |  | ||||||
|             showUndoDeleteSnackbar(mission); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void setDownloadManager(@NonNull DownloadManager downloadManager) { |  | ||||||
|         mDownloadManager = downloadManager; |  | ||||||
|  |  | ||||||
|         if (mPendingMap.size() < 1) return; |  | ||||||
|  |  | ||||||
|         showUndoDeleteSnackbar(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void restoreState(@Nullable Bundle savedInstanceState) { |  | ||||||
|         if (savedInstanceState == null) return; |  | ||||||
|  |  | ||||||
|         long[] list = savedInstanceState.getLongArray(KEY_STATE); |  | ||||||
|         if (list != null) { |  | ||||||
|             mPendingMap.ensureCapacity(mPendingMap.size() + list.length); |  | ||||||
|             for (long timestamp : list) mPendingMap.add(timestamp); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void saveState(@Nullable Bundle outState) { |  | ||||||
|         if (outState == null) return; |  | ||||||
|  |  | ||||||
|         for (Disposable disposable : mDisposableList) { |  | ||||||
|             disposable.dispose(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         long[] list = new long[mPendingMap.size()]; |  | ||||||
|         for (int i = 0; i < mPendingMap.size(); i++) list[i] = mPendingMap.get(i); |  | ||||||
|  |  | ||||||
|         outState.putLongArray(KEY_STATE, list); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void showUndoDeleteSnackbar() { |  | ||||||
|         if (mPendingMap.size() < 1) return; |  | ||||||
|  |  | ||||||
|         long timestamp = mPendingMap.iterator().next(); |  | ||||||
|  |  | ||||||
|         for (int i = 0; i < mDownloadManager.getCount(); i++) { |  | ||||||
|             DownloadMission mission = mDownloadManager.getMission(i); |  | ||||||
|             if (timestamp == mission.timestamp) { |  | ||||||
|                 showUndoDeleteSnackbar(mission); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void showUndoDeleteSnackbar(@NonNull DownloadMission mission) { |  | ||||||
|         final Snackbar snackbar = Snackbar.make(mView, mission.name, Snackbar.LENGTH_INDEFINITE); |  | ||||||
|         final Disposable disposable = Observable.timer(3, TimeUnit.SECONDS) |  | ||||||
|                 .subscribeOn(AndroidSchedulers.mainThread()) |  | ||||||
|                 .subscribe(l -> snackbar.dismiss()); |  | ||||||
|  |  | ||||||
|         mDisposableList.add(disposable); |  | ||||||
|  |  | ||||||
|         snackbar.setAction(R.string.undo, v -> { |  | ||||||
|             mPendingMap.remove(mission.timestamp); |  | ||||||
|             publishSubject.onNext(mission); |  | ||||||
|             disposable.dispose(); |  | ||||||
|             snackbar.dismiss(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() { |  | ||||||
|             @Override |  | ||||||
|             public void onDismissed(Snackbar transientBottomBar, int event) { |  | ||||||
|                 // TODO: disposable.isDisposed() is always true. fix this |  | ||||||
|                 if (!disposable.isDisposed()) { |  | ||||||
|                     Completable.fromAction(() -> deletePending(mission)) |  | ||||||
|                             .subscribeOn(Schedulers.io()) |  | ||||||
|                             .subscribe(); |  | ||||||
|                 } |  | ||||||
|                 mPendingMap.remove(mission.timestamp); |  | ||||||
|                 snackbar.removeCallback(this); |  | ||||||
|                 mDisposableList.remove(disposable); |  | ||||||
|                 showUndoDeleteSnackbar(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         snackbar.show(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void deletePending() { |  | ||||||
|         if (mPendingMap.size() < 1) return; |  | ||||||
|  |  | ||||||
|         HashSet<Integer> idSet = new HashSet<>(); |  | ||||||
|         for (int i = 0; i < mDownloadManager.getCount(); i++) { |  | ||||||
|             if (contains(mDownloadManager.getMission(i))) { |  | ||||||
|                 idSet.add(i); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (Integer id : idSet) { |  | ||||||
|             mDownloadManager.deleteMission(id); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         mPendingMap.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void deletePending(@NonNull DownloadMission mission) { |  | ||||||
|         for (int i = 0; i < mDownloadManager.getCount(); i++) { |  | ||||||
|             if (mission.timestamp == mDownloadManager.getMission(i).timestamp) { |  | ||||||
|                 mDownloadManager.deleteMission(i); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -24,7 +24,6 @@ public class DownloadActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |     protected void onCreate(Bundle savedInstanceState) { | ||||||
|  |  | ||||||
|         // Service |         // Service | ||||||
|         Intent i = new Intent(); |         Intent i = new Intent(); | ||||||
|         i.setClass(this, DownloadManagerService.class); |         i.setClass(this, DownloadManagerService.class); | ||||||
|   | |||||||
| @@ -55,20 +55,13 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|     private static final String TAG = "DialogFragment"; |     private static final String TAG = "DialogFragment"; | ||||||
|     private static final boolean DEBUG = MainActivity.DEBUG; |     private static final boolean DEBUG = MainActivity.DEBUG; | ||||||
|  |  | ||||||
|     @State |     @State protected StreamInfo currentInfo; | ||||||
|     protected StreamInfo currentInfo; |     @State protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty(); | ||||||
|     @State |     @State protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty(); | ||||||
|     protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty(); |     @State protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty(); | ||||||
|     @State |     @State protected int selectedVideoIndex = 0; | ||||||
|     protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty(); |     @State protected int selectedAudioIndex = 0; | ||||||
|     @State |     @State protected int selectedSubtitleIndex = 0; | ||||||
|     protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty(); |  | ||||||
|     @State |  | ||||||
|     protected int selectedVideoIndex = 0; |  | ||||||
|     @State |  | ||||||
|     protected int selectedAudioIndex = 0; |  | ||||||
|     @State |  | ||||||
|     protected int selectedSubtitleIndex = 0; |  | ||||||
|  |  | ||||||
|     private StreamItemAdapter<AudioStream> audioStreamsAdapter; |     private StreamItemAdapter<AudioStream> audioStreamsAdapter; | ||||||
|     private StreamItemAdapter<VideoStream> videoStreamsAdapter; |     private StreamItemAdapter<VideoStream> videoStreamsAdapter; | ||||||
| @@ -151,8 +144,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|     @Override |     @Override | ||||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { |     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
|         if (DEBUG) |         if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); | ||||||
|             Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); |  | ||||||
|         if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { |         if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) { | ||||||
|             getDialog().dismiss(); |             getDialog().dismiss(); | ||||||
|             return; |             return; | ||||||
| @@ -168,8 +160,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||||
|         if (DEBUG) |         if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]"); | ||||||
|             Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]"); |  | ||||||
|         return inflater.inflate(R.layout.download_dialog, container); |         return inflater.inflate(R.layout.download_dialog, container); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -302,8 +293,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { |     public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { | ||||||
|         if (DEBUG) |         if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]"); | ||||||
|             Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]"); |  | ||||||
|         boolean flag = true; |         boolean flag = true; | ||||||
|  |  | ||||||
|         switch (checkedId) { |         switch (checkedId) { | ||||||
| @@ -328,8 +318,7 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { | ||||||
|         if (DEBUG) |         if (DEBUG) Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]"); | ||||||
|             Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]"); |  | ||||||
|         switch (radioVideoAudioGroup.getCheckedRadioButtonId()) { |         switch (radioVideoAudioGroup.getCheckedRadioButtonId()) { | ||||||
|             case R.id.audio_button: |             case R.id.audio_button: | ||||||
|                 selectedAudioIndex = position; |                 selectedAudioIndex = position; | ||||||
|   | |||||||
| @@ -572,6 +572,9 @@ public class VideoDetailFragment | |||||||
|                                 .show(getFragmentManager(), TAG); |                                 .show(getFragmentManager(), TAG); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
|  |                 case 3: | ||||||
|  |                     shareUrl(item.getName(), item.getUrl()); | ||||||
|  |                     break; | ||||||
|                 default: |                 default: | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -10,10 +10,10 @@ import com.google.android.exoplayer2.source.MediaSource; | |||||||
| import com.google.android.exoplayer2.source.MergingMediaSource; | import com.google.android.exoplayer2.source.MergingMediaSource; | ||||||
|  |  | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
|  | import org.schabi.newpipe.extractor.stream.SubtitlesStream; | ||||||
| import org.schabi.newpipe.extractor.stream.AudioStream; | import org.schabi.newpipe.extractor.stream.AudioStream; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | import org.schabi.newpipe.extractor.stream.VideoStream; | ||||||
| import org.schabi.newpipe.extractor.stream.SubtitlesStream; |  | ||||||
| import org.schabi.newpipe.player.helper.PlayerDataSource; | import org.schabi.newpipe.player.helper.PlayerDataSource; | ||||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | import org.schabi.newpipe.player.helper.PlayerHelper; | ||||||
| import org.schabi.newpipe.util.ListHelper; | import org.schabi.newpipe.util.ListHelper; | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter { | |||||||
|             } else if (((VideoStream) stream).isVideoOnly()) { |             } else if (((VideoStream) stream).isVideoOnly()) { | ||||||
|                 switch (stream.getFormat()) { |                 switch (stream.getFormat()) { | ||||||
|                     case WEBM:// fully supported |                     case WEBM:// fully supported | ||||||
|                     case MPEG_4:// ¿is DASH MPEG-4? |                     case MPEG_4:// ¿is DASH MPEG-4 format? | ||||||
|                         woSoundIconVisibility = View.INVISIBLE; |                         woSoundIconVisibility = View.INVISIBLE; | ||||||
|                         break; |                         break; | ||||||
|                     default: |                     default: | ||||||
| @@ -143,7 +143,7 @@ public class StreamItemAdapter<T extends Stream> extends BaseAdapter { | |||||||
|     public static class StreamSizeWrapper<T extends Stream> implements Serializable { |     public static class StreamSizeWrapper<T extends Stream> implements Serializable { | ||||||
|         private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(Collections.emptyList(), null); |         private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(Collections.emptyList(), null); | ||||||
|         private final List<T> streamsList; |         private final List<T> streamsList; | ||||||
|         private long[] streamSizes; |         private final long[] streamSizes; | ||||||
|         private final String unknownSize; |         private final String unknownSize; | ||||||
|  |  | ||||||
|         public StreamSizeWrapper(List<T> streamsList, Context context) { |         public StreamSizeWrapper(List<T> streamsList, Context context) { | ||||||
|   | |||||||
| @@ -1,40 +0,0 @@ | |||||||
| package us.shandian.giga.get; |  | ||||||
|  |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Provides access to the storage of {@link DownloadMission}s |  | ||||||
|  */ |  | ||||||
| public interface DownloadDataSource { |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Load all missions |  | ||||||
|      * |  | ||||||
|      * @return a list of download missions |  | ||||||
|      */ |  | ||||||
|     List<DownloadMission> loadMissions(); |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Add a download mission to the storage |  | ||||||
|      * |  | ||||||
|      * @param downloadMission the download mission to add |  | ||||||
|      * @return the identifier of the mission |  | ||||||
|      */ |  | ||||||
|     void addMission(DownloadMission downloadMission); |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Update a download mission which exists in the storage |  | ||||||
|      * |  | ||||||
|      * @param downloadMission the download mission to update |  | ||||||
|      * @throws IllegalArgumentException if the mission was not added to storage |  | ||||||
|      */ |  | ||||||
|     void updateMission(DownloadMission downloadMission); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Delete a download mission |  | ||||||
|      * |  | ||||||
|      * @param downloadMission the mission to delete |  | ||||||
|      */ |  | ||||||
|     void deleteMission(DownloadMission downloadMission); |  | ||||||
| } |  | ||||||
| @@ -1,395 +0,0 @@ | |||||||
| package us.shandian.giga.get; |  | ||||||
|  |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.os.Handler; |  | ||||||
| import android.support.annotation.NonNull; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.util.Log; |  | ||||||
|  |  | ||||||
| import org.schabi.newpipe.download.ExtSDDownloadFailedActivity; |  | ||||||
|  |  | ||||||
| import java.io.File; |  | ||||||
| import java.io.FilenameFilter; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.RandomAccessFile; |  | ||||||
| import java.net.HttpURLConnection; |  | ||||||
| import java.net.URL; |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.Comparator; |  | ||||||
| import java.util.List; |  | ||||||
|  |  | ||||||
| import us.shandian.giga.util.Utility; |  | ||||||
|  |  | ||||||
| import static org.schabi.newpipe.BuildConfig.DEBUG; |  | ||||||
|  |  | ||||||
| public class DownloadManagerImpl implements DownloadManager { |  | ||||||
|     private static final String TAG = DownloadManagerImpl.class.getSimpleName(); |  | ||||||
|     private final DownloadDataSource mDownloadDataSource; |  | ||||||
|  |  | ||||||
|     private final ArrayList<DownloadMission> mMissions = new ArrayList<>(); |  | ||||||
|     @NonNull |  | ||||||
|     private final Context context; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Create a new instance |  | ||||||
|      * |  | ||||||
|      * @param searchLocations    the directories to search for unfinished downloads |  | ||||||
|      * @param downloadDataSource the data source for finished downloads |  | ||||||
|      */ |  | ||||||
|     public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) { |  | ||||||
|         mDownloadDataSource = downloadDataSource; |  | ||||||
|         this.context = null; |  | ||||||
|         loadMissions(searchLocations); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource, Context context) { |  | ||||||
|         mDownloadDataSource = downloadDataSource; |  | ||||||
|         this.context = context; |  | ||||||
|         loadMissions(searchLocations); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public int startMission(String url, String location, String name, boolean isAudio, int threads) { |  | ||||||
|         DownloadMission existingMission = getMissionByLocation(location, name); |  | ||||||
|         if (existingMission != null) { |  | ||||||
|             // Already downloaded or downloading |  | ||||||
|             if (existingMission.finished) { |  | ||||||
|                 // Overwrite mission |  | ||||||
|                 deleteMission(mMissions.indexOf(existingMission)); |  | ||||||
|             } else { |  | ||||||
|                 // Rename file (?) |  | ||||||
|                 try { |  | ||||||
|                     name = generateUniqueName(location, name); |  | ||||||
|                 } catch (Exception e) { |  | ||||||
|                     Log.e(TAG, "Unable to generate unique name", e); |  | ||||||
|                     name = System.currentTimeMillis() + name; |  | ||||||
|                     Log.i(TAG, "Using " + name); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         DownloadMission mission = new DownloadMission(name, url, location); |  | ||||||
|         mission.timestamp = System.currentTimeMillis(); |  | ||||||
|         mission.threadCount = threads; |  | ||||||
|         mission.addListener(new MissionListener(mission)); |  | ||||||
|         new Initializer(mission).start(); |  | ||||||
|         return insertMission(mission); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void resumeMission(int i) { |  | ||||||
|         DownloadMission d = getMission(i); |  | ||||||
|         if (!d.running && d.errCode == -1) { |  | ||||||
|             d.start(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void pauseMission(int i) { |  | ||||||
|         DownloadMission d = getMission(i); |  | ||||||
|         if (d.running) { |  | ||||||
|             d.pause(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void deleteMission(int i) { |  | ||||||
|         DownloadMission mission = getMission(i); |  | ||||||
|         if (mission.finished) { |  | ||||||
|             mDownloadDataSource.deleteMission(mission); |  | ||||||
|         } |  | ||||||
|         mission.delete(); |  | ||||||
|         mMissions.remove(i); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void loadMissions(Iterable<String> searchLocations) { |  | ||||||
|         mMissions.clear(); |  | ||||||
|         loadFinishedMissions(); |  | ||||||
|         for (String location : searchLocations) { |  | ||||||
|             loadMissions(location); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Sort a list of mission by its timestamp. Oldest first |  | ||||||
|      * @param missions the missions to sort |  | ||||||
|      */ |  | ||||||
|     static void sortByTimestamp(List<DownloadMission> missions) { |  | ||||||
|         Collections.sort(missions, new Comparator<DownloadMission>() { |  | ||||||
|             @Override |  | ||||||
|             public int compare(DownloadMission o1, DownloadMission o2) { |  | ||||||
|                 return Long.compare(o1.timestamp, o2.timestamp); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Loads finished missions from the data source |  | ||||||
|      */ |  | ||||||
|     private void loadFinishedMissions() { |  | ||||||
|         List<DownloadMission> finishedMissions = mDownloadDataSource.loadMissions(); |  | ||||||
|         if (finishedMissions == null) { |  | ||||||
|             finishedMissions = new ArrayList<>(); |  | ||||||
|         } |  | ||||||
|         // Ensure its sorted |  | ||||||
|         sortByTimestamp(finishedMissions); |  | ||||||
|  |  | ||||||
|         mMissions.ensureCapacity(mMissions.size() + finishedMissions.size()); |  | ||||||
|         for (DownloadMission mission : finishedMissions) { |  | ||||||
|             File downloadedFile = mission.getDownloadedFile(); |  | ||||||
|             if (!downloadedFile.isFile()) { |  | ||||||
|                 if (DEBUG) { |  | ||||||
|                     Log.d(TAG, "downloaded file removed: " + downloadedFile.getAbsolutePath()); |  | ||||||
|                 } |  | ||||||
|                 mDownloadDataSource.deleteMission(mission); |  | ||||||
|             } else { |  | ||||||
|                 mission.length = downloadedFile.length(); |  | ||||||
|                 mission.finished = true; |  | ||||||
|                 mission.running = false; |  | ||||||
|                 mMissions.add(mission); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void loadMissions(String location) { |  | ||||||
|  |  | ||||||
|         File f = new File(location); |  | ||||||
|  |  | ||||||
|         if (f.exists() && f.isDirectory()) { |  | ||||||
|             File[] subs = f.listFiles(); |  | ||||||
|  |  | ||||||
|             if (subs == null) { |  | ||||||
|                 Log.e(TAG, "listFiles() returned null"); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             for (File sub : subs) { |  | ||||||
|                 if (sub.isFile() && sub.getName().endsWith(".giga")) { |  | ||||||
|                     DownloadMission mis = Utility.readFromFile(sub.getAbsolutePath()); |  | ||||||
|                     if (mis != null) { |  | ||||||
|                         if (mis.finished) { |  | ||||||
|                             if (!sub.delete()) { |  | ||||||
|                                 Log.w(TAG, "Unable to delete .giga file: " + sub.getPath()); |  | ||||||
|                             } |  | ||||||
|                             continue; |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         mis.running = false; |  | ||||||
|                         mis.recovered = true; |  | ||||||
|                         insertMission(mis); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public DownloadMission getMission(int i) { |  | ||||||
|         return mMissions.get(i); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public int getCount() { |  | ||||||
|         return mMissions.size(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private int insertMission(DownloadMission mission) { |  | ||||||
|         int i = -1; |  | ||||||
|  |  | ||||||
|         DownloadMission m = null; |  | ||||||
|  |  | ||||||
|         if (mMissions.size() > 0) { |  | ||||||
|             do { |  | ||||||
|                 m = mMissions.get(++i); |  | ||||||
|             } while (m.timestamp > mission.timestamp && i < mMissions.size() - 1); |  | ||||||
|  |  | ||||||
|             //if (i > 0) i--; |  | ||||||
|         } else { |  | ||||||
|             i = 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         mMissions.add(i, mission); |  | ||||||
|  |  | ||||||
|         return i; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Get a mission by its location and name |  | ||||||
|      * |  | ||||||
|      * @param location the location |  | ||||||
|      * @param name     the name |  | ||||||
|      * @return the mission or null if no such mission exists |  | ||||||
|      */ |  | ||||||
|     private |  | ||||||
|     @Nullable |  | ||||||
|     DownloadMission getMissionByLocation(String location, String name) { |  | ||||||
|         for (DownloadMission mission : mMissions) { |  | ||||||
|             if (location.equals(mission.location) && name.equals(mission.name)) { |  | ||||||
|                 return mission; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Splits the filename into name and extension |  | ||||||
|      * <p> |  | ||||||
|      * Dots are ignored if they appear: not at all, at the beginning of the file, |  | ||||||
|      * at the end of the file |  | ||||||
|      * |  | ||||||
|      * @param name the name to split |  | ||||||
|      * @return a string array with a length of 2 containing the name and the extension |  | ||||||
|      */ |  | ||||||
|     private static String[] splitName(String name) { |  | ||||||
|         int dotIndex = name.lastIndexOf('.'); |  | ||||||
|         if (dotIndex <= 0 || (dotIndex == name.length() - 1)) { |  | ||||||
|             return new String[]{name, ""}; |  | ||||||
|         } else { |  | ||||||
|             return new String[]{name.substring(0, dotIndex), name.substring(dotIndex + 1)}; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Generates a unique file name. |  | ||||||
|      * <p> |  | ||||||
|      * e.g. "myname (1).txt" if the name "myname.txt" exists. |  | ||||||
|      * |  | ||||||
|      * @param location the location (to check for existing files) |  | ||||||
|      * @param name     the name of the file |  | ||||||
|      * @return the unique file name |  | ||||||
|      * @throws IllegalArgumentException if the location is not a directory |  | ||||||
|      * @throws SecurityException        if the location is not readable |  | ||||||
|      */ |  | ||||||
|     private static String generateUniqueName(String location, String name) { |  | ||||||
|         if (location == null) throw new NullPointerException("location is null"); |  | ||||||
|         if (name == null) throw new NullPointerException("name is null"); |  | ||||||
|         File destination = new File(location); |  | ||||||
|         if (!destination.isDirectory()) { |  | ||||||
|             throw new IllegalArgumentException("location is not a directory: " + location); |  | ||||||
|         } |  | ||||||
|         final String[] nameParts = splitName(name); |  | ||||||
|         String[] existingName = destination.list(new FilenameFilter() { |  | ||||||
|             @Override |  | ||||||
|             public boolean accept(File dir, String name) { |  | ||||||
|                 return name.startsWith(nameParts[0]); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         Arrays.sort(existingName); |  | ||||||
|         String newName; |  | ||||||
|         int downloadIndex = 0; |  | ||||||
|         do { |  | ||||||
|             newName = nameParts[0] + " (" + downloadIndex + ")." + nameParts[1]; |  | ||||||
|             ++downloadIndex; |  | ||||||
|             if (downloadIndex == 1000) {  // Probably an error on our side |  | ||||||
|                 throw new RuntimeException("Too many existing files"); |  | ||||||
|             } |  | ||||||
|         } while (Arrays.binarySearch(existingName, newName) >= 0); |  | ||||||
|         return newName; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private class Initializer extends Thread { |  | ||||||
|         private final DownloadMission mission; |  | ||||||
|         private final Handler handler; |  | ||||||
|  |  | ||||||
|         public Initializer(DownloadMission mission) { |  | ||||||
|             this.mission = mission; |  | ||||||
|             this.handler = new Handler(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void run() { |  | ||||||
|             try { |  | ||||||
|                 URL url = new URL(mission.url); |  | ||||||
|                 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |  | ||||||
|                 mission.length = conn.getContentLength(); |  | ||||||
|  |  | ||||||
|                 if (mission.length <= 0) { |  | ||||||
|                     mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED; |  | ||||||
|                     //mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 // Open again |  | ||||||
|                 conn = (HttpURLConnection) url.openConnection(); |  | ||||||
|                 conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length); |  | ||||||
|  |  | ||||||
|                 if (conn.getResponseCode() != 206) { |  | ||||||
|                     // Fallback to single thread if no partial content support |  | ||||||
|                     mission.fallback = true; |  | ||||||
|  |  | ||||||
|                     if (DEBUG) { |  | ||||||
|                         Log.d(TAG, "falling back"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (DEBUG) { |  | ||||||
|                     Log.d(TAG, "response = " + conn.getResponseCode()); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 mission.blocks = mission.length / BLOCK_SIZE; |  | ||||||
|  |  | ||||||
|                 if (mission.threadCount > mission.blocks) { |  | ||||||
|                     mission.threadCount = (int) mission.blocks; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (mission.threadCount <= 0) { |  | ||||||
|                     mission.threadCount = 1; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (mission.blocks * BLOCK_SIZE < mission.length) { |  | ||||||
|                     mission.blocks++; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|                 new File(mission.location).mkdirs(); |  | ||||||
|                 new File(mission.location + "/" + mission.name).createNewFile(); |  | ||||||
|                 RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw"); |  | ||||||
|                 af.setLength(mission.length); |  | ||||||
|                 af.close(); |  | ||||||
|  |  | ||||||
|                 mission.start(); |  | ||||||
|             } catch (IOException ie) { |  | ||||||
|                 if(context == null) throw new RuntimeException(ie); |  | ||||||
|  |  | ||||||
|                 if(ie.getMessage().contains("Permission denied")) { |  | ||||||
|                     handler.post(() -> |  | ||||||
|                         context.startActivity(new Intent(context, ExtSDDownloadFailedActivity.class))); |  | ||||||
|                 } else throw new RuntimeException(ie); |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 // TODO Notify |  | ||||||
|                 throw new RuntimeException(e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Waits for mission to finish to add it to the {@link #mDownloadDataSource} |  | ||||||
|      */ |  | ||||||
|     private class MissionListener implements DownloadMission.MissionListener { |  | ||||||
|         private final DownloadMission mMission; |  | ||||||
|  |  | ||||||
|         private MissionListener(DownloadMission mission) { |  | ||||||
|             if (mission == null) throw new NullPointerException("mission is null"); |  | ||||||
|             // Could the mission be passed in onFinish()? |  | ||||||
|             mMission = mission; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void onProgressUpdate(DownloadMission downloadMission, long done, long total) { |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void onFinish(DownloadMission downloadMission) { |  | ||||||
|             mDownloadDataSource.addMission(mMission); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         @Override |  | ||||||
|         public void onError(DownloadMission downloadMission, int errCode) { |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -24,7 +24,7 @@ import us.shandian.giga.util.Utility; | |||||||
| import static org.schabi.newpipe.BuildConfig.DEBUG; | import static org.schabi.newpipe.BuildConfig.DEBUG; | ||||||
|  |  | ||||||
| public class DownloadMission extends Mission { | public class DownloadMission extends Mission { | ||||||
|     private static final long serialVersionUID = 3L;// last bump: 16 october 2018 |     private static final long serialVersionUID = 3L;// last bump: 8 november 2018 | ||||||
|  |  | ||||||
|     static final int BUFFER_SIZE = 64 * 1024; |     static final int BUFFER_SIZE = 64 * 1024; | ||||||
|     final static int BLOCK_SIZE = 512 * 1024; |     final static int BLOCK_SIZE = 512 * 1024; | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import static org.schabi.newpipe.BuildConfig.DEBUG; | |||||||
|  |  | ||||||
| // Single-threaded fallback mode | // Single-threaded fallback mode | ||||||
| public class DownloadRunnableFallback implements Runnable { | public class DownloadRunnableFallback implements Runnable { | ||||||
|     private static final String TAG = "DownloadRunnableFallbac"; |     private static final String TAG = "DownloadRunnableFallback"; | ||||||
|  |  | ||||||
|     private final DownloadMission mMission; |     private final DownloadMission mMission; | ||||||
|     private int retryCount = 0; |     private int retryCount = 0; | ||||||
|   | |||||||
| @@ -73,6 +73,7 @@ public class DownloadManagerService extends Service { | |||||||
|     private StringBuilder downloadDoneList = null; |     private StringBuilder downloadDoneList = null; | ||||||
|     NotificationManager notificationManager = null; |     NotificationManager notificationManager = null; | ||||||
|     private boolean mForeground = false; |     private boolean mForeground = false; | ||||||
|  |      | ||||||
|     private final ArrayList<Handler> mEchoObservers = new ArrayList<>(1); |     private final ArrayList<Handler> mEchoObservers = new ArrayList<>(1); | ||||||
|  |  | ||||||
|     private BroadcastReceiver mNetworkStateListener; |     private BroadcastReceiver mNetworkStateListener; | ||||||
|   | |||||||
| @@ -68,8 +68,8 @@ public class MissionAdapter extends RecyclerView.Adapter<ViewHolder> { | |||||||
|     private Deleter mDeleter; |     private Deleter mDeleter; | ||||||
|     private int mLayout; |     private int mLayout; | ||||||
|     private DownloadManager.MissionIterator mIterator; |     private DownloadManager.MissionIterator mIterator; | ||||||
|     private Handler mHandler; |  | ||||||
|     private ArrayList<ViewHolderItem> mPendingDownloadsItems = new ArrayList<>(); |     private ArrayList<ViewHolderItem> mPendingDownloadsItems = new ArrayList<>(); | ||||||
|  |     private Handler mHandler; | ||||||
|     private MenuItem mClear; |     private MenuItem mClear; | ||||||
|     private View mEmptyMessage; |     private View mEmptyMessage; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ import us.shandian.giga.ui.adapter.MissionAdapter; | |||||||
| public class Deleter { | public class Deleter { | ||||||
|     private static final int TIMEOUT = 5000;// ms |     private static final int TIMEOUT = 5000;// ms | ||||||
|     private static final int DELAY = 350;// ms |     private static final int DELAY = 350;// ms | ||||||
|  |     private static final int DELAY_RESUME = 400;// ms | ||||||
|     private static final String BUNDLE_NAMES = "us.shandian.giga.ui.common.deleter.names"; |     private static final String BUNDLE_NAMES = "us.shandian.giga.ui.common.deleter.names"; | ||||||
|     private static final String BUNDLE_LOCATIONS = "us.shandian.giga.ui.common.deleter.locations"; |     private static final String BUNDLE_LOCATIONS = "us.shandian.giga.ui.common.deleter.locations"; | ||||||
|  |  | ||||||
| @@ -140,7 +141,7 @@ public class Deleter { | |||||||
|  |  | ||||||
|     public void resume() { |     public void resume() { | ||||||
|         if (running) return; |         if (running) return; | ||||||
|         mHandler.postDelayed(rShow, (int) (DELAY * 1.5f));// 150% of the delay |         mHandler.postDelayed(rShow, DELAY_RESUME); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void dispose(Bundle bundle) { |     public void dispose(Bundle bundle) { | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package us.shandian.giga.ui.common;// TODO: ¡git it! | package us.shandian.giga.ui.common; | ||||||
|  |  | ||||||
| import android.graphics.Canvas; | import android.graphics.Canvas; | ||||||
| import android.graphics.ColorFilter; | import android.graphics.ColorFilter; | ||||||
|   | |||||||
| @@ -1,12 +0,0 @@ | |||||||
| package us.shandian.giga.ui.fragment; |  | ||||||
|  |  | ||||||
| import us.shandian.giga.get.DownloadManager; |  | ||||||
| import us.shandian.giga.service.DownloadManagerService; |  | ||||||
|  |  | ||||||
| public class AllMissionsFragment extends MissionsFragment { |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected DownloadManager setupDownloadManager(DownloadManagerService.DMBinder binder) { |  | ||||||
|         return binder.getDownloadManager(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -15,7 +15,6 @@ import android.support.v7.widget.LinearLayoutManager; | |||||||
| import android.support.v7.widget.RecyclerView; | import android.support.v7.widget.RecyclerView; | ||||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||||
| import android.view.Menu; | import android.view.Menu; | ||||||
| import android.view.MenuInflater; |  | ||||||
| import android.view.MenuItem; | import android.view.MenuItem; | ||||||
| import android.view.View; | import android.view.View; | ||||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||||
| @@ -47,7 +46,7 @@ public class MissionsFragment extends Fragment { | |||||||
|     private Bundle mBundle; |     private Bundle mBundle; | ||||||
|     private boolean mForceUpdate; |     private boolean mForceUpdate; | ||||||
|  |  | ||||||
|     private final ServiceConnection mConnection = new ServiceConnection() { |     private ServiceConnection mConnection = new ServiceConnection() { | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public void onServiceConnected(ComponentName name, IBinder binder) { |         public void onServiceConnected(ComponentName name, IBinder binder) { | ||||||
| @@ -111,15 +110,6 @@ public class MissionsFragment extends Fragment { | |||||||
|         return v; |         return v; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |  | ||||||
|         super.onCreateOptionsMenu(menu, inflater); |  | ||||||
|         if (menu != null) { |  | ||||||
|             mSwitch = menu.findItem(R.id.switch_mode); |  | ||||||
|             mClear = menu.findItem(R.id.clear_list); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Added in API level 23. |      * Added in API level 23. | ||||||
|      */ |      */ | ||||||
| @@ -129,7 +119,7 @@ public class MissionsFragment extends Fragment { | |||||||
|  |  | ||||||
|         // Bug: in api< 23 this is never called |         // Bug: in api< 23 this is never called | ||||||
|         // so mActivity=null |         // so mActivity=null | ||||||
|         // so app crashes with null-pointer exception |         // so app crashes with nullpointer exception | ||||||
|         mActivity = activity; |         mActivity = activity; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -140,9 +130,11 @@ public class MissionsFragment extends Fragment { | |||||||
|     @Override |     @Override | ||||||
|     public void onAttach(Activity activity) { |     public void onAttach(Activity activity) { | ||||||
|         super.onAttach(activity); |         super.onAttach(activity); | ||||||
|  |          | ||||||
|         mActivity = activity; |         mActivity = activity; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onDestroy() { |     public void onDestroy() { | ||||||
|         super.onDestroy(); |         super.onDestroy(); | ||||||
| @@ -157,28 +149,10 @@ public class MissionsFragment extends Fragment { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onSaveInstanceState(Bundle outState) { |     public void onPrepareOptionsMenu(Menu menu) { | ||||||
|         super.onSaveInstanceState(outState); |         mSwitch = menu.findItem(R.id.switch_mode); | ||||||
|         if (mAdapter != null) { |         mClear = menu.findItem(R.id.clear_list); | ||||||
|             mAdapter.deleterDispose(outState); |         super.onPrepareOptionsMenu(menu); | ||||||
|             mForceUpdate = true; |  | ||||||
|             mBinder.removeMissionEventListener(mAdapter.getMessenger()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onResume() { |  | ||||||
|         super.onResume(); |  | ||||||
|         if (mAdapter != null) { |  | ||||||
|             mAdapter.deleterResume(); |  | ||||||
|  |  | ||||||
|             if (mForceUpdate) { |  | ||||||
|                 mForceUpdate = false; |  | ||||||
|                 mAdapter.forceUpdate(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             mBinder.addMissionEventListener(mAdapter.getMessenger()); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -203,8 +177,11 @@ public class MissionsFragment extends Fragment { | |||||||
|             mList.setLayoutManager(mGridManager); |             mList.setLayoutManager(mGridManager); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // destroy all created views in the recycler | ||||||
|         mList.setAdapter(null); |         mList.setAdapter(null); | ||||||
|         mAdapter.notifyDataSetChanged(); |         mAdapter.notifyDataSetChanged(); | ||||||
|  |          | ||||||
|  |         // re-attach the adapter in grid/lineal mode | ||||||
|         mAdapter.setLinear(mLinear); |         mAdapter.setLinear(mLinear); | ||||||
|         mList.setAdapter(mAdapter); |         mList.setAdapter(mAdapter); | ||||||
|  |  | ||||||
| @@ -214,4 +191,32 @@ public class MissionsFragment extends Fragment { | |||||||
|             mPrefs.edit().putBoolean("linear", mLinear).apply(); |             mPrefs.edit().putBoolean("linear", mLinear).apply(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onSaveInstanceState(Bundle outState) { | ||||||
|  |         super.onSaveInstanceState(outState); | ||||||
|  |  | ||||||
|  |         if (mAdapter != null) { | ||||||
|  |             mAdapter.deleterDispose(outState); | ||||||
|  |             mForceUpdate = true; | ||||||
|  |             mBinder.removeMissionEventListener(mAdapter.getMessenger()); | ||||||
|  |                 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onResume() { | ||||||
|  |         super.onResume(); | ||||||
|  |                                                   | ||||||
|  |         if (mAdapter != null) { | ||||||
|  |             mAdapter.deleterResume(); | ||||||
|  |  | ||||||
|  |             if (mForceUpdate) { | ||||||
|  |                 mForceUpdate = false; | ||||||
|  |                 mAdapter.forceUpdate(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             mBinder.addMissionEventListener(mAdapter.getMessenger()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import android.widget.Toast; | |||||||
| import org.schabi.newpipe.R; | import org.schabi.newpipe.R; | ||||||
|  |  | ||||||
| import java.io.BufferedOutputStream; | import java.io.BufferedOutputStream; | ||||||
|  | import java.io.File; | ||||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||||
|   | |||||||
| @@ -51,8 +51,8 @@ | |||||||
| 			android:layout_centerHorizontal="true" | 			android:layout_centerHorizontal="true" | ||||||
| 			android:scaleType="fitXY" | 			android:scaleType="fitXY" | ||||||
| 			android:gravity="center" | 			android:gravity="center" | ||||||
|             android:contentDescription="TODO" | 			android:padding="10dp" | ||||||
| 			android:padding="10dp"/> |             android:contentDescription="TODO" /> | ||||||
| 		 | 		 | ||||||
| 		<TextView | 		<TextView | ||||||
| 			android:id="@+id/item_name" | 			android:id="@+id/item_name" | ||||||
| @@ -60,14 +60,14 @@ | |||||||
| 			android:layout_height="wrap_content" | 			android:layout_height="wrap_content" | ||||||
| 			android:layout_below="@id/item_icon" | 			android:layout_below="@id/item_icon" | ||||||
| 			android:padding="6dp" | 			android:padding="6dp" | ||||||
| 			android:text="XXX.xx" |  | ||||||
| 			android:textSize="16sp" |  | ||||||
| 			android:textStyle="bold" |  | ||||||
| 			android:textColor="@color/white" |  | ||||||
| 			android:singleLine="true" | 			android:singleLine="true" | ||||||
| 			android:ellipsize="marquee" | 			android:ellipsize="marquee" | ||||||
| 			android:marqueeRepeatLimit="marquee_forever" | 			android:marqueeRepeatLimit="marquee_forever" | ||||||
| 			android:scrollHorizontally="true"/> | 			android:scrollHorizontally="true" | ||||||
|  | 			android:text="XXX.xx" | ||||||
|  | 			android:textSize="16sp" | ||||||
|  | 			android:textStyle="bold" | ||||||
|  | 			android:textColor="@color/white"/> | ||||||
| 		 | 		 | ||||||
| 		<TextView | 		<TextView | ||||||
| 			android:id="@+id/item_size" | 			android:id="@+id/item_size" | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | <LinearLayout | ||||||
|  |       xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
| 	android:layout_width="fill_parent" | 	android:layout_width="fill_parent" | ||||||
| 	android:layout_height="fill_parent" | 	android:layout_height="fill_parent" | ||||||
| 	android:orientation="vertical"> | 	android:orientation="vertical"> | ||||||
| @@ -11,7 +12,6 @@ | |||||||
| 	<android.support.v7.widget.RecyclerView | 	<android.support.v7.widget.RecyclerView | ||||||
| 		android:id="@+id/mission_recycler" | 		android:id="@+id/mission_recycler" | ||||||
| 		android:layout_width="match_parent" | 		android:layout_width="match_parent" | ||||||
| 		android:layout_height="match_parent" | 		android:layout_height="match_parent"/> | ||||||
| 		/> |  | ||||||
|      |      | ||||||
| </LinearLayout> | </LinearLayout> | ||||||
| @@ -1,25 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" |     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||||
|     xmlns:tools="http://schemas.android.com/tools"> |  | ||||||
|  |  | ||||||
|     <item |     <item android:id="@+id/switch_mode" | ||||||
|         android:id="@+id/switch_mode" |  | ||||||
|         android:icon="@drawable/list" |         android:icon="@drawable/list" | ||||||
|         android:title="@string/grid" |         android:title="@string/grid" | ||||||
|         app:showAsAction="ifRoom" /> |         app:showAsAction="ifRoom" /> | ||||||
|  |  | ||||||
|     <item |     <item android:id="@+id/action_settings" | ||||||
|         android:id="@+id/action_settings" |         app:showAsAction="never" | ||||||
|         android:title="@string/settings" |         android:title="@string/settings"/> | ||||||
|         app:showAsAction="never" /> |  | ||||||
|  |  | ||||||
|     <item |     <item android:id="@+id/clear_list" | ||||||
|         android:visible="false" |         android:visible="false" | ||||||
|         android:id="@+id/clear_list" |  | ||||||
|         android:icon="@drawable/ic_delete_sweep_white_24dp" |         android:icon="@drawable/ic_delete_sweep_white_24dp" | ||||||
|         android:title="@string/clear_finished_download" |         app:showAsAction="ifRoom" | ||||||
|         app:showAsAction="ifRoom" /> |         android:title="@string/clear_finished_download"/> | ||||||
|  |  | ||||||
|  |  | ||||||
| </menu> | </menu> | ||||||
| @@ -16,7 +16,7 @@ | |||||||
|     <string name="controls_download_desc">Download stream file</string> |     <string name="controls_download_desc">Download stream file</string> | ||||||
|     <string name="search">Search</string> |     <string name="search">Search</string> | ||||||
|     <string name="settings">Settings</string> |     <string name="settings">Settings</string> | ||||||
|     <string name="did_you_mean">Did you mean: %1$s?</string> |     <string name="did_you_mean">Did you mean: %1$s\?</string> | ||||||
|     <string name="share_dialog_title">Share with</string> |     <string name="share_dialog_title">Share with</string> | ||||||
|     <string name="choose_browser">Choose browser</string> |     <string name="choose_browser">Choose browser</string> | ||||||
|     <string name="screen_rotation">rotation</string> |     <string name="screen_rotation">rotation</string> | ||||||
|   | |||||||
| @@ -1,186 +0,0 @@ | |||||||
| package us.shandian.giga.get; |  | ||||||
|  |  | ||||||
| import org.junit.Ignore; |  | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| import java.io.File; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.RandomAccessFile; |  | ||||||
| import java.util.ArrayList; |  | ||||||
|  |  | ||||||
| import us.shandian.giga.get.DownloadDataSource; |  | ||||||
| import us.shandian.giga.get.DownloadManagerImpl; |  | ||||||
| import us.shandian.giga.get.DownloadMission; |  | ||||||
|  |  | ||||||
| import static org.junit.Assert.assertEquals; |  | ||||||
| import static org.junit.Assert.assertNotEquals; |  | ||||||
| import static org.junit.Assert.assertSame; |  | ||||||
| import static org.junit.Assert.assertTrue; |  | ||||||
| import static org.mockito.Mockito.mock; |  | ||||||
| import static org.mockito.Mockito.never; |  | ||||||
| import static org.mockito.Mockito.spy; |  | ||||||
| import static org.mockito.Mockito.times; |  | ||||||
| import static org.mockito.Mockito.verify; |  | ||||||
| import static org.mockito.Mockito.when; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Test for {@link DownloadManagerImpl} |  | ||||||
|  * |  | ||||||
|  * TODO: test loading from .giga files, startMission and improve tests |  | ||||||
|  */ |  | ||||||
| public class DownloadManagerImplTest { |  | ||||||
|  |  | ||||||
|     private DownloadManagerImpl downloadManager; |  | ||||||
|     private DownloadDataSource downloadDataSource; |  | ||||||
|     private ArrayList<DownloadMission> missions; |  | ||||||
|  |  | ||||||
|     @org.junit.Before |  | ||||||
|     public void setUp() throws Exception { |  | ||||||
|         downloadDataSource = mock(DownloadDataSource.class); |  | ||||||
|         missions = new ArrayList<>(); |  | ||||||
|         for(int i = 0; i < 50; ++i){ |  | ||||||
|             missions.add(generateFinishedDownloadMission()); |  | ||||||
|         } |  | ||||||
|         when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions)); |  | ||||||
|         downloadManager = new DownloadManagerImpl(new ArrayList<>(), downloadDataSource); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test(expected = NullPointerException.class) |  | ||||||
|     public void testConstructorWithNullAsDownloadDataSource() { |  | ||||||
|         new DownloadManagerImpl(new ArrayList<>(), null); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     private static DownloadMission generateFinishedDownloadMission() throws IOException { |  | ||||||
|         File file = File.createTempFile("newpipetest", ".mp4"); |  | ||||||
|         file.deleteOnExit(); |  | ||||||
|         RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); |  | ||||||
|         randomAccessFile.setLength(1000); |  | ||||||
|         randomAccessFile.close(); |  | ||||||
|         DownloadMission downloadMission = new DownloadMission(file.getName(), |  | ||||||
|                 "http://google.com/?q=how+to+google", file.getParent()); |  | ||||||
|         downloadMission.blocks = 1000; |  | ||||||
|         downloadMission.done = 1000; |  | ||||||
|         downloadMission.finished = true; |  | ||||||
|         return spy(downloadMission); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static void assertMissionEquals(String message, DownloadMission expected, DownloadMission actual) { |  | ||||||
|         if(expected == actual) return; |  | ||||||
|         assertEquals(message + ": Name", expected.name, actual.name); |  | ||||||
|         assertEquals(message + ": Location", expected.location, actual.location); |  | ||||||
|         assertEquals(message + ": Url", expected.url, actual.url); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void testThatMissionsAreLoaded() throws IOException { |  | ||||||
|         ArrayList<DownloadMission> missions = new ArrayList<>(); |  | ||||||
|         long millis = System.currentTimeMillis(); |  | ||||||
|         for(int i = 0; i < 50; ++i){ |  | ||||||
|             DownloadMission  mission = generateFinishedDownloadMission(); |  | ||||||
|             mission.timestamp = millis - i; // reverse order by timestamp |  | ||||||
|             missions.add(mission); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         downloadDataSource = mock(DownloadDataSource.class); |  | ||||||
|         when(downloadDataSource.loadMissions()).thenReturn(new ArrayList<>(missions)); |  | ||||||
|         downloadManager = new DownloadManagerImpl(new ArrayList<>(), downloadDataSource); |  | ||||||
|         verify(downloadDataSource, times(1)).loadMissions(); |  | ||||||
|  |  | ||||||
|         assertEquals(50, downloadManager.getCount()); |  | ||||||
|  |  | ||||||
|         for(int i = 0; i < 50; ++i) { |  | ||||||
|             assertMissionEquals("mission " + i, missions.get(50 - 1 - i), downloadManager.getMission(i)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Ignore |  | ||||||
|     @Test |  | ||||||
|     public void startMission() throws Exception { |  | ||||||
|         DownloadMission mission = missions.get(0); |  | ||||||
|         mission = spy(mission); |  | ||||||
|         missions.set(0, mission); |  | ||||||
|         String url = "https://github.com/favicon.ico"; |  | ||||||
|         // create a temp file and delete it so we have a temp directory |  | ||||||
|         File tempFile = File.createTempFile("favicon",".ico"); |  | ||||||
|         String name = tempFile.getName(); |  | ||||||
|         String location = tempFile.getParent(); |  | ||||||
|         assertTrue(tempFile.delete()); |  | ||||||
|         int id = downloadManager.startMission(url, location, name, true, 10); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void resumeMission() { |  | ||||||
|         DownloadMission mission = missions.get(0); |  | ||||||
|         mission.running = true; |  | ||||||
|         verify(mission, never()).start(); |  | ||||||
|         downloadManager.resumeMission(0); |  | ||||||
|         verify(mission, never()).start(); |  | ||||||
|         mission.running = false; |  | ||||||
|         downloadManager.resumeMission(0); |  | ||||||
|         verify(mission, times(1)).start(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void pauseMission() { |  | ||||||
|         DownloadMission mission = missions.get(0); |  | ||||||
|         mission.running = false; |  | ||||||
|         downloadManager.pauseMission(0); |  | ||||||
|         verify(mission, never()).pause(); |  | ||||||
|         mission.running = true; |  | ||||||
|         downloadManager.pauseMission(0); |  | ||||||
|         verify(mission, times(1)).pause(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void deleteMission() { |  | ||||||
|         DownloadMission mission = missions.get(0); |  | ||||||
|         assertEquals(mission, downloadManager.getMission(0)); |  | ||||||
|         downloadManager.deleteMission(0); |  | ||||||
|         verify(mission, times(1)).delete(); |  | ||||||
|         assertNotEquals(mission, downloadManager.getMission(0)); |  | ||||||
|         assertEquals(49, downloadManager.getCount()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test(expected = RuntimeException.class) |  | ||||||
|     public void getMissionWithNegativeIndex() { |  | ||||||
|         downloadManager.getMission(-1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void getMission() { |  | ||||||
|         assertSame(missions.get(0), downloadManager.getMission(0)); |  | ||||||
|         assertSame(missions.get(1), downloadManager.getMission(1)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Test |  | ||||||
|     public void sortByTimestamp() { |  | ||||||
|         ArrayList<DownloadMission> downloadMissions = new ArrayList<>(); |  | ||||||
|         DownloadMission mission = new DownloadMission(); |  | ||||||
|         mission.timestamp = 0; |  | ||||||
|  |  | ||||||
|         DownloadMission mission1 = new DownloadMission(); |  | ||||||
|         mission1.timestamp = Integer.MAX_VALUE + 1L; |  | ||||||
|  |  | ||||||
|         DownloadMission mission2 = new DownloadMission(); |  | ||||||
|         mission2.timestamp = 2L * Integer.MAX_VALUE ; |  | ||||||
|  |  | ||||||
|         DownloadMission mission3 = new DownloadMission(); |  | ||||||
|         mission3.timestamp = 2L * Integer.MAX_VALUE + 5L; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         downloadMissions.add(mission3); |  | ||||||
|         downloadMissions.add(mission1); |  | ||||||
|         downloadMissions.add(mission2); |  | ||||||
|         downloadMissions.add(mission); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         DownloadManagerImpl.sortByTimestamp(downloadMissions); |  | ||||||
|  |  | ||||||
|         assertEquals(mission, downloadMissions.get(0)); |  | ||||||
|         assertEquals(mission1, downloadMissions.get(1)); |  | ||||||
|         assertEquals(mission2, downloadMissions.get(2)); |  | ||||||
|         assertEquals(mission3, downloadMissions.get(3)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user
	 kapodamy
					kapodamy