public class AccessToken implements Parcelable {

	private String mToken;
	private String mTokenSecret;

	public AccessToken(String token, String tokenSecret) {
		mToken = token;
		mTokenSecret = tokenSecret;
	}
	
	AccessToken(HttpResponse response) throws FiveHundredException {
		try {
			Uri responseUri = Uri.parse(EntityUtils.toString(response.getEntity()));

            mToken = responseUri.getQueryParameter("oauth_token");
			mTokenSecret = responseUri.getQueryParameter("oauth_token_secret");
		} catch (ParseException | IOException | NullPointerException e) {
            throw new FiveHundredException(e);
        }
    }

	public String getToken() {
		return mToken;
	}

	public String getTokenSecret() {
		return mTokenSecret;
	}

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mToken);
        dest.writeString(mTokenSecret);
    }

    private AccessToken(Parcel in) {
        mToken = in.readString();
        mTokenSecret = in.readString();
    }

    public static final Creator<AccessToken> CREATOR = new Creator<AccessToken>() {
        public AccessToken createFromParcel(Parcel source) {
            return new AccessToken(source);
        }

        public AccessToken[] newArray(int size) {
            return new AccessToken[size];
        }
    };
} public class FiveHundredPxOAuthActivity extends Activity {

    public static final String ACCESS_TOKEN = "com.lukekorth.fivehundredpx.fivehundredpxoauthactivity.ACCESS_TOKEN";

    private static final String EXTRA_REQUEST_MADE = "com.lukekorth.fivehundredpx.fivehundredpxoauthactivity.EXTRA_REQUEST_MADE";

    private FiveHundredPxOAuthHelper mOAuthHelper;

    private OAuthSigningRequestTask mSigningRequestTask;
    private boolean mRequestMade;

    private OAuthAccessTokenRequestTask mAccessTokenRequestTask;
    private boolean mResponseReceived;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.loading);

        if (savedInstanceState != null) {
            mRequestMade = savedInstanceState.getBoolean(EXTRA_REQUEST_MADE);
        }

        mOAuthHelper = new FiveHundredPxOAuthHelper(BuildConfig.CONSUMER_KEY, BuildConfig.CONSUMER_SECRET);

        mSigningRequestTask = new OAuthSigningRequestTask();
        mSigningRequestTask.execute();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mRequestMade && !mResponseReceived) {
            setResult(Activity.RESULT_CANCELED);
            finish();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Uri response = intent.getData();
        if (response.getScheme().equals(FiveHundredPxOAuthHelper.FIVE_HUNDRED_PX_CALLBACK_URL_SCHEME)) {
            mResponseReceived = true;
            mOAuthHelper.parseAuthorizationResponse(response);
            mAccessTokenRequestTask = new OAuthAccessTokenRequestTask();
            mAccessTokenRequestTask.execute();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(EXTRA_REQUEST_MADE, mRequestMade);
    }

    private class OAuthSigningRequestTask extends AsyncTask<Void, Void, Void> {

        private String mUrl;
        private boolean mFailed;

        @Override
        protected Void doInBackground(Void... params) {
            try {
                mUrl = mOAuthHelper.getAuthorizationUrl();
                mFailed = false;
            } catch (OAuthNotAuthorizedException | OAuthExpectationFailedException |
                    OAuthMessageSignerException | OAuthCommunicationException e) {
                mFailed = true;
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            if (mFailed) {
                setResult(Activity.RESULT_FIRST_USER);
                finish();
            } else {
                mRequestMade = true;

                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    Bundle extras = new Bundle();
                    extras.putBinder("android.support.customtabs.extra.SESSION", null);
                    intent.putExtras(extras);
                }

                startActivity(intent);
            }
        }
    }

    private class OAuthAccessTokenRequestTask extends AsyncTask<Void, Void, Void> {

        private AccessToken mAccessToken;
        private boolean mFailed;

        @Override
        protected Void doInBackground(Void... params) {
            try {
                mAccessToken = mOAuthHelper.getAccessToken();
                mFailed = false;
            } catch (OAuthCommunicationException | OAuthExpectationFailedException |
                    OAuthMessageSignerException | OAuthNotAuthorizedException e) {
                mFailed = true;
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            if (mFailed) {
                setResult(Activity.RESULT_FIRST_USER);
                finish();
            } else {
                Intent resultIntent = new Intent().putExtra(ACCESS_TOKEN, mAccessToken);
                setResult(Activity.RESULT_OK, resultIntent);
                finish();
            }
        }
    }
} public class AlarmHelper {

    public static boolean isAlarmSet(Context context) {
        return (PendingIntent.getService(context, 0,
                new Intent(context, WallpaperService.class), PendingIntent.FLAG_NO_CREATE)) != null;
    }

    public static void scheduleWallpaperAlarm(Context context) {
        scheduleWallpaperAlarm(context, Settings.getUpdateInterval(context));
    }

    public static void scheduleWallpaperAlarm(Context context, int duration) {
        removeWallpaperAlarm(context);
        long interval = duration * 1000;
        getAlarmManager(context).setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
                SystemClock.elapsedRealtime() + interval, interval, getPendingIntent(context));
    }

    public static void removeWallpaperAlarm(Context context) {
        PendingIntent pendingIntent = getPendingIntent(context);
        getAlarmManager(context).cancel(getPendingIntent(context));
        pendingIntent.cancel();
    }

    private static AlarmManager getAlarmManager(Context context) {
        return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    }

    private static PendingIntent getPendingIntent(Context context) {
        return PendingIntent.getService(context, 0, new Intent(context, WallpaperService.class), 0);
    }
} public class LogReporting {

	private Context mContext;
	private ProgressDialog mLoading;
    private Intent mEmailIntent;
	
	public LogReporting(Context context) {
		mContext = context;
	}
	
	public void collectAndSendLogs() {
		mLoading = ProgressDialog.show(mContext, "", mContext.getString(R.string.loading), true);
		new GenerateLogFile().execute();
	}

	private class GenerateLogFile extends AsyncTask<Void, Void, Void> {

		@SuppressLint("NewApi")
		@Override
		protected Void doInBackground(Void... args) {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
			StringBuilder message = new StringBuilder();
			
			message.append("Android version: " + Build.VERSION.SDK_INT + "\n");
            message.append("Device manufacturer: " + Build.MANUFACTURER + "\n");
            message.append("Device model: " + Build.MODEL + "\n");
            message.append("Device product: " + Build.PRODUCT + "\n");
			message.append("App version: " + BuildConfig.VERSION_NAME + "\n");
            message.append("Debug: " + BuildConfig.DEBUG + "\n");
            message.append("Wallpaper height: " + Utils.getWallpaperHeight(mContext) + "\n");
            message.append("Wallpaper width: " + Utils.getWallpaperWidth(mContext) + "\n");
            message.append("Screen height: " + Utils.getScreenHeight(mContext) + "\n");
            message.append("Screen width: " + Utils.getScreenWidth(mContext) + "\n");
            message.append("Supports parallax: " + Utils.supportsParallax(mContext) + "\n");

			Map<String,?> keys = prefs.getAll();
			for(Map.Entry<String,?> entry : keys.entrySet()) {
                message.append(entry.getKey() + ": " + entry.getValue().toString() + "\n");
			}
			message.append("---------------------------");
            message.append("\n");

            try {
                mEmailIntent = MailableLog.buildEmailIntent(mContext, "photo-paper@lukekorth.com",
                        "Photo Paper Debug Log", "photo-paper.log", message.toString());
            } catch (IOException e) {
                LoggerFactory.getLogger("LogReporting").error(e.getMessage());
            }

            // Ensure we show the spinner and don't just flash the screen
            SystemClock.sleep(1000);

            return null;
		}

        @Override
		protected void onPostExecute(Void args) {
			if(mLoading != null && mLoading.isShowing()) {
                mLoading.cancel();
            }
		    mContext.startActivity(mEmailIntent);
		}
	}
} public class PicassoHelper {

    public static Picasso getPicasso(Context context) {
        return new Picasso.Builder(context.getApplicationContext())
                .downloader(new OkHttp3Downloader(context.getApplicationContext(), 512000000)) // 512mb
                .indicatorsEnabled(BuildConfig.DEBUG)
                .build();
    }

    public static void clearCache(Context context) {
        PicassoTools.clearCache(PicassoHelper.getPicasso(context));
    }
} public class Settings {

    private static SharedPreferences getPrefs(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context);
    }

    public static String getFavoriteGallery(Context context) {
        return getPrefs(context).getString("favorite_gallery", null);
    }

    public static String getFavoriteGalleryId(Context context) {
        return getPrefs(context).getString("favorite_gallery_id", null);
    }

    public static void setFavoriteGallery(Context context, String gallery) {
        getPrefs(context).edit().putString("favorite_gallery", gallery).apply();
    }

    public static void setFavoriteGalleryId(Context context, String id) {
        getPrefs(context).edit().putString("favorite_gallery_id", id).apply();
    }

    public static String getFeature(Context context) {
        return getPrefs(context).getString("feature", "popular");
    }

    public static void setFeature(Context context, String feature) {
        getPrefs(context).edit().putString("feature", feature).apply();
    }

    public static String getSearchQuery(Context context) {
        return getPrefs(context).getString("search_query", "");
    }

    public static void setSearchQuery(Context context, String query) {
        getPrefs(context).edit().putString("search_query", query).apply();
    }

    public static int[] getCategories(Context context) {
        Set<String> defaultCategory = new HashSet<String>();
        defaultCategory.add("8");
        Set<String> prefCategories = getPrefs(context).getStringSet("categories", defaultCategory);

        int[] categories = new int[prefCategories.size()];
        int i = 0;
        for (String category : prefCategories) {
            categories[i] = Integer.parseInt(category);
            i++;
        }

        return categories;
    }

    public static void setCategories(Context context, Set<String> categories) {
        getPrefs(context).edit().putStringSet("categories", categories).apply();
    }

    public static boolean isEnabled(Context context) {
        return getPrefs(context).getBoolean("enable", false);
    }

    public static int getUpdateInterval(Context context) {
        return Integer.parseInt(getPrefs(context).getString("update_interval", "3600"));
    }

    public static void setUpdateInterval(Context context, String interval) {
        getPrefs(context).edit().putString("update_interval", interval).apply();
    }

    public static boolean useParallax(Context context) {
        return getPrefs(context).getBoolean("use_parallax", false);
    }

    public static boolean useOnlyWifi(Context context) {
        return getPrefs(context).getBoolean("use_only_wifi", true);
    }

    public static long getLastUpdated(Context context) {
        return getPrefs(context).getLong("last_updated", 0);
    }

    public static void setUpdated(Context context) {
        getPrefs(context).edit().putLong("last_updated", System.currentTimeMillis()).apply();
    }

    public static void clearUpdated(Context context) {
        getPrefs(context).edit().putLong("last_updated", 0).apply();
    }
} public class Utils {

    public static boolean shouldGetPhotos(Context context, Realm realm) {
        return Settings.isEnabled(context) && Utils.needMorePhotos(context, realm) &&
                isCurrentNetworkOk(context);
    }

    public static boolean shouldUpdateWallpaper(Context context) {
        return Settings.isEnabled(context) && (((Settings.getLastUpdated(context) +
                 (Settings.getUpdateInterval(context) * 1000)) < System.currentTimeMillis()));
    }

    public static boolean isCurrentNetworkOk(Context context) {
        return !Settings.useOnlyWifi(context) ||
                (Settings.useOnlyWifi(context) && Utils.isConnectedToWifi(context));
    }

    public static boolean needMorePhotos(Context context, Realm realm) {
        return Photos.unseenPhotoCount(context, realm) <= 10;
    }

    public static boolean isConnectedToWifi(Context context) {
        ConnectivityManager connectivityManager =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
        return (activeNetwork != null && activeNetwork.isConnectedOrConnecting() &&
                activeNetwork.getType() == ConnectivityManager.TYPE_WIFI);
    }

    public static int dpToPx(Context context, double dp) {
        return (int) Math.round(dp * (context.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
    }

    public static int getWallpaperHeight(Context context) {
        return WallpaperManager.getInstance(context).getDesiredMinimumHeight();
    }

    public static int getWallpaperWidth(Context context) {
        return WallpaperManager.getInstance(context).getDesiredMinimumWidth();
    }

    public static int getScreenHeight(Context context) {
        return getScreenResolution(context).y;
    }

    public static int getScreenWidth(Context context) {
        return getScreenResolution(context).x;
    }

    private static Point getScreenResolution(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Point point = new Point();
        windowManager.getDefaultDisplay().getSize(point);
        return point;
    }

    public static boolean supportsParallax(Context context) {
        return ((double) getWallpaperWidth(context) / getScreenWidth(context)) >= 2;
    }

    public static String getListSummary(Context context, int indexArrayId, int valueArrayId,
                                        String index, String defaultValue) {
        String[] indexArray = context.getResources().getStringArray(indexArrayId);
        String[] valueArray = context.getResources().getStringArray(valueArrayId);
        int i;
        for (i = 0; i < indexArray.length; i++) {
            if (indexArray[i].equals(index)) {
                return valueArray[i];
            }
        }
        return defaultValue;
    }
} public class ActivityResumedEvent {
} public class Photos extends RealmObject {

    public static final String BASE_URL_500PX = "http://500px.com";

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    @Expose
    @SerializedName("id")
    @Required
    private String id;

    @Expose
    private String name;

    @Expose
    private String description;

    @Expose
    private String userName;

    @Expose
    private long createdAt;

    @Expose
    private String feature;

    @Expose
    private String search;

    @Expose
    private int category;

    @Expose
    @SerializedName("highest_rating")
    private double highestRating;

    @Expose
    @SerializedName("times_viewed")
    private int views;

    @Expose
    @SerializedName("image_url")
    public String imageUrl;

    @Expose
    @SerializedName("url")
    private String urlPath;

    @Expose
    private int palette;

    @Expose
    private boolean seen;

    @Expose
    private long seenAt;

    @Expose
    private int failedCount;

    @Expose
    private long addedAt;

    public String getPhotoId() {
        return id;
    }

    public void setPhotoId(String photoId) {
        this.id = photoId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public long getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(long createdAt) {
        this.createdAt = createdAt;
    }

    public String getFeature() {
        return feature;
    }

    public void setFeature(String feature) {
        this.feature = feature;
    }

    public String getSearch() {
        return search;
    }

    public void setSearch(String search) {
        this.search = search;
    }

    public int getCategory() {
        return category;
    }

    public void setCategory(int category) {
        this.category = category;
    }

    public double getHighestRating() {
        return highestRating;
    }

    public void setHighestRating(double highestRating) {
        this.highestRating = highestRating;
    }

    public int getViews() {
        return views;
    }

    public void setViews(int views) {
        this.views = views;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getUrlPath() {
        return urlPath;
    }

    public void setUrlPath(String urlPath) {
        this.urlPath = urlPath;
    }

    public int getPalette() {
        return palette;
    }

    public void setPalette(int palette) {
        this.palette = palette;
    }

    public boolean isSeen() {
        return seen;
    }

    public void setSeen(boolean seen) {
        this.seen = seen;
    }

    public long getSeenAt() {
        return seenAt;
    }

    public void setSeenAt(long seenAt) {
        this.seenAt = seenAt;
    }

    public int getFailedCount() {
        return failedCount;
    }

    public void setFailedCount(int failedCount) {
        this.failedCount = failedCount;
    }

    public long getAddedAt() {
        return addedAt;
    }

    public void setAddedAt(long addedAt) {
        this.addedAt = addedAt;
    }

    public static Photos create(Realm realm, Photo photo, String feature, String search) {
        Logger logger = LoggerFactory.getLogger("Photos");

        if (photo.nsfw) {
            logger.debug("Photo was nsfw");
            return null;
        }

        if (realm.where(Photos.class).equalTo("id", photo.id).count() > 0) {
            logger.debug("Photo already exists");
            return null;
        }

        try {
            realm.beginTransaction();

            Photos photoModel = realm.createObject(Photos.class);
            photoModel.setPhotoId(photo.id);
            photoModel.setName(photo.name);
            photoModel.setDescription(photo.description);
            photoModel.setUserName(photo.user.getFullName());
            photoModel.setCreatedAt(DATE_FORMAT.parse(photo.createdAt.substring(0, photo.createdAt.length() - 6)).getTime());
            photoModel.setFeature(feature);
            photoModel.setSearch(search);
            photoModel.setCategory(photo.category);
            photoModel.setHighestRating(photo.highestRating);
            photoModel.setViews(photo.views);
            photoModel.setImageUrl(photo.imageUrl);
            photoModel.setUrlPath(photo.url);
            photoModel.setSeen(false);
            photoModel.setSeenAt(0);
            photoModel.setAddedAt(photoModel.addedAt = System.currentTimeMillis());

            realm.commitTransaction();

            return photoModel;
        } catch (ParseException e) {
            logger.error(e.getMessage());
        }

        return null;
    }

    public static Photos getPhoto(Realm realm, int id) {
        return realm.where(Photos.class)
                .equalTo("id", id)
                .findFirst();
    }

    public static Photos getNextPhoto(Context context, Realm realm) {
        RealmResults<Photos> photos = getQuery(context, realm);
        if (photos.isEmpty()) {
            return null;
        } else {
            return photos.first();
        }
    }

    public static Photos getCurrentPhoto(Realm realm) {
        RealmResults<Photos> photos = getRecentlySeenPhotos(realm);
        if (photos.isEmpty()) {
            return null;
        } else {
            return photos.first();
        }
    }

    public static RealmResults<Photos> getRecentlySeenPhotos(Realm realm) {
        return realm.where(Photos.class)
                .equalTo("seen", true)
                .greaterThan("seenAt", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
                .findAllSorted("seenAt", Sort.DESCENDING);
    }

    public static List<Photos> getUnseenPhotos(Context context, Realm realm) {
        return getQuery(context, realm);
    }

    public static int unseenPhotoCount(Context context, Realm realm) {
        return getQuery(context, realm).size();
    }

    private static RealmResults<Photos> getQuery(Context context, Realm realm) {
        String feature = Settings.getFeature(context);
        RealmQuery<Photos> query = realm.where(Photos.class);
        if (feature.equals("search")) {
            query.equalTo("search", Settings.getSearchQuery(context));
        } else {
            query.equalTo("feature", feature);
        }

        query.beginGroup();
        int[] categories = Settings.getCategories(context);
        for (int i = 0; i < categories.length - 1; i++) {
            query.equalTo("category", categories[i])
                    .or();
        }
        query.equalTo("category", categories[categories.length - 1])
                .endGroup();

        return query.equalTo("seen", false)
                .findAllSorted(new String[] { "failedCount", "highestRating", "views", "createdAt" },
                        new Sort[] { Sort.ASCENDING, Sort.DESCENDING, Sort.DESCENDING, Sort.ASCENDING });
    }
} public class RemainingPhotosChangedEvent {
} public class User extends RealmObject {

    @Expose
    private int id;

    @Expose
    @SerializedName("username")
    private String userName;

    @Expose
    @SerializedName("firstname")
    private String firstName;

    @Expose
    @SerializedName("lastname")
    private String lastName;

    @Expose
    @SerializedName("fullname")
    private String fullName;

    @Expose
    @SerializedName("userpic_url")
    private String photo;

    @Expose
    private String accessToken;

    @Expose
    private String accessTokenSecret;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getPhoto() {
        return photo;
    }

    public void setPhoto(String photo) {
        this.photo = photo;
    }

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public String getAccessTokenSecret() {
        return accessTokenSecret;
    }

    public void setAccessTokenSecret(String accessTokenSecret) {
        this.accessTokenSecret = accessTokenSecret;
    }

    public static User getUser(Realm realm) {
        return realm.where(User.class).findFirst();
    }

    public static boolean isUserLoggedIn(Realm realm) {
        return (realm.where(User.class).findFirst() != null);
    }

    public static void logout(Realm realm) {
        realm.beginTransaction();
        realm.where(User.class).findAll().deleteAllFromRealm();
        realm.commitTransaction();
    }

    public static void newUser(Context context, Realm realm, AccessToken accessToken) {
        User.logout(realm);

        realm.beginTransaction();
        User user = realm.createObject(User.class);
        user.setAccessToken(accessToken.getToken());
        user.setAccessTokenSecret(accessToken.getTokenSecret());
        realm.commitTransaction();

        context.startService(new Intent(context, UserInfoIntentService.class));
    }

    public static AccessToken getLoggedInUserAccessToken(Realm realm) {
        User user = getUser(realm);
        return (user != null ? new AccessToken(user.getAccessToken(), user.getAccessTokenSecret()) : null);
    }
} public class UserUpdatedEvent {
} public class WallpaperChangedEvent {
} public class ClearCacheIntentService extends IntentService {

    public ClearCacheIntentService() {
        super("ClearCacheIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        LoggerFactory.getLogger("ClearCacheIntentService").debug("Clearing photo database and cache");

        Realm realm = Realm.getDefaultInstance();
        realm.beginTransaction();
        realm.where(Photos.class).findAll().deleteAllFromRealm();
        realm.commitTransaction();
        realm.close();

        PicassoHelper.clearCache(this);
        Settings.clearUpdated(this);

        WallpaperApplication.getBus().post(new WallpaperChangedEvent());
        PhotoDownloadIntentService.downloadPhotos(this);
    }
} public class PhotoDownloadIntentService extends IntentService {

    private Logger mLogger;
    private Realm mRealm;
    private Bus mBus;
    private int mErrorCount;
    private int mPage;
    private int mTotalPages;

    public PhotoDownloadIntentService() {
        super(PhotoDownloadIntentService.class.getName());
    }

    public static void downloadPhotos(Context context) {
        context.startService(new Intent(context, PhotoDownloadIntentService.class));
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        mLogger = LoggerFactory.getLogger("PhotoDownloadIntentService");
        mBus = WallpaperApplication.getBus();
        mErrorCount = 0;
        mPage = 1;
        mTotalPages = 1;
        mRealm = Realm.getDefaultInstance();

        if (Utils.shouldGetPhotos(this, mRealm)) {
            mLogger.debug("Attempting to fetch new photos");

            long startTime = System.currentTimeMillis();

            while (Photos.unseenPhotoCount(this, mRealm) < 100 && mPage <= mTotalPages &&
                    mErrorCount < 5 && Utils.isCurrentNetworkOk(this) &&
                    (System.currentTimeMillis() - startTime) < 300000) {
                getPhotos();
            }

            mLogger.debug("Done fetching photos");

            cachePhotos();
        } else {
            mBus.post(new RemainingPhotosChangedEvent());
            mLogger.debug("Not getting photos at this time");
        }

        mRealm.beginTransaction();
        mRealm.where(Photos.class)
                .equalTo("seen", true)
                .lessThan("seenAt", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30))
                .findAll()
                .deleteAllFromRealm();
        mRealm.commitTransaction();

        mRealm.close();
    }

    private void getPhotos() {
        try {
            String feature = Settings.getFeature(this);
            String search = feature.equals("search") ? Settings.getSearchQuery(this) : "";

            Call<PhotosResponse> call;
            switch (feature) {
                case "search":
                    call = WallpaperApplication.getNonLoggedInApiClient()
                            .getPhotosFromSearch(Settings.getSearchQuery(this), getCategoriesForRequest(), mPage);
                    break;
                case "user_favorites":
                    call = WallpaperApplication.getApiClient().getFavorites(User.getUser(mRealm).getId(),
                            Settings.getFavoriteGalleryId(this), getCategoriesForRequest(), mPage);
                    break;
                default:
                    call = WallpaperApplication.getApiClient().getPhotos(feature, getCategoriesForRequest(), mPage);
                    break;
            }

            Response<PhotosResponse> response = call.execute();
            int responseCode = response.code();
            mLogger.debug("Response code: " + responseCode);
            if (responseCode != 200) {
                mErrorCount++;
                return;
            }

            mPage = response.body().currentPage + 1;
            mTotalPages = response.body().totalPages;

            for (int i = 0; i < response.body().photos.length; i++) {
                if (Photos.create(mRealm, response.body().photos[i], feature, search) != null) {
                    mLogger.debug("Added photo");
                    mBus.post(new RemainingPhotosChangedEvent());
                }
                SystemClock.sleep(25);
            }
        } catch (IOException e) {
            mLogger.error(e.getMessage());
            mErrorCount++;
        }

        SystemClock.sleep(5000);
    }

    private void cachePhotos() {
        mLogger.debug("Caching photos");

        Picasso picasso = PicassoHelper.getPicasso(this);
        List<Photos> photos = Photos.getUnseenPhotos(this, mRealm);
        for (Photos photo : photos) {
            if (Utils.isCurrentNetworkOk(this)) {
                picasso.load(photo.imageUrl)
                        .tag("PhotoCacheIntentService")
                        .fetch();
                SystemClock.sleep(50);
            } else {
                picasso.cancelTag("PhotoCacheIntentService");
                break;
            }
        }

        mLogger.debug("Done caching photos");
    }

    private String getCategoriesForRequest() {
        String[] allCategories = getResources().getStringArray(R.array.categories);
        int[] categories = Settings.getCategories(this);
        String filter = "";
        for (int category : categories) {
            filter += allCategories[category] + ",";
        }

        try {
            return URLEncoder.encode(filter.substring(0, filter.length() - 1), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }
} public class WallpaperService extends IntentService {

    public WallpaperService() {
        super("WallpaperService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Logger logger = LoggerFactory.getLogger("WallpaperService");

        if (!Settings.isEnabled(this)) {
            logger.debug("App is not enabled");
            return;
        }

        PowerManager.WakeLock wakeLock = ((PowerManager) getSystemService(POWER_SERVICE))
                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "500pxApiService");
        wakeLock.acquire(TimeUnit.MINUTES.toMillis(1));

        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
        int width = wallpaperManager.getDesiredMinimumWidth();
        int height = wallpaperManager.getDesiredMinimumHeight();
        if (Utils.supportsParallax(this) && !Settings.useParallax(this)) {
            width = width / 2;
        }

        Realm realm = Realm.getDefaultInstance();
        Photos photo = Photos.getNextPhoto(this, realm);
        if (photo != null) {
            try {
                logger.debug("Setting wallpaper to " + width + "px wide by " + height + "px tall");

                RequestCreator request = PicassoHelper.getPicasso(this)
                        .load(photo.imageUrl)
                        .centerCrop()
                        .resize(width, height);

                if (Settings.useOnlyWifi(this) && !Utils.isConnectedToWifi(this)) {
                    request.networkPolicy(NetworkPolicy.OFFLINE);
                }

                Bitmap bitmap = request.get();

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    wallpaperManager.setBitmap(bitmap, null, false, WallpaperManager.FLAG_SYSTEM);
                    wallpaperManager.setBitmap(bitmap, null, false, WallpaperManager.FLAG_LOCK);
                } else {
                    wallpaperManager.setBitmap(bitmap);
                }

                realm.beginTransaction();
                photo.setPalette(Palette.generate(bitmap).getMutedColor(getResources().getColor(R.color.brown)));
                photo.setSeen(true);
                photo.setSeenAt(System.currentTimeMillis());
                realm.commitTransaction();

                Settings.setUpdated(this);

                FirebaseAnalytics.getInstance(this).logEvent("wallpaper_updated", null);
            } catch (IOException e) {
                logger.error(e.toString());
                for (StackTraceElement trace : e.getStackTrace()) {
                    logger.error(trace.toString());
                }

                if (e.getCause() != null) {
                    logger.error(e.getCause().toString());
                    for (StackTraceElement trace : e.getCause().getStackTrace()) {
                        logger.error(trace.toString());
                    }
                }

                realm.beginTransaction();
                if (photo.getFailedCount() > 10) {
                    photo.deleteFromRealm();
                } else {
                    photo.setFailedCount(photo.getFailedCount() + 1);
                }
                realm.commitTransaction();

                startService(new Intent(this, WallpaperService.class));
            }
        } else {
            logger.debug("Next photo was null");
        }

        if (Utils.needMorePhotos(this, realm)) {
            logger.debug("Getting more photos via PhotoDownloadIntentService");
            PhotoDownloadIntentService.downloadPhotos(this);
        }

        WallpaperApplication.getBus().post(new WallpaperChangedEvent());

        realm.close();
        wakeLock.release();
    }
}
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.MultiSelectListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.text.format.DateUtils;
import com.lukekorth.fivehundredpx.AccessToken;
import com.lukekorth.fivehundredpx.FiveHundredPxOAuthActivity;
import com.lukekorth.photo_paper.helpers.AlarmHelper;
import com.lukekorth.photo_paper.helpers.LogReporting;
import com.lukekorth.photo_paper.helpers.PicassoHelper;
import com.lukekorth.photo_paper.helpers.Settings;
import com.lukekorth.photo_paper.helpers.Utils;
import com.lukekorth.photo_paper.models.ActivityResumedEvent;
import com.lukekorth.photo_paper.models.Photos;
import com.lukekorth.photo_paper.models.RemainingPhotosChangedEvent;
import com.lukekorth.photo_paper.models.User;
import com.lukekorth.photo_paper.models.UserUpdatedEvent;
import com.lukekorth.photo_paper.models.WallpaperChangedEvent;
import com.lukekorth.photo_paper.services.ClearCacheIntentService;
import com.lukekorth.photo_paper.services.PhotoDownloadIntentService;
import com.lukekorth.photo_paper.services.WallpaperService;
import com.squareup.otto.Subscribe;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.util.Set;
import fr.nicolaspomepuy.discreetapprate.AppRate;
import fr.nicolaspomepuy.discreetapprate.RetryPolicy;
import io.realm.Realm;
        public void onBitmapFailed(Drawable errorDrawable) {
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };

    private Target mUserImageCallback = new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            mLogin.setIcon(new BitmapDrawable(bitmap));
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };

    @Subscribe
    public void onUserUpdated(UserUpdatedEvent event) {
        User user = User.getUser(mRealm);
        if (user != null) {
            mLogin.setTitle(user.getUserName());
            mLogin.setSummary(getString(R.string.click_to_logout));

            PicassoHelper.getPicasso(getActivity())
                    .load(user.getPhoto())
                    .error(android.R.drawable.stat_notify_error)
                    .into(mUserImageCallback);
        } else {
            mLogin.setTitle(R.string.login_with_500px);
            mLogin.setSummary(R.string.login_with_500px_summary);
            mLogin.setIcon(null);
        }
    }

    @Subscribe
    public void onWallpaperChanged(WallpaperChangedEvent event) {
        Photos photo = Photos.getCurrentPhoto(mRealm);
        if (photo != null) {
            CharSequence timeSet = DateUtils.getRelativeTimeSpanString(photo.getSeenAt(), System.currentTimeMillis(), 0);
            mCurrentPhoto.setTitle(photo.getName());
            mCurrentPhoto.setSummary("© " + photo.getUserName() + " / 500px\n" + getString(R.string.set) +
                    " " + timeSet);
            PicassoHelper.getPicasso(getActivity())
                    .load(photo.imageUrl)
                    .placeholder(new ColorDrawable(photo.getPalette()))
                    .error(android.R.drawable.stat_notify_error)
                    .into(mCurrentImageCallback);
        } else {
            mCurrentPhoto.setTitle(R.string.no_current_photo);
            mCurrentPhoto.setSummary("");
            mCurrentPhoto.setIcon(null);
        }

        onRemainingPhotosChanged(null);
    }

    @Subscribe
    public void onRemainingPhotosChanged(RemainingPhotosChangedEvent event) {
        if (Settings.isEnabled(getActivity())) {
            int photosRemaining = Photos.unseenPhotoCount(getActivity(), mRealm);
            if (photosRemaining > 0) {
                mNextPhoto.setEnabled(true);

                long nextPhotoTime = Settings.getLastUpdated(getActivity()) + (Settings.getUpdateInterval(getActivity()) * 1000);
                if (nextPhotoTime - 59000 < System.currentTimeMillis()) {
                    mNextPhoto.setTitle(getString(R.string.next_photo) + " " + getString(R.string.soon));
                } else {
                    CharSequence nextTime = DateUtils.getRelativeTimeSpanString(nextPhotoTime, System.currentTimeMillis(), 0);
                    mNextPhoto.setTitle(getString(R.string.next_photo) + " " + nextTime);
                }
                mNextPhoto.setSummary(photosRemaining + " " + getString(R.string.remaining_photos));
            } else {
                mNextPhoto.setEnabled(false);
                mNextPhoto.setTitle(R.string.no_photos_remaining);
                mNextPhoto.setSummary(R.string.select_different_feature_or_categories);
            }
        } else {
            mNextPhoto.setTitle(R.string.disabled);
            mNextPhoto.setSummary(R.string.enable_below);
        }
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference.getKey().equals("clear_cache")) {
            new AlertDialog.Builder(getActivity())
                    .setTitle(R.string.are_you_sure)
                    .setMessage(R.string.are_you_sure_message)
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            getActivity().startService(new Intent(getActivity(), ClearCacheIntentService.class));
                            dialog.dismiss();
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    })
                    .show();
        } else if (preference.getKey().equals(mNextPhoto.getKey())) {
            mNextPhoto.setTitle(R.string.loading);
            mNextPhoto.setSummary("");
            getActivity().startService(new Intent(getActivity(), WallpaperService.class));
            AlarmHelper.scheduleWallpaperAlarm(getActivity());
            return true;
        } else if (preference.getKey().equals("login")) {
            if (User.isUserLoggedIn(mRealm)) {
                User.logout(mRealm);
                WallpaperApplication.getBus().post(new UserUpdatedEvent());
            } else {
                startActivityForResult(new Intent(getActivity(), FiveHundredPxOAuthActivity.class),