/* * Copyright (C) 2015 AChep@xda <artemchep@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.achep.base.ui.activities; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import android.view.View; import com.achep.base.AppHeap; import com.achep.base.interfaces.IActivityBase; import com.achep.base.tests.Check; import com.achep.base.utils.power.PowerSaveDetector; import com.squareup.leakcanary.RefWatcher; import org.solovyev.android.checkout.ActivityCheckout; import org.solovyev.android.checkout.Checkout; import java.lang.reflect.Method; import static com.achep.base.Build.DEBUG; /** * Created by Artem Chepurnoy on 08.03.2015. */ final class ActivityBaseInternal implements IActivityBase { private static final String TAG = "ActivityBaseInternal"; //-- DEBUG ---------------------------------------------------------------- private static int sInstancesCount = 0; /* Only for debug purposes! */ @SuppressWarnings({"FieldCanBeLocal", "ConstantConditions"}) private final Object dFinalizeWatcher = DEBUG ? new Object() { /** * Logs the notifications' removal. */ @Override protected void finalize() throws Throwable { try { Log.d(TAG, "Finalizing the instance=" + this + " n=" + --sInstancesCount); } finally { super.finalize(); } } } : null; //-- BEGIN ---------------------------------------------------------------- private Activity mActivity; private ActivityCheckout mCheckout; private PowerSaveDetector mPowerSaveDetector; private boolean mCheckoutRequest; private boolean mInputMethodResetRequest; private boolean mCreated; public ActivityBaseInternal() { // Logs the current number of the Activities and // increases its count. Check `dFinalizeWatcher` field // for more information. if (DEBUG) Log.d(TAG, "Creating an instance=" + this + " watcher=" + dFinalizeWatcher + " n=" + ++sInstancesCount); } /* Mirrors Activity#onCreate(...) */ void onCreate(Activity activity, Bundle savedInstanceState) { if (mCheckoutRequest) mCheckout = Checkout.forActivity(activity, AppHeap.getCheckout()); mPowerSaveDetector = PowerSaveDetector.newInstance(activity); mActivity = activity; mCreated = true; } /* Mirrors Activity#onStart(...) */ void onStart() { if (mCheckout != null) { AppHeap.getCheckoutInternal().requestConnect(); mCheckout.start(); } mPowerSaveDetector.start(); } /* Mirrors Activity#onStop(...) */ void onStop() { if (mCheckout != null) { mCheckout.stop(); AppHeap.getCheckoutInternal().requestDisconnect(); } mPowerSaveDetector.stop(); } /* Mirrors Activity#onDestroy(...) */ void onDestroy() { mCheckout = null; if (mInputMethodResetRequest) performInputMethodServiceReset(); // Watch for the activity to detect possible leaks. RefWatcher refWatcher = AppHeap.getRefWatcher(); refWatcher.watch(this); } /** * @see #requestInputMethodReset() */ private void performInputMethodServiceReset() { Object imm = mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); Reflector.TypedObject windowToken = new Reflector.TypedObject( mActivity.getWindow().getDecorView().getWindowToken(), IBinder.class); Reflector.invokeMethodExceptionSafe(imm, "windowDismissed", windowToken); Reflector.TypedObject view = new Reflector.TypedObject(null, View.class); Reflector.invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view); } /* Mirrors Activity#onActivityResult(...) */ boolean onActivityResult(int requestCode, int resultCode, Intent data) { return mCheckout != null && mCheckout.onActivityResult(requestCode, resultCode, data); } //-- IActivityBase -------------------------------------------------------- /** * {@inheritDoc} */ @Override public void requestCheckout() { Check.getInstance().isFalse(mCheckoutRequest); Check.getInstance().isFalse(mCreated); // not created yet. mCheckoutRequest = true; } /** * {@inheritDoc} */ @Override public void requestInputMethodReset() { mInputMethodResetRequest = true; } /** * {@inheritDoc} */ @NonNull @Override public Activity getActivity() { return mActivity; } /** * {@inheritDoc} */ @Nullable @Override public ActivityCheckout getCheckout() { return mCheckout; } /** * {@inheritDoc} */ @NonNull @Override public PowerSaveDetector getPowerSaveDetector() { return mPowerSaveDetector; } /** * {@inheritDoc} */ @Override public boolean isPowerSaveMode() { return mPowerSaveDetector.isPowerSaveMode(); } /** * {@inheritDoc} */ @Override public boolean isNotPowerSaveMode() { return mPowerSaveDetector.isNotPowerSaveMode(); } //-- OTHER ---------------------------------------------------------------- private static class Reflector { public static final class TypedObject { private final Object object; private final Class type; public TypedObject(@Nullable Object object, @Nullable Class type) { this.object = object; this.type = type; } @Nullable private Object getObject() { return object; } @Nullable private Class getType() { return type; } } public static void invokeMethodExceptionSafe(final Object methodOwner, final String method, final TypedObject... arguments) { if (methodOwner == null) return; try { final Class<?>[] types; final Object[] objects; if (arguments != null) { int length = arguments.length; types = new Class[length]; objects = new Object[length]; for (int i = 0; i < length; i++) { types[i] = arguments[i].getType(); objects[i] = arguments[i].getObject(); } } else { types = new Class[0]; objects = new Object[0]; } final Method declaredMethod = methodOwner.getClass().getDeclaredMethod(method, types); declaredMethod.setAccessible(true); declaredMethod.invoke(methodOwner, objects); } catch (Throwable ignored) { /* unused */ } } } }