public class WallpaperBoardPreviewActivity extends AppCompatActivity implements View.OnClickListener,
        ActivityCompat.OnRequestPermissionsResultCallback, SlidingUpPanelLayout.PanelSlideListener,
        WallpaperPropertiesLoaderTask.Callback, WallpaperPaletteLoaderTask.Callback, View.OnLongClickListener {

    @BindView(R2.id.wallpaper)
    ImageView mImageView;
    @BindView(R2.id.progress)
    ProgressBar mProgress;
    @BindView(R2.id.back)
    ImageView mBack;
    @BindView(R2.id.bottom_panel)
    RelativeLayout mBottomPanel;
    @BindView(R2.id.name)
    TextView mName;
    @BindView(R2.id.author)
    TextView mAuthor;
    @BindView(R2.id.menu_preview)
    ImageView mMenuPreview;
    @BindView(R2.id.menu_save)
    ImageView mMenuSave;
    @BindView(R2.id.menu_apply)
    ImageView mMenuApply;
    @BindView(R2.id.recyclerview)
    RecyclerView mRecyclerView;
    @BindView(R2.id.sliding_layout)
    SlidingUpPanelLayout mSlidingLayout;

    private Runnable mRunnable;
    private Handler mHandler;
    private PhotoViewAttacher mAttacher;
    private ExitActivityTransition mExitTransition;
    private Tooltip mTooltip;

    private Wallpaper mWallpaper;
    private List<WallpaperProperty> mProperties;
    private ColorPalette mPalette;
    private List<Category> mCategories;

    private boolean mIsEnter = true;
    private boolean mIsResumed = false;

    private boolean mIsBottomPanelDragged = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.setTheme(Preferences.get(this).isDarkTheme() ?
                R.style.WallpaperThemeDark : R.style.WallpaperTheme);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wallpaper_preview);
        ButterKnife.bind(this);

        mProgress.getIndeterminateDrawable().setColorFilter(
                Color.parseColor("#CCFFFFFF"), PorterDuff.Mode.SRC_IN);

        String url = "";
        if (savedInstanceState != null) {
            url = savedInstanceState.getString(Extras.EXTRA_URL);
            mIsResumed = savedInstanceState.getBoolean(Extras.EXTRA_RESUMED);
        }

        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            url = bundle.getString(Extras.EXTRA_URL);
        }

        mWallpaper = Database.get(this).getWallpaper(url);
        if (mWallpaper == null) {
            finish();
            return;
        }

        mProperties = WallpaperProperty.getWallpaperProperties(this, mWallpaper);
        mPalette = new ColorPalette();
        mCategories = Database.get(this).getWallpaperCategories(mWallpaper.getCategory());

        mBack.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_back, Color.WHITE));
        mBack.setOnClickListener(this);

        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setHasFixedSize(false);
        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(
                getResources().getInteger(R.integer.wallpaper_details_column_count),
                StaggeredGridLayoutManager.VERTICAL));

        mSlidingLayout.setDragView(mBottomPanel);
        mSlidingLayout.setScrollableView(mRecyclerView);
        mSlidingLayout.setCoveredFadeColor(Color.TRANSPARENT);
        mSlidingLayout.addPanelSlideListener(this);
        mSlidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
        resetBottomPadding();
        initBottomPanel(Color.WHITE);

        mRecyclerView.setAdapter(new WallpaperDetailsAdapter(this,
                new ArrayList<>(), new ColorPalette(), new ArrayList<>()));

        if (!mIsResumed) {
            mExitTransition = ActivityTransition
                    .with(getIntent())
                    .to(this, mImageView, Extras.EXTRA_IMAGE)
                    .duration(300)
                    .start(savedInstanceState);
        }

        if (mImageView.getDrawable() == null) {
            int color = mWallpaper.getColor();
            if (color == 0) {
                color = ColorHelper.getAttributeColor(this, R.attr.card_background);
            }

            AnimationHelper.setBackgroundColor(mSlidingLayout, Color.TRANSPARENT, color).start();
            mProgress.getIndeterminateDrawable().setColorFilter(
                    ColorHelper.setColorAlpha(ColorHelper.getTitleTextColor(color), 0.7f),
                    PorterDuff.Mode.SRC_IN);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && savedInstanceState == null
                && mImageView.getDrawable() != null) {
            Transition transition = getWindow().getSharedElementEnterTransition();

            if (transition != null) {
                transition.addListener(new Transition.TransitionListener() {
                    @Override
                    public void onTransitionStart(Transition transition) {

                    }

                    @Override
                    public void onTransitionEnd(Transition transition) {
                        if (mIsEnter) {
                            mIsEnter = false;
                            mSlidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
                            mIsBottomPanelDragged = false;
                            loadWallpaper(mWallpaper.getThumbUrl());
                        }
                    }

                    @Override
                    public void onTransitionCancel(Transition transition) {

                    }

                    @Override
                    public void onTransitionPause(Transition transition) {

                    }

                    @Override
                    public void onTransitionResume(Transition transition) {

                    }
                });
                return;
            }
        }

        mRunnable = () -> {
            mSlidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
            mIsBottomPanelDragged = false;
            loadWallpaper(mWallpaper.getThumbUrl());
            mRunnable = null;
            mHandler = null;
        };
        mHandler = new Handler();
        mHandler.postDelayed(mRunnable, 700);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        ViewHelper.resetSpanCount(mRecyclerView, getResources().getInteger(
                R.integer.wallpaper_details_column_count));
        if (mRecyclerView.getAdapter() != null) {
            mRecyclerView.getAdapter().notifyDataSetChanged();
        }
        if (mTooltip != null) {
            mTooltip.dismiss();
        }
        new Handler().postDelayed(this::resetBottomPadding, 200);
        LocaleHelper.setLocale(this);
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        LocaleHelper.setLocale(newBase);
        super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (intent != null && intent.getExtras() != null) {
            this.setIntent(intent);
            String url = "";
            Bundle bundle = getIntent().getExtras();
            if (bundle != null) {
                url = bundle.getString(Extras.EXTRA_URL);
            }

            Wallpaper wallpaper = Database.get(this).getWallpaper(url);
            if (wallpaper == null) {
                return;
            }

            mWallpaper = wallpaper;
            loadWallpaper(mWallpaper.getThumbUrl());
            mSlidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (mWallpaper != null) {
            outState.putString(Extras.EXTRA_URL, mWallpaper.getUrl());
        }

        outState.putBoolean(Extras.EXTRA_RESUMED, true);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        if (Preferences.get(this).isCropWallpaper()) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
        }

        ImageLoader.getInstance().cancelDisplayTask(mImageView);
        WallpaperBoardApplication.sIsClickable = true;
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        if (mSlidingLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
            mSlidingLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
            return;
        }

        if (mTooltip != null) {
            mTooltip.dismiss();
            mTooltip = null;
        }

        if (mHandler != null && mRunnable != null)
            mHandler.removeCallbacks(mRunnable);

        if (mExitTransition != null) {
            mExitTransition.exit(this);
            return;
        }
        super.onBackPressed();
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.back) {
            onBackPressed();
        } else if (id == R.id.menu_preview) {
            if (mTooltip != null) {
                mTooltip.dismiss();
            }

            if (mProgress.getVisibility() == View.GONE) {
                loadWallpaper(mWallpaper.getUrl());
            }
        } else if (id == R.id.menu_save) {
            if (PermissionHelper.isStorageGranted(this)) {
                WallpaperDownloader.prepare(this)
                        .wallpaper(mWallpaper)
                        .start();
                return;
            }

            PermissionHelper.requestStorage(this);
        } else if (id == R.id.menu_apply) {
            Popup popup = Popup.Builder(this)
                    .to(view)
                    .list(PopupItem.getApplyItems(this))
                    .callback((applyPopup, position) -> {
                        PopupItem item = applyPopup.getItems().get(position);
                        if (item.getType() == PopupItem.Type.WALLPAPER_CROP) {
                            Preferences.get(this).setCropWallpaper(!item.getCheckboxValue());
                            item.setCheckboxValue(Preferences.get(this).isCropWallpaper());

                            applyPopup.updateItem(position, item);
                            if (Preferences.get(WallpaperBoardPreviewActivity.this).isCropWallpaper()) {
                                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                                return;
                            }

                            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
                            return;
                        } else if (item.getType() == PopupItem.Type.LOCKSCREEN) {
                            RectF rectF = null;
                            if (Preferences.get(WallpaperBoardPreviewActivity.this).isCropWallpaper()) {
                                if (mAttacher != null)
                                    rectF = mAttacher.getDisplayRect();
                            }

                            WallpaperApplyTask.prepare(this)
                                    .wallpaper(mWallpaper)
                                    .to(WallpaperApplyTask.Apply.LOCKSCREEN)
                                    .crop(rectF)
                                    .start(AsyncTask.THREAD_POOL_EXECUTOR);
                        } else if (item.getType() == PopupItem.Type.HOMESCREEN) {
                            RectF rectF = null;
                            if (Preferences.get(WallpaperBoardPreviewActivity.this).isCropWallpaper()) {
                                if (mAttacher != null)
                                    rectF = mAttacher.getDisplayRect();
                            }

                            WallpaperApplyTask.prepare(this)
                                    .wallpaper(mWallpaper)
                                    .to(WallpaperApplyTask.Apply.HOMESCREEN)
                                    .crop(rectF)
                                    .start(AsyncTask.THREAD_POOL_EXECUTOR);
                        } else if (item.getType() == PopupItem.Type.HOMESCREEN_LOCKSCREEN) {
                            RectF rectF = null;
                            if (Preferences.get(WallpaperBoardPreviewActivity.this).isCropWallpaper()) {
                                if (mAttacher != null)
                                    rectF = mAttacher.getDisplayRect();
                            }

                            WallpaperApplyTask.prepare(this)
                                    .wallpaper(mWallpaper)
                                    .to(WallpaperApplyTask.Apply.HOMESCREEN_LOCKSCREEN)
                                    .crop(rectF)
                                    .start(AsyncTask.THREAD_POOL_EXECUTOR);
                        }

                        applyPopup.dismiss();
                    })
                    .build();

            if (getResources().getBoolean(R.bool.enable_wallpaper_download)) {
                popup.getItems().remove(popup.getItems().size() - 1);
            }
            popup.show();
        }
    }

    @Override
    public boolean onLongClick(View view) {
        int id = view.getId();
        int res = 0;
        if (id == R.id.menu_apply) {
            res = R.string.wallpaper_apply;
        } else if (id == R.id.menu_save) {
            res = R.string.wallpaper_save_to_device;
        } else if (id == R.id.menu_preview) {
            res = R.string.wallpaper_preview_full;
        }

        if (res == 0) return false;

        Toast.makeText(this, res, Toast.LENGTH_SHORT).show();
        return true;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PermissionCode.STORAGE) {
            if (grantResults.length > 0 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                WallpaperDownloader.prepare(this)
                        .wallpaper(mWallpaper)
                        .start();

                if (!Preferences.get(this).isBackupRestored()) {
                    LocalFavoritesRestoreTask.with(this).start(AsyncTask.THREAD_POOL_EXECUTOR);
                }
            } else {
                Toast.makeText(this, R.string.permission_storage_denied, Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onPanelSlide(View panel, float slideOffset) {
        if (slideOffset > 0.1f) {
            if (mIsBottomPanelDragged) return;

            mIsBottomPanelDragged = true;
            int fromColor = ColorHelper.get(this, R.color.bottomPanelCollapsed);
            int toColor = ColorHelper.getAttributeColor(this, R.attr.main_background);

            if (mRecyclerView.getAdapter() != null) {
                WallpaperDetailsAdapter adapter = (WallpaperDetailsAdapter) mRecyclerView.getAdapter();
                adapter.setWallpaperProperties(mProperties);
                adapter.setColorPalette(mPalette);
                adapter.setCategories(mCategories);
            }
            AnimationHelper.setBackgroundColor(mBottomPanel, fromColor, toColor)
                    .interpolator(new LinearOutSlowInInterpolator())
                    .duration(400)
                    .start();
            mName.setVisibility(View.GONE);
            mAuthor.setVisibility(View.GONE);
            initBottomPanel(ColorHelper.getTitleTextColor(toColor));
        } else if (slideOffset == 0f) {
            if (!mIsBottomPanelDragged) return;

            mIsBottomPanelDragged = false;
            int fromColor = ColorHelper.getAttributeColor(this, R.attr.main_background);
            int toColor = ColorHelper.get(this, R.color.bottomPanelCollapsed);

            if (mRecyclerView.getAdapter() != null) {
                WallpaperDetailsAdapter adapter = (WallpaperDetailsAdapter) mRecyclerView.getAdapter();
                adapter.setWallpaperProperties(new ArrayList<>());
                adapter.setColorPalette(new ColorPalette());
                adapter.setCategories(new ArrayList<>());
            }
            AnimationHelper.setBackgroundColor(mBottomPanel, fromColor, toColor)
                    .interpolator(new LinearOutSlowInInterpolator())
                    .duration(400)
                    .start();
            mName.setVisibility(View.VISIBLE);
            mAuthor.setVisibility(View.VISIBLE);
            initBottomPanel(Color.WHITE);
        }
    }

    @Override
    public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState,
                                    SlidingUpPanelLayout.PanelState newState) {
        File file = ImageLoader.getInstance().getDiskCache().get(mWallpaper.getUrl());
        if (newState == SlidingUpPanelLayout.PanelState.COLLAPSED &&
                mTooltip == null
                && Preferences.get(this).isShowWallpaperTooltip() &&
                !file.exists() &&
                !Preferences.get(this).isTimeToShowWallpaperPreviewIntro() &&
                !Preferences.get(this).isHighQualityPreviewEnabled()) {
            mTooltip = Tooltip.Builder(this)
                    .to(mMenuPreview)
                    .content(R.string.wallpaper_tooltip_preview)
                    .desc(R.string.wallpaper_tooltip_preview_icon_tap)
                    .descIcon(R.drawable.ic_toolbar_preview_full)
                    .visibleDontShowAgain(true)
                    .cancelable(false)
                    .buttonCallback(tooltip -> {
                        Preferences.get(this).setShowWallpaperTooltip(!tooltip.getCheckboxState());
                        tooltip.dismiss();
                    })
                    .build();
            mTooltip.show();
        }
    }

    @Override
    public void onPropertiesReceived(Wallpaper wallpaper) {
        if (wallpaper == null) return;

        mWallpaper.setDimensions(wallpaper.getDimensions());
        mWallpaper.setSize(wallpaper.getSize());
        mWallpaper.setMimeType(wallpaper.getMimeType());

        mProperties = WallpaperProperty.getWallpaperProperties(this, mWallpaper);
        if (mRecyclerView.getAdapter() != null && mSlidingLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
            WallpaperDetailsAdapter adapter = (WallpaperDetailsAdapter) mRecyclerView.getAdapter();
            adapter.setWallpaperProperties(mProperties);
        }
    }

    @Override
    public void onPaletteGenerated(ColorPalette palette) {
        mPalette = palette;
        if (mRecyclerView.getAdapter() != null && mSlidingLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
            WallpaperDetailsAdapter adapter = (WallpaperDetailsAdapter) mRecyclerView.getAdapter();
            adapter.setColorPalette(mPalette);
        }
    }

    private void initBottomPanel(int color) {
        mName.setText(mWallpaper.getName());
        mName.setTextColor(color);
        mAuthor.setText(mWallpaper.getAuthor());
        mAuthor.setTextColor(ColorHelper.setColorAlpha(color, 0.7f));

        mMenuPreview.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_preview_full, color));
        mMenuSave.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_download, color));
        mMenuApply.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_apply_options, color));

        if (getResources().getBoolean(R.bool.enable_wallpaper_download)) {
            mMenuSave.setVisibility(View.VISIBLE);
        }

        mMenuPreview.setOnClickListener(this);
        mMenuSave.setOnClickListener(this);
        mMenuApply.setOnClickListener(this);
        mMenuPreview.setOnLongClickListener(this);
        mMenuSave.setOnLongClickListener(this);
        mMenuApply.setOnLongClickListener(this);
    }

    private void resetBottomPadding() {
        int navBar = WindowHelper.getNavigationBarHeight(this);
        int bottomPanelHeight = getResources().getDimensionPixelSize(R.dimen.sliding_panel_height);
        int marginTop = getResources().getDimensionPixelSize(R.dimen.icon_size_small) +
                (getResources().getDimensionPixelSize(R.dimen.content_margin) * 2);
        int paddingRight = navBar;
        int paddingBottom = 0;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            marginTop += WindowHelper.getStatusBarHeight(this);

            if (mBack.getLayoutParams() instanceof FrameLayout.LayoutParams) {
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mBack.getLayoutParams();
                params.topMargin = WindowHelper.getStatusBarHeight(this);
            }

            boolean tabletMode = getResources().getBoolean(R.bool.android_helpers_tablet_mode);
            if (tabletMode && getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                int halfScreen = WindowHelper.getScreenSize(this).y / 2;
                marginTop = halfScreen - marginTop;
            }

            if (tabletMode || getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
                bottomPanelHeight += navBar;
                paddingRight = 0;
                paddingBottom = navBar;
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                if (isInMultiWindowMode()) {
                    bottomPanelHeight = getResources().getDimensionPixelSize(R.dimen.sliding_panel_height);
                    paddingBottom = 0;
                }
            }
        }

        mSlidingLayout.setPanelHeight(bottomPanelHeight);
        mBottomPanel.setPadding(0, 0, paddingRight, 0);
        mRecyclerView.setPadding(0, 0, 0, paddingBottom);

        if (mBottomPanel.getLayoutParams() instanceof SlidingUpPanelLayout.LayoutParams) {
            SlidingUpPanelLayout.LayoutParams params = (SlidingUpPanelLayout.LayoutParams)
                    mBottomPanel.getLayoutParams();
            params.topMargin = marginTop;
        }
    }

    private void loadWallpaper(String url) {
        if (mAttacher != null) {
            mAttacher = null;
        }

        boolean highQualityPreview = Preferences.get(this).isHighQualityPreviewEnabled();
        File file = ImageLoader.getInstance().getDiskCache().get(mWallpaper.getUrl());
        if (file.exists() || highQualityPreview) {
            if (file.exists()) {
                LogUtil.d("full size wallpaper available in cache: " +file.getName());
            }
            url = mWallpaper.getUrl();
        }

        if (!file.exists() && highQualityPreview) {
            if (mImageView.getDrawable() != null) {
                Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
                WallpaperPaletteLoaderTask.with(bitmap)
                        .callback(WallpaperBoardPreviewActivity.this)
                        .start();
            }
        }

        WallpaperPropertiesLoaderTask.prepare(this)
                .callback(this)
                .wallpaper(mWallpaper)
                .start(AsyncTask.THREAD_POOL_EXECUTOR);

        DisplayImageOptions.Builder options = ImageConfig.getRawDefaultImageOptions();
        options.cacheInMemory(false);
        options.cacheOnDisk(true);

        ImageLoader.getInstance().handleSlowNetwork(true);
        ImageLoader.getInstance().displayImage(url, mImageView, options.build(), new SimpleImageLoadingListener() {

            @Override
            public void onLoadingStarted(String imageUri, View view) {
                super.onLoadingStarted(imageUri, view);
                if (Preferences.get(WallpaperBoardPreviewActivity.this).isCropWallpaper()) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }

                AnimationHelper.fade(mProgress).start();
            }

            @Override
            public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
                super.onLoadingFailed(imageUri, view, failReason);
                if (mWallpaper.getColor() == 0) {
                    mWallpaper.setColor(ColorHelper.getAttributeColor(
                            WallpaperBoardPreviewActivity.this, R.attr.colorAccent));
                }

                onWallpaperLoaded();
                if (mImageView.getDrawable() != null) {
                    Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
                    WallpaperPaletteLoaderTask.with(bitmap)
                            .callback(WallpaperBoardPreviewActivity.this)
                            .start();
                }
            }

            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                super.onLoadingComplete(imageUri, view, loadedImage);
                WallpaperPaletteLoaderTask.with(loadedImage)
                        .callback(WallpaperBoardPreviewActivity.this)
                        .start();

                if (loadedImage != null && mWallpaper.getColor() == 0) {
                    Palette.from(loadedImage).generate(palette -> {
                        if (isFinishing()) return;

                        int accent = ColorHelper.getAttributeColor(
                                WallpaperBoardPreviewActivity.this, R.attr.colorAccent);
                        int color = palette.getVibrantColor(accent);
                        if (color == accent)
                            color = palette.getMutedColor(accent);

                        mWallpaper.setColor(color);
                        Database.get(WallpaperBoardPreviewActivity.this).updateWallpaper(mWallpaper);

                        onWallpaperLoaded();
                    });
                    return;
                }

                onWallpaperLoaded();
            }
        }, null);
    }

    private void onWallpaperLoaded() {
        mAttacher = new PhotoViewAttacher(mImageView);
        mAttacher.setScaleType(ImageView.ScaleType.CENTER_CROP);
        AnimationHelper.fade(mProgress).start();
        mRunnable = null;
        mHandler = null;
        mIsResumed = false;

        TapIntroHelper.showWallpaperPreviewIntro(this, mWallpaper.getColor());
    }
}
 public abstract class WallpaperBoardApplication extends Application implements ApplicationCallback {

    public static boolean sIsClickable = true;
    private static boolean mIsLatestWallpapersLoaded;
    private static WallpaperBoardConfiguration mConfiguration;

    private Thread.UncaughtExceptionHandler mHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        Database.get(this).openDatabase();
        Preferences.get(this);

        if (!ImageLoader.getInstance().isInited())
            ImageLoader.getInstance().init(ImageConfig.getImageLoaderConfiguration(this));

        CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
                .setDefaultFontPath("fonts/Font-Regular.ttf")
                .setFontAttrId(R.attr.fontPath)
                .build());

        //Enable logging
        LogUtil.setLoggingTag(getString(R.string.app_name));
        LogUtil.setLoggingEnabled(true);

        mConfiguration = onInit();

        if (mConfiguration.isCrashReportEnabled()) {
            String[] urls = getResources().getStringArray(R.array.about_social_links);
            boolean isContainsValidEmail = false;
            for (String url : urls) {
                if (UrlHelper.getType(url) == UrlHelper.Type.EMAIL) {
                    isContainsValidEmail = true;
                    mConfiguration.setCrashReportEmail(url);
                    break;
                }
            }

            if (isContainsValidEmail) {
                mHandler = Thread.getDefaultUncaughtExceptionHandler();
                Thread.setDefaultUncaughtExceptionHandler(this::handleUncaughtException);
            } else {
                mConfiguration.setCrashReportEnabled(false);
                mConfiguration.setCrashReportEmail(null);
            }
        }

        LocaleHelper.setLocale(this);
    }

    public static WallpaperBoardConfiguration getConfig() {
        if (mConfiguration == null) {
            mConfiguration = new WallpaperBoardConfiguration();
        }
        return mConfiguration;
    }

    public static boolean isLatestWallpapersLoaded() {
        return mIsLatestWallpapersLoaded;
    }

    public static void setLatestWallpapersLoaded(boolean loaded) {
        mIsLatestWallpapersLoaded = loaded;
    }

    private void handleUncaughtException(Thread thread, Throwable throwable) {
        try {
            StringBuilder sb = new StringBuilder();
            SimpleDateFormat dateFormat = TimeHelper.getDefaultShortDateTimeFormat();
            String dateTime = dateFormat.format(new Date());
            sb.append("Crash Time : ").append(dateTime).append("\n");
            sb.append("Class Name : ").append(throwable.getClass().getName()).append("\n");
            sb.append("Caused By : ").append(throwable.toString()).append("\n");

            for (StackTraceElement element : throwable.getStackTrace()) {
                sb.append("\n");
                sb.append(element.toString());
            }

            Intent intent = new Intent(this, WallpaperBoardCrashReport.class);
            intent.putExtra(WallpaperBoardCrashReport.EXTRA_STACKTRACE, sb.toString());
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            startActivity(intent);
        } catch (Exception e) {
            if (mHandler != null) {
                mHandler.uncaughtException(thread, throwable);
                return;
            }
        }
        System.exit(1);
    }
}
 public class Database extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "wallpaper_board_database";
    private static final int DATABASE_VERSION = 5;

    private static final String TABLE_WALLPAPERS = "wallpapers";
    private static final String TABLE_CATEGORIES = "categories";

    private static final String KEY_URL = "url";
    private static final String KEY_THUMB_URL = "thumbUrl";
    private static final String KEY_MIME_TYPE = "mimeType";
    private static final String KEY_SIZE = "size";
    private static final String KEY_COLOR = "color";
    private static final String KEY_WIDTH = "width";
    private static final String KEY_HEIGHT = "height";
    private static final String KEY_COUNT = "count";

    public static final String KEY_ID = "id";
    public static final String KEY_NAME = "name";
    public static final String KEY_AUTHOR = "author";
    public static final String KEY_CATEGORY = "category";

    private static final String KEY_FAVORITE = "favorite";
    private static final String KEY_SELECTED = "selected";
    private static final String KEY_MUZEI_SELECTED = "muzeiSelected";
    private static final String KEY_ADDED_ON = "addedOn";

    private final Context mContext;

    private static WeakReference<Database> mDatabase;
    private SQLiteDatabase mSQLiteDatabase;
    private static List<String> mFavoriteUrlsBackup;

    public static Database get(@NonNull Context context) {
        if (mDatabase == null || mDatabase.get() == null) {
            mDatabase = new WeakReference<>(new Database(context));
        }
        return mDatabase.get();
    }

    private Database(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String CREATE_TABLE_CATEGORY = "CREATE TABLE " +TABLE_CATEGORIES+ "(" +
                KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                KEY_NAME + " TEXT NOT NULL," +
                KEY_SELECTED + " INTEGER DEFAULT 1," +
                KEY_MUZEI_SELECTED + " INTEGER DEFAULT 1, " +
                "UNIQUE (" +KEY_NAME+ "))";
        String CREATE_TABLE_WALLPAPER = "CREATE TABLE IF NOT EXISTS " +TABLE_WALLPAPERS+ "(" +
                KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                KEY_NAME+ " TEXT NOT NULL, " +
                KEY_AUTHOR + " TEXT, " +
                KEY_URL + " TEXT NOT NULL, " +
                KEY_THUMB_URL + " TEXT NOT NULL, " +
                KEY_MIME_TYPE + " TEXT, " +
                KEY_SIZE + " INTEGER DEFAULT 0, " +
                KEY_COLOR + " INTEGER DEFAULT 0, " +
                KEY_WIDTH + " INTEGER DEFAULT 0, " +
                KEY_HEIGHT + " INTEGER DEFAULT 0, " +
                KEY_CATEGORY + " TEXT NOT NULL," +
                KEY_FAVORITE + " INTEGER DEFAULT 0," +
                KEY_ADDED_ON + " TEXT NOT NULL, " +
                "UNIQUE (" +KEY_URL+ "))";
        db.execSQL(CREATE_TABLE_CATEGORY);
        db.execSQL(CREATE_TABLE_WALLPAPER);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /*
         * Need to clear shared preferences with version 1.5.0b-3
         */
        if (newVersion == 5) {
            Preferences.get(mContext).clearPreferences();
        }
        resetDatabase(db);
    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        resetDatabase(db);
    }

    private void resetDatabase(SQLiteDatabase db) {
        List<String> tables = new ArrayList<>();
        Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type=\'table\'", null);
        if (cursor.moveToFirst()) {
            do {
                tables.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }
        cursor.close();

        mFavoriteUrlsBackup = new ArrayList<>();
        cursor = db.query(TABLE_WALLPAPERS, new String[]{KEY_URL}, KEY_FAVORITE +" = ?",
                new String[]{"1"}, null, null, null);
        if (cursor.moveToFirst()) {
            do {
                mFavoriteUrlsBackup.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }
        cursor.close();

        for (int i = 0; i < tables.size(); i++) {
            try {
                String dropQuery = "DROP TABLE IF EXISTS " + tables.get(i);
                if (!tables.get(i).equalsIgnoreCase("SQLITE_SEQUENCE"))
                    db.execSQL(dropQuery);
            } catch (Exception ignored) {}
        }
        onCreate(db);
    }

    public void restoreFavorites() {
        if (mFavoriteUrlsBackup == null) return;

        for (String url : mFavoriteUrlsBackup) {
            ContentValues values = new ContentValues();
            values.put(KEY_FAVORITE, 1);
            mDatabase.get().mSQLiteDatabase.update(TABLE_WALLPAPERS,
                    values, KEY_URL +" = ?", new String[]{url});
        }

        mFavoriteUrlsBackup.clear();
        mFavoriteUrlsBackup = null;
    }

    public boolean openDatabase() {
        try {
            if (mDatabase == null || mDatabase.get() == null) {
                LogUtil.e("Database error: openDatabase() database instance is null");
                return false;
            }

            if (mDatabase.get().mSQLiteDatabase == null) {
                mDatabase.get().mSQLiteDatabase = mDatabase.get().getWritableDatabase();
            }

            if (!mDatabase.get().mSQLiteDatabase.isOpen()) {
                LogUtil.e("Database error: database openable false, trying to open the database again");
                mDatabase.get().mSQLiteDatabase = mDatabase.get().getWritableDatabase();
            }
            return mDatabase.get().mSQLiteDatabase.isOpen();
        } catch (SQLiteException | NullPointerException e) {
            LogUtil.e(Log.getStackTraceString(e));
            return false;
        }
    }

    public boolean closeDatabase() {
        try {
            if (mDatabase == null || mDatabase.get() == null) {
                LogUtil.e("Database error: closeDatabase() database instance is null");
                return false;
            }

            if (mDatabase.get().mSQLiteDatabase == null) {
                LogUtil.e("Database error: trying to close database which is not opened");
                return false;
            }
            mDatabase.get().mSQLiteDatabase.close();
            return true;
        } catch (SQLiteException | NullPointerException e) {
            LogUtil.e(Log.getStackTraceString(e));
            return false;
        }
    }

    public void add(@NonNull List<?> categoryLIst, @NonNull List<?> wallpaperList) {
        if (!openDatabase()) {
            LogUtil.e("Database error: add() failed to open database");
            return;
        }

        Iterator categoryIterator = categoryLIst.iterator();
        Iterator wallpaperIterator = wallpaperList.iterator();
        int size = categoryLIst.size() > wallpaperList.size() ? categoryLIst.size() : wallpaperList.size();

        String categoryQuery = "INSERT OR IGNORE INTO " +TABLE_CATEGORIES+ " (" +KEY_NAME+ ") VALUES (?);";
        String wallpaperQuery = "INSERT OR IGNORE INTO " +TABLE_WALLPAPERS+ " (" +KEY_NAME+ "," +KEY_AUTHOR+ "," +KEY_URL+ ","
                +KEY_THUMB_URL+ "," +KEY_CATEGORY+ "," +KEY_ADDED_ON+ ") VALUES (?,?,?,?,?,?);";

        SQLiteStatement categoryStatements = mDatabase.get().mSQLiteDatabase.compileStatement(categoryQuery);
        SQLiteStatement wallpaperStatements = mDatabase.get().mSQLiteDatabase.compileStatement(wallpaperQuery);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        int i = 0;
        do {
            categoryStatements.clearBindings();
            wallpaperStatements.clearBindings();

            if (categoryIterator.hasNext()) {
                Category category;
                if (categoryIterator.next() instanceof Category) {
                    category = (Category) categoryLIst.get(i);
                } else {
                    category = JsonHelper.getCategory(categoryLIst.get(i));
                }

                if (category != null) {
                    categoryStatements.bindString(1, category.getName());
                    categoryStatements.execute();
                }
            }

            if (wallpaperIterator.hasNext()) {
                Wallpaper wallpaper;
                if (wallpaperIterator.next() instanceof Wallpaper) {
                    wallpaper = (Wallpaper) wallpaperList.get(i);
                } else {
                    wallpaper = JsonHelper.getWallpaper(wallpaperList.get(i));
                }

                if (wallpaper != null) {
                    if (wallpaper.getUrl() != null) {
                        String name = wallpaper.getName();
                        if (name == null) name = "";

                        wallpaperStatements.bindString(1, name);

                        if (wallpaper.getAuthor() != null) {
                            wallpaperStatements.bindString(2, wallpaper.getAuthor());
                        } else {
                            wallpaperStatements.bindNull(2);
                        }

                        wallpaperStatements.bindString(3, wallpaper.getUrl());
                        wallpaperStatements.bindString(4, wallpaper.getThumbUrl());
                        wallpaperStatements.bindString(5, wallpaper.getCategory());
                        wallpaperStatements.bindString(6, TimeHelper.getLongDateTime());
                        wallpaperStatements.execute();
                    }
                }
            }
            i++;
        } while (i < size);

        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    public void addCategories(List<?> list) {
        if (!openDatabase()) {
            LogUtil.e("Database error: addCategories() failed to open database");
            return;
        }

        String query = "INSERT OR IGNORE INTO " +TABLE_CATEGORIES+ " (" +KEY_NAME+ ") VALUES (?);";
        SQLiteStatement statement = mDatabase.get().mSQLiteDatabase.compileStatement(query);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        for (int i = 0; i < list.size(); i++) {
            statement.clearBindings();

            Category category;
            if (list.get(i) instanceof Category) {
                category = (Category) list.get(i);
            } else {
                category = JsonHelper.getCategory(list.get(i));
            }

            if (category != null) {
                statement.bindString(1, category.getName());
                statement.execute();
            }
        }
        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    public void addWallpapers(@NonNull List<?> list) {
        if (!openDatabase()) {
            LogUtil.e("Database error: addWallpapers() failed to open database");
            return;
        }

        String query = "INSERT OR IGNORE INTO " +TABLE_WALLPAPERS+ " (" +KEY_NAME+ "," +KEY_AUTHOR+ "," +KEY_URL+ ","
                +KEY_THUMB_URL+ "," +KEY_CATEGORY+ "," +KEY_ADDED_ON+ ") VALUES (?,?,?,?,?,?);";
        SQLiteStatement statement = mDatabase.get().mSQLiteDatabase.compileStatement(query);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        for (int i = 0; i < list.size(); i++) {
            statement.clearBindings();

            Wallpaper wallpaper;
            if (list.get(i) instanceof Wallpaper) {
                wallpaper = (Wallpaper) list.get(i);
            } else {
                wallpaper = JsonHelper.getWallpaper(list.get(i));
            }

            if (wallpaper != null) {
                if (wallpaper.getUrl() != null) {
                    String name = wallpaper.getName();
                    if (name == null) name = "";

                    statement.bindString(1, name);

                    if (wallpaper.getAuthor() != null) {
                        statement.bindString(2, wallpaper.getAuthor());
                    } else {
                        statement.bindNull(2);
                    }

                    statement.bindString(3, wallpaper.getUrl());
                    statement.bindString(4, wallpaper.getThumbUrl());
                    statement.bindString(5, wallpaper.getCategory());
                    statement.bindString(6, TimeHelper.getLongDateTime());
                    statement.execute();
                }
            }
        }
        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    public void updateWallpaper(Wallpaper wallpaper) {
        if (!openDatabase()) {
            LogUtil.e("Database error: updateWallpaper() failed to open database");
            return;
        }

        if (wallpaper == null) return;

        ContentValues values = new ContentValues();
        if (wallpaper.getSize() > 0) {
            values.put(KEY_SIZE, wallpaper.getSize());
        }

        if (wallpaper.getMimeType() != null) {
            values.put(KEY_MIME_TYPE, wallpaper.getMimeType());
        }

        if (wallpaper.getDimensions() != null) {
            values.put(KEY_WIDTH, wallpaper.getDimensions().getWidth());
            values.put(KEY_HEIGHT, wallpaper.getDimensions().getHeight());
        }

        if (wallpaper.getColor() != 0) {
            values.put(KEY_COLOR, wallpaper.getColor());
        }

        if (values.size() > 0) {
            mDatabase.get().mSQLiteDatabase.update(TABLE_WALLPAPERS,
                    values, KEY_URL +" = ?", new String[]{wallpaper.getUrl()});
        }
    }

    public void updateWallpapers(@NonNull List<Wallpaper> wallpapers) {
        if (!openDatabase()) {
            LogUtil.e("Database error: updateWallpapers() failed to open database");
            return;
        }

        String query = "UPDATE " +TABLE_WALLPAPERS+ " SET " +KEY_FAVORITE+ " = ?, " +KEY_SIZE+ " = ?, "
                +KEY_MIME_TYPE+ " = ?, " +KEY_WIDTH+ " = ?," +KEY_HEIGHT+ " = ?, " +KEY_COLOR+ " = ? "
                +"WHERE " +KEY_URL+ " = ?";
        SQLiteStatement statement = mDatabase.get().mSQLiteDatabase.compileStatement(query);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        for (Wallpaper wallpaper : wallpapers) {
            statement.clearBindings();

            statement.bindLong(1, wallpaper.isFavorite() ? 1 : 0);
            statement.bindLong(2, wallpaper.getSize());

            String mimeType = wallpaper.getMimeType();
            if (mimeType != null) {
                statement.bindString(3, mimeType);
            } else {
                statement.bindNull(3);
            }

            ImageSize dimension = wallpaper.getDimensions();
            int width = dimension == null ? 0 : dimension.getWidth();
            int height = dimension == null ? 0 : dimension.getHeight();
            statement.bindLong(4, width);
            statement.bindLong(5, height);

            statement.bindLong(6, wallpaper.getColor());
            statement.bindString(7, wallpaper.getUrl());
            statement.execute();
        }

        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    public void selectCategory(int id, boolean isSelected) {
        if (!openDatabase()) {
            LogUtil.e("Database error: selectCategory() failed to open database");
            return;
        }

        ContentValues values = new ContentValues();
        values.put(KEY_SELECTED, isSelected ? 1 : 0);
        mDatabase.get().mSQLiteDatabase.update(TABLE_CATEGORIES, values, KEY_ID +" = ?", new String[]{String.valueOf(id)});
    }

    public void selectCategoryForMuzei(int id, boolean isSelected) {
        if (!openDatabase()) {
            LogUtil.e("Database error: selectCategoryForMuzei() failed to open database");
            return;
        }

        ContentValues values = new ContentValues();
        values.put(KEY_MUZEI_SELECTED, isSelected ? 1 : 0);
        mDatabase.get().mSQLiteDatabase.update(TABLE_CATEGORIES, values, KEY_ID +" = ?", new String[]{String.valueOf(id)});
    }

    public void favoriteWallpaper(String url, boolean isFavorite) {
        if (!openDatabase()) {
            LogUtil.e("Database error: favoriteWallpaper() failed to open database");
            return;
        }

        ContentValues values = new ContentValues();
        values.put(KEY_FAVORITE, isFavorite ? 1 : 0);
        mDatabase.get().mSQLiteDatabase.update(TABLE_WALLPAPERS, values,
                KEY_URL +" = ?", new String[]{url});
    }

    private List<String> getSelectedCategories(boolean isMuzei) {
        if (!openDatabase()) {
            LogUtil.e("Database error: getSelectedCategories() failed to open database");
            return new ArrayList<>();
        }

        List<String> categories = new ArrayList<>();
        String column = isMuzei ? KEY_MUZEI_SELECTED : KEY_SELECTED;
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_CATEGORIES, new String[]{KEY_NAME}, column +" = ?",
                new String[]{"1"}, null, null, KEY_NAME);
        if (cursor.moveToFirst()) {
            do {
                categories.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }
        cursor.close();
        return categories;
    }

    public List<Category> getCategories() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getCategories() failed to open database");
            return new ArrayList<>();
        }

        List<Category> categories = new ArrayList<>();
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_CATEGORIES,
                null, null, null, null, null, KEY_NAME);
        if (cursor.moveToFirst()) {
            do {
                Category category = Category.Builder()
                        .id(cursor.getInt(cursor.getColumnIndex(KEY_ID)))
                        .name(cursor.getString(cursor.getColumnIndex(KEY_NAME)))
                        .selected(cursor.getInt(cursor.getColumnIndex(KEY_SELECTED)) == 1)
                        .muzeiSelected(cursor.getInt(cursor.getColumnIndex(KEY_MUZEI_SELECTED)) == 1)
                        .build();
                categories.add(category);
            } while (cursor.moveToNext());
        }
        cursor.close();
        return categories;
    }

    public Category getCategoryPreview(@NonNull Category category) {
        String name = category.getName().toLowerCase(Locale.getDefault());
        String query = "SELECT wallpapers.thumbUrl, wallpapers.color, " +
                "(SELECT COUNT(*) FROM wallpapers WHERE LOWER(wallpapers.category) LIKE ?) AS " +KEY_COUNT+
                " FROM wallpapers WHERE LOWER(wallpapers.category) LIKE ? ORDER BY RANDOM() LIMIT 1";
        Cursor cursor = mDatabase.get().mSQLiteDatabase.rawQuery(query, new String[]{"%" +name+ "%", "%" +name+ "%"});
        if (cursor.moveToFirst()) {
            do {
                category.setColor(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)));
                category.setThumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)));
                category.setCount(cursor.getInt(cursor.getColumnIndex(KEY_COUNT)));
            } while (cursor.moveToNext());
        }
        cursor.close();
        return category;
    }

    public List<Category> getWallpaperCategories(String category) {
        if (!openDatabase()) {
            LogUtil.e("Database error: getWallpaperCategories() failed to open database");
            return new ArrayList<>();
        }

        List<Category> categories = new ArrayList<>();
        String[] strings = category.split("[,;]");
        for (String string : strings) {
            String s = string.toLowerCase(Locale.getDefault());
            String query = "SELECT categories.id, categories.name, " +
                    "(SELECT wallpapers.thumbUrl FROM wallpapers WHERE LOWER(wallpapers.category) LIKE ? ORDER BY RANDOM() LIMIT 1) AS thumbUrl, " +
                    "(SELECT COUNT(*) FROM wallpapers WHERE LOWER(wallpapers.category) LIKE ?) AS " +KEY_COUNT+
                    " FROM categories WHERE LOWER(categories.name) = ? LIMIT 1";
            Cursor cursor = mDatabase.get().mSQLiteDatabase.rawQuery(query, new String[]{"%" +s+ "%", "%" +s+ "%", s});
            if (cursor.moveToFirst()) {
                do {
                    Category c = Category.Builder()
                            .id(cursor.getInt(cursor.getColumnIndex(KEY_ID)))
                            .name(cursor.getString(cursor.getColumnIndex(KEY_NAME)))
                            .thumbUrl(cursor.getString(2))
                            .count(cursor.getInt(cursor.getColumnIndex(KEY_COUNT)))
                            .build();
                    categories.add(c);
                } while (cursor.moveToNext());
            }
            cursor.close();
        }
        return categories;
    }

    public int getCategoryCount(String category) {
        if (!openDatabase()) {
            LogUtil.e("Database error: getCategoryCount() failed to open database");
            return 0;
        }

        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, "LOWER(" +KEY_CATEGORY+ ") LIKE ?",
                new String[]{"%" +category.toLowerCase(Locale.getDefault())+ "%"}, null, null, null);
        int count = cursor.getCount();
        cursor.close();
        return count;
    }

    @Nullable
    public Wallpaper getWallpaper(String url) {
        if (!openDatabase()) {
            LogUtil.e("Database error: getWallpaper() failed to open database");
            return null;
        }

        Wallpaper wallpaper = null;
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS,
                null, KEY_URL +" = ?", new String[]{url}, null, null, null, "1");
        if (cursor.moveToFirst()) {
            do {
                int width = cursor.getInt(cursor.getColumnIndex(KEY_WIDTH));
                int height = cursor.getInt(cursor.getColumnIndex(KEY_HEIGHT));
                ImageSize dimensions = null;
                if (width  > 0 && height > 0) {
                    dimensions = new ImageSize(width, height);
                }

                int wId = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ wId;
                }

                wallpaper = Wallpaper.Builder()
                        .id(wId)
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .favorite(cursor.getInt(cursor.getColumnIndex(KEY_FAVORITE)) == 1)
                        .dimensions(dimensions)
                        .mimeType(cursor.getString(cursor.getColumnIndex(KEY_MIME_TYPE)))
                        .size(cursor.getInt(cursor.getColumnIndex(KEY_SIZE)))
                        .color(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)))
                        .build();
            } while (cursor.moveToNext());
        }
        cursor.close();
        return wallpaper;
    }

    public List<Wallpaper> getFilteredWallpapers(Filter filter) {
        if (!openDatabase()) {
            LogUtil.e("Database error: getFilteredWallpapers() failed to open database");
            return new ArrayList<>();
        }

        List<Wallpaper> wallpapers = new ArrayList<>();

        StringBuilder condition = new StringBuilder();
        List<String> selection = new ArrayList<>();
        for (int i = 0; i < filter.size(); i++) {
            Filter.Options options = filter.get(i);
            if (options != null) {
                if (condition.length() > 0 ) {
                    condition.append(" OR ").append("LOWER(")
                            .append(options.getColumn().getName())
                            .append(")").append(" LIKE ?");
                } else {
                    condition.append("LOWER(")
                            .append(options.getColumn().getName()).append(")")
                            .append(" LIKE ?");
                }
                selection.add("%" +options.getQuery().toLowerCase(Locale.getDefault())+ "%");
            }
        }

        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, condition.toString(),
                selection.toArray(new String[selection.size()]),
                null, null, KEY_NAME);
        if (cursor.moveToFirst()) {
            do {
                int id = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ id;
                }

                Wallpaper wallpaper = Wallpaper.Builder()
                        .id(id)
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .addedOn(cursor.getString(cursor.getColumnIndex(KEY_ADDED_ON)))
                        .favorite(cursor.getInt(cursor.getColumnIndex(KEY_FAVORITE)) == 1)
                        .color(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)))
                        .build();
                wallpapers.add(wallpaper);
            } while (cursor.moveToNext());
        }
        cursor.close();

        Collections.sort(wallpapers, new AlphanumComparator() {

            @Override
            public int compare(Object o1, Object o2) {
                String s1 = ((Wallpaper) o1).getName();
                String s2 = ((Wallpaper) o2).getName();
                return super.compare(s1, s2);
            }
        });
        return wallpapers;
    }

    public List<Wallpaper> getWallpapers() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getWallpapers() failed to open database");
            return new ArrayList<>();
        }

        List<Wallpaper> wallpapers = new ArrayList<>();
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS,
                null, null, null, null, null, getSortBy(Preferences.get(mContext).getSortBy()));
        if (cursor.moveToFirst()) {
            do {
                int colorIndex = cursor.getColumnIndex(KEY_COLOR);
                int id = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ id;
                }

                Wallpaper.Builder builder = Wallpaper.Builder()
                        .id(id)
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .favorite(cursor.getInt(cursor.getColumnIndex(KEY_FAVORITE)) == 1);
                if (colorIndex > -1) {
                    builder.color(cursor.getInt(colorIndex));
                }
                wallpapers.add(builder.build());
            } while (cursor.moveToNext());
        }
        cursor.close();

        if (Preferences.get(mContext).getSortBy() == PopupItem.Type.SORT_NAME) {
            Collections.sort(wallpapers, new AlphanumComparator() {

                @Override
                public int compare(Object o1, Object o2) {
                    String s1 = ((Wallpaper) o1).getName();
                    String s2 = ((Wallpaper) o2).getName();
                    return super.compare(s1, s2);
                }
            });
        }
        return wallpapers;
    }

    public List<Wallpaper> getLatestWallpapers() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getLatestWallpapers() failed to open database");
            return new ArrayList<>();
        }

        List<Wallpaper> wallpapers = new ArrayList<>();
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, null, null, null, null,
                KEY_ADDED_ON+ " DESC, " +KEY_ID,
                String.valueOf(WallpaperBoardApplication.getConfig().getLatestWallpapersDisplayMax()));
        if (cursor.moveToFirst()) {
            do {
                int width = cursor.getInt(cursor.getColumnIndex(KEY_WIDTH));
                int height = cursor.getInt(cursor.getColumnIndex(KEY_HEIGHT));
                ImageSize dimensions = null;
                if (width  > 0 && height > 0) {
                    dimensions = new ImageSize(width, height);
                }

                int id = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ id;
                }

                Wallpaper wallpaper = Wallpaper.Builder()
                        .id(id)
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .favorite(cursor.getInt(cursor.getColumnIndex(KEY_FAVORITE)) == 1)
                        .dimensions(dimensions)
                        .mimeType(cursor.getString(cursor.getColumnIndex(KEY_MIME_TYPE)))
                        .size(cursor.getInt(cursor.getColumnIndex(KEY_SIZE)))
                        .color(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)))
                        .build();
                wallpapers.add(wallpaper);
            } while (cursor.moveToNext());
        }
        cursor.close();
        return wallpapers;
    }

    @Nullable
    public Wallpaper getRandomWallpaper() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getRandomWallpaper() failed to open database");
            return null;
        }

        Wallpaper wallpaper = null;
        List<String> selected = getSelectedCategories(true);
        List<String> selection = new ArrayList<>();
        if (selected.size() == 0) return null;

        StringBuilder CONDITION = new StringBuilder();
        for (String item : selected) {
            if (CONDITION.length() > 0 ) {
                CONDITION.append(" OR ").append("LOWER(").append(KEY_CATEGORY).append(")").append(" LIKE ?");
            } else {
                CONDITION.append("LOWER(").append(KEY_CATEGORY).append(")").append(" LIKE ?");
            }
            selection.add("%" +item.toLowerCase(Locale.getDefault())+ "%");
        }

        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, CONDITION.toString(),
                selection.toArray(new String[selection.size()]), null, null, "RANDOM()", "1");
        if (cursor.moveToFirst()) {
            do {
                int id = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ id;
                }

                wallpaper = Wallpaper.Builder()
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .build();
            } while (cursor.moveToNext());
        }
        cursor.close();
        return wallpaper;
    }

    public int getWallpapersCount() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getWallpapersCount() failed to open database");
            return 0;
        }

        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, null, null, null, null, null, null);
        int rowCount = cursor.getCount();
        cursor.close();
        return rowCount;
    }

    public List<Wallpaper> getFavoriteWallpapers() {
        if (!openDatabase()) {
            LogUtil.e("Database error: getFavoriteWallpapers() failed to open database");
            return new ArrayList<>();
        }

        List<Wallpaper> wallpapers = new ArrayList<>();
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS, null, KEY_FAVORITE +" = ?",
                new String[]{"1"}, null, null, KEY_NAME +", "+ KEY_ID);
        if (cursor.moveToFirst()) {
            do {
                int width = cursor.getInt(cursor.getColumnIndex(KEY_WIDTH));
                int height = cursor.getInt(cursor.getColumnIndex(KEY_HEIGHT));
                ImageSize dimensions = null;
                if (width  > 0 && height > 0) {
                    dimensions = new ImageSize(width, height);
                }

                int id = cursor.getInt(cursor.getColumnIndex(KEY_ID));
                String name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
                if (name.length() == 0) {
                    name = "Wallpaper "+ id;
                }

                Wallpaper wallpaper = Wallpaper.Builder()
                        .id(id)
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .category(cursor.getString(cursor.getColumnIndex(KEY_CATEGORY)))
                        .favorite(cursor.getInt(cursor.getColumnIndex(KEY_FAVORITE)) == 1)
                        .dimensions(dimensions)
                        .mimeType(cursor.getString(cursor.getColumnIndex(KEY_MIME_TYPE)))
                        .size(cursor.getInt(cursor.getColumnIndex(KEY_SIZE)))
                        .color(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)))
                        .build();
                wallpapers.add(wallpaper);
            } while (cursor.moveToNext());
        }
        cursor.close();

        if (Preferences.get(mContext).getSortBy() == PopupItem.Type.SORT_NAME) {
            Collections.sort(wallpapers, new AlphanumComparator() {

                @Override
                public int compare(Object o1, Object o2) {
                    String s1 = ((Wallpaper) o1).getName();
                    String s2 = ((Wallpaper) o2).getName();
                    return super.compare(s1, s2);
                }
            });
        }
        return wallpapers;
    }

    public void deleteWallpapers(@NonNull List<Wallpaper> wallpapers) {
        if (!openDatabase()) {
            LogUtil.e("Database error: deleteWallpapers() failed to open database");
            return;
        }

        String query = "DELETE FROM " +TABLE_WALLPAPERS+ " WHERE " +KEY_URL+ " = ?";
        SQLiteStatement statement = mDatabase.get().mSQLiteDatabase.compileStatement(query);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        for (Wallpaper wallpaper : wallpapers) {
            statement.clearBindings();
            statement.bindString(1, wallpaper.getUrl());
            statement.execute();
        }

        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    public void resetAutoIncrement() {
        if (!openDatabase()) {
            LogUtil.e("Database error: resetAutoIncrement() failed to open database");
            return;
        }

        mSQLiteDatabase.delete("SQLITE_SEQUENCE", "NAME = ?", new String[]{TABLE_WALLPAPERS});
    }

    public void deleteCategories(@NonNull List<Category> categories) {
        if (!openDatabase()) {
            LogUtil.e("Database error: deleteCategories() failed to open database");
            return;
        }

        String query = "DELETE FROM " +TABLE_CATEGORIES+ " WHERE " +KEY_NAME+ " = ?";
        SQLiteStatement statement = mDatabase.get().mSQLiteDatabase.compileStatement(query);
        mDatabase.get().mSQLiteDatabase.beginTransaction();

        for (Category category : categories) {
            statement.clearBindings();
            statement.bindString(1, category.getName());
            statement.execute();
        }

        mDatabase.get().mSQLiteDatabase.setTransactionSuccessful();
        mDatabase.get().mSQLiteDatabase.endTransaction();
    }

    private String getSortBy(PopupItem.Type type) {
        switch (type) {
            case SORT_LATEST:
                return KEY_ADDED_ON +" DESC, "+ KEY_ID;
            case SORT_OLDEST:
                return KEY_ADDED_ON +", "+ KEY_ID +" DESC";
            case SORT_NAME:
                return KEY_NAME;
            case SORT_RANDOM:
                return "RANDOM()";
            default:
                return KEY_ADDED_ON +" DESC, "+ KEY_ID;
        }
    }
}
 public class TypefaceHelper {

    private static HashMap<String, WeakReference<Typeface>> mTypefaces = new HashMap<>();
    private static final String REGULAR = "regular";
    private static final String MEDIUM = "medium";
    private static final String BOLD = "bold";
    private static final String LOGO = "logo";

    @Nullable
    public static Typeface getRegular(@NonNull Context context) {
        try {
            if (!mTypefaces.containsKey(REGULAR) || mTypefaces.get(REGULAR).get() == null) {
                Typeface typeface = Typeface.createFromAsset(
                        context.getAssets(), "fonts/Font-Regular.ttf");
                mTypefaces.put(REGULAR, new WeakReference<>(typeface));
            }
            return mTypefaces.get(REGULAR).get();
        } catch (Exception e) {
            return null;
        }
    }

    @Nullable
    public static Typeface getMedium(@NonNull Context context) {
        try {
            if (!mTypefaces.containsKey(MEDIUM) || mTypefaces.get(MEDIUM).get() == null) {
                Typeface typeface = Typeface.createFromAsset(
                        context.getAssets(), "fonts/Font-Medium.ttf");
                mTypefaces.put(MEDIUM, new WeakReference<>(typeface));
            }
            return mTypefaces.get(MEDIUM).get();
        } catch (Exception e) {
            return null;
        }
    }

    @Nullable
    public static Typeface getBold(@NonNull Context context) {
        try {
            if (!mTypefaces.containsKey(BOLD) || mTypefaces.get(BOLD).get() == null) {
                Typeface typeface = Typeface.createFromAsset(
                        context.getAssets(), "fonts/Font-Bold.ttf");
                mTypefaces.put(BOLD, new WeakReference<>(typeface));
            }
            return mTypefaces.get(BOLD).get();
        } catch (Exception e) {
            return null;
        }
    }

    @Nullable
    public static Typeface getLogo(@NonNull Context context) {
        try {
            if (!mTypefaces.containsKey(LOGO) || mTypefaces.get(LOGO).get() == null) {
                Typeface typeface = Typeface.createFromAsset(
                        context.getAssets(), "fonts/Font-Logo.ttf");
                mTypefaces.put(LOGO, new WeakReference<>(typeface));
            }
            return mTypefaces.get(LOGO).get();
        } catch (Exception e) {
            return null;
        }
    }
}
 public class PopupItem {

    private final String mTitle;
    private int mIcon;
    private boolean mShowCheckbox;
    private boolean mCheckboxValue;
    private boolean mIsSelected;
    private Type mType;

    public PopupItem(String title) {
        mTitle = title;
        mShowCheckbox = false;
        mCheckboxValue = false;
        mIsSelected = false;
    }

    public PopupItem setIcon(@DrawableRes int icon) {
        mIcon = icon;
        return this;
    }

    public PopupItem setShowCheckbox(boolean showCheckbox) {
        mShowCheckbox = showCheckbox;
        return this;
    }

    public PopupItem setCheckboxValue(boolean checkboxValue) {
        mCheckboxValue = checkboxValue;
        return this;
    }

    public PopupItem setSelected(boolean selected) {
        mIsSelected = selected;
        return this;
    }

    public PopupItem setType(Type type) {
        mType = type;
        return this;
    }

    public String getTitle() {
        return mTitle;
    }

    @DrawableRes
    public int getIcon() {
        return mIcon;
    }

    public boolean isShowCheckbox() {
        return mShowCheckbox;
    }

    public boolean getCheckboxValue() {
        return mCheckboxValue;
    }

    public boolean isSelected() {
        return mIsSelected;
    }

    public Type getType() {
        return mType;
    }

    public enum Type {
        WALLPAPER_CROP,
        HOMESCREEN,
        LOCKSCREEN,
        HOMESCREEN_LOCKSCREEN,
        DOWNLOAD,
        SORT_LATEST,
        SORT_OLDEST,
        SORT_NAME,
        SORT_RANDOM,
    }

    public static List<PopupItem> getApplyItems(@NonNull Context context) {
        List<PopupItem> items = new ArrayList<>();

        //Todo: wait until google fix the issue, then enable wallpaper crop again on API 26+
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            items.add(new PopupItem(context.getResources().getString(R.string.menu_wallpaper_crop))
                    .setType(Type.WALLPAPER_CROP)
                    .setCheckboxValue(Preferences.get(context).isCropWallpaper())
                    .setShowCheckbox(true));
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            items.add(new PopupItem(context.getResources().getString(R.string.menu_apply_lockscreen))
                    .setType(Type.LOCKSCREEN)
                    .setIcon(R.drawable.ic_toolbar_lockscreen));
        }

        items.add(new PopupItem(context.getResources().getString(R.string.menu_apply_homescreen))
                .setType(Type.HOMESCREEN)
                .setIcon(R.drawable.ic_toolbar_homescreen));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            items.add(new PopupItem(context.getResources().getString(R.string.menu_apply_homescreen_lockscreen))
                    .setType(Type.HOMESCREEN_LOCKSCREEN)
                    .setIcon(R.drawable.ic_toolbar_homescreen_lockscreen));
        }

        if (context.getResources().getBoolean(R.bool.enable_wallpaper_download)) {
            items.add(new PopupItem(context.getResources().getString(R.string.menu_save))
                    .setType(Type.DOWNLOAD)
                    .setIcon(R.drawable.ic_toolbar_download));
        }
        return items;
    }

    public static List<PopupItem> getSortItems(@NonNull Context context, boolean selection) {
        List<PopupItem> items = new ArrayList<>();
        items.add(new PopupItem(context.getResources().getString(R.string.menu_sort_latest))
                .setType(Type.SORT_LATEST)
                .setIcon(R.drawable.ic_toolbar_sort_latest));

        items.add(new PopupItem(context.getResources().getString(R.string.menu_sort_oldest))
                .setType(Type.SORT_OLDEST)
                .setIcon(R.drawable.ic_toolbar_sort_oldest));

        items.add(new PopupItem(context.getResources().getString(R.string.menu_sort_name))
                .setType(Type.SORT_NAME)
                .setIcon(R.drawable.ic_toolbar_sort_name));

        items.add(new PopupItem(context.getResources().getString(R.string.menu_sort_random))
                .setType(Type.SORT_RANDOM)
                .setIcon(R.drawable.ic_toolbar_sort_random));

        if (selection) {
            Type type = Preferences.get(context).getSortBy();
            int order = Preferences.get(context).getSortByOrder(type);
            if (order >= 0 && order < items.size()) {{
                items.get(order).setSelected(true);
            }}
        }
        return items;
    }
}
 public class Wallpaper {

    private int mId;
    private final String mName;
    private final String mAuthor;
    private final String mThumbUrl;
    private final String mUrl;
    private final String mCategory;
    private final String mAddedOn;
    private int mColor;
    private String mMimeType;
    private int mSize;
    private boolean mIsFavorite;
    private ImageSize mDimensions;

    private Wallpaper(String name, String author, String url, String thumbUrl, String category, String addedOn) {
        mName = name;
        mAuthor = author;
        mUrl = url;
        mThumbUrl = thumbUrl;
        mCategory = category;
        mAddedOn = addedOn;
    }

    public int getId() {
        return mId;
    }

    public String getName() {
        return mName;
    }

    public String getAuthor() {
        return mAuthor;
    }

    public String getUrl() {
        return mUrl;
    }

    public String getThumbUrl() {
        return mThumbUrl;
    }

    public String getCategory() {
        return mCategory;
    }

    public String getAddedOn() {
        return mAddedOn;
    }

    public int getColor() {
        return mColor;
    }

    public String getMimeType() {
        return mMimeType;
    }

    public int getSize() {
        return mSize;
    }

    public ImageSize getDimensions() {
        return mDimensions;
    }

    public boolean isFavorite() {
        return mIsFavorite;
    }

    public void setId(int id) {
        mId = id;
    }

    public void setColor(int color) {
        mColor = color;
    }

    public void setMimeType(String mimeType) {
        mMimeType = mimeType;
    }

    public void setSize(int size) {
        mSize = size;
    }

    public void setDimensions(ImageSize dimensions) {
        mDimensions = dimensions;
    }

    public void setFavorite(boolean isFavorite) {
        mIsFavorite = isFavorite;
    }

    @Override
    public boolean equals(Object object) {
        boolean equals = false;
        if (object != null && object instanceof Wallpaper) {
            equals = mAuthor.equals(((Wallpaper) object).getAuthor()) &&
                    mUrl.equals(((Wallpaper) object).getUrl()) &&
                    mThumbUrl.equals(((Wallpaper) object).getThumbUrl()) &&
                    mCategory.equals(((Wallpaper) object).getCategory());
        }
        return equals;
    }

    public static Builder Builder() {
        return new Builder();
    }

    public static class Builder {

        private int mId;
        private String mName;
        private String mAuthor;
        private String mThumbUrl;
        private String mUrl;
        private String mCategory;
        private String mAddedOn;
        private int mColor;
        private String mMimeType;
        private int mSize;
        private boolean mIsFavorite;
        private ImageSize mDimensions;

        private Builder() {
            mId = -1;
            mSize = 0;
            mColor = 0;
        }

        public Builder id(int id) {
            mId = id;
            return this;
        }

        public Builder name(String name) {
            mName = name;
            return this;
        }

        public Builder author(String author) {
            mAuthor = author;
            return this;
        }

        public Builder url(String url) {
            mUrl = url;
            return this;
        }

        public Builder thumbUrl(String thumbUrl) {
            mThumbUrl = thumbUrl;
            return this;
        }

        public Builder category(String category) {
            mCategory = category;
            return this;
        }

        public Builder addedOn(String addedOn) {
            mAddedOn = addedOn;
            return this;
        }

        public Builder favorite(boolean isFavorite) {
            mIsFavorite = isFavorite;
            return this;
        }

        public Builder dimensions(ImageSize dimensions) {
            mDimensions = dimensions;
            return this;
        }

        public Builder mimeType(String mimeType) {
            mMimeType = mimeType;
            return this;
        }

        public Builder size(int size) {
            mSize = size;
            return this;
        }

        public Builder color(int color) {
            mColor = color;
            return this;
        }

        public Wallpaper build() {
            Wallpaper wallpaper = new Wallpaper(mName, mAuthor, mUrl, mThumbUrl, mCategory, mAddedOn);
            wallpaper.setId(mId);
            wallpaper.setDimensions(mDimensions);
            wallpaper.setFavorite(mIsFavorite);
            wallpaper.setMimeType(mMimeType);
            wallpaper.setSize(mSize);
            wallpaper.setColor(mColor);
            return wallpaper;
        }
    }
}
 public class Preferences {

    private static final String PREFERENCES_NAME = "wallpaper_board_preferences";

    private static final String KEY_LICENSED = "licensed";
    private static final String KEY_FIRST_RUN = "first_run";
    private static final String KEY_DARK_THEME = "dark_theme";
    private static final String KEY_ROTATE_TIME = "rotate_time";
    private static final String KEY_ROTATE_MINUTE = "rotate_minute";
    private static final String KEY_WIFI_ONLY = "wifi_only";
    private static final String KEY_WALLS_DIRECTORY = "wallpaper_download_directory";
    private static final String KEY_CROP_WALLPAPER = "crop_wallpaper";
    private static final String KEY_WALLPAPER_PREVIEW_INTRO = "wallpaper_preview_intro";
    private static final String KEY_CURRENT_LOCALE = "current_locale";
    private static final String KEY_LOCALE_DEFAULT = "localeDefault";
    private static final String KEY_WALLPAPER_TOOLTIP = "wallpaper_tooltip";
    private static final String KEY_SORT_BY = "sort_by";
    private static final String KEY_HIGH_QUALITY_PREVIEW = "high_quality_preview";
    private static final String KEY_BACKUP = "backup";
    private static final String KEY_PREVIOUS_BACKUP = "previousBackup";

    private static WeakReference<Preferences> mPreferences;
    private final Context mContext;

    private Preferences(@NonNull Context context) {
        mContext = context;
    }

    @NonNull
    public static Preferences get(@NonNull Context context) {
        if (mPreferences == null || mPreferences.get() == null) {
            mPreferences = new WeakReference<>(new Preferences(context));
        }
        return mPreferences.get();
    }

    private SharedPreferences getSharedPreferences() {
        return mPreferences.get().mContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
    }

    public void clearPreferences() {
        boolean isLicensed = isLicensed();
        getSharedPreferences().edit().clear().apply();

        if (isLicensed) {
            setFirstRun(false);
            setLicensed(true);
        }
    }

    public boolean isLicensed() {
        return getSharedPreferences().getBoolean(KEY_LICENSED, false);
    }

    public void setLicensed(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_LICENSED, bool).apply();
    }

    public boolean isFirstRun() {
        return getSharedPreferences().getBoolean(KEY_FIRST_RUN, true);
    }

    public void setFirstRun(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_FIRST_RUN, bool).apply();
    }

    public boolean isDarkTheme() {
        boolean useDarkTheme = mPreferences.get().mContext.getResources().getBoolean(R.bool.use_dark_theme);
        boolean isThemingEnabled = WallpaperBoardApplication.getConfig().isDashboardThemingEnabled();
        if (!isThemingEnabled) return useDarkTheme;
        return getSharedPreferences().getBoolean(KEY_DARK_THEME, useDarkTheme);
    }

    public void setDarkTheme(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_DARK_THEME, bool).apply();
    }

    public boolean isShadowEnabled() {
        return WallpaperBoardApplication.getConfig().isShadowEnabled();
    }

    public boolean isTimeToShowWallpaperPreviewIntro() {
        return getSharedPreferences().getBoolean(KEY_WALLPAPER_PREVIEW_INTRO, true);
    }

    public void setTimeToShowWallpaperPreviewIntro(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_WALLPAPER_PREVIEW_INTRO, bool).apply();
    }

    public void setRotateTime (int time) {
        getSharedPreferences().edit().putInt(KEY_ROTATE_TIME, time).apply();
    }

    public int getRotateTime() {
        return getSharedPreferences().getInt(KEY_ROTATE_TIME, 3600000);
    }

    public void setRotateMinute (boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_ROTATE_MINUTE, bool).apply();
    }

    public boolean isRotateMinute() {
        return getSharedPreferences().getBoolean(KEY_ROTATE_MINUTE, false);
    }

    public boolean isWifiOnly() {
        return getSharedPreferences().getBoolean(KEY_WIFI_ONLY, false);
    }

    public void setWifiOnly (boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_WIFI_ONLY, bool).apply();
    }

    void setWallsDirectory(String directory) {
        getSharedPreferences().edit().putString(KEY_WALLS_DIRECTORY, directory).apply();
    }

    public String getWallsDirectory() {
        return getSharedPreferences().getString(KEY_WALLS_DIRECTORY, "");
    }

    public boolean isCropWallpaper() {
        return getSharedPreferences().getBoolean(KEY_CROP_WALLPAPER,
                WallpaperBoardApplication.getConfig().isCropWallpaperEnabledByDefault());
    }

    public void setCropWallpaper(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_CROP_WALLPAPER, bool).apply();
    }

    public boolean isShowWallpaperTooltip() {
        return getSharedPreferences().getBoolean(KEY_WALLPAPER_TOOLTIP, true);
    }

    public void setShowWallpaperTooltip(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_WALLPAPER_TOOLTIP, bool).apply();
    }

    public boolean isHighQualityPreviewEnabled() {
        return getSharedPreferences().getBoolean(KEY_HIGH_QUALITY_PREVIEW,
                WallpaperBoardApplication.getConfig().isHighQualityPreviewEnabled());
    }

    public void setHighQualityPreviewEnabled(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_HIGH_QUALITY_PREVIEW, bool).apply();
    }

    public boolean isBackupRestored() {
        return getSharedPreferences().getBoolean(KEY_BACKUP, false);
    }

    public void setBackupRestored(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_BACKUP, bool).apply();
    }

    public boolean isPreviousBackupExist() {
        return getSharedPreferences().getBoolean(KEY_PREVIOUS_BACKUP, false);
    }

    public void setPreviousBackupExist(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_PREVIOUS_BACKUP, bool).apply();
    }

    public Locale getCurrentLocale() {
        if (isLocaleDefault()) {
            Locale defaultLocale = getDefaultLocale();
            if (defaultLocale != null) {
                return defaultLocale;
            }
        }

        String code = getSharedPreferences().getString(KEY_CURRENT_LOCALE, "en_US");
        return LocaleHelper.getLocale(code);
    }

    public void setCurrentLocale(String code) {
        getSharedPreferences().edit().putString(KEY_CURRENT_LOCALE, code).apply();
    }

    public boolean isLocaleDefault() {
        return getSharedPreferences().getBoolean(KEY_LOCALE_DEFAULT, true);
    }

    public void setLocaleDefault(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_LOCALE_DEFAULT, bool).apply();
    }

    @Nullable
    private Locale getDefaultLocale() {
        Locale locale = LocaleHelper.getSystem();
        List<Language> languages = LocaleHelper.getAvailableLanguages(mContext);

        Locale currentLocale = null;
        for (Language language : languages) {
            Locale l = language.getLocale();
            if (locale.toString().equals(l.toString())) {
                currentLocale = l;
                break;
            }
        }

        if (currentLocale == null) {
            for (Language language : languages) {
                Locale l = language.getLocale();
                if (locale.getLanguage().equals(l.getLanguage())) {
                    currentLocale = l;
                    break;
                }
            }
        }
        return currentLocale;
    }

    public void setSortBy(PopupItem.Type type) {
        getSharedPreferences().edit().putInt(KEY_SORT_BY, getSortByOrder(type)).apply();
    }

    public int getSortByOrder(PopupItem.Type type) {
        switch (type) {
            case SORT_LATEST:
                return 0;
            case SORT_OLDEST:
                return 1;
            case SORT_NAME:
                return 2;
            case SORT_RANDOM:
                return 3;
            default:
                return 2;
        }
    }

    public PopupItem.Type getSortBy(){
        int sort = getSharedPreferences().getInt(KEY_SORT_BY, 2);
        if (sort == 0) {
            return PopupItem.Type.SORT_LATEST;
        } else if (sort == 1) {
            return PopupItem.Type.SORT_OLDEST;
        } else if (sort == 2) {
            return PopupItem.Type.SORT_NAME;
        } else if (sort == 3) {
            return PopupItem.Type.SORT_RANDOM;
        }
        return PopupItem.Type.SORT_NAME;
    }

    public boolean isConnectedToNetwork() {
        try {
            ConnectivityManager connectivityManager = (ConnectivityManager)
                    mPreferences.get().mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
            return activeNetworkInfo != null && activeNetworkInfo.isConnected();
        } catch (Exception e) {
            return false;
        }
    }

    public boolean isConnectedAsPreferred() {
        try {
            if (isWifiOnly()) {
                ConnectivityManager connectivityManager = (ConnectivityManager)
                        mPreferences.get().mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
                return activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
                        activeNetworkInfo.isConnected();
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
 public class WallpaperApplyTask extends AsyncTask<Void, Void, Boolean> implements WallpaperPropertiesLoaderTask.Callback{

    private final WeakReference<Context> mContext;
    private Apply mApply;
    private RectF mRectF;
    private Executor mExecutor;
    private Wallpaper mWallpaper;
    private MaterialDialog mDialog;

    private WallpaperApplyTask(Context context) {
        mContext = new WeakReference<>(context);
    }

    public WallpaperApplyTask to(Apply apply) {
        mApply = apply;
        return this;
    }

    public WallpaperApplyTask wallpaper(@NonNull Wallpaper wallpaper) {
        mWallpaper = wallpaper;
        return this;
    }

    public WallpaperApplyTask crop(@Nullable RectF rectF) {
        mRectF = rectF;
        return this;
    }

    public AsyncTask start() {
        return start(SERIAL_EXECUTOR);
    }

    public AsyncTask start(@NonNull Executor executor) {
        if (mDialog == null) {
            int color = mWallpaper.getColor();
            if (color == 0) {
                color = ColorHelper.getAttributeColor(mContext.get(), R.attr.colorAccent);
            }

            final MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext.get());
            builder.widgetColor(color)
                    .typeface(TypefaceHelper.getMedium(mContext.get()), TypefaceHelper.getRegular(mContext.get()))
                    .progress(true, 0)
                    .cancelable(false)
                    .progressIndeterminateStyle(true)
                    .content(R.string.wallpaper_loading)
                    .positiveColor(color)
                    .positiveText(android.R.string.cancel)
                    .onPositive((dialog, which) -> {
                        ImageLoader.getInstance().stop();
                        cancel(true);
                    });

            mDialog = builder.build();
        }

        if (!mDialog.isShowing()) mDialog.show();

        mExecutor = executor;
        if (mWallpaper == null) {
            LogUtil.e("WallpaperApply cancelled, wallpaper is null");
            return null;
        }

        if (mWallpaper.getDimensions() == null) {
            return WallpaperPropertiesLoaderTask.prepare(mContext.get())
                    .wallpaper(mWallpaper)
                    .callback(this)
                    .start(AsyncTask.THREAD_POOL_EXECUTOR);
        }
        return executeOnExecutor(executor);
    }

    public static WallpaperApplyTask prepare(@NonNull Context context) {
        return new WallpaperApplyTask(context);
    }

    @Override
    public void onPropertiesReceived(Wallpaper wallpaper) {
        mWallpaper = wallpaper;
        if (mExecutor == null) mExecutor = SERIAL_EXECUTOR;
        if (mWallpaper.getDimensions() == null) {
            LogUtil.e("WallpaperApply cancelled, unable to retrieve wallpaper dimensions");

            if (mContext.get() == null) return;
            if (mContext.get() instanceof Activity) {
                if (((Activity) mContext.get()).isFinishing())
                    return;
            }

            if (mDialog != null && mDialog.isShowing()) {
                mDialog.dismiss();
            }

            Toast.makeText(mContext.get(), R.string.wallpaper_apply_failed,
                    Toast.LENGTH_LONG).show();
            return;
        }

        try {
            executeOnExecutor(mExecutor);
        } catch (IllegalStateException e) {
            LogUtil.e(Log.getStackTraceString(e));
        }
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        while (!isCancelled()) {
            try {
                Thread.sleep(1);
                ImageSize imageSize = WallpaperHelper.getTargetSize(mContext.get());

                LogUtil.d("original rectF: " +mRectF);

                if (mRectF != null && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
                    Point point = WindowHelper.getScreenSize(mContext.get());
                    int height = point.y - WindowHelper.getStatusBarHeight(mContext.get()) - WindowHelper.getNavigationBarHeight(mContext.get());
                    float heightFactor = (float) imageSize.getHeight() / (float) height;
                    mRectF = WallpaperHelper.getScaledRectF(mRectF, heightFactor, 1f);
                }

                if (mRectF == null && Preferences.get(mContext.get()).isCropWallpaper()) {
                    /*
                     * Create a center crop rectF if wallpaper applied from grid, not opening the preview first
                     */
                    float widthScaleFactor = (float) imageSize.getHeight() / (float) mWallpaper.getDimensions().getHeight();

                    float side = ((float) mWallpaper.getDimensions().getWidth() * widthScaleFactor - (float) imageSize.getWidth())/2f;
                    float leftRectF = 0f - side;
                    float rightRectF = (float) mWallpaper.getDimensions().getWidth() * widthScaleFactor - side;
                    float topRectF = 0f;
                    float bottomRectF = (float) imageSize.getHeight();
                    mRectF = new RectF(leftRectF, topRectF, rightRectF, bottomRectF);
                    LogUtil.d("created center crop rectF: " +mRectF);
                }

                ImageSize adjustedSize = imageSize;
                RectF adjustedRectF = mRectF;

                float scaleFactor = (float) mWallpaper.getDimensions().getHeight() / (float) imageSize.getHeight();
                if (scaleFactor > 1f) {
                    /*
                     * Applying original wallpaper size caused a problem (wallpaper zoomed in)
                     * if wallpaper dimension bigger than device screen resolution
                     *
                     * Solution: Resize wallpaper to match screen resolution
                     */

                    /*
                     * Use original wallpaper size:
                     * adjustedSize = new ImageSize(width, height);
                     */

                    /*
                     * Adjust wallpaper size to match screen resolution:
                     */
                    float widthScaleFactor = (float) imageSize.getHeight() / (float) mWallpaper.getDimensions().getHeight();
                    int adjustedWidth = Float.valueOf((float) mWallpaper.getDimensions().getWidth() * widthScaleFactor).intValue();
                    adjustedSize = new ImageSize(adjustedWidth, imageSize.getHeight());

                    if (adjustedRectF != null) {
                        /*
                         * If wallpaper crop enabled, original wallpaper size should be loaded first
                         */
                        adjustedSize = new ImageSize(mWallpaper.getDimensions().getWidth(), mWallpaper.getDimensions().getHeight());
                        adjustedRectF = WallpaperHelper.getScaledRectF(mRectF, scaleFactor, scaleFactor);
                        LogUtil.d("adjusted rectF: " + adjustedRectF);
                    }

                    LogUtil.d(String.format(Locale.getDefault(), "adjusted bitmap: %d x %d",
                            adjustedSize.getWidth(), adjustedSize.getHeight()));
                }

                int call = 1;
                do {
                    /*
                     * Load the bitmap first
                     */
                    Bitmap loadedBitmap = ImageLoader.getInstance().loadImageSync(
                            mWallpaper.getUrl(), adjustedSize, ImageConfig.getWallpaperOptions());
                    if (loadedBitmap != null) {
                        try {
                            /*
                             * Checking if loaded bitmap resolution supported by the device
                             * If texture size too big then resize it
                             */
                            Bitmap bitmapTemp = Bitmap.createBitmap(
                                    loadedBitmap.getWidth(),
                                    loadedBitmap.getHeight(),
                                    loadedBitmap.getConfig());
                            bitmapTemp.recycle();

                            /*
                             * Texture size is ok
                             */
                            LogUtil.d(String.format(Locale.getDefault(), "loaded bitmap: %d x %d",
                                    loadedBitmap.getWidth(), loadedBitmap.getHeight()));
                            publishProgress();

                            Bitmap bitmap = loadedBitmap;
                            if (Preferences.get(mContext.get()).isCropWallpaper() && adjustedRectF != null) {
                                LogUtil.d("rectF: " +adjustedRectF);
                                /*
                                 * Cropping bitmap
                                 */
                                ImageSize targetSize = WallpaperHelper.getTargetSize(mContext.get());

                                int targetWidth = Double.valueOf(
                                        ((double) loadedBitmap.getHeight() / (double) targetSize.getHeight())
                                                * (double) targetSize.getWidth()).intValue();

                                bitmap = Bitmap.createBitmap(
                                        targetWidth,
                                        loadedBitmap.getHeight(),
                                        loadedBitmap.getConfig());
                                Paint paint = new Paint();
                                paint.setFilterBitmap(true);
                                paint.setAntiAlias(true);
                                paint.setDither(true);

                                Canvas canvas = new Canvas(bitmap);
                                canvas.drawBitmap(loadedBitmap, null, adjustedRectF, paint);

                                float scale = (float) targetSize.getHeight() / (float) bitmap.getHeight();
                                if (scale < 1f) {
                                    LogUtil.d("bitmap size is bigger than screen resolution, resizing bitmap");
                                    int resizedWidth = Float.valueOf((float) bitmap.getWidth() * scale).intValue();
                                    bitmap = Bitmap.createScaledBitmap(bitmap, resizedWidth, targetSize.getHeight(), true);
                                }
                            }

                            /*
                             * Final bitmap generated
                             */
                            LogUtil.d(String.format(Locale.getDefault(), "generated bitmap: %d x %d ",
                                    bitmap.getWidth(), bitmap.getHeight()));

                            if (mApply == Apply.HOMESCREEN_LOCKSCREEN) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                    WallpaperManager.getInstance(mContext.get().getApplicationContext()).setBitmap(
                                            bitmap, null, true, WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
                                    return true;
                                }
                            }

                            if (mApply == Apply.HOMESCREEN) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                    WallpaperManager.getInstance(mContext.get().getApplicationContext()).setBitmap(
                                            bitmap, null, true, WallpaperManager.FLAG_SYSTEM);
                                    return true;
                                }

                                WallpaperManager.getInstance(mContext.get().getApplicationContext()).setBitmap(bitmap);
                                return true;
                            }

                            if (mApply == Apply.LOCKSCREEN) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                    WallpaperManager.getInstance(mContext.get().getApplicationContext()).setBitmap(
                                            bitmap, null, true, WallpaperManager.FLAG_LOCK);
                                    return true;
                                }
                            }
                        } catch (OutOfMemoryError e) {
                            LogUtil.e("loaded bitmap is too big, resizing it ...");
                            /*
                             * Texture size is too big
                             * Resizing bitmap
                             */

                            double scale = 1 - (0.1 * call);
                            int scaledWidth = Double.valueOf(adjustedSize.getWidth() * scale).intValue();
                            int scaledHeight = Double.valueOf(adjustedSize.getHeight() * scale).intValue();

                            adjustedRectF = WallpaperHelper.getScaledRectF(adjustedRectF,
                                    (float) scale, (float) scale);
                            adjustedSize = new ImageSize(scaledWidth, scaledHeight);
                        }
                    }

                    /*
                     * Continue to next iteration
                     */
                    call++;
                } while (call <= 5 && !isCancelled());
                return false;
            } catch (Exception e) {
                LogUtil.e(Log.getStackTraceString(e));
                return false;
            }
        }
        return false;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        if (mContext.get() == null) return;
        if (mContext.get() instanceof Activity) {
            if (((Activity) mContext.get()).isFinishing())
                return;
        }

        mDialog.setContent(R.string.wallpaper_applying);
    }

    @Override
    protected void onCancelled(Boolean aBoolean) {
        super.onCancelled(aBoolean);
        if (mContext.get() == null) return;
        if (mContext.get() instanceof Activity) {
            if (((Activity) mContext.get()).isFinishing())
                return;
        }

        Toast.makeText(mContext.get(), R.string.wallpaper_apply_cancelled,
                Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        super.onPostExecute(aBoolean);
        if (mContext.get() == null) return;
        if (mContext.get() instanceof Activity) {
            if (((Activity) mContext.get()).isFinishing())
                return;
        }

        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }

        if (aBoolean) {
            CafeBar.builder(mContext.get())
                    .theme(Preferences.get(mContext.get()).isDarkTheme() ? CafeBarTheme.LIGHT : CafeBarTheme.DARK)
                    .contentTypeface(TypefaceHelper.getRegular(mContext.get()))
                    .floating(true)
                    .fitSystemWindow()
                    .content(R.string.wallpaper_applied)
                    .show();
        } else {
            Toast.makeText(mContext.get(), R.string.wallpaper_apply_failed,
                    Toast.LENGTH_LONG).show();
        }
    }

    public enum Apply {
        LOCKSCREEN,
        HOMESCREEN,
        HOMESCREEN_LOCKSCREEN
    }
}
 public class Popup {

    private ListPopupWindow mPopupWindow;
    private PopupAdapter mAdapter;

    private Popup(Builder builder) {
        mPopupWindow = new ListPopupWindow(builder.mContext);
        mAdapter = new PopupAdapter(builder.mContext, builder.mItems);


        int width = getMeasuredWidth(builder.mContext);
        mPopupWindow.setContentWidth(width);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Drawable drawable = mPopupWindow.getBackground();
            if (drawable != null) {
                drawable.setColorFilter(ColorHelper.getAttributeColor(
                        builder.mContext, R.attr.card_background), PorterDuff.Mode.SRC_IN);
            }
        } else {
            mPopupWindow.setBackgroundDrawable(new ColorDrawable(
                    ColorHelper.getAttributeColor(builder.mContext, R.attr.card_background)));
        }

        mPopupWindow.setAnchorView(builder.mTo);
        mPopupWindow.setAdapter(mAdapter);
        mPopupWindow.setOnItemClickListener((adapterView, view, i, l) -> {
            if (builder.mCallback != null) {
                builder.mCallback.onClick(this, i);
                return;
            }

            mPopupWindow.dismiss();
        });
    }

    public void show() {
        if (mAdapter.getCount() == 0) {
            LogUtil.e("Popup size = 0, show() ignored");
            return;
        }
        mPopupWindow.show();
    }

    public void dismiss() {
        if (mPopupWindow.isShowing())
            mPopupWindow.dismiss();
    }

    public List<PopupItem> getItems() {
        return mAdapter.getItems();
    }

    public void updateItem(int position, PopupItem item) {
        mAdapter.updateItem(position, item);
    }

    public void removeItem(int position) {
        mAdapter.removeItem(position);
    }

    public static Builder Builder(@NonNull Context context) {
        return new Builder(context);
    }

    private int getMeasuredWidth(@NonNull Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);

        int maxWidth = context.getResources().getDimensionPixelSize(R.dimen.popup_max_width);
        int minWidth = context.getResources().getDimensionPixelSize(R.dimen.popup_min_width);
        String longestText = "";
        for (PopupItem item : mAdapter.getItems()) {
            if (item.getTitle().length() > longestText.length())
                longestText = item.getTitle();
        }

        int padding = context.getResources().getDimensionPixelSize(R.dimen.content_margin);
        int iconSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size_small);
        TextView textView = new TextView(context);
        textView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        textView.setTypeface(TypefaceHelper.getRegular(context));
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                .getDimension(R.dimen.text_content_subtitle));
        textView.setPadding(padding + iconSize + padding, 0, padding, 0);
        textView.setText(longestText);

        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(metrics.widthPixels, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        textView.measure(widthMeasureSpec, heightMeasureSpec);

        int measuredWidth = textView.getMeasuredWidth() + padding;
        if (measuredWidth <= minWidth) {
            return minWidth;
        }

        if (measuredWidth >= minWidth && measuredWidth <= maxWidth) {
            return measuredWidth;
        }
        return maxWidth;
    }

    public static class Builder {

        private final Context mContext;
        private Callback mCallback;
        private View mTo;
        private List<PopupItem> mItems;

        private Builder(Context context) {
            mContext = context;
            mItems = new ArrayList<>();
        }

        public Builder to(@Nullable View to) {
            mTo = to;
            return this;
        }

        public Builder list(@NonNull List<PopupItem> items) {
            mItems = items;
            return this;
        }

        public Builder callback(@Nullable Callback callback) {
            mCallback = callback;
            return this;
        }

        public Popup build() {
            return new Popup(this);
        }

        public void show() {
            build().show();
        }
    }

    class PopupAdapter extends BaseAdapter {

        private List<PopupItem> mItems;
        private final Context mContext;

        PopupAdapter(@NonNull Context context, @NonNull List<PopupItem> items) {
            mContext = context;
            mItems = items;
        }

        @Override
        public int getCount() {
            return mItems.size();
        }

        @Override
        public PopupItem getItem(int position) {
            return mItems.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View view, ViewGroup viewGroup) {
            ViewHolder holder;
            if (view == null) {
                view = View.inflate(mContext, R.layout.popup_item_list, null);
                holder = new ViewHolder(view);
                view.setTag(holder);
            } else {
                holder = (ViewHolder) view.getTag();
            }

            PopupItem item = mItems.get(position);
            holder.checkBox.setVisibility(View.GONE);
            if (item.isShowCheckbox()) {
                holder.checkBox.setChecked(item.getCheckboxValue());
                holder.checkBox.setVisibility(View.VISIBLE);
            }

            int color = ColorHelper.getAttributeColor(mContext, android.R.attr.textColorPrimary);
            if (item.isSelected()) {
                color = ColorHelper.getAttributeColor(mContext, R.attr.colorAccent);
            }

            if (item.getIcon() != 0) {
                Drawable drawable = DrawableHelper.getTintedDrawable(mContext, item.getIcon(), color);
                holder.title.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
            }

            holder.title.setText(item.getTitle());
            holder.title.setTextColor(color);
            return view;
        }

        class ViewHolder {

            @BindView(R2.id.checkbox)
            AppCompatCheckBox checkBox;
            @BindView(R2.id.title)
            TextView title;

            ViewHolder(@NonNull View view) {
                ButterKnife.bind(this, view);
            }
        }

        List<PopupItem> getItems() {
            return mItems;
        }

        void updateItem(int position, PopupItem item) {
            mItems.set(position, item);
            notifyDataSetChanged();
        }

        void removeItem(int position) {
            mItems.remove(position);
            notifyDataSetChanged();
        }
    }

    public interface Callback {
        void onClick(Popup popup, int position);
    }
}
 public abstract class Extras {

    public static final String TAG_COLLECTION = "collection";
    public static final String TAG_WALLPAPERS = "wallpapers";
    public static final String TAG_LATEST = "latest";
    public static final String TAG_CATEGORIES = "categories";
    public static final String TAG_WALLPAPER_SEARCH = "wallpaperSearch";
    public static final String TAG_FAVORITES = "favorites";
    public static final String TAG_SETTINGS = "settings";
    public static final String TAG_ABOUT = "about";

    public static final String EXTRA_URL = "url";
    public static final String EXTRA_POSITION = "position";
    public static final String EXTRA_IMAGE = "image";
    public static final String EXTRA_RESUMED = "resumed";
    public static final String EXTRA_FRAGMENT_ID = "fragmentId";
    public static final String EXTRA_CATEGORY = "category";
    public static final String EXTRA_COUNT = "count";
    public static final String EXTRA_ITEM = "item";

    public static final String EXTRA_KEY = "key";
    public static final String EXTRA_PRODUCT_ID = "productId";
    public static final String EXTRA_TYPE = "type";

    public static final int TYPE_CONTRIBUTORS = 1;
    public static final int TYPE_DASHBOARD_CONTRIBUTORS = 2;
    public static final int TYPE_DASHBOARD_TRANSLATOR = 3;

    public static final int ID_CATEGORY_WALLPAPERS = 0;
    public static final int ID_WALLPAPER_SEARCH = 1;
}
 public class WallpaperDownloader {

    private final Context mContext;
    private Wallpaper mWallpaper;

    private WallpaperDownloader(Context context) {
        mContext = context;
    }

    public WallpaperDownloader wallpaper(@NonNull Wallpaper wallpaper) {
        mWallpaper = wallpaper;
        return this;
    }

    public void start() {
        String fileName = mWallpaper.getName() +"."+ WallpaperHelper.getFormat(mWallpaper.getMimeType());
        File directory = WallpaperHelper.getDefaultWallpapersDirectory(mContext);
        File target = new File(directory, fileName);
        if (!directory.exists()) {
            if (!directory.mkdirs()) {
                LogUtil.e("Unable to create directory " +directory.toString());
                showCafeBar(R.string.wallpaper_download_failed);
                return;
            }
        }

        if (WallpaperHelper.isWallpaperSaved(mContext, mWallpaper)) {
            CafeBar.builder(mContext)
                    .theme(Preferences.get(mContext).isDarkTheme() ? CafeBarTheme.LIGHT : CafeBarTheme.DARK)
                    .floating(true)
                    .fitSystemWindow()
                    .duration(CafeBar.Duration.MEDIUM)
                    .typeface(TypefaceHelper.getRegular(mContext), TypefaceHelper.getBold(mContext))
                    .content(R.string.wallpaper_already_downloaded)
                    .neutralText(R.string.open)
                    .onNeutral(cafeBar -> {
                        Uri uri = FileHelper.getUriFromFile(mContext, mContext.getPackageName(), target);
                        if (uri == null) {
                            cafeBar.dismiss();
                            return;
                        }

                        try {
                            mContext.startActivity(new Intent()
                                    .setAction(Intent.ACTION_VIEW)
                                    .setDataAndType(uri, "image/*")
                                    .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
                        } catch (ActivityNotFoundException e) {
                            LogUtil.e(Log.getStackTraceString(e));
                        }

                        cafeBar.dismiss();
                    })
                    .show();
            return;
        }

        if (!URLUtil.isValidUrl(mWallpaper.getUrl())) {
            LogUtil.e("Download: wallpaper url is not valid");
            return;
        }

        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mWallpaper.getUrl()));
        request.setMimeType(mWallpaper.getMimeType());
        request.setTitle(fileName);
        request.setDescription(mContext.getResources().getString(R.string.wallpaper_downloading));
        request.allowScanningByMediaScanner();
        request.setVisibleInDownloadsUi(false);
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setDestinationUri(Uri.fromFile(target));

        DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);

        try {
            if (downloadManager != null) {
                downloadManager.enqueue(request);
                return;
            }

            LogUtil.e("Download: download manager is null");
        } catch (IllegalArgumentException e) {
            LogUtil.e(Log.getStackTraceString(e));
            return;
        }

        showCafeBar(R.string.wallpaper_downloading);
    }

    private void showCafeBar(int res) {
        CafeBar.builder(mContext)
                .theme(Preferences.get(mContext).isDarkTheme() ? CafeBarTheme.LIGHT : CafeBarTheme.DARK)
                .contentTypeface(TypefaceHelper.getRegular(mContext))
                .content(res)
                .floating(true)
                .fitSystemWindow()
                .show();
    }

    public static WallpaperDownloader prepare(@NonNull Context context) {
        return new WallpaperDownloader(context);
    }
}
 public static void setCardViewToFlat(@Nullable CardView cardView) {
    if (cardView == null) return;

    if (WallpaperBoardApplication.getConfig().getWallpapersGrid() ==
            WallpaperBoardConfiguration.GridStyle.FLAT) {
        cardView.setRadius(0f);
        cardView.setUseCompatPadding(false);

        Context context = ContextHelper.getBaseContext(cardView);
        int margin = context.getResources().getDimensionPixelSize(R.dimen.card_margin);

        if (cardView.getLayoutParams() instanceof GridLayoutManager.LayoutParams) {
            GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) cardView.getLayoutParams();
            params.setMargins(0, 0, margin, margin);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                params.setMarginEnd(margin);
            }
        } else if (cardView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
            StaggeredGridLayoutManager.LayoutParams params =
                    (StaggeredGridLayoutManager.LayoutParams) cardView.getLayoutParams();
            params.setMargins(0, 0, margin, margin);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                params.setMarginEnd(margin);
            }
        }
    }
}

