core/src/main/java/com/dm/material/dashboard/candybar/activities/CandyBarWallpaperActivity.java
public class CandyBarWallpaperActivity extends AppCompatActivity implements View.OnClickListener, View.OnLongClickListener,
        ActivityCompat.OnRequestPermissionsResultCallback, WallpaperPropertiesLoaderTask.Callback {

    private ImageView mImageView;
    private ProgressBar mProgress;
    private LinearLayout mBottomBar;
    private TextView mName;
    private TextView mAuthor;
    private ImageView mBack;
    private ImageView mMenuApply;
    private ImageView mMenuSave;

    private boolean mIsEnter;
    private boolean mIsResumed = false;

    private Wallpaper mWallpaper;
    private Runnable mRunnable;
    private Handler mHandler;
    private PhotoViewAttacher mAttacher;
    private ExitActivityTransition mExitTransition;

    @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);
        mIsEnter = true;

        mImageView = findViewById(R.id.wallpaper);
        mProgress = findViewById(R.id.progress);
        mBottomBar = findViewById(R.id.bottom_bar);
        mName = findViewById(R.id.name);
        mAuthor = findViewById(R.id.author);
        mBack = findViewById(R.id.back);
        mMenuApply = findViewById(R.id.menu_apply);
        mMenuSave = findViewById(R.id.menu_save);

        mProgress.getIndeterminateDrawable().setColorFilter(
                Color.parseColor("#CCFFFFFF"), PorterDuff.Mode.SRC_IN);
        mBack.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_back, Color.WHITE));
        mBack.setOnClickListener(this);

        String url = "";
        if (savedInstanceState != null) {
            url = savedInstanceState.getString(Extras.EXTRA_URL);
        }

        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            url = bundle.getString(Extras.EXTRA_URL);
        }

        mWallpaper = Database.get(this.getApplicationContext()).getWallpaper(url);
        if (mWallpaper == null) {
            finish();
            return;
        }

        initBottomBar();
        resetBottomBarPadding();

        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(findViewById(R.id.rootview), 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) {
            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;

                            AnimationHelper.fade(mBottomBar).duration(400).start();
                            loadWallpaper();
                        }
                    }

                    @Override
                    public void onTransitionCancel(Transition transition) {

                    }

                    @Override
                    public void onTransitionPause(Transition transition) {

                    }

                    @Override
                    public void onTransitionResume(Transition transition) {

                    }
                });
                return;
            }
        }

        mRunnable = () -> {
            AnimationHelper.fade(mBottomBar).duration(400).start();
            loadWallpaper();

            mRunnable = null;
            mHandler = null;
        };
        mHandler = new Handler();
        mHandler.postDelayed(mRunnable, 700);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleHelper.setLocale(this);
        resetBottomBarPadding();
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        LocaleHelper.setLocale(newBase);
        super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
    }

    @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);
        if (mAttacher != null) mAttacher.cleanup();
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        WallpapersAdapter.sIsClickable = true;
        if (mHandler != null && mRunnable != null)
            mHandler.removeCallbacks(mRunnable);

        if (mExitTransition != null) {
            mExitTransition.exit(this);
            return;
        }
        super.onBackPressed();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.back) {
            onBackPressed();
        } else if (id == R.id.menu_apply) {
            Popup popup = Popup.Builder(this)
                    .to(mMenuApply)
                    .list(PopupItem.getApplyItems(this))
                    .callback((p, position) -> {
                        PopupItem item = p.getItems().get(position);
                        if (item.getType() == PopupItem.Type.WALLPAPER_CROP) {
                            Preferences.get(this).setCropWallpaper(!item.getCheckboxValue());
                            item.setCheckboxValue(Preferences.get(this).isCropWallpaper());

                            p.updateItem(position, item);
                            if (Preferences.get(this).isCropWallpaper()) {
                                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                                return;
                            }

                            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                            return;
                        } else {
                            RectF rectF = null;
                            if (Preferences.get(this).isCropWallpaper()) {
                                if (mAttacher != null)
                                    rectF = mAttacher.getDisplayRect();
                            }

                            WallpaperApplyTask task = WallpaperApplyTask.prepare(this)
                                    .wallpaper(mWallpaper)
                                    .crop(rectF);

                            if (item.getType() == PopupItem.Type.LOCKSCREEN) {
                                task.to(WallpaperApplyTask.Apply.LOCKSCREEN);
                            } else if (item.getType() == PopupItem.Type.HOMESCREEN) {
                                task.to(WallpaperApplyTask.Apply.HOMESCREEN);
                            } else if (item.getType() == PopupItem.Type.HOMESCREEN_LOCKSCREEN) {
                                task.to(WallpaperApplyTask.Apply.HOMESCREEN_LOCKSCREEN);
                            }

                            task.start(AsyncTask.THREAD_POOL_EXECUTOR);
                        }

                        p.dismiss();
                    })
                    .build();

            if (getResources().getBoolean(R.bool.enable_wallpaper_download)) {
                popup.removeItem(popup.getItems().size() - 1);
            }
            popup.show();
        } else if (id == R.id.menu_save) {
            if (PermissionHelper.isStorageGranted(this)) {
                WallpaperDownloader.prepare(this)
                        .wallpaper(mWallpaper)
                        .start();
                return;
            }

            PermissionHelper.requestStorage(this);
        }
    }

    @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;
        }

        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();
            } else {
                Toast.makeText(this, R.string.permission_storage_denied, Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onPropertiesReceived(Wallpaper wallpaper) {
        if (wallpaper == null) return;

        mWallpaper.setDimensions(wallpaper.getDimensions());
        mWallpaper.setSize(wallpaper.getSize());
        mWallpaper.setMimeType(wallpaper.getMimeType());
    }

    private void initBottomBar() {
        mName.setText(mWallpaper.getName());
        mName.setTextColor(Color.WHITE);
        mAuthor.setText(mWallpaper.getAuthor());
        mAuthor.setTextColor(ColorHelper.setColorAlpha(Color.WHITE, 0.7f));
        mMenuSave.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_download, Color.WHITE));
        mMenuApply.setImageDrawable(DrawableHelper.getTintedDrawable(
                this, R.drawable.ic_toolbar_apply_options, Color.WHITE));

        if (getResources().getBoolean(R.bool.enable_wallpaper_download)) {
            mMenuSave.setVisibility(View.VISIBLE);
        }

        mMenuApply.setOnClickListener(this);
        mMenuSave.setOnClickListener(this);

        mMenuApply.setOnLongClickListener(this);
        mMenuSave.setOnLongClickListener(this);
    }

    private void resetBottomBarPadding() {
        LinearLayout container = findViewById(R.id.bottom_bar_container);
        int height = getResources().getDimensionPixelSize(R.dimen.bottom_bar_height);
        int bottom = 0;
        int right = WindowHelper.getNavigationBarHeight(this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (mBack.getLayoutParams() instanceof CoordinatorLayout.LayoutParams) {
                CoordinatorLayout.LayoutParams params = (CoordinatorLayout.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) {
                bottom = right;
                right = 0;
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                if (isInMultiWindowMode()) {
                    bottom = right = 0;
                }
            }
        }

        container.setPadding(0, 0, right, bottom);

        if (container.getLayoutParams() instanceof LinearLayout.LayoutParams) {
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) container.getLayoutParams();
            params.height = height + bottom;
        }
    }

    private void loadWallpaper() {
        if (mAttacher != null) {
            mAttacher.cleanup();
            mAttacher = null;
        }

        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(mWallpaper.getURL(), mImageView, options.build(), new SimpleImageLoadingListener() {

            @Override
            public void onLoadingStarted(String imageUri, View view) {
                super.onLoadingStarted(imageUri, view);
                if (Preferences.get(CandyBarWallpaperActivity.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(
                            CandyBarWallpaperActivity.this, R.attr.colorAccent));
                }

                onWallpaperLoaded();
            }

            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                super.onLoadingComplete(imageUri, view, loadedImage);

                if (loadedImage != null && mWallpaper.getColor() == 0) {
                    Palette.from(loadedImage).generate(palette -> {
                        int accent = ColorHelper.getAttributeColor(
                                CandyBarWallpaperActivity.this, R.attr.colorAccent);
                        int color = palette.getVibrantColor(accent);
                        if (color == accent)
                            color = palette.getMutedColor(accent);

                        mWallpaper.setColor(color);
                        Database.get(CandyBarWallpaperActivity.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());
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/utils/ImageConfig.java
public class ImageConfig {

    public static ImageLoaderConfiguration getImageLoaderConfiguration(@NonNull Context context) {
        return new ImageLoaderConfiguration.Builder(context)
                .threadPriority(Thread.NORM_PRIORITY - 2)
                .threadPoolSize(4)
                .tasksProcessingOrder(QueueProcessingType.FIFO)
                .diskCache(new UnlimitedDiskCache(new File(
                        context.getCacheDir().toString() + "/uil-images")))
                .diskCacheSize(256 * FileHelper.MB)
                .memoryCacheSize(6 * FileHelper.MB)
                .build();
    }

    public static DisplayImageOptions getDefaultImageOptions(boolean cacheOnDisk) {
        DisplayImageOptions.Builder options = new DisplayImageOptions.Builder();
        options.delayBeforeLoading(10)
                .resetViewBeforeLoading(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .imageScaleType(ImageScaleType.EXACTLY)
                .displayer(new FadeInBitmapDisplayer(700))
                .cacheOnDisk(cacheOnDisk)
                .cacheInMemory(false);
        return options.build();
    }

    public static DisplayImageOptions getWallpaperOptions() {
        DisplayImageOptions.Builder options = new DisplayImageOptions.Builder();
        options.delayBeforeLoading(10)
                .bitmapConfig(Bitmap.Config.ARGB_8888)
                .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
                .cacheOnDisk(true)
                .cacheInMemory(false);
        return options.build();
    }

    public static DisplayImageOptions.Builder getRawDefaultImageOptions() {
        DisplayImageOptions.Builder options = new DisplayImageOptions.Builder();
        options.delayBeforeLoading(10)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .imageScaleType(ImageScaleType.EXACTLY);
        return options;
    }

    public static DisplayImageOptions.Builder getRawImageOptions() {
        DisplayImageOptions.Builder options = new DisplayImageOptions.Builder();
        options.delayBeforeLoading(10)
                .bitmapConfig(Bitmap.Config.ARGB_8888)
                .imageScaleType(ImageScaleType.EXACTLY);
        return options;
    }

    public static ImageSize getThumbnailSize() {
        int quality = CandyBarApplication.getConfiguration().getWallpaperGridPreviewQuality();
        return new ImageSize((50 * quality), (50 * quality));
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/utils/views/HeaderView.java
public class HeaderView extends AppCompatImageView {

    private int mWidthRatio;
    private int mHeightRatio;

    public HeaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HeaderView);

        try {
            mWidthRatio = typedArray.getInteger(R.styleable.HeaderView_widthRatio, 16);
            mHeightRatio = typedArray.getInteger(R.styleable.HeaderView_heightRatio, 9);
        } finally {
            typedArray.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        double height = (widthMeasureSpec/mWidthRatio) * mHeightRatio;
        setMeasuredDimension(widthMeasureSpec, Double.valueOf(height).intValue());
    }

    public void setRatio(int widthRatio, int heightRatio) {
        mWidthRatio = widthRatio;
        mHeightRatio = heightRatio;
        double height = (getMeasuredWidth()/mWidthRatio) * mHeightRatio;
        setMeasuredDimension(getMeasuredWidth(), Double.valueOf(height).intValue());
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/items/PopupItem.java
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
    }

    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;
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/utils/Popup.java
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.setWidth(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 {

            AppCompatCheckBox checkBox;
            TextView title;

            ViewHolder(@NonNull View view) {
                checkBox = view.findViewById(R.id.checkbox);
                title = view.findViewById(R.id.title);
            }
        }

        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);
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/helpers/ViewHelper.java
public class ViewHelper {

    public static void resetViewBottomMargin(@Nullable View view) {
        if (view == null) return;

        Context context = ContextHelper.getBaseContext(view);
        int orientation = context.getResources().getConfiguration().orientation;

        if (!(view.getLayoutParams() instanceof CoordinatorLayout.LayoutParams))
            return;

        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) view.getLayoutParams();
        int left = params.leftMargin;
        int right = params.rightMargin;
        int bottom = params.bottomMargin;
        int top = params.topMargin;
        int bottomNavBar = 0;
        int rightNavBar = 0;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            boolean tabletMode = context.getResources().getBoolean(R.bool.android_helpers_tablet_mode);
            if (tabletMode || orientation == Configuration.ORIENTATION_PORTRAIT) {
                bottomNavBar = WindowHelper.getNavigationBarHeight(context);
            } else {
                rightNavBar = WindowHelper.getNavigationBarHeight(context);
            }
        }

        int navBar = WindowHelper.getNavigationBarHeight(context);
        if ((bottom > bottomNavBar) && ((bottom - navBar) > 0))
            bottom -= navBar;
        if ((right > rightNavBar) && ((right - navBar) > 0))
            right -= navBar;

        params.setMargins(left, top, (right + rightNavBar), (bottom + bottomNavBar));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            params.setMarginEnd((right + rightNavBar));
        }
        view.setLayoutParams(params);
    }

    public static void setFastScrollColor(@Nullable RecyclerFastScroller fastScroll) {
        if (fastScroll == null) return;

        Context context = fastScroll.getContext();
        if (context instanceof ContextThemeWrapper) {
            context = ((ContextThemeWrapper) context).getBaseContext();
        }

        int accent = ColorHelper.getAttributeColor(context, R.attr.colorAccent);

        fastScroll.setBarColor(ColorHelper.setColorAlpha(accent, 0.8f));
        fastScroll.setHandleNormalColor(accent);
        fastScroll.setHandlePressedColor(ColorHelper.getDarkerColor(accent, 0.7f));
    }

    public static Point getWallpaperViewRatio(String viewStyle) {
        switch (viewStyle.toLowerCase(Locale.getDefault())) {
            case "square":
                return new Point(1, 1);
            case "landscape":
                return new Point(16, 9);
            case "portrait":
                return new Point(4, 5);
            default:
                return new Point(1, 1);
        }
    }

    public static Home.Style getHomeImageViewStyle(String viewStyle) {
        switch (viewStyle.toLowerCase(Locale.getDefault())) {
            case "card_square":
                return new Home.Style(new Point(1, 1), Home.Style.Type.CARD_SQUARE);
            case "card_landscape":
                return new Home.Style(new Point(16, 9), Home.Style.Type.CARD_LANDSCAPE);
            case "square":
                return new Home.Style(new Point(1, 1), Home.Style.Type.SQUARE);
            case "landscape":
                return new Home.Style(new Point(16, 9), Home.Style.Type.LANDSCAPE);
            default:
                return new Home.Style(new Point(16, 9), Home.Style.Type.CARD_LANDSCAPE);
        }
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/preferences/Preferences.java
public class Preferences {

    private final Context mContext;

    private static final String PREFERENCES_NAME = "candybar_preferences";

    private static final String KEY_FIRST_RUN = "first_run";
    private static final String KEY_DARK_THEME = "dark_theme";
    private static final String KEY_APP_VERSION = "app_version";
    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_directory";
    private static final String KEY_PREMIUM_REQUEST = "premium_request";
    private static final String KEY_PREMIUM_REQUEST_PRODUCT = "premium_request_product";
    private static final String KEY_PREMIUM_REQUEST_COUNT = "premium_request_count";
    private static final String KEY_PREMIUM_REQUEST_TOTAL = "premium_request_total";
    private static final String KEY_REGULAR_REQUEST_USED= "regular_request_used";
    private static final String KEY_INAPP_BILLING_TYPE = "inapp_billing_type";
    private static final String KEY_LICENSED = "licensed";
    private static final String KEY_LATEST_CRASHLOG = "last_crashlog";
    private static final String KEY_PREMIUM_REQUEST_ENABLED = "premium_request_enabled";
    private static final String KEY_AVAILABLE_WALLPAPERS_COUNT = "available_wallpapers_count";
    private static final String KEY_CROP_WALLPAPER = "crop_wallpaper";
    private static final String KEY_HOME_INTRO = "home_intro";
    private static final String KEY_ICONS_INTRO = "icons_intro";
    private static final String KEY_REQUEST_INTRO = "request_intro";
    private static final String KEY_WALLPAPERS_INTRO = "wallpapers_intro";
    private static final String KEY_WALLPAPER_PREVIEW_INTRO = "wallpaper_preview_intro";

    private static final String KEY_LANGUAGE_PREFERENCE = "language_preference";
    private static final String KEY_CURRENT_LOCALE = "current_locale";

    private static WeakReference<Preferences> mPreferences;

    @NonNull
    public static Preferences get(@NonNull Context context) {
        if (mPreferences == null || mPreferences.get() == null) {
            mPreferences = new WeakReference<>(new Preferences(context));
        }
        return mPreferences.get();
    }

    private Preferences(Context context) {
        mContext = context;
    }

    private SharedPreferences getSharedPreferences() {
        return 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 isFirstRun() {
        return getSharedPreferences().getBoolean(KEY_FIRST_RUN, true);
    }

    public void setFirstRun(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_FIRST_RUN, bool).apply();
    }

    public boolean isTimeToShowHomeIntro() {
        return getSharedPreferences().getBoolean(KEY_HOME_INTRO, true);
    }

    public void setTimeToShowHomeIntro(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_HOME_INTRO, bool).apply();
    }

    public boolean isTimeToShowIconsIntro() {
        return getSharedPreferences().getBoolean(KEY_ICONS_INTRO, true);
    }

    public void setTimeToShowIconsIntro(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_ICONS_INTRO, bool).apply();
    }

    public boolean isTimeToShowRequestIntro() {
        return getSharedPreferences().getBoolean(KEY_REQUEST_INTRO, true);
    }

    public void setTimeToShowRequestIntro(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_REQUEST_INTRO, bool).apply();
    }

    public boolean isTimeToShowWallpapersIntro() {
        return getSharedPreferences().getBoolean(KEY_WALLPAPERS_INTRO, true);
    }

    public void setTimeToShowWallpapersIntro(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_WALLPAPERS_INTRO, bool).apply();
    }

    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 boolean isDarkTheme() {
        boolean useDarkTheme = mContext.getResources().getBoolean(R.bool.use_dark_theme);
        boolean isThemingEnabled = CandyBarApplication.getConfiguration().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 isToolbarShadowEnabled() {
        return CandyBarApplication.getConfiguration().getShadowOptions().isToolbarEnabled();
    }

    public boolean isCardShadowEnabled() {
        return CandyBarApplication.getConfiguration().getShadowOptions().isCardEnabled();
    }

    public boolean isFabShadowEnabled() {
        return CandyBarApplication.getConfiguration().getShadowOptions().isFabEnabled();
    }

    public boolean isTapIntroShadowEnabled() {
        return CandyBarApplication.getConfiguration().getShadowOptions().isTapIntroEnabled();
    }

    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();
    }

    public void setWallsDirectory(String directory) {
        getSharedPreferences().edit().putString(KEY_WALLS_DIRECTORY, directory).apply();
    }

    public String getWallsDirectory() {
        return getSharedPreferences().getString(KEY_WALLS_DIRECTORY, "");
    }

    public boolean isPremiumRequestEnabled() {
        return getSharedPreferences().getBoolean(KEY_PREMIUM_REQUEST_ENABLED,
                mContext.getResources().getBoolean(R.bool.enable_premium_request));
    }

    public void setPremiumRequestEnabled(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_PREMIUM_REQUEST_ENABLED, bool).apply();
    }

    public boolean isPremiumRequest() {
        return getSharedPreferences().getBoolean(KEY_PREMIUM_REQUEST, false);
    }

    public void setPremiumRequest(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_PREMIUM_REQUEST, bool).apply();
    }

    public String getPremiumRequestProductId() {
        return getSharedPreferences().getString(KEY_PREMIUM_REQUEST_PRODUCT, "");
    }

    public void setPremiumRequestProductId(String productId) {
        getSharedPreferences().edit().putString(KEY_PREMIUM_REQUEST_PRODUCT, productId).apply();
    }

    public int getPremiumRequestCount() {
        return getSharedPreferences().getInt(KEY_PREMIUM_REQUEST_COUNT, 0);
    }

    public void setPremiumRequestCount(int count) {
        getSharedPreferences().edit().putInt(KEY_PREMIUM_REQUEST_COUNT, count).apply();
    }

    public int getPremiumRequestTotal() {
        int count = getPremiumRequestCount();
        return getSharedPreferences().getInt(KEY_PREMIUM_REQUEST_TOTAL, count);
    }

    public void setPremiumRequestTotal(int count) {
        getSharedPreferences().edit().putInt(KEY_PREMIUM_REQUEST_TOTAL, count).apply();
    }

    public int getRegularRequestUsed() {
        return getSharedPreferences().getInt(KEY_REGULAR_REQUEST_USED, 0);
    }

    public void setRegularRequestUsed(int used) {
        getSharedPreferences().edit().putInt(KEY_REGULAR_REQUEST_USED, used).apply();
    }

    public int getInAppBillingType() {
        return getSharedPreferences().getInt(KEY_INAPP_BILLING_TYPE, -1);
    }

    public void setInAppBillingType(int type) {
        getSharedPreferences().edit().putInt(KEY_INAPP_BILLING_TYPE, type).apply();
    }

    public boolean isLicensed() {
        return getSharedPreferences().getBoolean(KEY_LICENSED, false);
    }

    public void setLicensed(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_LICENSED, bool).apply();
    }

    public boolean isCropWallpaper() {
        return getSharedPreferences().getBoolean(KEY_CROP_WALLPAPER, false);
    }

    public void setCropWallpaper(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_CROP_WALLPAPER, bool).apply();
    }

    public String getLatestCrashLog() {
        return getSharedPreferences().getString(KEY_LATEST_CRASHLOG, "");
    }

    public void setLatestCrashLog(String string) {
        getSharedPreferences().edit().putString(KEY_LATEST_CRASHLOG, string).apply();
    }

    public int getAvailableWallpapersCount() {
        return getSharedPreferences().getInt(KEY_AVAILABLE_WALLPAPERS_COUNT, 0);
    }

    public void setAvailableWallpapersCount(int count) {
        getSharedPreferences().edit().putInt(KEY_AVAILABLE_WALLPAPERS_COUNT, count).apply();
    }

    private int getVersion() {
        return getSharedPreferences().getInt(KEY_APP_VERSION, 0);
    }

    private void setVersion(int version) {
        getSharedPreferences().edit().putInt(KEY_APP_VERSION, version).apply();
    }

    public boolean isNewVersion() {
        int version = 0;
        try {
            version = mContext.getPackageManager().getPackageInfo(
                    mContext.getPackageName(), 0).versionCode;
        } catch (PackageManager.NameNotFoundException ignored) {}
        if (version > getVersion()) {
            boolean resetLimit = mContext.getResources().getBoolean(R.bool.reset_icon_request_limit);
            if (resetLimit) setRegularRequestUsed(0);
            setVersion(version);
            return true;
        } else {
            return false;
        }
    }

    public Locale getCurrentLocale() {
        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 isTimeToSetLanguagePreference() {
        return getSharedPreferences().getBoolean(KEY_LANGUAGE_PREFERENCE, true);
    }

    private void setTimeToSetLanguagePreference(boolean bool) {
        getSharedPreferences().edit().putBoolean(KEY_LANGUAGE_PREFERENCE, bool).apply();
    }

    public void setLanguagePreference() {
        Locale locale = Locale.getDefault();
        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;
                }
            }
        }

        if (currentLocale != null) {
            setCurrentLocale(currentLocale.toString());
            LocaleHelper.setLocale(mContext);
            setTimeToSetLanguagePreference(false);
        }
    }

    public boolean isConnectedToNetwork() {
        try {
            ConnectivityManager connectivityManager = (ConnectivityManager)
                    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)
                        mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
                return activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
                        activeNetworkInfo.isConnected();
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/items/Wallpaper.java
public class Wallpaper {

    private final String mUrl;
    private final String mThumbUrl;
    private final String mAuthor;
    private String mName;
    private int mColor;
    private int mSize;
    private String mMimeType;
    private ImageSize mDimensions;

    private Wallpaper(String name, String author, String url, String thumbUrl) {
        mName = name;
        mAuthor = author;
        mUrl = url;
        mThumbUrl = thumbUrl;
    }

    public String getName() {
        return mName;
    }

    public String getAuthor() {
        return mAuthor;
    }

    public String getThumbUrl() {
        return mThumbUrl;
    }

    public String getURL() {
        return mUrl;
    }

    public int getColor() {
        return mColor;
    }

    public String getMimeType() {
        return mMimeType;
    }

    public ImageSize getDimensions() {
        return mDimensions;
    }

    public int getSize() {
        return mSize;
    }

    public void setName(String name) {
        mName = name;
    }

    public void setColor(int color) {
        mColor = color;
    }

    public void setMimeType(String mimeType) {
        mMimeType = mimeType;
    }

    public void setDimensions(ImageSize dimensions) {
        mDimensions = dimensions;
    }

    public void setSize(int size) {
        mSize = size;
    }

    @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());
        }
        return equals;
    }

    public static Builder Builder() {
        return new Builder();
    }

    public static class Builder {

        private String mName;
        private String mAuthor;
        private String mThumbUrl;
        private String mUrl;
        private int mColor;
        private int mSize;
        private String mMimeType;
        private ImageSize mDimensions;

        private Builder() {
            mColor = 0;
            mSize = 0;
        }

        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 dimensions(ImageSize dimensions) {
            mDimensions = dimensions;
            return this;
        }

        public Builder mimeType(String mimeType) {
            mMimeType = mimeType;
            return this;
        }

        public Builder color(int color) {
            mColor = color;
            return this;
        }

        public Builder size(int size) {
            mSize = size;
            return this;
        }

        public Wallpaper build() {
            Wallpaper wallpaper = new Wallpaper(mName, mAuthor, mUrl, mThumbUrl);
            wallpaper.setDimensions(mDimensions);
            wallpaper.setMimeType(mMimeType);
            wallpaper.setColor(mColor);
            wallpaper.setSize(mSize);
            return wallpaper;
        }
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/databases/Database.java
public class Database extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "candybar_database";
    private static final int DATABASE_VERSION = 9;

    private static final String TABLE_REQUEST = "icon_request";
    private static final String TABLE_PREMIUM_REQUEST = "premium_request";
    private static final String TABLE_WALLPAPERS = "wallpapers";

    private static final String KEY_ID = "id";

    private static final String KEY_ORDER_ID = "order_id";
    private static final String KEY_PRODUCT_ID = "product_id";

    private static final String KEY_NAME = "name";
    private static final String KEY_ACTIVITY = "activity";
    private static final String KEY_REQUESTED_ON = "requested_on";

    private static final String KEY_AUTHOR = "author";
    private static final String KEY_THUMB_URL = "thumbUrl";
    private static final String KEY_URL = "url";
    private static final String KEY_ADDED_ON = "added_on";
    private static final String KEY_MIME_TYPE = "mimeType";
    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_SIZE = "size";

    private final Context mContext;

    private static WeakReference<Database> mDatabase;
    private SQLiteDatabase mSQLiteDatabase;

    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_REQUEST = "CREATE TABLE IF NOT EXISTS " +TABLE_REQUEST+ "(" +
                KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                KEY_NAME + " TEXT NOT NULL, " +
                KEY_ACTIVITY + " TEXT NOT NULL, " +
                KEY_REQUESTED_ON + " DATETIME DEFAULT CURRENT_TIMESTAMP, " +
                "UNIQUE (" +KEY_ACTIVITY+ ") ON CONFLICT REPLACE)";
        String CREATE_TABLE_PREMIUM_REQUEST = "CREATE TABLE IF NOT EXISTS " +TABLE_PREMIUM_REQUEST+ "(" +
                KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                KEY_ORDER_ID + " TEXT NOT NULL, " +
                KEY_PRODUCT_ID + " TEXT NOT NULL, " +
                KEY_NAME + " TEXT NOT NULL, " +
                KEY_ACTIVITY + " TEXT NOT NULL, " +
                KEY_REQUESTED_ON + " DATETIME DEFAULT CURRENT_TIMESTAMP, " +
                "UNIQUE (" +KEY_ACTIVITY+ ") ON CONFLICT REPLACE)";
        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 NOT NULL, " +
                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_ADDED_ON + " TEXT NOT NULL, " +
                "UNIQUE (" +KEY_URL+ "))";
        db.execSQL(CREATE_TABLE_REQUEST);
        db.execSQL(CREATE_TABLE_PREMIUM_REQUEST);
        db.execSQL(CREATE_TABLE_WALLPAPER);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /*
         * Need to clear shared preferences with version 3.4.0
         */
        if (newVersion == 9) {
            Preferences.get(mContext).clearPreferences();
        }
        resetDatabase(db, oldVersion);
    }

    @Override
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        resetDatabase(db, oldVersion);
    }

    private void resetDatabase(SQLiteDatabase db, int oldVersion) {
        Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type=\'table\'", null);
        List<String> tables = new ArrayList<>();
        if (cursor.moveToFirst()) {
            do {
                tables.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }
        cursor.close();

        List<Request> requests = getRequestedApps(db);
        List<Request> premiumRequest = getPremiumRequest(db);

        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);

        for (Request request : requests) {
            addRequest(db, request);
        }

        if (oldVersion <= 3) {
            return;
        }

        for (Request premium : premiumRequest) {
            Request r = Request.Builder()
                    .name(premium.getName())
                    .activity(premium.getActivity())
                    .orderId(premium.getOrderId())
                    .productId(premium.getProductId())
                    .requestedOn(premium.getRequestedOn())
                    .build();
            addPremiumRequest(db, r);
        }
    }

    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 addRequest(@Nullable SQLiteDatabase db, Request request) {
        SQLiteDatabase database = db;
        if (database == null) {
            if (!openDatabase()) {
                LogUtil.e("Database error: addRequest() failed to open database");
                return;
            }

            database = mDatabase.get().mSQLiteDatabase;
        }

        ContentValues values = new ContentValues();
        values.put(KEY_NAME, request.getName());
        values.put(KEY_ACTIVITY, request.getActivity());

        String requestedOn = request.getRequestedOn();
        if (requestedOn == null) requestedOn = TimeHelper.getLongDateTime();
        values.put(KEY_REQUESTED_ON, requestedOn);

        database.insert(TABLE_REQUEST, null, values);
    }

    public boolean isRequested(String activity) {
        if (!openDatabase()) {
            LogUtil.e("Database error: isRequested() failed to open database");
            return false;
        }

        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_REQUEST, null, KEY_ACTIVITY + " = ?",
                new String[]{activity}, null, null, null, null);
        int rowCount = cursor.getCount();
        cursor.close();
        return rowCount > 0;
    }

    private List<Request> getRequestedApps(@Nullable SQLiteDatabase db) {
        SQLiteDatabase database = db;
        if (database == null) {
            if (!openDatabase()) {
                LogUtil.e("Database error: getRequestedApps() failed to open database");
                return new ArrayList<>();
            }

            database = mDatabase.get().mSQLiteDatabase;
        }

        List<Request> requests = new ArrayList<>();
        Cursor cursor = database.query(TABLE_REQUEST, null, null, null, null, null, null);
        if (cursor.moveToFirst()) {
            do {
                Request request = Request.Builder()
                        .name(cursor.getString(cursor.getColumnIndex(KEY_NAME)))
                        .activity(cursor.getString(cursor.getColumnIndex(KEY_ACTIVITY)))
                        .requestedOn(cursor.getString(cursor.getColumnIndex(KEY_REQUESTED_ON)))
                        .requested(true)
                        .build();

                requests.add(request);
            } while (cursor.moveToNext());
        }
        cursor.close();
        return requests;
    }

    public void addPremiumRequest(@Nullable SQLiteDatabase db, Request request) {
        SQLiteDatabase database = db;
        if (database == null) {
            if (!openDatabase()) {
                LogUtil.e("Database error: addPremiumRequest() failed to open database");
                return;
            }

            database = mDatabase.get().mSQLiteDatabase;
        }

        ContentValues values = new ContentValues();
        values.put(KEY_ORDER_ID, request.getOrderId());
        values.put(KEY_PRODUCT_ID, request.getProductId());
        values.put(KEY_NAME, request.getName());
        values.put(KEY_ACTIVITY, request.getActivity());

        String requestedOn = request.getRequestedOn();
        if (requestedOn == null) requestedOn = TimeHelper.getLongDateTime();
        values.put(KEY_REQUESTED_ON, requestedOn);

        database.insert(TABLE_PREMIUM_REQUEST, null, values);
    }

    public List<Request> getPremiumRequest(@Nullable SQLiteDatabase db) {
        SQLiteDatabase database = db;
        if (database == null) {
            if (!openDatabase()) {
                LogUtil.e("Database error: getPremiumRequest() failed to open database");
                return new ArrayList<>();
            }

            database = mDatabase.get().mSQLiteDatabase;
        }

        List<Request> requests = new ArrayList<>();

        Cursor cursor = database.query(TABLE_PREMIUM_REQUEST,
                null, null, null, null, null, null);
        if (cursor.moveToFirst()) {
            do {
                Request request = Request.Builder()
                        .name(cursor.getString(cursor.getColumnIndex(KEY_NAME)))
                        .activity(cursor.getString(cursor.getColumnIndex(KEY_ACTIVITY)))
                        .orderId(cursor.getString(cursor.getColumnIndex(KEY_ORDER_ID)))
                        .productId(cursor.getString(cursor.getColumnIndex(KEY_PRODUCT_ID)))
                        .build();
                requests.add(request);
            } while (cursor.moveToNext());
        }
        cursor.close();
        return requests;
    }

    public void addWallpapers(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_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, 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 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;
    }

    @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 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)))
                        .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> 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, KEY_ADDED_ON + " DESC, " +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()
                        .name(name)
                        .author(cursor.getString(cursor.getColumnIndex(KEY_AUTHOR)))
                        .url(cursor.getString(cursor.getColumnIndex(KEY_URL)))
                        .thumbUrl(cursor.getString(cursor.getColumnIndex(KEY_THUMB_URL)))
                        .color(cursor.getInt(cursor.getColumnIndex(KEY_COLOR)))
                        .mimeType(cursor.getString(cursor.getColumnIndex(KEY_MIME_TYPE)))
                        .dimensions(dimensions)
                        .size(cursor.getInt(cursor.getColumnIndex(KEY_SIZE)))
                        .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;
        Cursor cursor = mDatabase.get().mSQLiteDatabase.query(TABLE_WALLPAPERS,
                null, null, null, 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)))
                        .build();
            } while (cursor.moveToNext());
        }
        cursor.close();
        return wallpaper;
    }

    public void deleteIconRequestData() {
        if (!openDatabase()) {
            LogUtil.e("Database error: deleteIconRequestData() failed to open database");
            return;
        }

        mDatabase.get().mSQLiteDatabase.delete("SQLITE_SEQUENCE", "NAME = ?", new String[]{TABLE_REQUEST});
        mDatabase.get().mSQLiteDatabase.delete(TABLE_REQUEST, null, null);
    }

    public void deleteWallpapers(List<Wallpaper> wallpapers) {
        if (!openDatabase()) {
            LogUtil.e("Database error: deleteWallpapers() failed to open database");
            return;
        }

        for (Wallpaper wallpaper : wallpapers) {
            mDatabase.get().mSQLiteDatabase.delete(TABLE_WALLPAPERS, KEY_URL +" = ?",
                    new String[]{wallpaper.getURL()});
        }
    }

    public void deleteWallpapers() {
        if (!openDatabase()) {
            LogUtil.e("Database error: deleteWallpapers() failed to open database");
            return;
        }

        mDatabase.get().mSQLiteDatabase.delete("SQLITE_SEQUENCE", "NAME = ?", new String[]{TABLE_WALLPAPERS});
        mDatabase.get().mSQLiteDatabase.delete(TABLE_WALLPAPERS, null, null);
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/utils/Extras.java
public abstract class Extras {

    public static final String TAG_HOME = "home";
    public static final String TAG_APPLY = "apply";
    public static final String TAG_ICONS = "icons";
    public static final String TAG_REQUEST = "request";
    public static final String TAG_WALLPAPERS = "wallpapers";
    public static final String TAG_SETTINGS = "settings";
    public static final String TAG_FAQS = "faqs";
    public static final String TAG_ABOUT = "about";

    public static final String EXTRA_POSITION = "position";
    public static final String EXTRA_SIZE = "size";
    public static final String EXTRA_URL = "url";
    public static final String EXTRA_IMAGE = "image";
    public static final String EXTRA_RESUMED = "resumed";

    public enum Error {
        APPFILTER_NULL,
        DATABASE_ERROR,
        INSTALLED_APPS_NULL,
        ICON_REQUEST_NULL,
        ICON_REQUEST_PROPERTY_NULL,
        ICON_REQUEST_PROPERTY_COMPONENT_NULL;

        public String getMessage() {
            switch (this) {
                case APPFILTER_NULL:
                    return "Error: Unable to read appfilter.xml";
                case DATABASE_ERROR:
                    return "Error: Unable to read database";
                case INSTALLED_APPS_NULL:
                    return "Error: Unable to collect installed apps";
                case ICON_REQUEST_NULL:
                    return "Error: Icon request is null";
                case ICON_REQUEST_PROPERTY_NULL:
                    return "Error: Icon request property is null";
                case ICON_REQUEST_PROPERTY_COMPONENT_NULL:
                    return "Error: Email client component is null";
                default:
                    return "Error: Unknown";
            }
        }

        public void showToast(Context context) {
            if (context == null) return;
            Toast.makeText(context, getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/utils/WallpaperDownloader.java
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(CafeBarTheme.Custom(ColorHelper.getAttributeColor(mContext, R.attr.card_background)))
                    .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;
                        }

                        mContext.startActivity(new Intent()
                                .setAction(Intent.ACTION_VIEW)
                                .setDataAndType(uri, "image/*")
                                .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
                        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);
            }
        } catch (IllegalArgumentException e) {
            LogUtil.e(Log.getStackTraceString(e));
            return;
        }

        showCafeBar(R.string.wallpaper_downloading);
    }

    private void showCafeBar(int res) {
        CafeBar.builder(mContext)
                .theme(CafeBarTheme.Custom(ColorHelper.getAttributeColor(mContext, R.attr.card_background)))
                .contentTypeface(TypefaceHelper.getRegular(mContext))
                .content(res)
                .floating(true)
                .fitSystemWindow()
                .show();
    }

    public static WallpaperDownloader prepare(@NonNull Context context) {
        return new WallpaperDownloader(context);
    }
}
library/PreLollipopTransitions/src/main/java/com/kogitune/activitytransition/ActivityTransitionLauncher.java
public class ActivityTransitionLauncher {

