com.github.piasy.dialogfragmentanywhere.BaseDialogFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.github.piasy.dialogfragmentanywhere.BaseDialogFragment.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Piasy
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.github.piasy.dialogfragmentanywhere;

import android.app.Dialog;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentTransaction;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import com.github.piasy.safelyandroid.activity.StartActivityDelegate;
import com.github.piasy.safelyandroid.dialogfragment.SupportDialogFragmentDismissDelegate;
import com.github.piasy.safelyandroid.fragment.SupportFragmentTransactionDelegate;
import com.github.piasy.safelyandroid.fragment.TransactionCommitter;

/**
 * Created by Piasy on 16/4/28.
 */
@SuppressWarnings("PMD.TooManyMethods")
public abstract class BaseDialogFragment extends DialogFragment implements TransactionCommitter {

    public static final int LOCATE_LEFT = 1;
    public static final int LOCATE_ABOVE = 2;
    public static final int LOCATE_RIGHT = 3;
    public static final int LOCATE_BELOW = 4;

    protected static final float DEFAULT_DIM_AMOUNT = 0.2F;
    protected static final String ANCHOR_VIEW_X = "ANCHOR_VIEW_X";
    protected static final String ANCHOR_VIEW_Y = "ANCHOR_VIEW_Y";
    protected static final String ANCHOR_VIEW_WIDTH = "ANCHOR_VIEW_WIDTH";
    protected static final String ANCHOR_VIEW_HEIGHT = "ANCHOR_VIEW_HEIGHT";
    protected static final String LOCATE_TO_ANCHOR = "LOCATE_TO_ANCHOR";
    protected static final String OFFSET_X = "OFFSET_X";
    protected static final String OFFSET_Y = "OFFSET_Y";

    private final SupportDialogFragmentDismissDelegate mSupportDialogFragmentDismissDelegate = new SupportDialogFragmentDismissDelegate();

    private final SupportFragmentTransactionDelegate mSupportFragmentTransactionDelegate = new SupportFragmentTransactionDelegate();

    public static Bundle anchorTo(View anchor, @Locate int locate, int offsetX, int offsetY) {
        Bundle args = new Bundle();
        int[] loc = new int[2];
        anchor.getLocationOnScreen(loc);
        args.putInt(ANCHOR_VIEW_X, loc[0]);
        args.putInt(ANCHOR_VIEW_Y, loc[1]);
        args.putInt(ANCHOR_VIEW_WIDTH, anchor.getWidth());
        args.putInt(ANCHOR_VIEW_HEIGHT, anchor.getHeight());
        args.putInt(LOCATE_TO_ANCHOR, locate);
        args.putInt(OFFSET_X, offsetX);
        args.putInt(OFFSET_Y, offsetY);
        return args;
    }

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(final Bundle savedInstanceState) {
        return new Dialog(getActivity(), getTheme()) {
            @Override
            public void onBackPressed() {
                if (isCanceledOnBackPressed()) {
                    super.onBackPressed();
                }
            }
        };
    }

    @Override
    public void onStart() {
        super.onStart();
        // Less dimmed background; see http://stackoverflow.com/q/13822842/56285
        final Window window = getDialog().getWindow();
        final WindowManager.LayoutParams params = window.getAttributes();
        params.dimAmount = getDimAmount(); // dim only a little bit
        window.setAttributes(params);

        window.setLayout(getWidth(), getHeight());
        window.setGravity(getGravity());

        // Transparent background; see http://stackoverflow.com/q/15007272/56285
        // (Needed to make dialog's alpha shadow look good)
        window.setBackgroundDrawableResource(android.R.color.transparent);

        final Resources res = getResources();
        final int titleDividerId = res.getIdentifier("titleDivider", "id", "android");
        if (titleDividerId > 0) {
            final View titleDivider = getDialog().findViewById(titleDividerId);
            if (titleDivider != null) {
                titleDivider.setBackgroundColor(res.getColor(android.R.color.transparent));
            }
        }
    }

    public void onDestroyView() {
        super.onDestroyView();
        unbindView();
    }