import android.animation.AnimatorInflater;
import android.animation.StateListAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.danimahardhika.android.helpers.animation.AnimationHelper;
import com.danimahardhika.android.helpers.core.ColorHelper;
import com.danimahardhika.android.helpers.core.DrawableHelper;
import com.danimahardhika.android.helpers.core.WindowHelper;
import com.danimahardhika.android.helpers.permission.PermissionHelper;
import com.danimahardhika.cafebar.CafeBar;
import com.danimahardhika.cafebar.CafeBarTheme;
import com.dm.wallpaper.board.R;
import com.dm.wallpaper.board.R2;
import com.dm.wallpaper.board.activities.WallpaperBoardPreviewActivity;
import com.dm.wallpaper.board.applications.WallpaperBoardApplication;
import com.dm.wallpaper.board.databases.Database;
import com.dm.wallpaper.board.helpers.TypefaceHelper;
import com.dm.wallpaper.board.items.PopupItem;
import com.dm.wallpaper.board.items.Wallpaper;
import com.dm.wallpaper.board.preferences.Preferences;
import com.dm.wallpaper.board.tasks.WallpaperApplyTask;
import com.dm.wallpaper.board.utils.Popup;
import com.dm.wallpaper.board.utils.Extras;
import com.dm.wallpaper.board.utils.ImageConfig;
import com.dm.wallpaper.board.utils.WallpaperDownloader;
import com.kogitune.activitytransition.ActivityTransitionLauncher;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import static com.dm.wallpaper.board.helpers.ViewHelper.setCardViewToFlat;

