package com.loopeer.codereader.sync; import android.app.DownloadManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; import com.loopeer.codereader.CodeReaderApplication; import com.loopeer.codereader.Navigator; import com.loopeer.codereader.R; import com.loopeer.codereader.coreader.db.CoReaderDbHelper; import com.loopeer.codereader.event.DownloadFailDeleteEvent; import com.loopeer.codereader.event.DownloadProgressEvent; import com.loopeer.codereader.event.DownloadRepoMessageEvent; import com.loopeer.codereader.model.Repo; import com.loopeer.codereader.utils.FileCache; import com.loopeer.codereader.utils.RxBus; import com.loopeer.codereader.utils.Unzip; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import rx.Observable; import rx.Subscriber; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; public class DownloadRepoService extends Service { public static final int DOWNLOAD_COMPLETE = 0; public static final int DOWNLOAD_REPO = 1; public static final int DOWNLOAD_PROGRESS = 2; public static final int DOWNLOAD_REMOVE_DOWNLOAD = 3; private static final String TAG = "DownloadRepoService"; public static final Uri DOWNLOAD_CONTENT_URI = Uri.parse("content://downloads/my_downloads"); public static final String MEDIA_TYPE_ZIP = "application/zip"; private HashMap<Long, Repo> mDownloadingRepos; private Subscription mProgressSubscription; private DownloadChangeObserver mDownloadChangeObserver; @Override public void onCreate() { super.onCreate(); mDownloadingRepos = new HashMap<>(); mDownloadChangeObserver = new DownloadChangeObserver(); getContentResolver().registerContentObserver(DOWNLOAD_CONTENT_URI, true, mDownloadChangeObserver); } @Override public int onStartCommand(Intent intent, int flags, int startId) { parseIntent(intent); return super.onStartCommand(intent, flags, startId); } private void parseIntent(Intent intent) { Intent in = intent; int type = in.getIntExtra(Navigator.EXTRA_DOWNLOAD_SERVICE_TYPE, 0); Repo repo = (Repo) in.getSerializableExtra(Navigator.EXTRA_REPO); long id = in.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); switch (type) { case DOWNLOAD_COMPLETE: doRepoDownloadComplete(id); break; case DOWNLOAD_REPO: downloadFile(repo); break; case DOWNLOAD_PROGRESS: checkDownloadProgress(); break; case DOWNLOAD_REMOVE_DOWNLOAD: removeDownloadingRepo(id); break; } } private void doRepoDownloadComplete(long id) { CoReaderDbHelper.getInstance(CodeReaderApplication.getAppContext()) .updateRepoUnzipProgress(id, 1, true); RxBus.getInstance().send(new DownloadProgressEvent(id, true)); Observable.create((Observable.OnSubscribe<Void>) subscriber -> { Cursor cursor = null; try { DownloadManager manager = (DownloadManager) DownloadRepoService.this.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Query baseQuery = new DownloadManager.Query() .setFilterById(id); cursor = manager.query(baseQuery); final int statusColumnId = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS); final int localFilenameColumnId = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME); final int descName = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION); if (cursor.moveToNext()) { final long status = cursor.getLong(statusColumnId); final String path = cursor.getString(localFilenameColumnId); final String name = cursor.getString(descName); if (status == DownloadManager.STATUS_SUCCESSFUL) { File zipFile = new File(path); FileCache fileCache = FileCache.getInstance(); fileCache.deleteFilesByDirectory(new File(fileCache.getCacheDir().getPath() + File.separator + name)); Unzip decomp = new Unzip(zipFile.getPath() , fileCache.getCacheDir().getPath() + File.separator + name, getApplicationContext()); decomp.DecompressZip(); if (zipFile.exists()) zipFile.delete(); CoReaderDbHelper.getInstance(CodeReaderApplication.getAppContext()) .updateRepoUnzipProgress(id, 1, false); CoReaderDbHelper.getInstance( CodeReaderApplication.getAppContext()).resetRepoDownloadId(mDownloadingRepos.get(id).downloadId); RxBus.getInstance().send(new DownloadProgressEvent(id, false)); RxBus.getInstance().send(new DownloadRepoMessageEvent( getString(R.string.repo_download_complete, mDownloadingRepos.get(id).name))); } else { } } mDownloadingRepos.remove(id); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } finally { cursor.close(); } }) .onErrorResumeNext(Observable.empty()) .subscribeOn(Schedulers.io()) .doOnError(e -> Log.d(TAG, e.toString())) .doOnCompleted(this::checkTaskEmptyToFinish) .subscribe(); } private void checkTaskEmptyToFinish() { if (mDownloadingRepos.isEmpty()) { stopSelf(); } } private void downloadFile(Repo repo) { RemoteRepoFetcher dataFetcher = new RemoteRepoFetcher(this, repo.netDownloadUrl, repo.name); long downloadId = dataFetcher.download(); if (downloadId <= 0) { CoReaderDbHelper.getInstance(getApplicationContext()).deleteRepo(Long.parseLong(repo.id)); return; } repo.downloadId = downloadId; mDownloadingRepos.put(downloadId, repo); CoReaderDbHelper.getInstance(getApplicationContext()).updateRepoDownloadId(downloadId, repo.id); RxBus.getInstance().send(new DownloadRepoMessageEvent(getString(R.string.repo_download_start, repo.name))); checkDownloadProgress(); } private void checkDownloadProgress() { if (mDownloadingRepos.isEmpty()) { List<Repo> repos = CoReaderDbHelper.getInstance(this).readRepos(); for (Repo repo : repos) { if (repo.isDownloading()) { mDownloadingRepos.put(repo.downloadId, repo); } } } if (mDownloadingRepos.isEmpty()) { stopSelf(); return; } if (mProgressSubscription != null && !mProgressSubscription.isUnsubscribed()) { mProgressSubscription.unsubscribe(); } mProgressSubscription = checkDownloadingProgress(this); } private void clearDownloadProgressSubscription() { if (mProgressSubscription != null && !mProgressSubscription.isUnsubscribed()) { mProgressSubscription.unsubscribe(); } } @Override public void onDestroy() { super.onDestroy(); clearDownloadProgressSubscription(); getContentResolver().unregisterContentObserver(mDownloadChangeObserver); } public Subscription checkDownloadingProgress(Context context) { return Observable.create(new Observable.OnSubscribe<List<Repo>>() { @Override public void call(Subscriber<? super List<Repo>> subscriber) { DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); List<Repo> repos = new ArrayList(mDownloadingRepos.values());; for (Repo repo : repos) { DownloadManager.Query q = new DownloadManager.Query(); q.setFilterById(repo.downloadId); Cursor cursor = downloadManager.query(q); cursor.moveToFirst(); int bytes_downloaded = cursor.getInt(cursor .getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); int bytes_total = cursor.getInt( cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); String mediaType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE)); int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)); int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); if (status == DownloadManager.STATUS_FAILED && !MEDIA_TYPE_ZIP.equals(mediaType) && reason == DownloadManager.ERROR_UNKNOWN) { RxBus.getInstance() .send(new DownloadRepoMessageEvent( getString(R.string.repo_download_fail, repo.name))); RxBus.getInstance() .send(new DownloadFailDeleteEvent(repo)); CoReaderDbHelper.getInstance(context).deleteRepo(Long.parseLong(repo.id)); } else if (status != DownloadManager.STATUS_SUCCESSFUL) { final float dl_progress = 1f * bytes_downloaded / bytes_total; repo.factor = dl_progress; } else if (status == DownloadManager.STATUS_SUCCESSFUL){ repo.factor = 1; } if (repo.factor < 0) repo.factor = 0; if (repo.factor >= 0) { CoReaderDbHelper.getInstance(CodeReaderApplication.getAppContext()) .updateRepoDownloadProgress(repo.downloadId, repo.factor); RxBus.getInstance().send(new DownloadProgressEvent(repo.id, repo.downloadId, repo.factor, repo.isUnzip)); } cursor.close(); } subscriber.onCompleted(); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } private void removeDownloadingRepo(long id) { DownloadManager downloadManager = (DownloadManager) this.getSystemService(Context.DOWNLOAD_SERVICE); int i = downloadManager.remove(id); if (i > 0) mDownloadingRepos.remove(id); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } class DownloadChangeObserver extends ContentObserver { public DownloadChangeObserver(){ super(new Handler()); } @Override public void onChange(boolean selfChange) { checkDownloadProgress(); } } }