mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Show download progress. Caching of images now without glide
This commit is contained in:
		@@ -8,6 +8,7 @@ import com.bumptech.glide.request.target.Target;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.gson.reflect.TypeToken;
 | 
			
		||||
import com.jakewharton.disklrucache.DiskLruCache;
 | 
			
		||||
import com.squareup.okhttp.Response;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
@@ -21,6 +22,8 @@ import java.util.concurrent.TimeoutException;
 | 
			
		||||
 | 
			
		||||
import eu.kanade.mangafeed.data.models.Page;
 | 
			
		||||
import eu.kanade.mangafeed.util.DiskUtils;
 | 
			
		||||
import okio.BufferedSink;
 | 
			
		||||
import okio.Okio;
 | 
			
		||||
import rx.Observable;
 | 
			
		||||
 | 
			
		||||
public class CacheManager {
 | 
			
		||||
@@ -184,5 +187,62 @@ public class CacheManager {
 | 
			
		||||
        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.Headers;
 | 
			
		||||
import com.squareup.okhttp.MediaType;
 | 
			
		||||
import com.squareup.okhttp.OkHttpClient;
 | 
			
		||||
import com.squareup.okhttp.Request;
 | 
			
		||||
import com.squareup.okhttp.RequestBody;
 | 
			
		||||
import com.squareup.okhttp.Response;
 | 
			
		||||
import com.squareup.okhttp.ResponseBody;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.CookieManager;
 | 
			
		||||
import java.net.CookiePolicy;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
public class Page {
 | 
			
		||||
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
 | 
			
		||||
 | 
			
		||||
public class Page implements NetworkHelper.ProgressListener {
 | 
			
		||||
 | 
			
		||||
    private int pageNumber;
 | 
			
		||||
    private String url;
 | 
			
		||||
    private String imageUrl;
 | 
			
		||||
    private String imagePath;
 | 
			
		||||
    private int status;
 | 
			
		||||
    private int progress;
 | 
			
		||||
 | 
			
		||||
    public static final int DOWNLOAD = 0;
 | 
			
		||||
    public static final int READY = 1;
 | 
			
		||||
@@ -55,6 +58,10 @@ public class Page {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getProgress() {
 | 
			
		||||
        return progress;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        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 com.bumptech.glide.Glide;
 | 
			
		||||
import com.bumptech.glide.RequestManager;
 | 
			
		||||
 | 
			
		||||
import javax.inject.Singleton;
 | 
			
		||||
 | 
			
		||||
import dagger.Module;
 | 
			
		||||
@@ -59,9 +56,4 @@ public class DataModule {
 | 
			
		||||
        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 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 javax.inject.Inject;
 | 
			
		||||
@@ -28,7 +23,6 @@ import timber.log.Timber;
 | 
			
		||||
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
 | 
			
		||||
 | 
			
		||||
    @Inject PreferencesHelper prefs;
 | 
			
		||||
    @Inject RequestManager glideDownloader;
 | 
			
		||||
 | 
			
		||||
    private Source source;
 | 
			
		||||
    private Chapter chapter;
 | 
			
		||||
@@ -106,28 +100,11 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
 | 
			
		||||
                    source.getRemainingImageUrlsFromPageList(pageList)
 | 
			
		||||
                         .doOnNext(this::replacePageUrl)
 | 
			
		||||
                )
 | 
			
		||||
                .flatMap(this::downloadImage)
 | 
			
		||||
                .flatMap(source::getCachedImage)
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .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) {
 | 
			
		||||
        for (int i = 0; i < pageList.size(); i++) {
 | 
			
		||||
            if (pageList.get(i).getPageNumber() == page.getPageNumber()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -102,6 +102,31 @@ public abstract class Source extends BaseSource {
 | 
			
		||||
                .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) {
 | 
			
		||||
        if (pages != null)
 | 
			
		||||
            mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,25 +6,35 @@ import android.support.v4.app.Fragment;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.ProgressBar;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.davemorrissey.labs.subscaleview.ImageSource;
 | 
			
		||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
import butterknife.Bind;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import eu.kanade.mangafeed.R;
 | 
			
		||||
import eu.kanade.mangafeed.data.models.Page;
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
    @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_text) TextView progressText;
 | 
			
		||||
    @Bind(R.id.image_error) TextView errorText;
 | 
			
		||||
 | 
			
		||||
    private Page page;
 | 
			
		||||
    private Subscription progressSubscription;
 | 
			
		||||
 | 
			
		||||
    public static ReaderPageFragment newInstance(Page page) {
 | 
			
		||||
        ReaderPageFragment fragment = new ReaderPageFragment();
 | 
			
		||||
@@ -35,11 +45,11 @@ public class ReaderPageFragment extends Fragment {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
 | 
			
		||||
        setRetainInstance(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void replacePage(Page page) {
 | 
			
		||||
        unsubscribeProgress();
 | 
			
		||||
        this.page = page;
 | 
			
		||||
        loadImage();
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,13 +65,13 @@ public class ReaderPageFragment extends Fragment {
 | 
			
		||||
        switch (page.getStatus()) {
 | 
			
		||||
            case (Page.READY):
 | 
			
		||||
                imageView.setImage(ImageSource.uri(page.getImagePath()).tilingDisabled());
 | 
			
		||||
                progressBar.setVisibility(View.GONE);
 | 
			
		||||
                progressContainer.setVisibility(View.GONE);
 | 
			
		||||
                break;
 | 
			
		||||
            case (Page.DOWNLOAD):
 | 
			
		||||
                progressBar.setVisibility(View.VISIBLE);
 | 
			
		||||
                progressContainer.setVisibility(View.VISIBLE);
 | 
			
		||||
                break;
 | 
			
		||||
            case (Page.ERROR):
 | 
			
		||||
                progressBar.setVisibility(View.GONE);
 | 
			
		||||
                progressContainer.setVisibility(View.GONE);
 | 
			
		||||
                errorText.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -78,9 +88,42 @@ public class ReaderPageFragment extends Fragment {
 | 
			
		||||
        imageView.setOnTouchListener((v, motionEvent) ->
 | 
			
		||||
                ((ReaderActivity) getActivity()).onImageTouch(motionEvent));
 | 
			
		||||
 | 
			
		||||
        observeProgress();
 | 
			
		||||
        loadImage();
 | 
			
		||||
 | 
			
		||||
        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"
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
 | 
			
		||||
    <ProgressBar
 | 
			
		||||
        android:id="@+id/progress"
 | 
			
		||||
        style="?android:attr/progressBarStyleLarge"
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
        android:layout_height="fill_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_gravity="center_vertical|center_horizontal"
 | 
			
		||||
        android:visibility="gone" />
 | 
			
		||||
        android:id="@+id/progress_container"
 | 
			
		||||
        android:orientation="vertical">
 | 
			
		||||
 | 
			
		||||
        <ProgressBar
 | 
			
		||||
            android:id="@+id/progress"
 | 
			
		||||
            style="?android:attr/progressBarStyleLarge"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:layout_gravity="center_horizontal"/>
 | 
			
		||||
 | 
			
		||||
        <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
 | 
			
		||||
        android:layout_width="wrap_content"
 | 
			
		||||
 
 | 
			
		||||
@@ -73,5 +73,7 @@
 | 
			
		||||
    <string name="loading">Loading…</string>
 | 
			
		||||
    <string name="toast_added_favorites">Added to favorites</string>
 | 
			
		||||
    <string name="action_favorite">Favorite</string>
 | 
			
		||||
    <string name="downloading">Downloading…</string>
 | 
			
		||||
    <string name="download_progress">Downloaded %1$d%%</string>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user