Android Open Source - Muzei Artwork Cache






From Project

Back to project page Muzei.

License

The source code is released under:

Apache License

If you think the Android project Muzei listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 Google Inc./* w  ww  .  j a  va  2  s  .co m*/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.apps.muzei;

import android.app.AlarmManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.Pair;

import com.google.android.apps.muzei.api.Artwork;
import com.google.android.apps.muzei.api.internal.SourceState;
import com.google.android.apps.muzei.event.ArtworkLoadingStateChangedEvent;
import com.google.android.apps.muzei.event.CurrentArtworkDownloadedEvent;
import com.google.android.apps.muzei.render.BitmapRegionLoader;
import com.google.android.apps.muzei.util.IOUtil;
import com.google.android.apps.muzei.util.LogUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import de.greenrobot.event.EventBus;

import static com.google.android.apps.muzei.util.LogUtil.LOGE;
import static com.google.android.apps.muzei.util.LogUtil.LOGW;

public class ArtworkCache {
    private static final String TAG = LogUtil.makeLogTag(ArtworkCache.class);

    private Context mApplicationContext;
    private File mAppCacheRoot;
    private File mArtCacheRoot;

    private static final int MAX_CACHE_SIZE = 3; // 3 items per source

    private static final String PREF_ARTWORK_DOWNLOAD_ATTEMPT = "artwork_download_attempt";

    private static ArtworkCache sInstance;

    public static ArtworkCache getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new ArtworkCache(context);
        }

        return sInstance;
    }

    private ArtworkCache(Context context) {
        mApplicationContext = context.getApplicationContext();

        // TODO: instead of best available, optimize for stable location since these aren't
        // meant to be too temporary
        mAppCacheRoot = IOUtil.getBestAvailableCacheRoot(mApplicationContext);
        mArtCacheRoot = new File(mAppCacheRoot, "artcache");
    }

    public synchronized void maybeDownloadCurrentArtworkSync() {
        SourceManager sm = SourceManager.getInstance(mApplicationContext);
        ComponentName selectedSource = sm.getSelectedSource();
        SourceState selectedSourceState = sm.getSelectedSourceState();
        Artwork currentArtwork = selectedSourceState != null
                ? selectedSourceState.getCurrentArtwork() : null;
        if (currentArtwork == null) {
            return;
        }

        File destFile = getArtworkCacheFile(selectedSource, currentArtwork);
        if (destFile == null) {
            return;
        }

        if (destFile.exists() && destFile.length() > 0) {
            EventBus.getDefault().postSticky(new ArtworkLoadingStateChangedEvent(false, false));
            EventBus.getDefault().post(new CurrentArtworkDownloadedEvent());
            return;
        }

        // ensure cache root for this source exists
        destFile.getParentFile().mkdirs();

        EventBus.getDefault().postSticky(new ArtworkLoadingStateChangedEvent(true, false));

        InputStream in;
        try {
            in = IOUtil.openUri(mApplicationContext, currentArtwork.getImageUri(), "image/");
        } catch (IOUtil.OpenUriException e) {
            LOGE(TAG, "Error downloading current artwork. URI: " + currentArtwork.getImageUri(), e);
            if (e.isRetryable()) {
                scheduleRetryArtworkDownload();
            }
            EventBus.getDefault().postSticky(new ArtworkLoadingStateChangedEvent(false, true));
            return;
        }

        cancelArtworkDownloadRetries();

        // Input stream successfully opened. Save to cache file
        try {
            File tempFile = new File(mArtCacheRoot, "temp.download");
            IOUtil.readFullyWriteToFile(in, tempFile);
            destFile.delete();
            if (!tempFile.renameTo(destFile)) {
                throw new IOException("Couldn't move temp artwork file to final cache location.");
            }
            // Attempt to parse the newly downloaded file as an image, ensuring it is in a valid format
            BitmapRegionLoader.newInstance(new FileInputStream(destFile));
        } catch (IOException e) {
            LOGE(TAG, "Error caching and loading the current artwork. URI: " + currentArtwork.getImageUri(), e);
            destFile.delete();
            EventBus.getDefault().postSticky(new ArtworkLoadingStateChangedEvent(false, true));
            scheduleRetryArtworkDownload();
            return;
        }

        cleanupCache(selectedSource);

        EventBus.getDefault().postSticky(new ArtworkLoadingStateChangedEvent(false, false));
        EventBus.getDefault().post(new CurrentArtworkDownloadedEvent());
    }

    public File getArtworkCacheFile(ComponentName source, Artwork artwork) {
        File cacheRootForSource = getCacheRootForSource(source);
        if (cacheRootForSource == null) {
            LOGW(TAG, "Empty source cache root.");
            return null;
        }

        if (artwork == null) {
            LOGW(TAG, "Empty artwork info.");
            return null;
        }

        Uri uri = artwork.getImageUri();
        if (uri == null) {
            LOGW(TAG, "Empty artwork image.");
            return null;
        }

        return new File(cacheRootForSource, IOUtil.getCacheFilenameForUri(uri));
    }

    private File getCacheRootForSource(ComponentName source) {
        if (source == null) {
            LOGW(TAG, "Empty source.");
            return null;
        }

        String sourceDirName = source.flattenToShortString().replace('/', '_').replace('$', '_');
        return new File(mArtCacheRoot, sourceDirName);
    }

    private void cleanupCache(ComponentName source) {
        // Ensure cache doesn't go over MAX_CACHE_SIZE
        File[] cacheFiles = getCacheRootForSource(source).listFiles();
        if (cacheFiles == null || cacheFiles.length < MAX_CACHE_SIZE) {
            return;
        }

        SortedSet<Pair<Long, File>> latestFiles = new TreeSet<Pair<Long, File>>(
                new Comparator<Pair<Long, File>>() {
                    @Override
                    public int compare(Pair<Long, File> p1, Pair<Long, File> p2) {
                        return -(p1.first < p2.first ? -1 : 1);
                    }
                });

        for (File file : cacheFiles) {
            latestFiles.add(new Pair<Long, File>(file.lastModified(), file));
        }

        // Remove everything but the first MAX_CACHE_SIZE files in the latest files set
        Iterator<Pair<Long, File>> it = latestFiles.iterator();
        int n = 0;
        while (it.hasNext()) {
            File cacheFile = it.next().second;
            if (++n <= MAX_CACHE_SIZE) {
                continue;
            }

            cacheFile.delete();
        }
    }

    private void cancelArtworkDownloadRetries() {
        AlarmManager am = (AlarmManager) mApplicationContext
                .getSystemService(Context.ALARM_SERVICE);
        am.cancel(TaskQueueService.getArtworkDownloadRetryPendingIntent(mApplicationContext));
    }

    private void scheduleRetryArtworkDownload() {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mApplicationContext);
        int reloadAttempt = sp.getInt(PREF_ARTWORK_DOWNLOAD_ATTEMPT, 0);
        sp.edit().putInt(PREF_ARTWORK_DOWNLOAD_ATTEMPT, reloadAttempt + 1).commit();

        AlarmManager am = (AlarmManager) mApplicationContext
                .getSystemService(Context.ALARM_SERVICE);
        long retryTimeMillis = SystemClock.elapsedRealtime() + (1 << reloadAttempt) * 2000;
        am.set(AlarmManager.ELAPSED_REALTIME, retryTimeMillis,
                TaskQueueService.getArtworkDownloadRetryPendingIntent(mApplicationContext));
    }

    public static Intent maybeRetryDownloadDueToGainedConnectivity(Context context) {
        return (PreferenceManager.getDefaultSharedPreferences(context)
                .getInt(PREF_ARTWORK_DOWNLOAD_ATTEMPT, 0) > 0)
                ? TaskQueueService.getDownloadCurrentArtworkIntent(context)
                : null;
    }
}




