I try to display images from internet in Google map marker using Fresco (following this guide), but only a placeholder is displayed.
In callback marker is redrawing. Fresco's DraweeView is working fine with my url.
My marker:
public class MarkerHolder {
private final String TAG = MarkerHolder.class.getSimpleName();
private DraweeHolder mDraweeHolder;
private Handler mUiHandler;
public MarkerHolder() {
mUiHandler = new Handler(Looper.getMainLooper());
}
public void create(Context context, Uri uri, Drawable placeHolderDrawable, Callback<Drawable> drawCallback) {
mDraweeHolder = DraweeHolder.create(new GenericDraweeHierarchyBuilder(context.getResources())
.setPlaceholderImage(placeHolderDrawable)
.build(), context);
mDraweeHolder.setController(Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setOldController(mDraweeHolder.getController())
.build());
mDraweeHolder.getTopLevelDrawable().setCallback(new Drawable.Callback() {
#Override
public void invalidateDrawable(#NonNull Drawable who) {
mDraweeHolder.onDraw();
drawCallback.onAction(who);
}
#Override
public void scheduleDrawable(#NonNull Drawable who, #NonNull Runnable what, long when) {
mUiHandler.postAtTime(what, who, when);
}
#Override
public void unscheduleDrawable(#NonNull Drawable who, #NonNull Runnable what) {
mUiHandler.removeCallbacks(what, who);
}
});
mDraweeHolder.onAttach();
mDraweeHolder.onDraw();
drawCallback.onAction(mDraweeHolder.getTopLevelDrawable());
}
public void destroy() {
mDraweeHolder.onDetach();
mDraweeHolder.getTopLevelDrawable().setCallback(null);
}
public interface Callback<T> {
public void onAction(T t);
}
}
Related
I am making an Android TV app. I want to load images from URLs into BackgroundManager drawables using Picasso.
In Glide, it is done using the following code:
Glide.with(getActivity())
.load(uri)
.centerCrop()
.error(mDefaultBackground)
.into(new SimpleTarget<GlideDrawable>(width, height) {
#Override
public void onResourceReady(GlideDrawable resource,
GlideAnimation<? super GlideDrawable> glideAnimation) {
mBackgroundManager.setDrawable(resource);
}
});
How do I do the same using Picasso?
here is solution i found.
public class PicassoBackgroundManager {
private static final String TAG = PicassoBackgroundManager.class.getSimpleName();
private static int BACKGROUND_UPDATE_DELAY = 500;
private final int DEFAULT_BACKGROUND_RES_ID = R.drawable.default_background;
private static Drawable mDefaultBackground;
// Handler attached with main thread
private final Handler mHandler = new Handler(Looper.getMainLooper());
private Activity mActivity;
private BackgroundManager mBackgroundManager = null;
private DisplayMetrics mMetrics;
private URI mBackgroundURI;
private PicassoBackgroundManagerTarget mBackgroundTarget;
Timer mBackgroundTimer; // null when no UpdateBackgroundTask is running.
public PicassoBackgroundManager (Activity activity) {
mActivity = activity;
mDefaultBackground = activity.getDrawable(DEFAULT_BACKGROUND_RES_ID);
mBackgroundManager = BackgroundManager.getInstance(activity);
mBackgroundManager.attach(activity.getWindow());
mBackgroundTarget = new PicassoBackgroundManagerTarget(mBackgroundManager);
mMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
/**
* if UpdateBackgroundTask is already running, cancel this task and start new task.
*/
private void startBackgroundTimer() {
if (mBackgroundTimer != null) {
mBackgroundTimer.cancel();
}
mBackgroundTimer = new Timer();
/* set delay time to reduce too much background image loading process */
mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
}
private class UpdateBackgroundTask extends TimerTask {
#Override
public void run() {
/* Here is TimerTask thread, not UI thread */
mHandler.post(new Runnable() {
#Override
public void run() {
/* Here is main (UI) thread */
if (mBackgroundURI != null) {
updateBackground(mBackgroundURI);
}
}
});
}
}
public void updateBackgroundWithDelay(String url) {
try {
URI uri = new URI(url);
updateBackgroundWithDelay(uri);
} catch (URISyntaxException e) {
/* skip updating background */
Log.e(TAG, e.toString());
}
}
/**
* updateBackground with delay
* delay time is measured in other Timer task thread.
* #param uri
*/
public void updateBackgroundWithDelay(URI uri) {
mBackgroundURI = uri;
startBackgroundTimer();
}
private void updateBackground(URI uri) {
try {
Picasso.with(mActivity)
.load(uri.toString())
.resize(mMetrics.widthPixels, mMetrics.heightPixels)
.centerCrop()
.error(mDefaultBackground)
.into(mBackgroundTarget);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
/**
* Copied from AOSP sample code.
* Inner class
* Picasso target for updating default_background images
*/
public class PicassoBackgroundManagerTarget implements Target {
BackgroundManager mBackgroundManager;
public PicassoBackgroundManagerTarget(BackgroundManager backgroundManager) {
this.mBackgroundManager = backgroundManager;
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
this.mBackgroundManager.setBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable drawable) {
this.mBackgroundManager.setDrawable(drawable);
}
#Override
public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PicassoBackgroundManagerTarget that = (PicassoBackgroundManagerTarget) o;
if (!mBackgroundManager.equals(that.mBackgroundManager))
return false;
return true;
}
#Override
public int hashCode() {
return mBackgroundManager.hashCode();
}
}
}
Then in your activity or fragment use this snippet below
PicassoBackgroundManager picassoBackgroundManager = new PicassoBackgroundManager(getActivity());
picassoBackgroundManager.updateBackgroundWithDelay("some.image.url");
I am implementing an android fingerprint authentication. I want to know which user, who has registered in device before, is authenticating. Is there any information about the user, who has registered and is valid for the device, in the FingerprintManager.AuthenticationResult argument in onAuthenticationSucceeded method?!
I am using this sample.
this is my class, which is implementing FingerprintManager.AuthenticationCallback:
public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
private static final long ERROR_TIMEOUT_MILLIS = 1600;
private static final long SUCCESS_DELAY_MILLIS = 1300;
private final FingerprintManager mFingerprintManager;
private final ImageView mIcon;
private final TextView mErrorTextView;
private final Callback mCallback;
private CancellationSignal mCancellationSignal;
private boolean mSelfCancelled;
/**
* Constructor for {#link FingerprintUiHelper}.
*/
FingerprintUiHelper(FingerprintManager fingerprintManager,
ImageView icon, TextView errorTextView, Callback callback) {
mFingerprintManager = fingerprintManager;
mIcon = icon;
mErrorTextView = errorTextView;
mCallback = callback;
}
public boolean isFingerprintAuthAvailable() {
// The line below prevents the false positive inspection from Android Studio
// noinspection ResourceType
return mFingerprintManager.isHardwareDetected()
&& mFingerprintManager.hasEnrolledFingerprints();
}
public void startListening(FingerprintManager.CryptoObject cryptoObject) {
if (!isFingerprintAuthAvailable()) {
return;
}
mCancellationSignal = new CancellationSignal();
mSelfCancelled = false;
// The line below prevents the false positive inspection from Android Studio
// noinspection ResourceType
mFingerprintManager
.authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
public void stopListening() {
if (mCancellationSignal != null) {
mSelfCancelled = true;
mCancellationSignal.cancel();
mCancellationSignal = null;
}
}
#Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
if (!mSelfCancelled) {
showError(errString);
mIcon.postDelayed(new Runnable() {
#Override
public void run() {
mCallback.onError();
}
}, ERROR_TIMEOUT_MILLIS);
}
}
#Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
showError(helpString);
}
#Override
public void onAuthenticationFailed() {
showError(mIcon.getResources().getString(
R.string.fingerprint_not_recognized));
}
#Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mIcon.setImageResource(R.drawable.ic_fingerprint_success);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.success_color, null));
mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.fingerprint_success));
mIcon.postDelayed(new Runnable() {
#Override
public void run() {
mCallback.onAuthenticated();
}
}, SUCCESS_DELAY_MILLIS);
}
private void showError(CharSequence error) {
mIcon.setImageResource(R.drawable.ic_fingerprint_error);
mErrorTextView.setText(error);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.warning_color, null));
mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
}
private Runnable mResetErrorTextRunnable = new Runnable() {
#Override
public void run() {
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.hint_color, null));
mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.fingerprint_hint));
mIcon.setImageResource(R.drawable.ic_fp_40px);
}
};
public interface Callback {
void onAuthenticated();
void onError();
}
}
I am using palette-v7:23.2.1 with glide:3.7.0 like mentioned below, but sometimes the dark vibrant color is not successfully extracted and I get the default color instead.
After I clear the glide's cache and try it with the same image again, I get the right color. The strange is, that light vibrant color is always extracted, but the dark one not.
What could be the problem and how to solve it?
In onCreateView():
Glide.with(mContext)
.load(artworkUrl)
.asBitmap()
.into(new BitmapImageViewTarget(mArtworkInToolbar) {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
new Palette.Builder(bitmap).generate(paletteAsyncListener);
super.onResourceReady(bitmap, anim);
}
});
Listener:
public final Palette.PaletteAsyncListener paletteAsyncListener =
new Palette.PaletteAsyncListener() {
#Override
public void onGenerated(Palette palette) {
if (palette == null) return;
int default = ContextCompat.getColor(mContext, R.color.colorPrimary);
int color = palette.getVibrantColor(default); //always ok
int colorDark = palette.getDarkVibrantColor(default); //not always
// --- Setting the color --
}
};
***Here this is worked for me***
make a class name PaletteBitmap.java
public class PaletteBitmap {
public final Palette palette;
public final Bitmap bitmap;
public PaletteBitmap(#NonNull Bitmap bitmap, #NonNull Palette palette) {
this.bitmap = bitmap;
this.palette = palette;
}
Bitmap getBitmap()
{
return bitmap;
}
}
class PaletteBitmapResource implements Resource<PaletteBitmap> {
private final PaletteBitmap paletteBitmap;
private final BitmapPool bitmapPool;
public PaletteBitmapResource(#NonNull PaletteBitmap paletteBitmap, #NonNull BitmapPool bitmapPool) {
this.paletteBitmap = paletteBitmap;
this.bitmapPool = bitmapPool;
}
#Override public PaletteBitmap get() {
return paletteBitmap;
}
#Override public int getSize() {
return Util.getBitmapByteSize(paletteBitmap.bitmap);
}
#Override public void recycle() {
if (!bitmapPool.put(paletteBitmap.bitmap)) {
paletteBitmap.bitmap.recycle();
}
}
}
class PaletteBitmapTranscoder implements ResourceTranscoder<Bitmap, PaletteBitmap> {
private final BitmapPool bitmapPool;
public PaletteBitmapTranscoder(#NonNull Context context) {
this.bitmapPool = Glide.get(context).getBitmapPool();
}
#Override public Resource<PaletteBitmap> transcode(Resource<Bitmap> toTranscode) {
Bitmap bitmap = toTranscode.get();
Palette palette = new Palette.Builder(bitmap).generate();
PaletteBitmap result = new PaletteBitmap(bitmap, palette);
return new PaletteBitmapResource(result, bitmapPool);
}
#Override public String getId() {
return PaletteBitmapTranscoder.class.getName();
}
}
**Now in Your onCreateView() do like this**
Glide.with(this).fromUri().asBitmap()
.transcode(new PaletteBitmapTranscoder(this),PaletteBitmap.class)
.fitCenter()load(uri).into(new ImageViewTarget<PaletteBitmap>(imageView){
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
protected void setResource(PaletteBitmap resource) {
imageView.setImageBitmap(resource.getBitmap());
int colorP = resource.palette.getMutedColor(ContextCompat.getColor(context, R.color.colorPrimary));
int colorD=resource.palette.getDarkMutedColor(ContextCompat.getColor(context,R.color .colorPrimaryDark));
// dowhatever you want
}
});;
In Picasso there exists the RequestHandler class. And I can add custom RequestHandlers to Picasso.
How can this be done in Glide?
I for example want that following URI can be handled by a custom RequestHandler: "appicon:custom_data_to_interprete_manually"
EDIT - what I have so far
public class GlideConfiguration implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) {
// Apply options to the builder here.
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
#Override
public void registerComponents(Context context, Glide glide) {
glide.register(CustomModelParams.class, CustomModelParams.class, new CustomFactory());
}
class CustomModelParams
{
final String data;
public CustomModelParams(String data)
{
this.data = data;
}
public String getId()
{
return data;
}
}
class CustomFactory implements ModelLoaderFactory<CustomModelParams, CustomModelParams>
{
#Override
public ModelLoader<CustomModelParams, CustomModelParams> build(Context context, GenericLoaderFactory loaderFactory) {
return new CustomModelLoader();
}
#Override
public void teardown() {
}
}
class CustomModelLoader implements ModelLoader<CustomModelParams, CustomModelParams>
{
public CustomModelLoader() {
super();
}
#Override
public DataFetcher<CustomModelParams> getResourceFetcher(final CustomModelParams model, int width, int height)
{
return new DataFetcher<CustomModelParams>()
{
#Override
public CustomModelParams loadData(Priority priority) throws Exception { return model; }
#Override
public void cleanup() { }
#Override
public String getId() { return model.getId(); }
#Override
public void cancel() { }
};
}
}
class CustomBitmapDecoder implements ResourceDecoder<CustomModelParams, Bitmap>
{
private final Context context;
public CustomBitmapDecoder(Context context)
{
this.context = context;
}
#Override
public Resource<Bitmap> decode(CustomModelParams source, int width, int height) throws IOException
{
BitmapPool pool = Glide.get(context).getBitmapPool();
Bitmap bitmap = pool.getDirty(width, height, Bitmap.Config.ARGB_8888);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
// TODO
// create custom bitmap from CustomModelParams!!!
return BitmapResource.obtain(bitmap, pool);
}
#Override
public String getId()
{
return CustomBitmapDecoder.class.getName();
}
}
}
QUESTION
How do I link those classes together? The Decoder must somehow be linked with the new Model
How do I define, that my custom loader can handle a request? I have to somehow determine if the url I get can be handled by this loader...
You can use ModelLoaders. See the Downloading custom sizes wiki for an example of a custom ModelLoader. Note that instead of a BaseGlideUrlLoader, you will want to register a ModelLoader that handles your particular data model.
I have implemented very simular code for my project (It loads other applications icons from Package Manager) and
#Override
public void registerComponents(Context context, Glide glide) {
glide.register(MyDataModel.class, Bitmap.class, new MyUrlLoader.Factory());
}
doesn't work for me.
So to make custom loaded works I just use Glide Builder
private final GenericRequestBuilder<MyDataModel, Bitmap, Bitmap, Bitmap> mGlideBuilder;
mGlideBuilder = Glide.with(mContext)
.using(new MyUrlLoader(mContext), Bitmap.class)
.from(MyDataModel.class)
.as(Bitmap.class)
.decoder(new MyBitmapDecoder())
.diskCacheStrategy(DiskCacheStrategy.NONE);
mGlideBuilder.load(entry).into(holder.icon);
and MyBitmapDecoder and MyUrlLoader declared as
public class MyBitmapDecoder implements ResourceDecoder<Bitmap, Bitmap> {
public class MyUrlLoader implements ModelLoader<MyDataModel, Bitmap> {
Picasso is asynchronous, so i was wondering if there is any way i can test if an image is fully loaded, before executing any additional code?
Picasso.with(context).load(imageURI).into(ImageView);
// image fully loaded? do something else ..
If the image is fully loaded it will be set on the ImageView synchronously.
You can use the callback to assert this.
final AtomicBoolean loaded = new AtomicBoolean();
Picasso.with(context).load(imageURI).into(imageView, new Callback.EmptyCallback() {
#Override public void onSuccess() {
loaded.set(true);
}
});
if (loaded.get()) {
// The image was immediately available.
}
Using overloaded method .into(ImageView target, Callback callback) is appropriate for your case. You can use the base implementation or extend your own like
Base:
Picasso.with(context).load(url).into(target, new Callback(){
#Override
public void onSuccess() {
}
#Override
public void onError() {
}
});
Extended version:
package main.java.app.picasso.test;
/**
* Created by nikola on 9/9/14.
*/
public abstract class TargetCallback implements Callback {
private ImageView mTarget;
public abstract void onSuccess(ImageView target);
public abstract void onError(ImageView target);
public TargetCallback(ImageView imageView){
mTarget = imageView;
}
#Override
public void onSuccess() {
onSuccess(mTarget);
}
#Override
public void onError() {
onError(mTarget);
}
}
Usage:
Picasso.with(context).load(url).into(target, new TargetCallback(target) {
#Override
public void onSuccess(ImageView target) {
}
#Override
public void onError(ImageView target) {
}
});