package com.dm.wallpaper.board.adapters;






/*
 * Wallpaper Board
 *
 * Copyright (c) 2017 Dani Mahardhika
 *
 * 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.
 */

public class LatestAdapter extends RecyclerView.Adapter<LatestAdapter.ViewHolder> {

    private final Context mContext;
    private List<Wallpaper> mWallpapers;
    private final DisplayImageOptions.Builder mOptions;

    public LatestAdapter(@NonNull Context context, @NonNull List<Wallpaper> wallpapers) {
        mContext = context;
        mWallpapers = wallpapers;
        WallpaperBoardApplication.sIsClickable = true;

        mOptions = ImageConfig.getRawDefaultImageOptions();
        mOptions.resetViewBeforeLoading(true);
        mOptions.cacheInMemory(true);
        mOptions.cacheOnDisk(true);
        mOptions.displayer(new FadeInBitmapDisplayer(700));
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(
                R.layout.fragment_latest_item_grid, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Wallpaper wallpaper = mWallpapers.get(position);
        holder.name.setText(wallpaper.getName());
        holder.author.setText(wallpaper.getAuthor());

        if (mContext.getResources().getBoolean(R.bool.enable_wallpaper_download)) {
            holder.download.setVisibility(View.VISIBLE);
        } else {
            holder.download.setVisibility(View.GONE);
        }

        setFavorite(holder.favorite, Color.WHITE, position, false);
        resetImageViewHeight(holder.image, wallpaper.getDimensions());

        ImageLoader.getInstance().displayImage(
                wallpaper.getThumbUrl(),
                new ImageViewAware(holder.image),
                mOptions.build(),
                ImageConfig.getBigThumbnailSize(),
                new SimpleImageLoadingListener() {

                    @Override
                    public void onLoadingStarted(String imageUri, View view) {
                        super.onLoadingStarted(imageUri, view);
                        int color;
                        if (wallpaper.getColor() == 0) {
                            color = ColorHelper.getAttributeColor(
                                    mContext, R.attr.card_background);
                        } else {
                            color = wallpaper.getColor();
                        }

                        holder.card.setCardBackgroundColor(color);
                    }

                    @Override
                    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                        super.onLoadingComplete(imageUri, view, loadedImage);
                        if (loadedImage != null && wallpaper.getColor() == 0) {
                            Palette.from(loadedImage).generate(palette -> {
                                if (mContext == null) return;
                                if (((Activity) mContext).isFinishing()) return;

                                int vibrant = ColorHelper.getAttributeColor(
                                        mContext, R.attr.card_background);
                                int color = palette.getVibrantColor(vibrant);
                                if (color == vibrant)
                                    color = palette.getMutedColor(vibrant);
                                holder.card.setCardBackgroundColor(color);

                                wallpaper.setColor(color);
                                Database.get(mContext).updateWallpaper(wallpaper);
                            });
                        }
                    }
                },
                null);
    }

