mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-07 11:17:25 +01:00
Show download progress. Caching of images now without glide
This commit is contained in:
parent
3561392d24
commit
1339e32de7
@ -8,6 +8,7 @@ import com.bumptech.glide.request.target.Target;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.jakewharton.disklrucache.DiskLruCache;
|
import com.jakewharton.disklrucache.DiskLruCache;
|
||||||
|
import com.squareup.okhttp.Response;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -21,6 +22,8 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
import eu.kanade.mangafeed.data.models.Page;
|
import eu.kanade.mangafeed.data.models.Page;
|
||||||
import eu.kanade.mangafeed.util.DiskUtils;
|
import eu.kanade.mangafeed.util.DiskUtils;
|
||||||
|
import okio.BufferedSink;
|
||||||
|
import okio.Okio;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
|
||||||
public class CacheManager {
|
public class CacheManager {
|
||||||
@ -184,5 +187,62 @@ public class CacheManager {
|
|||||||
return mDiskCache.getDirectory();
|
return mDiskCache.getDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isImageInCache(final String imageUrl) {
|
||||||
|
try {
|
||||||
|
return mDiskCache.get(DiskUtils.hashKeyForDisk(imageUrl)) != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImagePath(final String imageUrl) {
|
||||||
|
try {
|
||||||
|
String imageName = DiskUtils.hashKeyForDisk(imageUrl) + ".0";
|
||||||
|
File file = new File(mDiskCache.getDirectory(), imageName);
|
||||||
|
return file.getCanonicalPath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean putImageToDiskCache(final String imageUrl, final Response response) {
|
||||||
|
DiskLruCache.Editor editor = null;
|
||||||
|
BufferedSink sink = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String key = DiskUtils.hashKeyForDisk(imageUrl);
|
||||||
|
editor = mDiskCache.edit(key);
|
||||||
|
if (editor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream outputStream = new BufferedOutputStream(editor.newOutputStream(0));
|
||||||
|
sink = Okio.buffer(Okio.sink(outputStream));
|
||||||
|
sink.writeAll(response.body().source());
|
||||||
|
sink.flush();
|
||||||
|
|
||||||
|
mDiskCache.flush();
|
||||||
|
editor.commit();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (editor != null) {
|
||||||
|
editor.abortUnlessCommitted();
|
||||||
|
}
|
||||||
|
if (sink != null) {
|
||||||
|
try {
|
||||||
|
sink.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,24 @@ package eu.kanade.mangafeed.data.helpers;
|
|||||||
|
|
||||||
import com.squareup.okhttp.CacheControl;
|
import com.squareup.okhttp.CacheControl;
|
||||||
import com.squareup.okhttp.Headers;
|
import com.squareup.okhttp.Headers;
|
||||||
|
import com.squareup.okhttp.MediaType;
|
||||||
import com.squareup.okhttp.OkHttpClient;
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
import com.squareup.okhttp.Request;
|
import com.squareup.okhttp.Request;
|
||||||
import com.squareup.okhttp.RequestBody;
|
import com.squareup.okhttp.RequestBody;
|
||||||
import com.squareup.okhttp.Response;
|
import com.squareup.okhttp.Response;
|
||||||
|
import com.squareup.okhttp.ResponseBody;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
import java.net.CookieStore;
|
import java.net.CookieStore;
|
||||||
|
|
||||||
|
import eu.kanade.mangafeed.data.models.Page;
|
||||||
|
import okio.Buffer;
|
||||||
|
import okio.BufferedSource;
|
||||||
|
import okio.ForwardingSource;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Source;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
|
||||||
public final class NetworkHelper {
|
public final class NetworkHelper {
|
||||||
@ -82,8 +91,79 @@ public final class NetworkHelper {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<Response> getProgressResponse(final String url, final Headers headers, final Page page) {
|
||||||
|
return Observable.<Response>create(subscriber -> {
|
||||||
|
try {
|
||||||
|
if (!subscriber.isUnsubscribed()) {
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.cacheControl(NULL_CACHE_CONTROL)
|
||||||
|
.headers(headers != null ? headers : NULL_HEADERS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OkHttpClient progressClient = mClient.clone();
|
||||||
|
|
||||||
|
progressClient.networkInterceptors().add(chain -> {
|
||||||
|
Response originalResponse = chain.proceed(chain.request());
|
||||||
|
return originalResponse.newBuilder()
|
||||||
|
.body(new ProgressResponseBody(originalResponse.body(), page))
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
subscriber.onNext(progressClient.newCall(request).execute());
|
||||||
|
}
|
||||||
|
subscriber.onCompleted();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
subscriber.onError(e);
|
||||||
|
}
|
||||||
|
}).retry(3);
|
||||||
|
}
|
||||||
|
|
||||||
public CookieStore getCookies() {
|
public CookieStore getCookies() {
|
||||||
return cookieManager.getCookieStore();
|
return cookieManager.getCookieStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ProgressResponseBody extends ResponseBody {
|
||||||
|
|
||||||
|
private final ResponseBody responseBody;
|
||||||
|
private final ProgressListener progressListener;
|
||||||
|
private BufferedSource bufferedSource;
|
||||||
|
|
||||||
|
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
|
||||||
|
this.responseBody = responseBody;
|
||||||
|
this.progressListener = progressListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public MediaType contentType() {
|
||||||
|
return responseBody.contentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public long contentLength() throws IOException {
|
||||||
|
return responseBody.contentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public BufferedSource source() throws IOException {
|
||||||
|
if (bufferedSource == null) {
|
||||||
|
bufferedSource = Okio.buffer(source(responseBody.source()));
|
||||||
|
}
|
||||||
|
return bufferedSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Source source(Source source) {
|
||||||
|
return new ForwardingSource(source) {
|
||||||
|
long totalBytesRead = 0L;
|
||||||
|
@Override public long read(Buffer sink, long byteCount) throws IOException {
|
||||||
|
long bytesRead = super.read(sink, byteCount);
|
||||||
|
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||||
|
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
|
||||||
|
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressListener {
|
||||||
|
void update(long bytesRead, long contentLength, boolean done);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package eu.kanade.mangafeed.data.models;
|
package eu.kanade.mangafeed.data.models;
|
||||||
|
|
||||||
public class Page {
|
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
||||||
|
|
||||||
|
public class Page implements NetworkHelper.ProgressListener {
|
||||||
|
|
||||||
private int pageNumber;
|
private int pageNumber;
|
||||||
private String url;
|
private String url;
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
private String imagePath;
|
private String imagePath;
|
||||||
private int status;
|
private int status;
|
||||||
|
private int progress;
|
||||||
|
|
||||||
public static final int DOWNLOAD = 0;
|
public static final int DOWNLOAD = 0;
|
||||||
public static final int READY = 1;
|
public static final int READY = 1;
|
||||||
@ -55,6 +58,10 @@ public class Page {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Page{" +
|
return "Page{" +
|
||||||
@ -64,4 +71,9 @@ public class Page {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(long bytesRead, long contentLength, boolean done) {
|
||||||
|
progress = (int) ((100 * bytesRead) / contentLength);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,6 @@ package eu.kanade.mangafeed.injection.module;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.RequestManager;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@ -59,9 +56,4 @@ public class DataModule {
|
|||||||
return new SourceManager(app);
|
return new SourceManager(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
RequestManager provideGlideDownloader(Application app) {
|
|
||||||
return Glide.with(app);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,11 +2,6 @@ package eu.kanade.mangafeed.presenter;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.bumptech.glide.RequestManager;
|
|
||||||
import com.bumptech.glide.request.FutureTarget;
|
|
||||||
import com.bumptech.glide.request.target.Target;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -28,7 +23,6 @@ import timber.log.Timber;
|
|||||||
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||||
|
|
||||||
@Inject PreferencesHelper prefs;
|
@Inject PreferencesHelper prefs;
|
||||||
@Inject RequestManager glideDownloader;
|
|
||||||
|
|
||||||
private Source source;
|
private Source source;
|
||||||
private Chapter chapter;
|
private Chapter chapter;
|
||||||
@ -106,28 +100,11 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
source.getRemainingImageUrlsFromPageList(pageList)
|
source.getRemainingImageUrlsFromPageList(pageList)
|
||||||
.doOnNext(this::replacePageUrl)
|
.doOnNext(this::replacePageUrl)
|
||||||
)
|
)
|
||||||
.flatMap(this::downloadImage)
|
.flatMap(source::getCachedImage)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread());
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<Page> downloadImage(Page page) {
|
|
||||||
if (page.getImageUrl() != null) {
|
|
||||||
FutureTarget<File> future = glideDownloader.load(page.getImageUrl())
|
|
||||||
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
|
|
||||||
|
|
||||||
try {
|
|
||||||
File cacheFile = future.get();
|
|
||||||
page.setImagePath(cacheFile.getCanonicalPath());
|
|
||||||
page.setStatus(Page.READY);
|
|
||||||
} catch (Exception e) {
|
|
||||||
page.setStatus(Page.ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Observable.just(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void replacePageUrl(Page page) {
|
private void replacePageUrl(Page page) {
|
||||||
for (int i = 0; i < pageList.size(); i++) {
|
for (int i = 0; i < pageList.size(); i++) {
|
||||||
if (pageList.get(i).getPageNumber() == page.getPageNumber()) {
|
if (pageList.get(i).getPageNumber() == page.getPageNumber()) {
|
||||||
|
@ -102,6 +102,31 @@ public abstract class Source extends BaseSource {
|
|||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<Page> getCachedImage(final Page page) {
|
||||||
|
Observable<Page> obs = Observable.just(page);
|
||||||
|
if (page.getImageUrl() == null)
|
||||||
|
return obs;
|
||||||
|
|
||||||
|
if (!mCacheManager.isImageInCache(page.getImageUrl())) {
|
||||||
|
obs = mNetworkService.getProgressResponse(page.getImageUrl(), mRequestHeaders, page)
|
||||||
|
.flatMap(resp -> {
|
||||||
|
if (!mCacheManager.putImageToDiskCache(page.getImageUrl(), resp)) {
|
||||||
|
throw new IllegalStateException("Unable to save image");
|
||||||
|
}
|
||||||
|
return Observable.just(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return obs.flatMap(p -> {
|
||||||
|
page.setImagePath(mCacheManager.getImagePath(page.getImageUrl()));
|
||||||
|
page.setStatus(Page.READY);
|
||||||
|
return Observable.just(page);
|
||||||
|
}).onErrorResumeNext(e -> {
|
||||||
|
page.setStatus(Page.ERROR);
|
||||||
|
return Observable.just(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void savePageList(String chapterUrl, List<Page> pages) {
|
public void savePageList(String chapterUrl, List<Page> pages) {
|
||||||
if (pages != null)
|
if (pages != null)
|
||||||
mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
|
mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
|
||||||
|
@ -6,25 +6,35 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.davemorrissey.labs.subscaleview.ImageSource;
|
import com.davemorrissey.labs.subscaleview.ImageSource;
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import eu.kanade.mangafeed.R;
|
import eu.kanade.mangafeed.R;
|
||||||
import eu.kanade.mangafeed.data.models.Page;
|
import eu.kanade.mangafeed.data.models.Page;
|
||||||
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
|
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
public class ReaderPageFragment extends Fragment {
|
public class ReaderPageFragment extends Fragment {
|
||||||
|
|
||||||
@Bind(R.id.page_image_view) SubsamplingScaleImageView imageView;
|
@Bind(R.id.page_image_view) SubsamplingScaleImageView imageView;
|
||||||
|
@Bind(R.id.progress_container) LinearLayout progressContainer;
|
||||||
@Bind(R.id.progress) ProgressBar progressBar;
|
@Bind(R.id.progress) ProgressBar progressBar;
|
||||||
|
@Bind(R.id.progress_text) TextView progressText;
|
||||||
@Bind(R.id.image_error) TextView errorText;
|
@Bind(R.id.image_error) TextView errorText;
|
||||||
|
|
||||||
private Page page;
|
private Page page;
|
||||||
|
private Subscription progressSubscription;
|
||||||
|
|
||||||
public static ReaderPageFragment newInstance(Page page) {
|
public static ReaderPageFragment newInstance(Page page) {
|
||||||
ReaderPageFragment fragment = new ReaderPageFragment();
|
ReaderPageFragment fragment = new ReaderPageFragment();
|
||||||
@ -35,11 +45,11 @@ public class ReaderPageFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void replacePage(Page page) {
|
public void replacePage(Page page) {
|
||||||
|
unsubscribeProgress();
|
||||||
this.page = page;
|
this.page = page;
|
||||||
loadImage();
|
loadImage();
|
||||||
}
|
}
|
||||||
@ -55,13 +65,13 @@ public class ReaderPageFragment extends Fragment {
|
|||||||
switch (page.getStatus()) {
|
switch (page.getStatus()) {
|
||||||
case (Page.READY):
|
case (Page.READY):
|
||||||
imageView.setImage(ImageSource.uri(page.getImagePath()).tilingDisabled());
|
imageView.setImage(ImageSource.uri(page.getImagePath()).tilingDisabled());
|
||||||
progressBar.setVisibility(View.GONE);
|
progressContainer.setVisibility(View.GONE);
|
||||||
break;
|
break;
|
||||||
case (Page.DOWNLOAD):
|
case (Page.DOWNLOAD):
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressContainer.setVisibility(View.VISIBLE);
|
||||||
break;
|
break;
|
||||||
case (Page.ERROR):
|
case (Page.ERROR):
|
||||||
progressBar.setVisibility(View.GONE);
|
progressContainer.setVisibility(View.GONE);
|
||||||
errorText.setVisibility(View.VISIBLE);
|
errorText.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +88,42 @@ public class ReaderPageFragment extends Fragment {
|
|||||||
imageView.setOnTouchListener((v, motionEvent) ->
|
imageView.setOnTouchListener((v, motionEvent) ->
|
||||||
((ReaderActivity) getActivity()).onImageTouch(motionEvent));
|
((ReaderActivity) getActivity()).onImageTouch(motionEvent));
|
||||||
|
|
||||||
|
observeProgress();
|
||||||
loadImage();
|
loadImage();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
unsubscribeProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeProgress() {
|
||||||
|
if (page == null || page.getStatus() != Page.DOWNLOAD)
|
||||||
|
return;
|
||||||
|
|
||||||
|
progressSubscription = Observable.interval(75, TimeUnit.MILLISECONDS)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(tick -> {
|
||||||
|
if (page.getProgress() == 0) {
|
||||||
|
progressText.setText(R.string.downloading);
|
||||||
|
}
|
||||||
|
else if (page.getProgress() == 100) {
|
||||||
|
progressContainer.setVisibility(View.GONE);
|
||||||
|
unsubscribeProgress();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
progressText.setText(getString(R.string.download_progress, page.getProgress()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unsubscribeProgress() {
|
||||||
|
if (progressSubscription != null)
|
||||||
|
progressSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,29 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|center_horizontal"
|
||||||
|
android:id="@+id/progress_container"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress"
|
android:id="@+id/progress"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|center_horizontal"
|
android:layout_gravity="center_horizontal"/>
|
||||||
android:visibility="gone" />
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:id="@+id/progress_text"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -73,5 +73,7 @@
|
|||||||
<string name="loading">Loading…</string>
|
<string name="loading">Loading…</string>
|
||||||
<string name="toast_added_favorites">Added to favorites</string>
|
<string name="toast_added_favorites">Added to favorites</string>
|
||||||
<string name="action_favorite">Favorite</string>
|
<string name="action_favorite">Favorite</string>
|
||||||
|
<string name="downloading">Downloading…</string>
|
||||||
|
<string name="download_progress">Downloaded %1$d%%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user