public class AbstractPlugin<T, D> {

    final public Stack<NamedSuperCall> mSuperListeners = new Stack<>();

    private D mDelegate;

    private T mOriginal;

    public final void addToDelegate(final D delegate, final T original) {
        mDelegate = delegate;
        mOriginal = original;
        onAddedToDelegate();
    }

    public D getCompositeDelegate() {
        return mDelegate;
    }

    public T getOriginal() {
        return mOriginal;
    }

    public final void removeFromDelegate() {
        mDelegate = null;
        mOriginal = null;
        onRemovedFromDelegated();
    }

    public void verifyMethodCalledFromDelegate(final String method) {
        if (mSuperListeners.isEmpty()) {
            throw new IllegalStateException("Do not call " + method
                    + " on a ActivityPlugin directly. You have to call mDelegate." + method
                    + " or the call order of the plugins would be mixed up.");
        }
        final String superListener = mSuperListeners.peek().getMethodName();
        if (!superListener.equals(method)) {
            throw new IllegalStateException("You may have called "
                    + method + " from " + superListener + " instead of calling getOriginal()."
                    + method + ". Do not call " + method
                    + " on a ActivityPlugin directly. You have to call mDelegate." + method
                    + " or the call order of the plugins would be mixed up.");
        }
    }

    /**
     * callback when this plugin was added to a delegate. {@link #getOriginal()} and {@link
     * #getCompositeDelegate()} are now set
     */
    protected void onAddedToDelegate() {

    }