    private final Activity activity;
    private String fromViewName;
    private View fromView;
    private Bitmap bitmap;


    private ActivityTransitionLauncher(Activity activity) {
        this.activity = activity;
    }

    public static ActivityTransitionLauncher with(Activity activity) {
        return new ActivityTransitionLauncher(activity);
    }

    public ActivityTransitionLauncher from(View fromView, String name) {
        this.fromView = fromView;
        this.fromViewName = name;
        return this;
    }

    public ActivityTransitionLauncher image(final Bitmap bitmap) {
        this.bitmap = bitmap;
        return this;
    }

    public Bundle createBundle() {
        return TransitionBundleFactory.createTransitionBundle(activity, fromView, bitmap);
    }

    public Bundle createOptions() {
        return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, fromView, fromViewName).toBundle();
    }

    public void launch(Intent intent) {

        intent.putExtras(createBundle());
        if (Build.VERSION.SDK_INT >= 16) {
            ActivityCompat.startActivity(activity, intent, createOptions());
            return;
        }
        activity.startActivity(intent);
        activity.overridePendingTransition(0, 0);
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/applications/CandyBarApplication.java
public abstract class CandyBarApplication extends Application implements ApplicationCallback {

    private static Configuration mConfiguration;
    private Thread.UncaughtExceptionHandler mHandler;

    public static Request.Property sRequestProperty;
    public static String sZipPath = null;

    public static Configuration getConfiguration() {
        if (mConfiguration == null) {
            mConfiguration = new Configuration();
        }
        return mConfiguration;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Database.get(this).openDatabase();

        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 or disable logging
        LogUtil.setLoggingTag(getString(R.string.app_name));
        LogUtil.setLoggingEnabled(true);

        mConfiguration = onInit();

        if (mConfiguration.mIsCrashReportEnabled) {
            mHandler = Thread.getDefaultUncaughtExceptionHandler();
            Thread.setDefaultUncaughtExceptionHandler(this::handleUncaughtException);
        }

        if (Preferences.get(this).isTimeToSetLanguagePreference()) {
            Preferences.get(this).setLanguagePreference();
            return;
        }

        LocaleHelper.setLocale(this);
    }

    private void handleUncaughtException(Thread thread, Throwable throwable) {
        try {
            StringBuilder sb = new StringBuilder();
            SimpleDateFormat dateFormat = new SimpleDateFormat(
                    "yyyy-MM-dd HH:mm:ss", Locale.getDefault());
            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());
        }

            Preferences.get(this).setLatestCrashLog(sb.toString());

            Intent intent = new Intent(this, CandyBarCrashReport.class);
            intent.putExtra(CandyBarCrashReport.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 static class Configuration {

        private NavigationIcon mNavigationIcon = NavigationIcon.STYLE_1;
        private NavigationViewHeader mNavigationViewHeader = NavigationViewHeader.NORMAL;

        private GridStyle mHomeGrid = GridStyle.CARD;
        private GridStyle mApplyGrid = GridStyle.CARD;
        private Style mRequestStyle = Style.PORTRAIT_FLAT_LANDSCAPE_CARD;
        private GridStyle mWallpapersGrid = GridStyle.CARD;
        private Style mAboutStyle = Style.PORTRAIT_FLAT_LANDSCAPE_CARD;
        private IconColor mIconColor = IconColor.PRIMARY_TEXT;
        private List<OtherApp> mOtherApps = null;

        private boolean mIsHighQualityPreviewEnabled = false;
        private boolean mIsColoredApplyCard = true;
        private boolean mIsAutomaticIconsCountEnabled = true;
        private int mCustomIconsCount = 0;
        private boolean mIsShowTabIconsCount = false;
        private boolean mIsShowTabAllIcons = false;
        private String mTabAllIconsTitle = "All Icons";
        private String[] mCategoryForTabAllIcons = null;

        private ShadowOptions mShadowOptions = new ShadowOptions();
        private boolean mIsDashboardThemingEnabled = true;
        private int mWallpaperGridPreviewQuality = 4;

        private boolean mIsGenerateAppFilter = true;
        private boolean mIsGenerateAppMap = false;
        private boolean mIsGenerateThemeResources = false;
        private boolean mIsIncludeIconRequestToEmailBody = true;

        private boolean mIsCrashReportEnabled = true;
        private JsonStructure mWallpaperJsonStructure = new JsonStructure.Builder("Wallpapers").build();

        public Configuration setNavigationIcon(@NonNull NavigationIcon navigationIcon) {
            mNavigationIcon = navigationIcon;
            return this;
        }

        public Configuration setNavigationViewHeaderStyle(@NonNull NavigationViewHeader navigationViewHeader) {
            mNavigationViewHeader = navigationViewHeader;
            return this;
        }

        public Configuration setAutomaticIconsCountEnabled(boolean automaticIconsCountEnabled) {
            mIsAutomaticIconsCountEnabled = automaticIconsCountEnabled;
            return this;
        }

        public Configuration setHomeGridStyle(@NonNull GridStyle gridStyle) {
            mHomeGrid = gridStyle;
            return this;
        }

        public Configuration setApplyGridStyle(@NonNull GridStyle gridStyle) {
            mApplyGrid = gridStyle;
            return this;
        }

        public Configuration setRequestStyle(@NonNull Style style) {
            mRequestStyle = style;
            return this;
        }

        public Configuration setWallpapersGridStyle(@NonNull GridStyle gridStyle) {
            mWallpapersGrid = gridStyle;
            return this;
        }

        public Configuration setAboutStyle(@NonNull Style style) {
            mAboutStyle = style;
            return this;
        }

        public Configuration setSocialIconColor(@NonNull IconColor iconColor) {
            mIconColor = iconColor;
            return this;
        }

        public Configuration setColoredApplyCard(boolean coloredApplyCard) {
            mIsColoredApplyCard = coloredApplyCard;
            return this;
        }

        public Configuration setCustomIconsCount(int customIconsCount) {
            mCustomIconsCount = customIconsCount;
            return this;
        }

        public Configuration setShowTabIconsCount(boolean showTabIconsCount) {
            mIsShowTabIconsCount = showTabIconsCount;
            return this;
        }

        public Configuration setShowTabAllIcons(boolean showTabAllIcons) {
            mIsShowTabAllIcons = showTabAllIcons;
            return this;
        }

        public Configuration setTabAllIconsTitle(@NonNull String title) {
            mTabAllIconsTitle = title;
            if (mTabAllIconsTitle.length() == 0) mTabAllIconsTitle = "All Icons";
            return this;
        }

        public Configuration setCategoryForTabAllIcons(@NonNull String[] categories) {
            mCategoryForTabAllIcons = categories;
            return this;
        }

        public Configuration setShadowEnabled(boolean shadowEnabled) {
            mShadowOptions = new ShadowOptions(shadowEnabled);
            return this;
        }

        public Configuration setShadowEnabled(@NonNull ShadowOptions shadowOptions) {
            mShadowOptions = shadowOptions;
            return this;
        }

        public Configuration setDashboardThemingEnabled(boolean dashboardThemingEnabled) {
            mIsDashboardThemingEnabled = dashboardThemingEnabled;
            return this;
        }

        public Configuration setWallpaperGridPreviewQuality(@IntRange (from = 1, to = 10) int quality) {
            mWallpaperGridPreviewQuality = quality;
            return this;
        }

        public Configuration setGenerateAppFilter(boolean generateAppFilter) {
            mIsGenerateAppFilter = generateAppFilter;
            return this;
        }

        public Configuration setGenerateAppMap(boolean generateAppMap) {
            mIsGenerateAppMap = generateAppMap;
            return this;
        }

        public Configuration setGenerateThemeResources(boolean generateThemeResources) {
            mIsGenerateThemeResources = generateThemeResources;
            return this;
        }

        public Configuration setIncludeIconRequestToEmailBody(boolean includeIconRequestToEmailBody) {
            mIsIncludeIconRequestToEmailBody = includeIconRequestToEmailBody;
            return this;
        }

        public Configuration setCrashReportEnabled(boolean crashReportEnabled) {
            mIsCrashReportEnabled = crashReportEnabled;
            return this;
        }

        public Configuration setWallpaperJsonStructure(@NonNull JsonStructure jsonStructure) {
            mWallpaperJsonStructure = jsonStructure;
            return this;
        }

        public Configuration setOtherApps(@NonNull OtherApp[] otherApps) {
            mOtherApps = Arrays.asList(otherApps);
            return this;
        }

        public Configuration setHighQualityPreviewEnabled(boolean highQualityPreviewEnabled) {
            mIsHighQualityPreviewEnabled = highQualityPreviewEnabled;
            return this;
        }

        public NavigationIcon getNavigationIcon() {
            return mNavigationIcon;
        }

        public NavigationViewHeader getNavigationViewHeader() {
            return mNavigationViewHeader;
        }

        public GridStyle getHomeGrid() {
            return mHomeGrid;
        }

        public GridStyle getApplyGrid() {
            return mApplyGrid;
        }

        public Style getRequestStyle() {
            return mRequestStyle;
        }

        public GridStyle getWallpapersGrid() {
            return mWallpapersGrid;
        }

        public Style getAboutStyle() {
            return mAboutStyle;
        }

        public IconColor getSocialIconColor() {
            return mIconColor;
        }

        public boolean isColoredApplyCard() {
            return mIsColoredApplyCard;
        }

        public boolean isAutomaticIconsCountEnabled() {
            return mIsAutomaticIconsCountEnabled;
        }

        public int getCustomIconsCount() {
            return mCustomIconsCount;
        }

        public boolean isShowTabIconsCount() {
            return mIsShowTabIconsCount;
        }

        public boolean isShowTabAllIcons() {
            return mIsShowTabAllIcons;
        }

        public String getTabAllIconsTitle() {
            return mTabAllIconsTitle;
        }

        public String[] getCategoryForTabAllIcons() {
            return mCategoryForTabAllIcons;
        }

        @NonNull
        public ShadowOptions getShadowOptions() {
            return mShadowOptions;
        }

        public boolean isDashboardThemingEnabled() {
            return mIsDashboardThemingEnabled;
        }

        public int getWallpaperGridPreviewQuality() {
            return mWallpaperGridPreviewQuality;
        }

        public boolean isGenerateAppFilter() {
            return mIsGenerateAppFilter;
        }

        public boolean isGenerateAppMap() {
            return mIsGenerateAppMap;
        }

        public boolean isGenerateThemeResources() {
            return mIsGenerateThemeResources;
        }

        public boolean isIncludeIconRequestToEmailBody() {
            return mIsIncludeIconRequestToEmailBody;
        }

        public boolean isHighQualityPreviewEnabled() {
            return mIsHighQualityPreviewEnabled;
        }

        public JsonStructure getWallpaperJsonStructure() {
            return mWallpaperJsonStructure;
        }

        @Nullable
        public List<OtherApp> getOtherApps() {
            return mOtherApps;
        }
    }

    public enum NavigationIcon {
        DEFAULT,
        STYLE_1,
        STYLE_2,
        STYLE_3,
        STYLE_4
    }

    public enum NavigationViewHeader {
        NORMAL,
        MINI,
        NONE
    }

    public enum GridStyle {
        CARD,
        FLAT
    }

    public enum Style {
        PORTRAIT_FLAT_LANDSCAPE_CARD,
        PORTRAIT_FLAT_LANDSCAPE_FLAT
    }

    public enum IconColor {
        PRIMARY_TEXT,
        ACCENT
    }

    public static class ShadowOptions {

        private boolean mIsToolbarEnabled;
        private boolean mIsCardEnabled;
        private boolean mIsFabEnabled;
        private boolean mIsTapIntroEnabled;

        public ShadowOptions() {
            mIsToolbarEnabled = mIsCardEnabled = mIsFabEnabled = mIsTapIntroEnabled = true;
        }

        public ShadowOptions(boolean shadowEnabled) {
            mIsToolbarEnabled = mIsCardEnabled = mIsFabEnabled = mIsTapIntroEnabled= shadowEnabled;
        }

        public ShadowOptions setToolbarEnabled(boolean toolbarEnabled) {
            mIsToolbarEnabled = toolbarEnabled;
            return this;
        }

        public ShadowOptions setCardEnabled(boolean cardEnabled) {
            mIsCardEnabled = cardEnabled;
            return this;
        }

        public ShadowOptions setFabEnabled(boolean fabEnabled) {
            mIsFabEnabled = fabEnabled;
            return this;
        }

        public ShadowOptions setTapIntroEnabled(boolean tapIntroEnabled) {
            mIsTapIntroEnabled = tapIntroEnabled;
            return this;
        }

        public boolean isToolbarEnabled() {
            return mIsToolbarEnabled;
        }

        public boolean isCardEnabled() {
            return mIsCardEnabled;
        }

        public boolean isFabEnabled() {
            return mIsFabEnabled;
        }

        public boolean isTapIntroEnabled() {
            return mIsTapIntroEnabled;
        }
    }

    public static class OtherApp {

        private String mIcon;
        private String mTitle;
        private String mDescription;
        private String mUrl;

        public OtherApp(String icon, String title, String description, String url) {
            mIcon = icon;
            mTitle = title;
            mDescription = description;
            mUrl = url;
        }

        public String getIcon() {
            return mIcon;
        }

        public String getTitle() {
            return mTitle;
        }

        public String getDescription() {
            return mDescription;
        }

        public String getUrl() {
            return mUrl;
        }
    }
}
core/src/main/java/com/dm/material/dashboard/candybar/tasks/WallpaperApplyTask.java
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);
        mApply = Apply.HOMESCREEN;
    }

    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);
        mDialog.setContent(R.string.wallpaper_applying);
    }

    @Override
    protected void onCancelled(Boolean aBoolean) {
        super.onCancelled(aBoolean);
        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 (((AppCompatActivity) mContext.get()).isFinishing()) {
            return;
        }

        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }

        if (aBoolean) {
            CafeBar.builder(mContext.get())
                    .theme(CafeBarTheme.Custom(ColorHelper.getAttributeColor(
                            mContext.get(), R.attr.card_background)))
                    .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
    }
}


