I made this code because of this issue.
This code is based on TWiStErRob's gist.
Last active
March 31, 2021 03:57
-
-
Save wonsuc/7724258a760701eb3622c9afd42db971 to your computer and use it in GitHub Desktop.
Downloading progress with FirebaseUI Storage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
tools:context=".ui.activity.GlideActivity"> | |
<include layout="@layout/view_toolbar" /> | |
<android.support.v7.widget.RecyclerView | |
android:id="@+id/recycler_view" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:background="@android:color/black" | |
tools:listitem="@layout/item_glide" /> | |
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.os.Handler; | |
import android.os.Looper; | |
import android.util.Log; | |
import com.bumptech.glide.Glide; | |
import com.bumptech.glide.Registry; | |
import com.bumptech.glide.annotation.GlideModule; | |
import com.bumptech.glide.module.AppGlideModule; | |
import com.google.firebase.storage.StorageReference; | |
import java.io.InputStream; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* Glide module to register {@link com.firebase.ui.storage.images.FirebaseImageLoader}. | |
* See: http://bumptech.github.io/glide/doc/generatedapi.html | |
*/ | |
@GlideModule | |
public class FirebaseAppGlideModule extends AppGlideModule { | |
public static final String TAG = "FirebaseAppGlideModule"; | |
@Override | |
public void registerComponents(Context context, Glide glide, Registry registry) { | |
// Register FirebaseImageLoader to handle StorageReference | |
registry.append(StorageReference.class, InputStream.class, new FirebaseImageLoader.Factory()); | |
} | |
public interface UIProgressListener { | |
void onProgress(long bytesRead, long expectedLength); | |
/** | |
* Control how often the listener needs an update. 0% and 100% will always be dispatched. | |
* | |
* @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress) | |
*/ | |
float getGranualityPercentage(); | |
} | |
public static void forget(String key) { | |
DispatchingProgressListener.forget(key); | |
} | |
public static void expect(String key, UIProgressListener listener) { | |
Log.i(TAG, "expect:key:" + key); | |
DispatchingProgressListener.expect(key, listener); | |
} | |
interface ResponseProgressListener { | |
void update(String key, long bytesRead, long contentLength); | |
} | |
static class DispatchingProgressListener implements ResponseProgressListener { | |
public static final String TAG = "DPListener"; | |
private static final Map<String, List<UIProgressListener>> LISTENERS = new HashMap<>(); | |
private static final Map<String, List<Long>> PROGRESSES = new HashMap<>(); | |
private final Handler handler; | |
DispatchingProgressListener() { | |
this.handler = new Handler(Looper.getMainLooper()); | |
} | |
static void forget(String key) { | |
LISTENERS.remove(key); | |
PROGRESSES.remove(key); | |
} | |
static void expect(String key, UIProgressListener listener) { | |
List<UIProgressListener> listeners = LISTENERS.get(key); | |
if (listeners == null) listeners = new ArrayList<>(); | |
if (!listeners.contains(listener)) { | |
listeners.add(listener); | |
LISTENERS.put(key, listeners); | |
} | |
} | |
@Override | |
public void update(String key, final long bytesRead, final long contentLength) { | |
Log.i(TAG, String.format( | |
"update:%s: %d/%d = %.2f%%%n", | |
key, | |
bytesRead, | |
contentLength, | |
(100f * bytesRead) / contentLength) | |
); | |
final List<UIProgressListener> listeners = LISTENERS.get(key); | |
if (listeners == null) { | |
return; | |
} | |
if (contentLength <= bytesRead) { | |
forget(key); | |
} | |
for (final UIProgressListener listener : listeners) { | |
int index = listeners.indexOf(listener); | |
if (needsDispatch(key, index, bytesRead, contentLength, listener.getGranualityPercentage())) { | |
handler.post(new Runnable() { | |
@Override | |
public void run() { | |
listener.onProgress(bytesRead, contentLength); | |
} | |
}); | |
} | |
} | |
} | |
private boolean needsDispatch(String key, int index, long current, long total, float granularity) { | |
if (granularity == 0 || current == 0 || total == current) { | |
return true; | |
} | |
float percent = 100f * current / total; | |
long currentProgress = (long) (percent / granularity); | |
List<Long> progresses = PROGRESSES.get(key); | |
if (progresses == null) progresses = new ArrayList<>(); | |
Long lastProgress = progresses.size() > index ? progresses.get(index) : null; | |
if (lastProgress == null) { | |
progresses.add(currentProgress); | |
PROGRESSES.put(key, progresses); | |
return true; | |
} else if (currentProgress != lastProgress) { | |
progresses.set(index, currentProgress); | |
PROGRESSES.put(key, progresses); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.util.Log; | |
import com.bumptech.glide.Priority; | |
import com.bumptech.glide.load.DataSource; | |
import com.bumptech.glide.load.Key; | |
import com.bumptech.glide.load.Options; | |
import com.bumptech.glide.load.data.DataFetcher; | |
import com.bumptech.glide.load.model.ModelLoader; | |
import com.bumptech.glide.load.model.ModelLoaderFactory; | |
import com.bumptech.glide.load.model.MultiModelLoaderFactory; | |
import com.google.android.gms.tasks.OnFailureListener; | |
import com.google.android.gms.tasks.OnSuccessListener; | |
import com.google.firebase.storage.StorageReference; | |
import com.google.firebase.storage.StreamDownloadTask; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.nio.charset.Charset; | |
import java.security.MessageDigest; | |
/** | |
* ModelLoader implementation to download images from FirebaseStorage with Glide. | |
* <p> | |
* <p> | |
* First, register this class in your AppGlideModule: | |
* <pre> | |
* {@literal @}Override | |
* public void registerComponents(Context context, Registry registry) { | |
* // Register FirebaseImageLoader to handle StorageReference | |
* registry.append(StorageReference.class, InputStream.class, | |
* new FirebaseImageLoader.Factory()); | |
* } | |
* </pre> | |
* <p> | |
* <p> | |
* Then load a StorageReference into an ImageView. | |
* <pre> | |
* StorageReference ref = FirebaseStorage.getInstance().getReference().child("myimage"); | |
* ImageView iv = (ImageView) findViewById(R.id.my_image_view); | |
* | |
* GlideApp.with(this) | |
* .load(ref) | |
* .into(iv); | |
* </pre> | |
*/ | |
public class FirebaseImageLoader implements ModelLoader<StorageReference, InputStream> { | |
private static final String TAG = "FirebaseImageLoader"; | |
/** | |
* Factory to create {@link FirebaseImageLoader}. | |
*/ | |
public static class Factory implements ModelLoaderFactory<StorageReference, InputStream> { | |
@Override | |
public ModelLoader<StorageReference, InputStream> build(MultiModelLoaderFactory factory) { | |
return new FirebaseImageLoader(); | |
} | |
@Override | |
public void teardown() { | |
// No-op | |
} | |
} | |
@Nullable | |
@Override | |
public LoadData<InputStream> buildLoadData(StorageReference reference, | |
int height, | |
int width, | |
Options options) { | |
return new LoadData<>(new FirebaseStorageKey(reference), new FirebaseStorageFetcher(reference)); | |
} | |
@Override | |
public boolean handles(StorageReference reference) { | |
return true; | |
} | |
private static class FirebaseStorageKey implements Key { | |
private StorageReference mRef; | |
public FirebaseStorageKey(StorageReference ref) { | |
mRef = ref; | |
} | |
@Override | |
public void updateDiskCacheKey(MessageDigest digest) { | |
digest.update(mRef.getPath().getBytes(Charset.defaultCharset())); | |
} | |
} | |
private static class FirebaseStorageFetcher implements DataFetcher<InputStream> { | |
public static final String TAG = "FirebaseStorageFetcher"; | |
private StorageReference mRef; | |
private StreamDownloadTask mStreamTask; | |
private InputStream mInputStream; | |
private FirebaseAppGlideModule.ResponseProgressListener mProgressListener; | |
public FirebaseStorageFetcher(StorageReference ref) { | |
// Log.i(TAG, "FirebaseStorageFetcher:ref_path:" + ref.getPath()); | |
mRef = ref; | |
mProgressListener = new FirebaseAppGlideModule.DispatchingProgressListener(); | |
} | |
@Override | |
public void loadData(Priority priority, final DataCallback<? super InputStream> callback) { | |
// Log.i(TAG, "loadData"); | |
mStreamTask = mRef.getStream(); | |
mStreamTask | |
.addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() { | |
@Override | |
public void onSuccess(StreamDownloadTask.TaskSnapshot snapshot) { | |
String key = mRef.getPath(); | |
InputStream in = snapshot.getStream(); | |
long length = snapshot.getTotalByteCount(); | |
try { | |
ProgressInputStream is = new ProgressInputStream(key, in, length); | |
is.setListener(new ProgressInputStream.StreamProgressListener() { | |
@Override | |
public void update(String key, long bytesRead, long contentLength) { | |
Log.i(TAG, "onSuccess:update:" + | |
"key:" + key + | |
"|bytesRead:" + bytesRead + | |
"|contentLength:" + contentLength); | |
mProgressListener.update(key, bytesRead, contentLength); | |
} | |
}); | |
mInputStream = is; | |
} catch (IOException e) { | |
e.printStackTrace(); | |
callback.onLoadFailed(e); | |
return; | |
} | |
callback.onDataReady(mInputStream); | |
} | |
}) | |
.addOnFailureListener(new OnFailureListener() { | |
@Override | |
public void onFailure(@NonNull Exception e) { | |
callback.onLoadFailed(e); | |
} | |
}); | |
} | |
@Override | |
public void cleanup() { | |
// Close stream if possible | |
if (mInputStream != null) { | |
try { | |
mInputStream.close(); | |
mInputStream = null; | |
} catch (IOException e) { | |
Log.w(TAG, "Could not close stream", e); | |
} | |
} | |
} | |
@Override | |
public void cancel() { | |
// Cancel task if possible | |
if (mStreamTask != null && mStreamTask.isInProgress()) { | |
mStreamTask.cancel(); | |
} | |
} | |
@NonNull | |
@Override | |
public Class<InputStream> getDataClass() { | |
return InputStream.class; | |
} | |
@NonNull | |
@Override | |
public DataSource getDataSource() { | |
return DataSource.REMOTE; | |
} | |
} | |
private static class ProgressInputStream extends InputStream { | |
private final String key; | |
private InputStream in; | |
private long length, sumRead; | |
private StreamProgressListener listener; | |
public interface StreamProgressListener { | |
void update(String key, long bytesRead, long contentLength); | |
} | |
public ProgressInputStream(String key, InputStream inputStream, long length) throws IOException { | |
Log.i(TAG, "ProgressInputStream"); | |
this.key = key; | |
this.in = inputStream; | |
this.sumRead = 0; | |
this.length = length; | |
} | |
@Override | |
public int read(byte[] b) throws IOException { | |
int readCount = in.read(b); | |
evaluate(readCount); | |
return readCount; | |
} | |
@Override | |
public int read(byte[] b, int off, int len) throws IOException { | |
int readCount = in.read(b, off, len); | |
evaluate(readCount); | |
return readCount; | |
} | |
@Override | |
public long skip(long n) throws IOException { | |
long skip = in.skip(n); | |
evaluate(skip); | |
return skip; | |
} | |
@Override | |
public int read() throws IOException { | |
int read = in.read(); | |
if (read != -1) { | |
evaluate(1); | |
} | |
return read; | |
} | |
public ProgressInputStream setListener(StreamProgressListener listener) { | |
this.listener = listener; | |
return this; | |
} | |
private void evaluate(long readCount) { | |
if (readCount != -1) { | |
sumRead += readCount; | |
} | |
notifyListener(); | |
} | |
private void notifyListener() { | |
if (listener != null) listener.update(key, sumRead, length); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |
android:innerRadiusRatio="2.3" | |
android:shape="ring" | |
android:thickness="3.8sp" | |
android:useLevel="true"> | |
<solid android:color="#ff0000" /> | |
</shape> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?><!-- Display indeterminate progress at the beginning and end, see setImageLevel calls inside MyProgressTarget --> | |
<level-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<!-- keep [1, 9999] range first to optimize lookup, see LevelListDrawable.LevelListState#indexOfLevel --> | |
<item | |
android:drawable="@android:drawable/progress_horizontal" | |
android:maxLevel="9999" | |
android:minLevel="1" /> | |
<item | |
android:drawable="@android:drawable/progress_indeterminate_horizontal" | |
android:maxLevel="0" | |
android:minLevel="0" /> | |
<item | |
android:drawable="@android:drawable/progress_indeterminate_horizontal" | |
android:maxLevel="10000" | |
android:minLevel="10000" /> | |
</level-list> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.annotation.SuppressLint; | |
import android.graphics.Bitmap; | |
import android.os.Bundle; | |
import android.support.v7.widget.LinearLayoutManager; | |
import android.support.v7.widget.RecyclerView; | |
import android.support.v7.widget.RecyclerView.Adapter; | |
import android.support.v7.widget.RecyclerView.ViewHolder; | |
import android.util.Log; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.View.OnClickListener; | |
import android.view.ViewGroup; | |
import android.widget.ImageView; | |
import android.widget.ProgressBar; | |
import android.widget.TextView; | |
import com.bumptech.glide.load.engine.DiskCacheStrategy; | |
import com.bumptech.glide.request.target.BitmapImageViewTarget; | |
import com.bumptech.glide.request.target.Target; | |
import com.google.firebase.storage.FirebaseStorage; | |
import com.google.firebase.storage.StorageReference; | |
import com.pwdr.panda.R; | |
import com.pwdr.panda.image.glide.GlideApp; | |
import com.pwdr.panda.image.glide.ProgressTarget; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.Locale; | |
import butterknife.BindView; | |
import butterknife.ButterKnife; | |
public class GlideActivity extends BaseActivity { | |
@BindView(R.id.recycler_view) | |
RecyclerView mRecyclerView; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_glide); | |
ButterKnife.bind(this); | |
mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); | |
StorageReference storageRef = FirebaseStorage.getInstance().getReference(); | |
List<StorageReference> imageRefs = Arrays.asList( | |
storageRef.child("-L201jy5revqsEAg_xE5").child("0cabae62-e2aa-4272-a409-837f88c06e1d.jpg"), | |
storageRef.child("table.png"), | |
storageRef.child("RWU.png"), | |
storageRef.child("RWU.png") | |
); | |
mRecyclerView.setAdapter(new ProgressAdapter(imageRefs)); | |
} | |
private static class ProgressViewHolder extends ViewHolder { | |
private final Context context; | |
private final ImageView image; | |
private final TextView text; | |
private final ProgressBar progress; | |
/** | |
* Cache target because all the views are tied to this view holder. | |
*/ | |
private final ProgressTarget<Bitmap> target; | |
ProgressViewHolder(View root) { | |
super(root); | |
context = root.getContext(); | |
image = root.findViewById(R.id.image); | |
text = root.findViewById(R.id.text); | |
progress = root.findViewById(R.id.progress); | |
target = new MyProgressTarget<>(context, new BitmapImageViewTarget(image), progress, image, text); | |
} | |
void bind(StorageReference imageRef) { | |
target.setModel(imageRef); // update target's cache | |
GlideApp.with(context) | |
.asBitmap() | |
.placeholder(R.drawable.glide_progress) | |
.load(imageRef) | |
.diskCacheStrategy(DiskCacheStrategy.NONE) | |
.skipMemoryCache(true) | |
.centerCrop() | |
.into(target) | |
; | |
} | |
} | |
/** | |
* Demonstrates 3 different ways of showing the progress: | |
* <ul> | |
* <li>Update a full fledged progress bar</li> | |
* <li>Update a text view to display size/percentage</li> | |
* <li>Update the placeholder via Drawable.level</li> | |
* </ul> | |
* This last one is tricky: the placeholder that Glide sets can be used as a progress drawable | |
* without any extra Views in the view hierarchy if it supports levels via <code>usesLevel="true"</code> | |
* or <code>level-list</code>. | |
* | |
* @param <Z> automatically match any real Glide target so it can be used flexibly without reimplementing. | |
*/ | |
@SuppressLint("SetTextI18n") // text set only for debugging | |
private static class MyProgressTarget<Z> extends ProgressTarget<Z> { | |
public static final String TAG = "MyProgressTarget"; | |
private final TextView text; | |
private final ProgressBar progress; | |
private final ImageView image; | |
public MyProgressTarget(Context context, Target<Z> target, ProgressBar progress, ImageView image, TextView text) { | |
super(context, target); | |
this.progress = progress; | |
this.image = image; | |
this.text = text; | |
} | |
@Override | |
public float getGranualityPercentage() { | |
return 0.1f; // this matches the format string for #text below | |
} | |
@Override | |
protected void onConnecting() { | |
Log.i(TAG, "onConnecting"); | |
progress.setIndeterminate(true); | |
progress.setVisibility(View.VISIBLE); | |
image.setImageLevel(0); | |
text.setVisibility(View.VISIBLE); | |
text.setText("connecting"); | |
} | |
@Override | |
protected void onDownloading(long bytesRead, long expectedLength) { | |
Log.i(TAG, "onDownloading:this:" + this); | |
progress.setIndeterminate(false); | |
progress.setProgress((int) (100 * bytesRead / expectedLength)); | |
image.setImageLevel((int) (10000 * bytesRead / expectedLength)); | |
text.setText(String.format(Locale.ROOT, "downloading %.2f/%.2f MB %.1f%%", | |
bytesRead / 1e6, expectedLength / 1e6, 100f * bytesRead / expectedLength)); | |
} | |
@Override | |
protected void onDownloaded() { | |
Log.i(TAG, "onDownloaded"); | |
progress.setIndeterminate(true); | |
image.setImageLevel(10000); | |
text.setText("decoding and transforming"); | |
} | |
@Override | |
protected void onDelivered() { | |
Log.i(TAG, "onDelivered"); | |
progress.setVisibility(View.INVISIBLE); | |
image.setImageLevel(0); // reset ImageView default | |
text.setVisibility(View.INVISIBLE); | |
} | |
} | |
private static class ProgressAdapter extends Adapter<ProgressViewHolder> { | |
private final List<StorageReference> models; | |
public ProgressAdapter(List<StorageReference> models) { | |
this.models = models; | |
} | |
@Override | |
public ProgressViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_glide, parent, false); | |
return new ProgressViewHolder(view); | |
} | |
@Override | |
public void onBindViewHolder(ProgressViewHolder holder, int position) { | |
holder.bind(models.get(position)); | |
} | |
@Override | |
public int getItemCount() { | |
return models.size(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="150dp" | |
android:layout_margin="4dp"> | |
<!-- see interactions with MyProgressTarget.image | |
scaleType is fitXY because the LevelListDrawable in github_232_progress contains a fixed sized | |
indeterminate drawable. fitXY stretches everything out so it's screen-wide. | |
.centerCrop() on the Glide load will load an appropriately resized bitmap, so that won't be stretched. --> | |
<ImageView | |
android:id="@+id/image" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:scaleType="fitXY" | |
tools:ignore="ContentDescription" | |
tools:src="@drawable/glide_progress" /> | |
<!-- see interactions with MyProgressTarget.text --> | |
<TextView | |
android:id="@+id/text" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="bottom|end" | |
android:background="#60000000" | |
android:padding="4dp" | |
android:textColor="#ffffff" | |
tools:text="progress: ??.? %" /> | |
<!-- see interactions with MyProgressTarget.progress --> | |
<ProgressBar | |
android:id="@+id/progress" | |
style="?android:attr/progressBarStyleHorizontal" | |
android:layout_width="64dp" | |
android:layout_height="64dp" | |
android:layout_gravity="top|end" | |
android:max="100" | |
android:progress="0" | |
android:progressDrawable="@drawable/glide_circular" /> | |
</FrameLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.pwdr.panda.image.glide.targets; | |
import android.content.Context; | |
import android.graphics.drawable.Drawable; | |
import android.support.annotation.Nullable; | |
import com.bumptech.glide.request.target.Target; | |
import com.bumptech.glide.request.transition.Transition; | |
import com.google.firebase.storage.StorageReference; | |
import com.pwdr.panda.image.glide.FirebaseAppGlideModule; | |
import com.pwdr.panda.image.glide.GlideApp; | |
public abstract class ProgressTarget<Z> extends WrappingTarget<Z> implements FirebaseAppGlideModule.UIProgressListener { | |
public static final String TAG = "ProgressTarget"; | |
private Context mContext; | |
private StorageReference model; | |
private boolean ignoreProgress = true; | |
public ProgressTarget(Context context, Target<Z> target) { | |
this(context, null, target); | |
} | |
public ProgressTarget(Context context, StorageReference model, Target<Z> target) { | |
super(target); | |
this.mContext = context; | |
this.model = model; | |
} | |
public final StorageReference getModel() { | |
return model; | |
} | |
public final void setModel(StorageReference model) { | |
GlideApp.with(mContext).clear(this); // indirectly calls cleanup | |
this.model = model; | |
} | |
/** | |
* Convert a model into an path string that is used to match up the StorageReference download requests. | |
* | |
* @param model return the representation of the given model, DO NOT use {@link #getModel()} inside this method. | |
* @return a stable path representation of the model, otherwise the progress reporting won't work | |
*/ | |
protected String toPathString(StorageReference model) { | |
if (model == null) return null; | |
return model.getPath(); | |
} | |
@Override | |
public float getGranualityPercentage() { | |
return 1.0f; | |
} | |
@Override | |
public void onProgress(long bytesRead, long expectedLength) { | |
if (ignoreProgress) { | |
return; | |
} | |
if (expectedLength == Long.MAX_VALUE) { | |
onConnecting(); | |
} else if (bytesRead == expectedLength) { | |
onDownloaded(); | |
} else { | |
onDownloading(bytesRead, expectedLength); | |
} | |
} | |
/** | |
* Called when the Glide load has started. | |
* At this time it is not known if the Glide will even go and use the network to fetch the image. | |
*/ | |
protected abstract void onConnecting(); | |
/** | |
* Called when there's any progress on the download; not called when loading from cache. | |
* At this time we know how many bytes have been transferred through the wire. | |
*/ | |
protected abstract void onDownloading(long bytesRead, long expectedLength); | |
/** | |
* Called when the bytes downloaded reach the length reported by the server; not called when loading from cache. | |
* At this time it is fairly certain, that Glide either finished reading the stream. | |
* This means that the image was either already decoded or saved the network stream to cache. | |
* In the latter case there's more work to do: decode the image from cache and transform. | |
* These cannot be listened to for progress so it's unsure how fast they'll be, best to show indeterminate progress. | |
*/ | |
protected abstract void onDownloaded(); | |
/** | |
* Called when the Glide load has finished either by successfully loading the image or failing to load or cancelled. | |
* In any case the best is to hide/reset any progress displays. | |
*/ | |
protected abstract void onDelivered(); | |
private void start() { | |
FirebaseAppGlideModule.expect(toPathString(model), this); | |
ignoreProgress = false; | |
onProgress(0, Long.MAX_VALUE); | |
} | |
private void cleanup() { | |
ignoreProgress = true; | |
StorageReference model = this.model; // save in case it gets modified | |
onDelivered(); | |
FirebaseAppGlideModule.forget(toPathString(model)); | |
this.model = null; | |
} | |
@Override | |
public void onLoadStarted(Drawable placeholder) { | |
super.onLoadStarted(placeholder); | |
start(); | |
} | |
@Override | |
public void onResourceReady(Z resource, Transition<? super Z> transition) { | |
cleanup(); | |
super.onResourceReady(resource, transition); | |
} | |
@Override | |
public void onLoadFailed(@Nullable Drawable errorDrawable) { | |
cleanup(); | |
super.onLoadFailed(errorDrawable); | |
} | |
@Override | |
public void onLoadCleared(Drawable placeholder) { | |
cleanup(); | |
super.onLoadCleared(placeholder); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.graphics.drawable.Drawable; | |
import android.support.annotation.Nullable; | |
import com.bumptech.glide.request.Request; | |
import com.bumptech.glide.request.target.SizeReadyCallback; | |
import com.bumptech.glide.request.target.Target; | |
import com.bumptech.glide.request.transition.Transition; | |
public class WrappingTarget<Z> implements Target<Z> { | |
protected final Target<Z> target; | |
public WrappingTarget(Target<Z> target) { | |
this.target = target; | |
} | |
@Override | |
public void getSize(SizeReadyCallback cb) { | |
target.getSize(cb); | |
} | |
@Override | |
public void removeCallback(SizeReadyCallback cb) { | |
target.removeCallback(cb); | |
} | |
@Override | |
public void onLoadStarted(Drawable placeholder) { | |
target.onLoadStarted(placeholder); | |
} | |
@Override | |
public void onLoadFailed(@Nullable Drawable errorDrawable) { | |
target.onLoadFailed(errorDrawable); | |
} | |
@Override | |
public void onResourceReady(Z resource, Transition<? super Z> transition) { | |
target.onResourceReady(resource, transition); | |
} | |
@Override | |
public void onLoadCleared(Drawable placeholder) { | |
target.onLoadCleared(placeholder); | |
} | |
@Override | |
public Request getRequest() { | |
return target.getRequest(); | |
} | |
@Override | |
public void setRequest(Request request) { | |
target.setRequest(request); | |
} | |
@Override | |
public void onStart() { | |
target.onStart(); | |
} | |
@Override | |
public void onStop() { | |
target.onStop(); | |
} | |
@Override | |
public void onDestroy() { | |
target.onDestroy(); | |
} | |
} |
For example, "com.pwdr.panda.image.glide.GlideApp" and FirebaseAppGlideModule are missing?
@roconmachine Sorry, I edited gist, CoreAppGlideModule.java
to FirebaseAppGlideModule.java
.
com.pwdr.panda.image.glide.GlideApp.java
will be generated automatically by Glide library, you don't need to create the class manually.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@CameraCornet Sorry I just saw your comment now, if you still need any help please feel free to ask me anytime.