/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.linkbubble;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.ValueCallback;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.linkbubble.physics.Draggable;
import com.linkbubble.ui.BubbleDraggable;
import com.linkbubble.ui.BubbleFlowDraggable;
import com.linkbubble.ui.BubbleFlowView;
import com.linkbubble.ui.CanvasView;
import com.linkbubble.ui.ExpandedActivity;
import com.linkbubble.ui.Prompt;
import com.linkbubble.ui.TabView;
import com.linkbubble.util.ActionItem;
import com.linkbubble.util.Analytics;
import com.linkbubble.util.AppPoller;
import com.linkbubble.util.CrashTracking;
import com.linkbubble.util.Util;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
public class MainController implements Choreographer.FrameCallback {
private static final String TAG = "MainController";
private static final int PIXELS_TO_SKIP_BEFORE_SCROLL = 50;
private static final int ANIMATION_DURATION_BUBBLES_HIDE = 850;
private static final int ANIMATION_DURATION_BUBBLES_SHOW = 765;
protected static MainController sInstance;
// Simple event classes used for the event bus
public static class BeginBubbleDragEvent {
}
public static class EndBubbleDragEvent {
}
public static class BeginAnimateFinalTabAwayEvent {
public TabView mTab;
}
public static class EndAnimateFinalTabAwayEvent {
}
public static class BeginExpandTransitionEvent {
public float mPeriod;
}
public static class EndExpandTransitionEvent {
}
public static class BeginCollapseTransitionEvent {
public BeginCollapseTransitionEvent() {
mFromCloseSystemDialogs = false;
}
public BeginCollapseTransitionEvent(boolean fromCloseSystemDialogs) {
mFromCloseSystemDialogs = fromCloseSystemDialogs;
}
public float mPeriod;
public boolean mFromCloseSystemDialogs;
}
public static class EndCollapseTransitionEvent {
}
public static class OrientationChangedEvent {
}
public static class CurrentTabChangedEvent {
public CurrentTabChangedEvent() {
mUnhideNotification = false;
}
public CurrentTabChangedEvent(TabView tabView, boolean unhideNotification) {
mTab = tabView;
mUnhideNotification = unhideNotification;
}
public TabView mTab;
public boolean mUnhideNotification;
}
public static class CurrentTabResumeEvent {
public CurrentTabResumeEvent() {
}
public CurrentTabResumeEvent(TabView tabView) {
mTab = tabView;
}
public TabView mTab;
}
public static class CurrentTabPauseEvent {
public CurrentTabPauseEvent() {
}
public CurrentTabPauseEvent(TabView tabView) {
mTab = tabView;
}
public TabView mTab;
}
public static class DraggableBubbleMovedEvent {
public int mX, mY;
}
public static class HideContentEvent {};
public static class UnhideContentEvent {};
public static class ScreenOnEvent {};
public static class ScreenOffEvent {};
public static class UserPresentEvent {};
public static void addRootWindow(View v, WindowManager.LayoutParams lp) {
MainController mc = get();
if (!mc.mRootViews.contains(v)) {
mc.mRootViews.add(v);
if (mc.mRootWindowsVisible) {
mc.mWindowManager.addView(v, lp);
}
}
}
public static void removeRootWindow(View v) {
MainController mc = get();
if (mc.mRootViews.contains(v)) {
mc.mRootViews.remove(v);
if (mc.mRootWindowsVisible) {
mc.mWindowManager.removeView(v);
}
}
}
public static void updateRootWindowLayout(View v, WindowManager.LayoutParams lp) {
MainController mc = get();
if (mc.mRootWindowsVisible && mc.mRootViews.contains(v)) {
mc.mWindowManager.updateViewLayout(v, lp);
}
}
private void enableRootWindows() {
if (!mRootWindowsVisible) {
for (View v : mRootViews) {
WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
//lp.alpha = 1.0f;
//mWindowManager.updateViewLayout(v, lp);
mWindowManager.addView(v, lp);
// Hack to ensure BubbleFlowDraggable doesn't display in Bubble mode, fix #457
if (v instanceof BubbleFlowView) {
((BubbleFlowView)v).forceCollapseEnd();
((BubbleFlowDraggable)v).setCurrentTabAsActive();
}
}
mRootWindowsVisible = true;
}
}
private void disableRootWindows() {
if (mRootWindowsVisible) {
for (View v : mRootViews) {
//WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
//lp.alpha = 0.5f;
//mWindowManager.updateViewLayout(v, lp);
// Hack to ensure BubbleFlowDraggable doesn't display in Bubble mode, fix #457
if (v instanceof BubbleFlowView) {
((BubbleFlowView)v).forceCollapseEnd();
}
mWindowManager.removeView(v);
}
mRootWindowsVisible = false;
}
}
private Vector<View> mRootViews = new Vector<View>();
private boolean mRootWindowsVisible = true;
private WindowManager mWindowManager;
private OrientationChangedEvent mOrientationChangedEvent = new OrientationChangedEvent();
private BeginExpandTransitionEvent mBeginExpandTransitionEvent = new BeginExpandTransitionEvent();
private ExpandedActivity.MinimizeExpandedActivityEvent mMinimizeExpandedActivityEvent = new ExpandedActivity.MinimizeExpandedActivityEvent();
private UserPresentEvent mUserPresentEvent = new UserPresentEvent();
private ScreenOnEvent mScreenOnEvent = new ScreenOnEvent();
private ScreenOffEvent mScreenOffEvent = new ScreenOffEvent();
private boolean mScreenOn = true;
private java.util.Timer mTimer = null;
private boolean mHeightSizeTopMargin = false;
private static class OpenUrlInfo {
String mUrlAsString;
long mStartTime;
OpenUrlInfo(String url, long startTime) {
mUrlAsString = url;
mStartTime = startTime;
}
};
private ArrayList<OpenUrlInfo> mOpenUrlInfos = new ArrayList<OpenUrlInfo>();
// End of event bus classes
public static MainController get() {
return sInstance;
}
public static void create(Context context, EventHandler eventHandler) {
if (sInstance != null) {
throw new RuntimeException("Only one instance of MainController allowed at any one time");
}
sInstance = new MainController(context, eventHandler);
}
public static void destroy() {
if (sInstance == null) {
throw new RuntimeException("No instance to destroy");
}
Settings.get().saveData();
MainApplication app = (MainApplication) sInstance.mContext.getApplicationContext();
Bus bus = app.getBus();
bus.unregister(sInstance);
if (Settings.get().isIncognitoMode()) {
CookieManager cookieManager = CookieManager.getInstance();
if (cookieManager != null && cookieManager.hasCookies()) {
cookieManager.removeAllCookie();
}
}
if (Constant.PROFILE_FPS) {
sInstance.mWindowManager.removeView(sInstance.mTextView);
}
sInstance.mBubbleDraggable.destroy();
sInstance.mBubbleFlowDraggable.destroy();
sInstance.mCanvasView.destroy();
sInstance.mChoreographer.removeFrameCallback(sInstance);
sInstance.endAppPolling();
sInstance = null;
}
public interface EventHandler {
public void onDestroy();
}
protected EventHandler mEventHandler;
protected int mBubblesLoaded;
private AppPoller mAppPoller;
protected Context mContext;
private String mAppPackageName;
private Choreographer mChoreographer;
protected boolean mUpdateScheduled;
protected CanvasView mCanvasView;
private BubbleFlowDraggable mBubbleFlowDraggable;
private BubbleDraggable mBubbleDraggable;
private long mPreviousFrameTime;
private WindowManager.LayoutParams mOriginalBubbleFlowDraggableParams;
private float mOriginalLocationY = 0;
private int mPreviousBubbleAdjustmentValue = 0;
private float mCurrentAdjustment = 0;
// false if the user has forcibilty minimized the Bubbles from ContentView. Set back to true once a new link is loaded.
private boolean mCanAutoDisplayLink;
BubbleFlowView.AnimationEventListener mOnBubbleFlowExpandFinishedListener = new BubbleFlowView.AnimationEventListener() {
@Override
public void onAnimationEnd(BubbleFlowView sender) {
TabView currentTab = ((BubbleFlowDraggable)sender).getCurrentTab();
if (currentTab != null && currentTab.getContentView() != null) {
currentTab.getContentView().saveLoadTime();
}
}
};
BubbleFlowView.AnimationEventListener mOnBubbleFlowCollapseFinishedListener = new BubbleFlowView.AnimationEventListener() {
@Override
public void onAnimationEnd(BubbleFlowView sender) {
onBubbleFlowCollapseFinished();
}
};
private void onBubbleFlowCollapseFinished() {
mBubbleDraggable.setVisibility(View.VISIBLE);
TabView tab = mBubbleFlowDraggable.getCurrentTab();
if (tab != null) {
tab.setImitator(mBubbleDraggable);
}
mSetBubbleFlowGone = true;
mBubbleFlowDraggable.postDelayed(mSetBubbleFlowGoneRunnable, 33);
}
private boolean mSetBubbleFlowGone = false;
Runnable mSetBubbleFlowGoneRunnable = new Runnable() {
@Override
public void run() {
if (mSetBubbleFlowGone) {
mBubbleFlowDraggable.setVisibility(View.GONE);
}
}
};
Runnable mSetBubbleGoneRunnable = new Runnable() {
@Override
public void run() {
mBubbleDraggable.setVisibility(View.GONE);
}
};
/*
* Pass all the input along to mBubbleDraggable
*/
BubbleFlowView.TouchInterceptor mBubbleFlowTouchInterceptor = new BubbleFlowView.TouchInterceptor() {
@Override
public boolean onTouchActionDown(MotionEvent event) {
return mBubbleDraggable.getDraggableHelper().onTouchActionDown(event);
}
@Override
public boolean onTouchActionMove(MotionEvent event) {
return mBubbleDraggable.getDraggableHelper().onTouchActionMove(event);
}
@Override
public boolean onTouchActionUp(MotionEvent event) {
boolean result = mBubbleDraggable.getDraggableHelper().onTouchActionUp(event);
mBubbleFlowDraggable.setTouchInterceptor(null);
return result;
}
};
protected MainController(Context context, EventHandler eventHandler) {
Util.Assert(sInstance == null, "non-null instance");
sInstance = this;
mContext = context;
mAppPackageName = mContext.getPackageName();
mEventHandler = eventHandler;
mAppPoller = new AppPoller(context);
mAppPoller.setListener(mAppPollerListener);
mCanAutoDisplayLink = true;
mCanDisplay = true;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (Constant.PROFILE_FPS) {
mTextView = new TextView(mContext);
mTextView.setTextColor(0xff00ffff);
mTextView.setTextSize(32.0f);
mWindowManagerParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowManagerParams.x = 500;
mWindowManagerParams.y = 16;
mWindowManagerParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowManagerParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowManagerParams.type = WindowManager.LayoutParams.TYPE_PHONE;
mWindowManagerParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
mWindowManagerParams.format = PixelFormat.TRANSPARENT;
mWindowManagerParams.setTitle("LinkBubble: Debug Text");
mWindowManager.addView(mTextView, mWindowManagerParams);
}
mUpdateScheduled = false;
mChoreographer = Choreographer.getInstance();
mCanvasView = new CanvasView(mContext);
MainApplication app = (MainApplication) mContext.getApplicationContext();
Bus bus = app.getBus();
bus.register(this);
updateIncognitoMode(Settings.get().isIncognitoMode());
LayoutInflater inflater = LayoutInflater.from(mContext);
mBubbleDraggable = (BubbleDraggable) inflater.inflate(R.layout.view_bubble_draggable, null);
Point bubbleRestingPoint = Settings.get().getBubbleRestingPoint();
int fromX = Settings.get().getBubbleStartingX(bubbleRestingPoint);
mBubbleDraggable.configure(fromX, bubbleRestingPoint.y, bubbleRestingPoint.x, bubbleRestingPoint.y,
Constant.BUBBLE_SLIDE_ON_SCREEN_TIME, mCanvasView);
mBubbleDraggable.setOnUpdateListener(new BubbleDraggable.OnUpdateListener() {
@Override
public void onUpdate(Draggable draggable, float dt) {
if (!draggable.isDragging()) {
mBubbleFlowDraggable.syncWithBubble(draggable);
}
}
});
mBubbleFlowDraggable = (BubbleFlowDraggable) inflater.inflate(R.layout.view_bubble_flow, null);
mBubbleFlowDraggable.configure(null);
mBubbleFlowDraggable.collapse(0, null);
mBubbleFlowDraggable.setBubbleDraggable(mBubbleDraggable);
mBubbleFlowDraggable.setVisibility(View.GONE);
mOriginalBubbleFlowDraggableParams = (WindowManager.LayoutParams)mBubbleFlowDraggable.getLayoutParams();
mOriginalLocationY = mBubbleFlowDraggable.getChildAt(0).getY();
mBubbleDraggable.setBubbleFlowDraggable(mBubbleFlowDraggable);
}
/*
* Begin the destruction process.
*/
public void finish() {
mEventHandler.onDestroy();
}
private TextView mTextView;
private WindowManager.LayoutParams mWindowManagerParams = new WindowManager.LayoutParams();
public void adjustBubblesPanel(int newY, int oldY, boolean afterTouchAdjust, boolean resetToOriginal) {
Settings settings = Settings.get();
if (null != settings && !settings.isHideBubbles()) {
return;
}
TabView currentTabView = getCurrentTab();
if (null == mOriginalBubbleFlowDraggableParams || null == currentTabView || !afterTouchAdjust && !resetToOriginal &&
(mPreviousBubbleAdjustmentValue - newY > (0 - PIXELS_TO_SKIP_BEFORE_SCROLL) &&
mPreviousBubbleAdjustmentValue - newY < PIXELS_TO_SKIP_BEFORE_SCROLL)) {
return;
}
final int toolbarHeight = currentTabView.toolbarHeight();
int adjustOn = newY - mPreviousBubbleAdjustmentValue;
if (afterTouchAdjust) {
adjustOn = 0;
}
FrameLayout.LayoutParams currentParams = null;
try {
currentParams = (FrameLayout.LayoutParams) mBubbleFlowDraggable.getChildAt(0).getLayoutParams();
}
catch (NullPointerException exc) {
}
if (null == currentParams) {
return;
}
float oldAdjustment = mCurrentAdjustment;
if (!afterTouchAdjust && 0 == oldY &&
adjustOn > (0 - PIXELS_TO_SKIP_BEFORE_SCROLL) && adjustOn < PIXELS_TO_SKIP_BEFORE_SCROLL || resetToOriginal) {
mCurrentAdjustment = mOriginalLocationY;
mHeightSizeTopMargin = false;
}
else if (0 == adjustOn) {
int half = (mOriginalBubbleFlowDraggableParams.height + currentParams.height) / 2;
int third = (mOriginalBubbleFlowDraggableParams.height + currentParams.height) / 3;
if (0 - mCurrentAdjustment > third && mPreviousBubbleAdjustmentValue > half) {
mCurrentAdjustment = 0 - (mOriginalBubbleFlowDraggableParams.height + currentParams.height + toolbarHeight);
mHeightSizeTopMargin = true;
}
else {
mCurrentAdjustment = mOriginalLocationY;
mHeightSizeTopMargin = false;
}
}
else {
mCurrentAdjustment += 0 - adjustOn;
if (mCurrentAdjustment + (mOriginalBubbleFlowDraggableParams.height + currentParams.height + toolbarHeight) < 0) {
mCurrentAdjustment = 0 - (mOriginalBubbleFlowDraggableParams.height + currentParams.height + toolbarHeight);
mHeightSizeTopMargin = true;
} else if (mCurrentAdjustment > mOriginalLocationY) {
mCurrentAdjustment = mOriginalLocationY;
mHeightSizeTopMargin = false;
}
}
if (0 != adjustOn) {
mPreviousBubbleAdjustmentValue = newY;
}
if (mCurrentAdjustment == oldAdjustment) {
return;
}
int animationDuration = ANIMATION_DURATION_BUBBLES_HIDE;
if (adjustOn < 0 ) {
animationDuration = ANIMATION_DURATION_BUBBLES_SHOW;
}
if (currentTabView.adjustBubblesPanel(mCurrentAdjustment, mHeightSizeTopMargin, animationDuration)) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mBubbleFlowDraggable.getChildAt(0), "translationY", mCurrentAdjustment)
.setDuration(animationDuration);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
if (!mHeightSizeTopMargin) {
if (mBubbleFlowDraggable.isExpanded()) {
mBubbleFlowDraggable.setVisibility(View.VISIBLE);
}
}
}
@Override
public void onAnimationEnd(Animator animator) {
if (!mHeightSizeTopMargin) {
if (mBubbleFlowDraggable.isExpanded()) {
mBubbleFlowDraggable.setVisibility(View.VISIBLE);
}
} else {
TabView view = mBubbleFlowDraggable.getCurrentTab();
if (null != view && !view.mIsClosing) {
mBubbleFlowDraggable.setVisibility(View.GONE);
}
}
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animator.start();
}
}
public void onPageLoaded(TabView tab, boolean withError) {
// Ensure this is not an edge case where the Tab has already been destroyed, re #254
if (getActiveTabCount() == 0 || isTabActive(tab) == false) {
return;
}
// Debounce the saving call so we don't attempt to save after every concurrent page load, causing potential problems with the database connection.
if (mTimer != null) {
try {
mTimer.cancel();
mTimer.purge();
}
catch (NullPointerException exc) {
// We can have a crash here sometimes when several pages loaded at one time, some of threads will do those calls in any case
}
}
mTimer = new java.util.Timer();
java.util.TimerTask tt = new java.util.TimerTask() {
@Override
public void run() {
mTimer = null;
saveCurrentTabs();
};
};
mTimer.schedule(tt, 200);
}
public void autoContentDisplayLinkLoaded(TabView tab) {
// Ensure this is not an edge case where the Tab has already been destroyed, re #254
if (getActiveTabCount() == 0 || isTabActive(tab) == false) {
return;
}
if (Settings.get().getAutoContentDisplayLinkLoaded()) {
displayTab(tab);
}
}
public boolean displayTab(TabView tab) {
if (!mBubbleDraggable.isDragging() && mCanAutoDisplayLink) {
switch (mBubbleDraggable.getCurrentMode()) {
case BubbleView:
mBubbleFlowDraggable.setCenterItem(tab);
mBubbleDraggable.switchToExpandedView();
return true;
}
}
return false;
}
public void saveCurrentTabs() {
if (mBubbleFlowDraggable != null) {
mBubbleFlowDraggable.saveCurrentTabs();
}
}
public void updateIncognitoMode(boolean incognito) {
CookieSyncManager.createInstance(mContext);
CookieManager.getInstance().setAcceptCookie(true);
if (mBubbleFlowDraggable != null) {
mBubbleFlowDraggable.updateIncognitoMode(incognito);
}
}
@SuppressWarnings("unused")
@Subscribe
public void onEndCollapseTransition(EndCollapseTransitionEvent e) {
showBadge(true);
}
@SuppressWarnings("unused")
@Subscribe
public void onBeginExpandTransition(MainController.BeginExpandTransitionEvent e) {
showBadge(false);
}
@SuppressWarnings("unused")
@Subscribe
public void onEndExpandTransition(MainController.EndExpandTransitionEvent e) {
if (Constant.ACTIVITY_WEBVIEW_RENDERING == false) {
showExpandedActivity();
}
}
@SuppressWarnings("unused")
@Subscribe
public void onExpandedActivityReadyEvent(ExpandedActivity.ExpandedActivityReadyEvent event) {
if (mDeferredExpandBubbleFlowTime > -1) {
doExpandBubbleFlow(mDeferredExpandBubbleFlowTime, mDeferredExpandBubbleFlowHideDraggable);
mDeferredExpandBubbleFlowTime = -1;
mDeferredExpandBubbleFlowHideDraggable = false;
}
}
static public long sStartExpandedActivityTime = -1;
public void showExpandedActivity() {
Log.e(TAG, "showExpandedActivity()");
sStartExpandedActivityTime = System.currentTimeMillis();
Intent intent = new Intent(mContext, ExpandedActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContext.getApplicationContext().startActivity(intent);
}
public void scheduleUpdate() {
if (!mUpdateScheduled) {
mUpdateScheduled = true;
mChoreographer.postFrameCallback(this);
}
}
// TODO: think of a better name
public void startDraggingFromContentView() {
// When we start dragging, configure the BubbleFlowView to pass all its input to our TouchInterceptor so we
// can re-route it to the BubbleDraggable. This is a bit messy, but necessary so as to cleanly using the same
// MotionEvent chain for the BubbleFlowDraggable and BubbleDraggable so the items visually sync up.
mBubbleFlowDraggable.setTouchInterceptor(mBubbleFlowTouchInterceptor);
mBubbleFlowDraggable.collapse(Constant.BUBBLE_ANIM_TIME, mOnBubbleFlowCollapseFinishedListener);
mBubbleDraggable.setVisibility(View.VISIBLE);
if (mBubbleFlowDraggable.getTouchInterceptor() != null) {
mBubbleFlowDraggable.setInterceptingTouch(true);
}
}
public BubbleDraggable getBubbleDraggable() {
return mBubbleDraggable;
}
public int getActiveTabCount() {
return mBubbleFlowDraggable != null ? mBubbleFlowDraggable.getActiveTabCount() : 0;
}
public int getVisibleTabCount() {
return mBubbleFlowDraggable != null ? mBubbleFlowDraggable.getVisibleTabCount() : 0;
}
public boolean isUrlActive(String urlAsString) {
return mBubbleFlowDraggable != null ? mBubbleFlowDraggable.isUrlActive(urlAsString) : false;
}
public boolean wasUrlRecentlyLoaded(String urlAsString, long urlLoadStartTime) {
for (OpenUrlInfo openUrlInfo : mOpenUrlInfos) {
long delta = urlLoadStartTime - openUrlInfo.mStartTime;
if (openUrlInfo.mUrlAsString.equals(urlAsString) && delta < 7 * 1000) {
//Log.d("blerg", "urlAsString:" + urlAsString + ", openUrlInfo.mUrlAsString:" + openUrlInfo.mUrlAsString + ", delta: " + delta);
if (mBubbleFlowDraggable != null && mBubbleFlowDraggable.isUrlActive(urlAsString)) {
return true;
}
}
}
return false;
}
public int getTabIndex(TabView tab) {
return mBubbleFlowDraggable != null ? mBubbleFlowDraggable.getIndexOfView(tab) : -1;
}
public boolean isTabActive(TabView tab) {
int index = getTabIndex(tab);
if (index > -1) {
return true;
}
return false;
}
private static final int MAX_SAMPLE_COUNT = 60 * 10;
private static final float MAX_VALID_TIME = 10.0f / 60.0f;
private float [] mSamples = new float[MAX_SAMPLE_COUNT];
private int mSampleCount = 0;
public void doFrame(long frameTimeNanos) {
mUpdateScheduled = false;
//if (mHiddenByUser == true) {
// return;
//}
float t0 = mPreviousFrameTime / 1000000000.0f;
float t1 = frameTimeNanos / 1000000000.0f;
float t = t1 - t0;
mPreviousFrameTime = frameTimeNanos;
float dt;
if (Constant.DYNAMIC_ANIM_STEP) {
dt = Util.clamp(0.0f, t, 3.0f / 60.0f);
} else {
dt = 1.0f / 60.0f;
}
if (mBubbleFlowDraggable.update()) {
scheduleUpdate();
}
mBubbleDraggable.update(dt);
mCanvasView.update(dt);
if (getActiveTabCount() == 0 && mBubblesLoaded > 0 && !mUpdateScheduled) {
// Will be non-zero in the event a link has been dismissed by a user, but its TabView
// instance is still animating off screen. In that case, keep triggering an update so that when the
// item finishes, we are ready to call onDestroy().
if (mBubbleFlowDraggable.getVisibleTabCount() == 0 && Prompt.isShowing() == false) {
finish();
} else {
scheduleUpdate();
}
}
if (mHiddenByUser == false) {
updateKeyguardLocked();
}
if (Constant.PROFILE_FPS) {
if (t < MAX_VALID_TIME) {
mSamples[mSampleCount % MAX_SAMPLE_COUNT] = t;
++mSampleCount;
}
float total = 0.0f;
float worst = 0.0f;
float best = 99999999.0f;
int badFrames = 0;
int frameCount = Math.min(mSampleCount, MAX_SAMPLE_COUNT);
for (int i = 0; i < frameCount; ++i) {
total += mSamples[i];
worst = Math.max(worst, mSamples[i]);
best = Math.min(best, mSamples[i]);
if (mSamples[i] > 1.5f / 60.0f) {
++badFrames;
}
}
String sbest = String.format("%.2f", 1000.0f * best);
String sworst = String.format("%.2f", 1000.0f * worst);
String savg = String.format("%.2f", 1000.0f * total / frameCount);
String badpc = String.format("%.2f", 100.0f * badFrames / frameCount);
String s = "Best=" + sbest + "\nWorst=" + sworst + "\nAvg=" + savg + "\nBad=" + badFrames + "\nBad %=" + badpc + "%";
mTextView.setSingleLine(false);
mTextView.setText(s);
scheduleUpdate();
}
}
public void onCloseSystemDialogs() {
long delta = System.currentTimeMillis() - mLastOpenTabFromNotificationTime;
// Intent.ACTION_CLOSE_SYSTEM_DIALOGS gets triggered when NotificationOpenTabActivity is instantiated. Ignore that to stop minimizing...
if (delta < 200) {
return;
}
switchToBubbleView(true);
}
// Before this version select elements would crash WebView in a background service
// STABLE_SELECT_WEBVIEW_VERSIONCODE used to be set to 249007650 because drop downs
// worked on some devices but not all. For example it works on a Samsung Galaxy S4 but not
// on Nexus 6P. Hopefully the next update will work across all devices.
public static final long STABLE_SELECT_WEBVIEW_VERSIONCODE = Long.MAX_VALUE;
static private long mVersionNumber = 0;
public long getWebviewVersion(Context context) {
if (mVersionNumber != 0) {
return mVersionNumber;
}
PackageManager pm = context.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo("com.google.android.webview", 0);
mVersionNumber = pi.versionCode;
} catch (PackageManager.NameNotFoundException e) {
mVersionNumber = -1;
}
return mVersionNumber;
}
public boolean hasStableWebViewForSelects(Context context) {
return getWebviewVersion(context) >= STABLE_SELECT_WEBVIEW_VERSIONCODE;
}
public void onOrientationChanged() {
Config.init(mContext);
Settings.get().onOrientationChange();
mBubbleDraggable.onOrientationChanged();
mBubbleFlowDraggable.onOrientationChanged();
MainApplication.postEvent(mContext, mOrientationChangedEvent);
}
private boolean handleResolveInfo(ResolveInfo resolveInfo, String urlAsString, long urlLoadStartTime) {
if (Settings.get().didRecentlyRedirectToApp(urlAsString)) {
return false;
}
boolean isLinkBubble = resolveInfo.activityInfo != null
&& resolveInfo.activityInfo.packageName.equals(mAppPackageName);
if (isLinkBubble == false && MainApplication.loadResolveInfoIntent(mContext, resolveInfo, urlAsString, -1)) {
if (getActiveTabCount() == 0 && Prompt.isShowing() == false) {
finish();
}
String title = String.format(mContext.getString(R.string.link_loaded_with_app),
resolveInfo.loadLabel(mContext.getPackageManager()));
MainApplication.saveUrlInHistory(mContext, resolveInfo, urlAsString, title);
Settings.get().addRedirectToApp(urlAsString);
Settings.get().trackLinkLoadTime(System.currentTimeMillis() - urlLoadStartTime, Settings.LinkLoadType.AppRedirectInstant, urlAsString);
return true;
}
return false;
}
public TabView openUrl(final String urlAsString, long urlLoadStartTime, final boolean setAsCurrentTab,
String openedFromAppName) {
Analytics.trackOpenUrl(openedFromAppName);
if (wasUrlRecentlyLoaded(urlAsString, urlLoadStartTime) && !urlAsString.equals(mContext.getString(R.string.empty_bubble_page))) {
Toast.makeText(mContext, R.string.duplicate_link_will_not_be_loaded, Toast.LENGTH_SHORT).show();
return null;
}
URL url;
try {
url = new URL(urlAsString);
} catch (MalformedURLException e) { // If this is not a valid scheme, back out. #271
Toast.makeText(mContext, mContext.getString(R.string.unsupported_scheme), Toast.LENGTH_SHORT).show();
if (getActiveTabCount() == 0 && Prompt.isShowing() == false) {
finish();
}
return null;
}
if (Settings.get().redirectUrlToBrowser(url)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(urlAsString));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (MainApplication.openInBrowser(mContext, intent, false, false)) {
if (getActiveTabCount() == 0 && Prompt.isShowing() == false) {
finish();
}
String title = String.format(mContext.getString(R.string.link_redirected), Settings.get().getDefaultBrowserLabel());
MainApplication.saveUrlInHistory(mContext, null, urlAsString, title);
return null;
}
}
boolean showAppPicker = false;
PackageManager packageManager = mContext.getPackageManager();
String urlString = urlAsString.toString();
List<ResolveInfo> tempResolveInfos = new ArrayList<>();
if (!urlString.equals(mContext.getString(R.string.empty_bubble_page))) {
tempResolveInfos = Settings.get().getAppsThatHandleUrl(urlString, packageManager);
}
final List<ResolveInfo> resolveInfos = tempResolveInfos;
ResolveInfo defaultAppResolveInfo = Settings.get().getDefaultAppForUrl(url, resolveInfos);
if (resolveInfos != null && resolveInfos.size() > 0) {
if (defaultAppResolveInfo != null) {
if (handleResolveInfo(defaultAppResolveInfo, urlAsString, urlLoadStartTime)) {
return null;
}
} else if (resolveInfos.size() == 1) {
if (handleResolveInfo(resolveInfos.get(0), urlAsString, urlLoadStartTime)) {
return null;
}
} else {
// If LinkBubble is a valid resolve target, do not show other options to open the content.
for (ResolveInfo info : resolveInfos) {
if (info.activityInfo.packageName.startsWith("com.linkbubble.playstore")
|| info.activityInfo.packageName.startsWith("com.brave.playstore")) {
showAppPicker = false;
break;
} else {
showAppPicker = true;
}
}
}
}
boolean openedFromItself = false;
if (null != openedFromAppName && (openedFromAppName.equals(Analytics.OPENED_URL_FROM_NEW_TAB)
|| openedFromAppName.equals(Analytics.OPENED_URL_FROM_HISTORY))) {
showAppPicker = true;
openedFromItself = true;
}
mCanAutoDisplayLink = true;
final TabView result = openUrlInTab(urlAsString, urlLoadStartTime, setAsCurrentTab, showAppPicker,
!(null == openedFromAppName ? false : openedFromAppName.equals(Analytics.OPENED_URL_FROM_MAIN_NEW_TAB)));
// Show app picker after creating the tab to load so that we have the instance to close if redirecting to an app, re #292.
if (!openedFromItself && showAppPicker && MainApplication.sShowingAppPickerDialog == false && 0 != resolveInfos.size()) {
AlertDialog dialog = ActionItem.getActionItemPickerAlert(mContext, resolveInfos, R.string.pick_default_app,
new ActionItem.OnActionItemDefaultSelectedListener() {
@Override
public void onSelected(ActionItem actionItem, boolean always) {
boolean loaded = false;
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo.activityInfo.packageName.equals(actionItem.mPackageName)
&& resolveInfo.activityInfo.name.equals(actionItem.mActivityClassName)) {
if (always) {
Settings.get().setDefaultApp(urlAsString, resolveInfo);
}
// Jump out of the loop and load directly via a BubbleView below
if (resolveInfo.activityInfo.packageName.equals(mAppPackageName)) {
break;
}
loaded = MainApplication.loadIntent(mContext, actionItem.mPackageName,
actionItem.mActivityClassName, urlAsString, -1, true);
break;
}
}
if (loaded) {
Settings.get().addRedirectToApp(urlAsString);
closeTab(result, contentViewShowing(), false);
if (getActiveTabCount() == 0 && Prompt.isShowing() == false) {
finish();
}
// L_WATCH: L currently lacks getRecentTasks(), so minimize here
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
MainController.get().switchToBubbleView(false);
}
}
}
});
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
MainApplication.sShowingAppPickerDialog = false;
}
});
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
Util.showThemedDialog(dialog);
MainApplication.sShowingAppPickerDialog = true;
}
return result;
}
protected TabView openUrlInTab(String url, long urlLoadStartTime, boolean setAsCurrentTab, boolean hasShownAppPicker,
boolean performEmptyClick) {
setHiddenByUser(false);
if (getActiveTabCount() == 0) {
mBubbleDraggable.setVisibility(View.VISIBLE);
collapseBubbleFlow(0);
mBubbleFlowDraggable.setVisibility(View.GONE);
// Only do this snap if ContentView is showing. No longer obliterates slide-in animation
if (contentViewShowing()) {
mBubbleDraggable.snapToBubbleView();
} else {
Point bubbleRestingPoint = Settings.get().getBubbleRestingPoint();
int fromX = Settings.get().getBubbleStartingX(bubbleRestingPoint);
mBubbleDraggable.slideOnScreen(fromX, bubbleRestingPoint.y, bubbleRestingPoint.x, bubbleRestingPoint.y,
Constant.BUBBLE_SLIDE_ON_SCREEN_TIME);
}
}
TabView result = mBubbleFlowDraggable.openUrlInTab(url, urlLoadStartTime, setAsCurrentTab, hasShownAppPicker, performEmptyClick);
showBadge(getActiveTabCount() > 1 ? true : false);
++mBubblesLoaded;
mOpenUrlInfos.add(new OpenUrlInfo(url, urlLoadStartTime));
return result;
}
protected void restoreTab(TabView tab) {
mBubbleFlowDraggable.restoreTab(tab);
// Only do this if there's just 1 tab open. Fix #446
if (getActiveTabCount() == 1) {
// If the bubble was closed when in BubbleView mode, forcibly reset to Bubble mode
if (mBubbleDraggable.getCurrentMode() == BubbleDraggable.Mode.BubbleView) {
mBubbleDraggable.setVisibility(View.VISIBLE);
collapseBubbleFlow(0);
mBubbleFlowDraggable.setVisibility(View.GONE);
// Ensure CanvasView has a valid ContentView
CurrentTabChangedEvent event = new CurrentTabChangedEvent();
event.mTab = tab;
MainApplication.postEvent(mContext, event);
mBubbleDraggable.snapToBubbleView();
} else {
final float bubblePeriod = (float) Constant.BUBBLE_ANIM_TIME / 1000.f;
final float contentPeriod = bubblePeriod * 0.666667f; // 0.66667 is the normalized t value when f = 1.0f for overshoot interpolator of 0.5 tension
expandBubbleFlow((long) (contentPeriod * 1000), false);
if (Constant.ACTIVITY_WEBVIEW_RENDERING == false) {
// No need to do this if above is true because it's already done
showExpandedActivity();
}
}
} else {
showBadge(true);
}
++mBubblesLoaded;
}
public void showBadge(boolean show) {
if (mBubbleDraggable != null) {
int tabCount = mBubbleFlowDraggable.getActiveTabCount();
mBubbleDraggable.mBadgeView.setCount(tabCount);
if (show) {
if (tabCount > 1 && mBubbleDraggable.getCurrentMode() == BubbleDraggable.Mode.BubbleView) {
mBubbleDraggable.mBadgeView.show();
}
} else {
mBubbleDraggable.mBadgeView.hide();
}
}
}
public boolean contentViewShowing() {
return mBubbleDraggable != null && mBubbleDraggable.getCurrentMode() == BubbleDraggable.Mode.ContentView;
}
private long mLastOpenTabFromNotificationTime = -1;
public void openTabFromNotification(int notificationId) {
mLastOpenTabFromNotificationTime = System.currentTimeMillis();
if (mBubbleFlowDraggable != null) {
boolean contentViewShowing = contentViewShowing();
mBubbleFlowDraggable.setCurrentTabByNotification(notificationId, contentViewShowing);
if (!contentViewShowing) {
mBubbleDraggable.switchToExpandedView();
}
}
}
public void startFileBrowser(String[] acceptTypes, ValueCallback<Uri[]> filePathCallback) {
MainApplication.postEvent(mContext,
new ExpandedActivity.ShowFileBrowserEvent(acceptTypes, filePathCallback));
}
public boolean closeTab(int notificationId) {
if (mBubbleFlowDraggable != null) {
TabView tabView = mBubbleFlowDraggable.getTabByNotification(notificationId);
if (tabView != null) {
return closeTab(tabView, contentViewShowing() && mScreenOn, true);
}
}
return false;
}
public boolean closeCurrentTab(Constant.BubbleAction action, boolean animateOff) {
if (mBubbleFlowDraggable != null) {
return closeTab(mBubbleFlowDraggable.getCurrentTab(), action, animateOff, true);
}
return false;
}
public boolean closeTab(TabView tabView, boolean animateOff, boolean canShowUndoPrompt) {
return closeTab(tabView, Constant.BubbleAction.Close, animateOff, canShowUndoPrompt);
}
public boolean closeTab(TabView tabView, Constant.BubbleAction action, boolean animateOff, boolean canShowUndoPrompt) {
if (tabView == null) {
CrashTracking.log("closeTab attempt on null tabView");
return false;
}
// If the tab is already closing, do nothing. Otherwise we could end up in a weird state,
// where we attempt to show multiple prompts and crashing upon tab restore.
if (null == tabView || tabView.mIsClosing == true) {
CrashTracking.log("Ignoring duplicate tabView close request");
return false;
}
if (null != tabView) {
tabView.mIsClosing = true;
}
else {
CrashTracking.log("attempt to access on null tabView");
return false;
}
boolean contentViewShowing = contentViewShowing();
if (contentViewShowing && mBubbleFlowDraggable.getVisibility() == View.GONE && mBubbleFlowDraggable.isExpanded()) {
mBubbleFlowDraggable.setVisibility(View.VISIBLE);
}
CrashTracking.log("MainController.closeTab(): action:" + action.toString() + ", contentViewShowing:" + contentViewShowing
+ ", visibleTabCount:" + getVisibleTabCount() + ", activeTabCount:" + getActiveTabCount() + ", canShowUndoPrompt:" + canShowUndoPrompt
+ ", animateOff:" + animateOff + ", canShowUndoPrompt:" + canShowUndoPrompt);
if (mBubbleFlowDraggable != null) {
mBubbleFlowDraggable.closeTab(tabView, animateOff, action, tabView != null ? tabView.getTotalTrackedLoadTime() : -1);
}
int activeTabCount = getActiveTabCount();
if (activeTabCount > 0
&& null != mBubbleFlowDraggable.getCurrentTab()
&& mBubbleFlowDraggable.isExpanded()) {
adjustBubblesPanel(0, 0, false, true);
}
showBadge(activeTabCount > 1 ? true : false);
if (activeTabCount == 0) {
hideBubbleDraggable();
// Ensure BubbleFlowDraggable gets at least 1 update in the event items are animating off screen. See #237.
scheduleUpdate();
MainApplication.postEvent(mContext, mMinimizeExpandedActivityEvent);
}
if (tabView == null) {
CrashTracking.logHandledException(new RuntimeException("tabView = null"));
} else {
if (canShowUndoPrompt && Settings.get().getShowUndoCloseTab()) {
showClosePrompt(tabView);
} else {
destroyTabOnDelay(tabView);
}
}
return getActiveTabCount() > 0;
}
private void destroyTabOnDelay(final TabView tabView) {
mBubbleDraggable.postDelayed(new Runnable() {
@Override
public void run() {
tabView.destroy();
}
}, 500);
}
private void showClosePrompt(final TabView tabView) {
String title = null;
if (tabView.getUrl() != null && MainApplication.sTitleHashMap != null) {
String urlAsString = tabView.getUrl().toString();
title = MainApplication.sTitleHashMap.get(urlAsString);
}
String message;
if (title != null) {
message = String.format(mContext.getResources().getString(R.string.undo_close_tab_title), title);
} else {
message = mContext.getResources().getString(R.string.undo_close_tab_no_title);
}
tabView.mWasRestored = false;
Prompt.show(message,
mContext.getResources().getString(R.string.action_undo).toUpperCase(),
Prompt.LENGTH_SHORT,
true,
new Prompt.OnPromptEventListener() {
@Override
public void onActionClick() {
if (tabView.mWasRestored == false) {
tabView.mIsClosing = false;
restoreTab(tabView);
tabView.getContentView().onRestored();
}
}
@Override
public void onClose() {
if (tabView.mWasRestored == false) {
tabView.destroy();
}
}
}
);
}
public void closeAllBubbles() {
closeAllBubbles(true);
}
public void closeAllBubbles(boolean removeFromCurrentTabs) {
mBubbleFlowDraggable.closeAllBubbles(removeFromCurrentTabs);
hideBubbleDraggable();
}
private void hideBubbleDraggable() {
mBubbleDraggable.setVisibility(View.GONE);
}
private long mDeferredExpandBubbleFlowTime = -1;
private boolean mDeferredExpandBubbleFlowHideDraggable;
public void expandBubbleFlow(long time, boolean hideDraggable) {
if (Constant.ACTIVITY_WEBVIEW_RENDERING) {
mDeferredExpandBubbleFlowTime = time;
mDeferredExpandBubbleFlowHideDraggable = hideDraggable;
showExpandedActivity();
} else {
doExpandBubbleFlow(time, hideDraggable);
}
}
private void doExpandBubbleFlow(long time, boolean hideDraggable) {
mBeginExpandTransitionEvent.mPeriod = time / 1000.0f;
mBubbleFlowDraggable.setVisibility(View.VISIBLE);
mSetBubbleFlowGone = false; // cancel any pending operation to set visibility to GONE (see #190)
mBubbleFlowDraggable.expand(time, mOnBubbleFlowExpandFinishedListener);
MainApplication.postEvent(mContext, mBeginExpandTransitionEvent);
if (hideDraggable) {
mBubbleDraggable.postDelayed(mSetBubbleGoneRunnable, 33);
}
}
public void collapseBubbleFlow(long time) {
try {
mCurrentAdjustment = mOriginalLocationY;
TabView currentTabView = getCurrentTab();
if (null != currentTabView) {
if (currentTabView.adjustBubblesPanel(mCurrentAdjustment, false, 0)) {
ObjectAnimator
.ofFloat(mBubbleFlowDraggable.getChildAt(0), "translationY", mCurrentAdjustment)
.setDuration(ANIMATION_DURATION_BUBBLES_SHOW)
.start();
}
}
if (mBubbleFlowDraggable.isExpanded()) {
mBubbleFlowDraggable.setVisibility(View.VISIBLE);
}
}
catch (NullPointerException exc) {
}
mBubbleFlowDraggable.collapse(time, mOnBubbleFlowCollapseFinishedListener);
}
public void switchToBubbleView(boolean fromCloseSystemDialogs) {
mCanAutoDisplayLink = false;
if (MainController.get().getActiveTabCount() > 0) {
mBubbleDraggable.switchToBubbleView(fromCloseSystemDialogs);
} else {
// If there's no tabs, ensuring pressing Home will cause the CanvasView to go away. Fix #448
MainApplication.postEvent(mContext, new MainController.EndCollapseTransitionEvent());
}
}
public void switchToExpandedView() {
mBubbleDraggable.switchToExpandedView();
}
public void beginAppPolling() {
if (mAppPoller != null) {
mAppPoller.beginAppPolling();
}
}
public void endAppPolling() {
if (mAppPoller != null) {
mAppPoller.endAppPolling();
}
}
AppPoller.AppPollerListener mAppPollerListener = new AppPoller.AppPollerListener() {
@Override
public void onAppChanged() {
switchToBubbleView(false);
}
};
public void showPreviousBubble() {
mBubbleFlowDraggable.previousTab();
}
public void showNextBubble() {
mBubbleFlowDraggable.nextTab();
}
@SuppressWarnings("unused")
@Subscribe
public void onStateChangedEvent(MainApplication.StateChangedEvent event) {
closeAllBubbles(false);
final Vector<String> urls = Settings.get().loadCurrentTabs();
if (urls.size() > 0) {
for (String url : urls) {
MainApplication.openLink(mContext, url, null);
}
}
}
@SuppressWarnings("unused")
@Subscribe
public void onEndAnimateFinalTabAway(MainController.EndAnimateFinalTabAwayEvent event) {
mBubbleFlowDraggable.setVisibility(View.GONE);
}
public boolean reloadAllTabs(Context context) {
CrashTracking.log("MainController.reloadAllTabs()");
boolean reloaded = false;
closeAllBubbles(false);
final Vector<String> urls = Settings.get().loadCurrentTabs();
if (urls.size() > 0) {
for (String url : urls) {
MainApplication.openLink(context.getApplicationContext(), url, null);
reloaded = true;
}
}
return reloaded;
}
private boolean mHiddenByUser = false;
public void setHiddenByUser(boolean hiddenByUser) {
if (mHiddenByUser != hiddenByUser) {
mHiddenByUser = hiddenByUser;
if (mHiddenByUser) {
switch (mBubbleDraggable.getCurrentMode()) {
case ContentView:
mBubbleDraggable.snapToBubbleView();
break;
}
MainApplication.postEvent(mContext, new HideContentEvent());
MainApplication.postEvent(mContext, new MainService.ShowUnhideNotificationEvent());
} else {
MainApplication.postEvent(mContext, new CurrentTabChangedEvent(mBubbleFlowDraggable.getCurrentTab(), true));
MainApplication.postEvent(mContext, new MainService.ShowDefaultNotificationEvent());
MainApplication.postEvent(mContext, new UnhideContentEvent());
}
setCanDisplay(!mHiddenByUser);
}
}
private boolean mCanDisplay;
//private static final String SCREEN_LOCK_TAG = "screenlock";
private void setCanDisplay(boolean canDisplay) {
if (canDisplay == mCanDisplay) {
return;
}
//Log.d(SCREEN_LOCK_TAG, "*** setCanDisplay() - old:" + mCanDisplay + ", new:" + canDisplay);
mCanDisplay = canDisplay;
if (canDisplay) {
enableRootWindows();
} else {
disableRootWindows();
}
}
private void updateKeyguardLocked() {
KeyguardManager keyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager != null) {
boolean isLocked = keyguardManager.isKeyguardLocked();
//Log.d(SCREEN_LOCK_TAG, "keyguardManager.isKeyguardLocked():" + mCanDisplay);
setCanDisplay(!isLocked);
}
}
void updateScreenState(String action) {
//Log.d(SCREEN_LOCK_TAG, "---" + action);
CrashTracking.log("BubbleFlowView - updateScreenState(): " + action);
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
setCanDisplay(false);
MainApplication.postEvent(mContext, mScreenOffEvent);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
updateKeyguardLocked();
mScreenOn = true;
MainApplication.postEvent(mContext, mScreenOnEvent);
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
setCanDisplay(mHiddenByUser ? false : true);
MainApplication.postEvent(mContext, mUserPresentEvent);
}
}
public boolean isScreenOn() {
return mScreenOn;
}
public TabView getCurrentTab() {
TabView tab = mBubbleFlowDraggable.getCurrentTab();
return tab;
}
public static void doCrash() {
throw new RuntimeException("Forced Exception");
}
}