package ru.ltst.u2020mvp.ui.debug; import android.app.Activity; import android.app.Application; import android.content.Context; import android.os.PowerManager; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.f2prateek.rx.preferences.Preference; import com.jakewharton.madge.MadgeFrameLayout; import com.jakewharton.scalpel.ScalpelFrameLayout; import javax.inject.Inject; import butterknife.Bind; import butterknife.ButterKnife; import ru.ltst.u2020mvp.ApplicationScope; import ru.ltst.u2020mvp.R; import ru.ltst.u2020mvp.data.PixelGridEnabled; import ru.ltst.u2020mvp.data.PixelRatioEnabled; import ru.ltst.u2020mvp.data.ScalpelEnabled; import ru.ltst.u2020mvp.data.ScalpelWireframeEnabled; import ru.ltst.u2020mvp.data.SeenDebugDrawer; import ru.ltst.u2020mvp.ui.ActivityHierarchyServer; import ru.ltst.u2020mvp.ui.ViewContainer; import rx.subscriptions.CompositeSubscription; import static android.content.Context.POWER_SERVICE; import static android.os.PowerManager.ACQUIRE_CAUSES_WAKEUP; import static android.os.PowerManager.FULL_WAKE_LOCK; import static android.os.PowerManager.ON_AFTER_RELEASE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; /** * An {@link ViewContainer} for debug builds which wraps a sliding drawer on the right that holds * all of the debug information and settings. */ @ApplicationScope public final class DebugViewContainer implements ViewContainer { private final Preference<Boolean> seenDebugDrawer; private final Preference<Boolean> pixelGridEnabled; private final Preference<Boolean> pixelRatioEnabled; private final Preference<Boolean> scalpelEnabled; private final Preference<Boolean> scalpelWireframeEnabled; static class ViewHolder { @Bind(R.id.debug_drawer_layout) DrawerLayout drawerLayout; @Bind(R.id.debug_drawer) ViewGroup debugDrawer; @Bind(R.id.madge_container) MadgeFrameLayout madgeFrameLayout; @Bind(R.id.debug_content) ScalpelFrameLayout content; } @Inject public DebugViewContainer(@SeenDebugDrawer Preference<Boolean> seenDebugDrawer, @PixelGridEnabled Preference<Boolean> pixelGridEnabled, @PixelRatioEnabled Preference<Boolean> pixelRatioEnabled, @ScalpelEnabled Preference<Boolean> scalpelEnabled, @ScalpelWireframeEnabled Preference<Boolean> scalpelWireframeEnabled) { this.seenDebugDrawer = seenDebugDrawer; this.pixelGridEnabled = pixelGridEnabled; this.pixelRatioEnabled = pixelRatioEnabled; this.scalpelEnabled = scalpelEnabled; this.scalpelWireframeEnabled = scalpelWireframeEnabled; } @Override public ViewGroup forActivity(final Activity activity) { activity.setContentView(R.layout.debug_activity_frame); final ViewHolder viewHolder = new ViewHolder(); ButterKnife.bind(viewHolder, activity); final Context drawerContext = new ContextThemeWrapper(activity, R.style.Theme_U2020_Debug); final DebugView debugView = new DebugView(drawerContext); viewHolder.debugDrawer.addView(debugView); // Set up the contextual actions to watch views coming in and out of the content area. ContextualDebugActions contextualActions = debugView.getContextualDebugActions(); contextualActions.setActionClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewHolder.drawerLayout.closeDrawers(); } }); viewHolder.content.setOnHierarchyChangeListener(HierarchyTreeChangeListener.wrap(contextualActions)); viewHolder.drawerLayout.setDrawerShadow(R.drawable.debug_drawer_shadow, GravityCompat.END); viewHolder.drawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener() { @Override public void onDrawerOpened(View drawerView) { debugView.onDrawerOpened(); } }); // If you have not seen the debug drawer before, show it with a message if (!seenDebugDrawer.get()) { viewHolder.drawerLayout.postDelayed(() -> { viewHolder.drawerLayout.openDrawer(GravityCompat.END); Toast.makeText(drawerContext, R.string.debug_drawer_welcome, Toast.LENGTH_LONG).show(); }, 1000); seenDebugDrawer.set(true); } final CompositeSubscription subscriptions = new CompositeSubscription(); setupMadge(viewHolder, subscriptions); setupScalpel(viewHolder, subscriptions); final Application app = activity.getApplication(); app.registerActivityLifecycleCallbacks(new ActivityHierarchyServer.Empty() { @Override public void onActivityDestroyed(Activity lifecycleActivity) { if (lifecycleActivity == activity) { subscriptions.unsubscribe(); app.unregisterActivityLifecycleCallbacks(this); } } }); riseAndShine(activity); return viewHolder.content; } private void setupMadge(final ViewHolder viewHolder, CompositeSubscription subscriptions) { subscriptions.add(pixelGridEnabled.asObservable().subscribe(enabled -> { viewHolder.madgeFrameLayout.setOverlayEnabled(enabled); })); subscriptions.add(pixelRatioEnabled.asObservable().subscribe(enabled -> { viewHolder.madgeFrameLayout.setOverlayRatioEnabled(enabled); })); } private void setupScalpel(final ViewHolder viewHolder, CompositeSubscription subscriptions) { subscriptions.add(scalpelEnabled.asObservable().subscribe(enabled -> { viewHolder.content.setLayerInteractionEnabled(enabled); })); subscriptions.add(scalpelWireframeEnabled.asObservable().subscribe(enabled -> { viewHolder.content.setDrawViews(!enabled); })); } /** * Show the activity over the lock-screen and wake up the device. If you launched the app manually * both of these conditions are already true. If you deployed from the IDE, however, this will * save you from hundreds of power button presses and pattern swiping per day! */ public static void riseAndShine(Activity activity) { activity.getWindow().addFlags(FLAG_SHOW_WHEN_LOCKED); PowerManager power = (PowerManager) activity.getSystemService(POWER_SERVICE); PowerManager.WakeLock lock = power.newWakeLock(FULL_WAKE_LOCK | ACQUIRE_CAUSES_WAKEUP | ON_AFTER_RELEASE, "wakeup!"); lock.acquire(); lock.release(); } }