    @Nullable
    @Override
    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
            @Nullable final Bundle savedInstanceState) {
        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        getDialog().setCanceledOnTouchOutside(isCanceledOnTouchOutside());
        return inflater.inflate(getLayoutRes(), container, false);
    }

    /**
     * CONTRACT: the new life cycle method {@link #initFields()}, {@link #bindView(View)}
     * and {@link #startBusiness()} might use other infrastructure initialised in subclass's
     * onViewCreated, e.g. DI, MVP, so those subclass should do those
     * infrastructure init job before this method is invoked.
     */
    @Override
    public void onViewCreated(final View view, final Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.post(new Runnable() {
            @Override
            public void run() {
                anchorDialog();
            }
        });
        initFields();
        bindView(view);
        startBusiness();
    }

    @Override
    public void onResume() {
        super.onResume();
        mSupportDialogFragmentDismissDelegate.onResumed(this);
        mSupportFragmentTransactionDelegate.onResumed();
    }

    protected final boolean startActivitySafely(final Intent intent) {
        return StartActivityDelegate.startActivitySafely(this, intent);
    }

    protected boolean safeCommit(@NonNull final FragmentTransaction transaction) {
        return mSupportFragmentTransactionDelegate.safeCommit(this, transaction);
    }

    public boolean safeDismiss() {
        return mSupportDialogFragmentDismissDelegate.safeDismiss(this);
    }

    @Override
    public boolean isCommitterResumed() {
        return isResumed();
    }

    void anchorDialog() {
        final Bundle args = getArguments();
        if (args == null || getDialog() == null) {
            return;
        }

        final Window window = getDialog().getWindow();
        final WindowManager.LayoutParams params = window.getAttributes();

        int anchorX = args.getInt(ANCHOR_VIEW_X);
        int anchorY = args.getInt(ANCHOR_VIEW_Y);
        int anchorWidth = args.getInt(ANCHOR_VIEW_WIDTH);
        int anchorHeight = args.getInt(ANCHOR_VIEW_HEIGHT);
        int dialogWidth = window.getDecorView().getWidth();
        int dialogHeight = window.getDecorView().getHeight();
        int locate = args.getInt(LOCATE_TO_ANCHOR);
        int offsetX = args.getInt(OFFSET_X);
        int offsetY = args.getInt(OFFSET_Y);

        switch (locate) {
        case LOCATE_LEFT:
            params.x = anchorX - dialogWidth + offsetX;
            params.y = anchorY - Math.abs(dialogHeight - anchorHeight) / 2 - getStatusBarHeight() + offsetY;
            break;
        case LOCATE_RIGHT:
            params.x = anchorX + anchorWidth + offsetX;
            params.y = anchorY - Math.abs(dialogHeight - anchorHeight) / 2 - getStatusBarHeight() + offsetY;
            break;
        case LOCATE_BELOW:
            params.x = anchorX - Math.abs(dialogWidth - anchorWidth) / 2 + offsetX;
            params.y = anchorY + anchorHeight - getStatusBarHeight() + offsetY;
            break;
        case LOCATE_ABOVE:
            params.x = anchorX - Math.abs(dialogWidth - anchorWidth) / 2 + offsetX;
            params.y = anchorY - dialogHeight - getStatusBarHeight() + offsetY;
        default:
            break;
        }
        window.setAttributes(params);
    }

    @LayoutRes
    protected abstract int getLayoutRes();

    protected float getDimAmount() {
        return DEFAULT_DIM_AMOUNT;
    }

    protected int getGravity() {
        return Gravity.LEFT | Gravity.TOP;
    }

    protected abstract int getWidth();

    protected abstract int getHeight();

    protected boolean isCanceledOnTouchOutside() {
        return true;
    }

    protected boolean isCanceledOnBackPressed() {
        return true;
    }

    /**
     * init necessary fields.
     */
    @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract")
    protected void initFields() {

    }

    /**
     * bind views, should override this method when bind view manually.
     */
    protected void bindView(final View rootView) {

    }

    /**
     * start specific business logic.
     */
    @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract")
    protected void startBusiness() {

    }

    /**
     * unbind views, should override this method when unbind view manually.
     */
    protected void unbindView() {

    }

    private int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    @IntDef({ LOCATE_LEFT, LOCATE_ABOVE, LOCATE_RIGHT, LOCATE_BELOW })
    public @interface Locate {
    }
}