    @Override
    public int getItemCount() {
        return mWallpapers.size();
    }

    private void resetImageViewHeight(@NonNull ImageView imageView, ImageSize imageSize) {
        if (imageSize == null) imageSize = new ImageSize(400, 300);

        int width = WindowHelper.getScreenSize(mContext).x;
        int spanCount = mContext.getResources().getInteger(R.integer.latest_wallpapers_column_count);
        if (spanCount > 1) {
            width = width/spanCount;
        }
        double scaleFactor = (double) width / (double) imageSize.getWidth();
        double measuredHeight = (double) imageSize.getHeight() * scaleFactor;
        imageView.getLayoutParams().height = Double.valueOf(measuredHeight).intValue();
        imageView.requestLayout();
    }

    class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        @BindView(R2.id.image)
        ImageView image;
        @BindView(R2.id.name)
        TextView name;
        @BindView(R2.id.author)
        TextView author;
        @BindView(R2.id.favorite)
        ImageView favorite;
        @BindView(R2.id.download)
        ImageView download;
        @BindView(R2.id.apply)
        ImageView apply;
        @BindView(R2.id.card)
        CardView card;

        ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);

            if (mContext.getResources().getInteger(R.integer.latest_wallpapers_column_count) == 1) {
                if (card.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
                    StaggeredGridLayoutManager.LayoutParams params =
                            (StaggeredGridLayoutManager.LayoutParams) card.getLayoutParams();
                    params.leftMargin = 0;
                    params.rightMargin = 0;
                    params.topMargin = 0;
                    params.bottomMargin = 0;

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        params.setMarginEnd(0);
                    }
                }
            } else {

                setCardViewToFlat(card);