Java Source Code List

com.example.muzei.examplecontractwidget.ArtworkUpdateReceiver.java
com.example.muzei.examplecontractwidget.ArtworkUpdateService.java
com.example.muzei.examplecontractwidget.MuzeiAppWidgetProvider.java
com.example.muzei.examplesource500px.Config.java
com.example.muzei.examplesource500px.FiveHundredPxExampleArtSource.java
com.example.muzei.examplesource500px.FiveHundredPxService.java
com.example.muzei.watchface.ArtworkImageLoader.java
com.example.muzei.watchface.MuzeiExampleWatchface.java
com.example.muzei.watchface.WatchfaceArtworkImageLoader.java
com.google.android.apps.muzei.ActivateMuzeiIntentService.java
com.google.android.apps.muzei.ArtDetailViewport.java
com.google.android.apps.muzei.ArtworkCacheIntentService.java
com.google.android.apps.muzei.ArtworkCache.java
com.google.android.apps.muzei.FullScreenActivity.java
com.google.android.apps.muzei.LockScreenVisibleReceiver.java
com.google.android.apps.muzei.MuzeiActivity.java
com.google.android.apps.muzei.MuzeiApplication.java
com.google.android.apps.muzei.MuzeiWallpaperService.java
com.google.android.apps.muzei.MuzeiWatchFace.java
com.google.android.apps.muzei.MuzeiWearableListenerService.java
com.google.android.apps.muzei.MuzeiWearableListenerService.java
com.google.android.apps.muzei.NetworkChangeReceiver.java
com.google.android.apps.muzei.NewWallpaperNotificationReceiver.java
com.google.android.apps.muzei.PhotoSetAsTargetActivity.java
com.google.android.apps.muzei.SourceManager.java
com.google.android.apps.muzei.SourcePackageChangeReceiver.java
com.google.android.apps.muzei.SourceSubscriberService.java
com.google.android.apps.muzei.TaskQueueService.java
com.google.android.apps.muzei.WearableController.java
com.google.android.apps.muzei.api.Artwork.java
com.google.android.apps.muzei.api.MuzeiArtSource.java
com.google.android.apps.muzei.api.MuzeiContract.java
com.google.android.apps.muzei.api.RemoteMuzeiArtSource.java
com.google.android.apps.muzei.api.UserCommand.java
com.google.android.apps.muzei.api.internal.ProtocolConstants.java
com.google.android.apps.muzei.api.internal.SourceState.java
com.google.android.apps.muzei.event.ArtDetailOpenedClosedEvent.java
com.google.android.apps.muzei.event.ArtworkLoadingStateChangedEvent.java
com.google.android.apps.muzei.event.ArtworkSizeChangedEvent.java
com.google.android.apps.muzei.event.BlurAmountChangedEvent.java
com.google.android.apps.muzei.event.CurrentArtworkDownloadedEvent.java
com.google.android.apps.muzei.event.DimAmountChangedEvent.java
com.google.android.apps.muzei.event.GainedNetworkConnectivityEvent.java
com.google.android.apps.muzei.event.GalleryChosenUrisChangedEvent.java
com.google.android.apps.muzei.event.GreyAmountChangedEvent.java
com.google.android.apps.muzei.event.LockScreenVisibleChangedEvent.java
com.google.android.apps.muzei.event.SelectedSourceChangedEvent.java
com.google.android.apps.muzei.event.SelectedSourceStateChangedEvent.java
com.google.android.apps.muzei.event.SwitchingPhotosStateChangedEvent.java
com.google.android.apps.muzei.event.WallpaperActiveStateChangedEvent.java
com.google.android.apps.muzei.event.WallpaperSizeChangedEvent.java
com.google.android.apps.muzei.featuredart.FeaturedArtSource.java
com.google.android.apps.muzei.gallery.GalleryArtSource.java
com.google.android.apps.muzei.gallery.GalleryDatabase.java
com.google.android.apps.muzei.gallery.GalleryEmptyStateGraphicView.java
com.google.android.apps.muzei.gallery.GallerySettingsActivity.java
com.google.android.apps.muzei.gallery.GalleryStore.java
com.google.android.apps.muzei.provider.MuzeiProvider.java
com.google.android.apps.muzei.render.BitmapRegionLoader.java
com.google.android.apps.muzei.render.DemoRenderController.java
com.google.android.apps.muzei.render.GLColorOverlay.java
com.google.android.apps.muzei.render.GLPicture.java
com.google.android.apps.muzei.render.GLTextureView.java
com.google.android.apps.muzei.render.GLUtil.java
com.google.android.apps.muzei.render.ImageUtil.java
com.google.android.apps.muzei.render.MuzeiBlurRenderer.java
com.google.android.apps.muzei.render.MuzeiRendererFragment.java
com.google.android.apps.muzei.render.RealRenderController.java
com.google.android.apps.muzei.render.RenderController.java
com.google.android.apps.muzei.settings.AboutActivity.java
com.google.android.apps.muzei.settings.Prefs.java
com.google.android.apps.muzei.settings.SettingsActivity.java
com.google.android.apps.muzei.settings.SettingsAdvancedFragment.java
com.google.android.apps.muzei.settings.SettingsChooseSourceFragment.java
com.google.android.apps.muzei.util.AnimatedMuzeiLoadingSpinnerView.java
com.google.android.apps.muzei.util.AnimatedMuzeiLogoFragment.java
com.google.android.apps.muzei.util.AnimatedMuzeiLogoView.java
com.google.android.apps.muzei.util.CheatSheet.java
com.google.android.apps.muzei.util.DrawInsetsFrameLayout.java
com.google.android.apps.muzei.util.IOUtil.java
com.google.android.apps.muzei.util.ImageBlurrer.java
com.google.android.apps.muzei.util.LogUtil.java
com.google.android.apps.muzei.util.LogoPaths.java
com.google.android.apps.muzei.util.MathUtil.java
com.google.android.apps.muzei.util.MultiSelectionController.java
com.google.android.apps.muzei.util.ObservableHorizontalScrollView.java
com.google.android.apps.muzei.util.PanScaleProxyView.java
com.google.android.apps.muzei.util.PanView.java
com.google.android.apps.muzei.util.ScrimUtil.java
com.google.android.apps.muzei.util.Scrollbar.java
com.google.android.apps.muzei.util.SelectionBuilder.java
com.google.android.apps.muzei.util.ShadowDipsTextView.java
com.google.android.apps.muzei.util.SvgPathParser.java
com.google.android.apps.muzei.util.TickingFloatAnimator.java
com.google.android.apps.muzei.util.TypefaceUtil.java
com.google.android.apps.muzei.util.Zoomer.java
net.rbgrn.android.glwallpaperservice.BaseConfigChooser.java
net.rbgrn.android.glwallpaperservice.GLWallpaperService.java