Question: import android.animation.AnimatorInflater;
import android.animation.StateListAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.CardView;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.danimahardhika.android.helpers.core.ColorHelper;
import com.danimahardhika.android.helpers.permission.PermissionHelper;
import com.dm.material.dashboard.candybar.R;
import com.dm.material.dashboard.candybar.activities.CandyBarWallpaperActivity;
import com.dm.material.dashboard.candybar.applications.CandyBarApplication;
import com.dm.material.dashboard.candybar.databases.Database;
import com.dm.material.dashboard.candybar.helpers.ViewHelper;
import com.dm.material.dashboard.candybar.items.PopupItem;
import com.dm.material.dashboard.candybar.items.Wallpaper;
import com.dm.material.dashboard.candybar.preferences.Preferences;
import com.dm.material.dashboard.candybar.tasks.WallpaperApplyTask;
import com.dm.material.dashboard.candybar.utils.Extras;
import com.dm.material.dashboard.candybar.utils.ImageConfig;
import com.dm.material.dashboard.candybar.utils.Popup;
import com.dm.material.dashboard.candybar.utils.WallpaperDownloader;
import com.dm.material.dashboard.candybar.utils.views.HeaderView;
import com.kogitune.activitytransition.ActivityTransitionLauncher;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
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;
        if (mIsShowName) {
            holder.name.setText(wallpaper.getName());
            holder.author.setText(wallpaper.getAuthor());
        }

        ImageLoader.getInstance().displayImage(wallpaper.getThumbUrl(), new ImageViewAware(holder.image),
                mOptions.build(), ImageConfig.getThumbnailSize(), new SimpleImageLoadingListener() {
                    @Override
                    public void onLoadingStarted(String imageUri, View view) {
                        super.onLoadingStarted(imageUri, view);
                        if (mIsAutoGeneratedColor && mIsShowName) {
                            int color = wallpaper.getColor();
                            if (color == 0) {
                                color = ColorHelper.getAttributeColor(
                                        mContext, R.attr.card_background);
                            }

                            int text = ColorHelper.getTitleTextColor(color);
                            holder.name.setTextColor(text);
                            holder.author.setTextColor(ColorHelper.getBodyTextColor(color));
                            holder.card.setCardBackgroundColor(color);
                        }
                    }

                    @Override
                    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                        super.onLoadingComplete(imageUri, view, loadedImage);
                        if (mIsAutoGeneratedColor && mIsShowName && loadedImage != null && wallpaper.getColor() == 0) {
                            Palette.from(loadedImage).generate(palette -> {
                                int defaultColor = ColorHelper.getAttributeColor(
                                        mContext, R.attr.card_background);
                                int color = palette.getVibrantColor(defaultColor);
                                if (color == defaultColor)
                                    color = palette.getMutedColor(defaultColor);

                                holder.card.setCardBackgroundColor(color);
                                int text = ColorHelper.getTitleTextColor(color);
                                holder.name.setTextColor(text);
                                holder.author.setTextColor(ColorHelper.getBodyTextColor(color));

                                wallpaper.setColor(color);
                                Database.get(mContext).updateWallpaper(wallpaper);
                            });
                        }
                    }
                }, null);
    }

    @Override
    public int getItemCount() {
        return mWallpapers.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
            View.OnLongClickListener {

        private final CardView card;
        private final HeaderView image;
        private TextView name;
        private TextView author;

        ViewHolder(View itemView) {
            super(itemView);
            String viewStyle = mContext.getResources().getString(
                    R.string.wallpaper_grid_preview_style);
            Point ratio = ViewHelper.getWallpaperViewRatio(viewStyle);

            image = itemView.findViewById(R.id.image);
            image.setRatio(ratio.x, ratio.y);

            card = itemView.findViewById(R.id.card);
            if (CandyBarApplication.getConfiguration().getWallpapersGrid() == CandyBarApplication.GridStyle.FLAT) {
                if (card.getLayoutParams() instanceof GridLayoutManager.LayoutParams) {
                    card.setRadius(0f);
                    card.setUseCompatPadding(false);
                    int margin = mContext.getResources().getDimensionPixelSize(R.dimen.card_margin);
                    GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) card.getLayoutParams();
                    params.setMargins(0, 0, margin, margin);

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        params.setMarginEnd(margin);
                    }
                }
            }

            if (!Preferences.get(mContext).isCardShadowEnabled()) {
                card.setCardElevation(0);
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                StateListAnimator stateListAnimator = AnimatorInflater
                        .loadStateListAnimator(mContext, R.animator.card_lift);
                card.setStateListAnimator(stateListAnimator);
            }

            if (mIsShowName) {
                name = itemView.findViewById(R.id.name);
                author = itemView.findViewById(R.id.author);
            }

            card.setOnClickListener(this);
            card.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View view) {
            int id = view.getId();
            int position = getAdapterPosition();
            if (id == R.id.card) {
                if (sIsClickable) {
                    sIsClickable = false;
                    try {
                        Bitmap bitmap = null;
                        if (image.getDrawable() != null) {
                            bitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
                        }

                        final Intent intent = new Intent(mContext, CandyBarWallpaperActivity.class);
                        intent.putExtra(Extras.EXTRA_URL, mWallpapers.get(position).getURL());

Answer:                         ActivityTransitionLauncher.with((AppCompatActivity) mContext)