    /**
     * callback when this plugin was removed from the delegate. {@link #getOriginal()} and {@link
     * #getCompositeDelegate()} are now {@code null}
     */
    protected void onRemovedFromDelegated() {

    }
} public abstract class CallFun0<R> extends NamedSuperCall {

    public CallFun0(final String methodName) {
        super(methodName);
    }

    public abstract R call();
} public abstract class CallFun1<R, T1> extends NamedSuperCall {

    public CallFun1(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1);
} public abstract class CallFun2<R, T1, T2> extends NamedSuperCall {

    public CallFun2(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1, final T2 p2);
} public abstract class CallFun3<R, T1, T2, T3> extends NamedSuperCall {

    public CallFun3(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1, final T2 p2, final T3 p3);
} public abstract class CallFun4<R, T1, T2, T3, T4> extends NamedSuperCall {

    public CallFun4(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4);
} public abstract class CallFun5<R, T1, T2, T3, T4, T5> extends NamedSuperCall {

    public CallFun5(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1, final T2 p2, final T3 p3,  final T4 p4, T5 p5);
} public abstract class CallFun6<R, T1, T2, T3, T4, T5, T6> extends NamedSuperCall {

    public CallFun6(final String methodName) {
        super(methodName);
    }

    public abstract R call(final T1 p1, final T2 p2, final T3 p3,  final T4 p4, T5 p5, T6 p6);
} public abstract class CallVoid0 extends NamedSuperCall {

    public CallVoid0(final String methodName) {
        super(methodName);
    }

    public abstract void call();
} public abstract class CallVoid1<T1> extends NamedSuperCall {

    public CallVoid1(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1);
} public abstract class CallVoid2<T1, T2> extends NamedSuperCall {

    public CallVoid2(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2);
} public abstract class CallVoid3<T1, T2, T3> extends NamedSuperCall {

    public CallVoid3(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3);
} public abstract class CallVoid4<T1, T2, T3, T4> extends NamedSuperCall {

    public CallVoid4(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3,  final T4 p4);
} public abstract class CallVoid5<T1, T2, T3, T4, T5> extends NamedSuperCall {

    public CallVoid5(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3,  final T4 p4, T5 p5);
} public abstract class CallVoid6<T1, T2, T3, T4, T5, T6> extends NamedSuperCall {

    public CallVoid6(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6);
} public abstract class CallVoid7<T1, T2, T3, T4, T5, T6, T7> extends NamedSuperCall {

    public CallVoid7(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6,
            T7 p7);
} public abstract class CallVoid8<T1, T2, T3, T4, T5, T6, T7, T8> extends NamedSuperCall {

    public CallVoid8(final String methodName) {
        super(methodName);
    }

    public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6,
            T7 p7, T8 p8);
}
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.Application;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.SearchManager;
import android.app.VoiceInteractor;
import android.app.assist.AssistContent;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.ViewModelStore;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.service.vr.VrListenerService;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.SharedElementCallback;
import android.support.v4.app.SupportActivity;
import android.support.v4.app.SupportActivity.ExtraData;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarDrawerToggle.Delegate;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.view.ActionMode;
import android.support.v7.view.ActionMode.Callback;
import android.support.v7.widget.Toolbar;
import android.transition.Scene;
import android.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SearchEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.pascalwelsch.compositeandroid.core.AbstractPlugin;
import com.pascalwelsch.compositeandroid.core.CallFun0;
import com.pascalwelsch.compositeandroid.core.CallFun1;
import com.pascalwelsch.compositeandroid.core.CallFun2;
import com.pascalwelsch.compositeandroid.core.CallFun3;
import com.pascalwelsch.compositeandroid.core.CallFun4;
import com.pascalwelsch.compositeandroid.core.CallFun5;
import com.pascalwelsch.compositeandroid.core.CallFun6;
import com.pascalwelsch.compositeandroid.core.CallVoid0;
import com.pascalwelsch.compositeandroid.core.CallVoid1;
import com.pascalwelsch.compositeandroid.core.CallVoid2;
import com.pascalwelsch.compositeandroid.core.CallVoid3;
import com.pascalwelsch.compositeandroid.core.CallVoid4;
import com.pascalwelsch.compositeandroid.core.CallVoid5;
import com.pascalwelsch.compositeandroid.core.CallVoid6;
import com.pascalwelsch.compositeandroid.core.CallVoid7;
import com.pascalwelsch.compositeandroid.core.CallVoid8;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.Executor;
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
            DatabaseErrorHandler errorHandler) {
        verifyMethodCalledFromDelegate("openOrCreateDatabase(String, Integer, CursorFactory, DatabaseErrorHandler)");
        return ((CallFun4<SQLiteDatabase, String, Integer, CursorFactory, DatabaseErrorHandler>) mSuperListeners
                .pop()).call(name, mode, factory, errorHandler);
    }

    /**
     * Call immediately after one of the flavors of {@link #startActivity(Intent)}
     * or {@link #finish} to specify an explicit transition animation to
     * perform next.
     *
     * <p>As of {@link Build.VERSION_CODES#JELLY_BEAN} an alternative
     * to using this with starting activities is to supply the desired animation
     * information through a {@link ActivityOptions} bundle to
     * {@link #startActivity(Intent, Bundle)} or a related function.  This allows
     * you to specify a custom animation even when starting an activity from
     * outside the context of the current top activity.
     *
     * @param enterAnim A resource ID of the animation resource to use for
     *                  the incoming activity.  Use 0 for no animation.
     * @param exitAnim  A resource ID of the animation resource to use for
     */
    public void overridePendingTransition(int enterAnim, int exitAnim) {
        verifyMethodCalledFromDelegate("overridePendingTransition(Integer, Integer)");
        ((CallVoid2<Integer, Integer>) mSuperListeners.pop()).call(enterAnim, exitAnim);
    }

    public Drawable peekWallpaper() {
        verifyMethodCalledFromDelegate("peekWallpaper()");
        return ((CallFun0<Drawable>) mSuperListeners.pop()).call();
    }

    /**
     * Postpone the entering activity transition when Activity was started with
     * {@link ActivityOptions#makeSceneTransitionAnimation(Activity,
     * Pair[])}.
     * <p>This method gives the Activity the ability to delay starting the entering and
     * shared element transitions until all data is loaded. Until then, the Activity won't
     * draw into its window, leaving the window transparent. This may also cause the
     * returning animation to be delayed until data is ready. This method should be
     * called in {@link #onCreate(Bundle)} or in
     * {@link #onActivityReenter(int, Intent)}.
     * {@link #startPostponedEnterTransition()} must be called to allow the Activity to
     * start the transitions. If the Activity did not use
     * {@link ActivityOptions#makeSceneTransitionAnimation(Activity,
     * Pair[])}, then this method does nothing.</p>
     */
    public void postponeEnterTransition() {
        verifyMethodCalledFromDelegate("postponeEnterTransition()");
        ((CallVoid0) mSuperListeners.pop()).call();
    }

    public void putExtraData(ExtraData extraData) {
        verifyMethodCalledFromDelegate("putExtraData(SupportActivity.ExtraData)");
        ((CallVoid1<SupportActivity.ExtraData>) mSuperListeners.pop()).call(extraData);
    }

    /**
     * Cause this Activity to be recreated with a new instance.  This results
     * in essentially the same flow as when the Activity is created due to
     * a configuration change -- the current instance will go through its
     * lifecycle to {@link #onDestroy} and a new instance then created after it.
     */
    public void recreate() {
        verifyMethodCalledFromDelegate("recreate()");
        ((CallVoid0) mSuperListeners.pop()).call();
    }

    /**
     * Add a new {@link ComponentCallbacks} to the base application of the
     * Context, which will be called at the same times as the ComponentCallbacks
     * methods of activities and other components are called.  Note that you
     * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
     * appropriate in the future; this will not be removed for you.
     *
     * @param callback The interface to call.  This can be either a
     *                 {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
     */
    public void registerComponentCallbacks(ComponentCallbacks callback) {
        verifyMethodCalledFromDelegate("registerComponentCallbacks(ComponentCallbacks)");
        ((CallVoid1<ComponentCallbacks>) mSuperListeners.pop()).call(callback);
    }

    /**
     * Registers a context menu to be shown for the given view (multiple views
     * can show the context menu). This method will set the
     * {@link OnCreateContextMenuListener} on the view to this activity, so
     * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
     * called when it is time to show the context menu.
     *
     * @param view The view that should show a context menu.
     * @see #unregisterForContextMenu(View)
     */
    public void registerForContextMenu(View view) {
        verifyMethodCalledFromDelegate("registerForContextMenu(View)");
        ((CallVoid1<View>) mSuperListeners.pop()).call(view);
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        verifyMethodCalledFromDelegate("registerReceiver(BroadcastReceiver, IntentFilter)");
        return ((CallFun2<Intent, BroadcastReceiver, IntentFilter>) mSuperListeners.pop()).call(receiver, filter);
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
        verifyMethodCalledFromDelegate("registerReceiver(BroadcastReceiver, IntentFilter, Integer)");
        return ((CallFun3<Intent, BroadcastReceiver, IntentFilter, Integer>) mSuperListeners.pop())
                .call(receiver, filter, flags);
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission,
            Handler scheduler) {
        verifyMethodCalledFromDelegate("registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)");
        return ((CallFun4<Intent, BroadcastReceiver, IntentFilter, String, Handler>) mSuperListeners.pop())
                .call(receiver, filter, broadcastPermission, scheduler);
    }

    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission,
            Handler scheduler, int flags) {
        verifyMethodCalledFromDelegate("registerReceiver(BroadcastReceiver, IntentFilter, String, Handler, Integer)");
        return ((CallFun5<Intent, BroadcastReceiver, IntentFilter, String, Handler, Integer>) mSuperListeners.pop())