From 1acc56a4eb9a74f28e32eadf62f389d3221b50ff Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Sun, 25 Sep 2016 21:23:25 -0700 Subject: Moving the widget auto-advance logic to AppWidgetHostView instead of handling it in launcher This fixes the bug where launcher ignores auto-advance property changes during app update or widget remote views update as well as simplifies widget management On potential downside of this refactoring is that the auto advance will keep running even when all-apps or widgets tray is open. We could eventually use onVisibilityAggregated to handle visibility changes, but currenly the workspace visibility is not being updated properly in these cases Change-Id: Ie7331fec1877f43ad23e634d37571d8f3ef51e59 --- src/com/android/launcher3/Launcher.java | 118 +-------------------- .../launcher3/LauncherAppWidgetHostView.java | 110 ++++++++++++++++++- 2 files changed, 110 insertions(+), 118 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0a14c421d..5428f3314 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -30,7 +30,6 @@ import android.app.AlertDialog; import android.app.SearchManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -56,7 +55,6 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; import android.os.Trace; @@ -81,7 +79,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; -import android.widget.Advanceable; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -274,27 +271,16 @@ public class Launcher extends Activity private IconCache mIconCache; private ExtractedColors mExtractedColors; private LauncherAccessibilityDelegate mAccessibilityDelegate; + private Handler mHandler = new Handler(); private boolean mIsResumeFromActionScreenOff; - @Thunk boolean mUserPresent = true; - private boolean mVisible; - private boolean mHasFocus; - private boolean mAttached; + private boolean mHasFocus = false; + private boolean mAttached = false; /** Maps launcher activity components to their list of shortcut ids. */ private MultiHashMap mDeepShortcutMap = new MultiHashMap<>(); private View.OnTouchListener mHapticFeedbackTouchListener; - // Related to the auto-advancing of widgets - private final int ADVANCE_MSG = 1; - private static final int ADVANCE_INTERVAL = 20000; - private static final int ADVANCE_STAGGER = 250; - - private boolean mAutoAdvanceRunning = false; - private long mAutoAdvanceSentTime; - private long mAutoAdvanceTimeLeft = -1; - @Thunk HashMap mWidgetsToAdvance = new HashMap<>(); - // Determines how long to wait after a rotation before restoring the screen orientation to // match the sensor state. private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500; @@ -1568,10 +1554,6 @@ public class Launcher extends Activity mWorkspace.addInScreen(hostView, item.container, item.screenId, item.cellX, item.cellY, item.spanX, item.spanY, insert); - - if (!item.isCustomWidget()) { - addWidgetToAutoAdvanceIfNeeded(hostView, appWidgetInfo); - } } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -1579,9 +1561,7 @@ public class Launcher extends Activity public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action)) { - mUserPresent = false; mDragLayer.clearResizeFrame(); - updateAutoAdvanceState(); // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop @@ -1592,9 +1572,6 @@ public class Launcher extends Activity } } mIsResumeFromActionScreenOff = true; - } else if (Intent.ACTION_USER_PRESENT.equals(action)) { - mUserPresent = true; - updateAutoAdvanceState(); } } }; @@ -1606,11 +1583,9 @@ public class Launcher extends Activity // Listen for broadcasts related to user-presence final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(Intent.ACTION_USER_PRESENT); registerReceiver(mReceiver, filter); FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView()); mAttached = true; - mVisible = true; if (mLauncherCallbacks != null) { mLauncherCallbacks.onAttachedToWindow(); @@ -1620,13 +1595,10 @@ public class Launcher extends Activity @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - mVisible = false; - if (mAttached) { unregisterReceiver(mReceiver); mAttached = false; } - updateAutoAdvanceState(); if (mLauncherCallbacks != null) { mLauncherCallbacks.onDetachedFromWindow(); @@ -1634,12 +1606,10 @@ public class Launcher extends Activity } public void onWindowVisibilityChanged(int visibility) { - mVisible = visibility == View.VISIBLE; - updateAutoAdvanceState(); // The following code used to be in onResume, but it turns out onResume is called when // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged // is a more appropriate event to handle - if (mVisible) { + if (visibility == View.VISIBLE) { if (!mWorkspaceLoading) { final ViewTreeObserver observer = mWorkspace.getViewTreeObserver(); // We want to let Launcher draw itself at least once before we force it to build @@ -1674,72 +1644,6 @@ public class Launcher extends Activity } } - @Thunk void sendAdvanceMessage(long delay) { - mHandler.removeMessages(ADVANCE_MSG); - Message msg = mHandler.obtainMessage(ADVANCE_MSG); - mHandler.sendMessageDelayed(msg, delay); - mAutoAdvanceSentTime = System.currentTimeMillis(); - } - - @Thunk void updateAutoAdvanceState() { - boolean autoAdvanceRunning = mVisible && mUserPresent && !mWidgetsToAdvance.isEmpty(); - if (autoAdvanceRunning != mAutoAdvanceRunning) { - mAutoAdvanceRunning = autoAdvanceRunning; - if (autoAdvanceRunning) { - long delay = mAutoAdvanceTimeLeft == -1 ? ADVANCE_INTERVAL : mAutoAdvanceTimeLeft; - sendAdvanceMessage(delay); - } else { - if (!mWidgetsToAdvance.isEmpty()) { - mAutoAdvanceTimeLeft = Math.max(0, ADVANCE_INTERVAL - - (System.currentTimeMillis() - mAutoAdvanceSentTime)); - } - mHandler.removeMessages(ADVANCE_MSG); - mHandler.removeMessages(0); // Remove messages sent using postDelayed() - } - } - } - - @Thunk final Handler mHandler = new Handler(new Handler.Callback() { - - @Override - public boolean handleMessage(Message msg) { - if (msg.what == ADVANCE_MSG) { - int i = 0; - for (View key: mWidgetsToAdvance.keySet()) { - final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId); - final int delay = ADVANCE_STAGGER * i; - if (v instanceof Advanceable) { - mHandler.postDelayed(new Runnable() { - public void run() { - ((Advanceable) v).advance(); - } - }, delay); - } - i++; - } - sendAdvanceMessage(ADVANCE_INTERVAL); - } - return true; - } - }); - - private void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) { - if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return; - View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId); - if (v instanceof Advanceable) { - mWidgetsToAdvance.put(hostView, appWidgetInfo); - ((Advanceable) v).fyiWillBeAdvancedByHostKThx(); - updateAutoAdvanceState(); - } - } - - private void removeWidgetToAutoAdvance(View hostView) { - if (mWidgetsToAdvance.containsKey(hostView)) { - mWidgetsToAdvance.remove(hostView); - updateAutoAdvanceState(); - } - } - public void showOutOfSpaceMessage(boolean isHotseatLayout) { int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space); Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); @@ -1934,9 +1838,6 @@ public class Launcher extends Activity public void onDestroy() { super.onDestroy(); - // Remove all pending runnables - mHandler.removeMessages(ADVANCE_MSG); - mHandler.removeMessages(0); mWorkspace.removeCallbacks(mBuildLayersRunnable); mWorkspace.removeFolderListeners(); @@ -1959,8 +1860,6 @@ public class Launcher extends Activity } mAppWidgetHost = null; - mWidgetsToAdvance.clear(); - TextKeyListener.getInstance().release(); ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) @@ -2280,7 +2179,6 @@ public class Launcher extends Activity } else if (itemInfo instanceof LauncherAppWidgetInfo) { final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo; mWorkspace.removeWorkspaceItem(v); - removeWidgetToAutoAdvance(v); if (deleteFromDb) { deleteWidgetInfo(widgetInfo); } @@ -3229,10 +3127,6 @@ public class Launcher extends Activity // Change the state *after* we've called all the transition code mState = State.WORKSPACE; - // Resume the auto-advance of widgets - mUserPresent = true; - updateAutoAdvanceState(); - if (changed) { // Send an accessibility event to announce the context change getWindow().getDecorView() @@ -3338,9 +3232,6 @@ public class Launcher extends Activity // Change the state *after* we've called all the transition code mState = toState; - // Pause the auto-advance of widgets until we are out of AllApps - mUserPresent = false; - updateAutoAdvanceState(); closeFolder(); closeShortcutsContainer(); @@ -3566,7 +3457,6 @@ public class Launcher extends Activity mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); - mWidgetsToAdvance.clear(); if (mHotseat != null) { mHotseat.resetLayout(); } diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index ed1079ff3..fa5e519b1 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -20,6 +20,9 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.graphics.Rect; +import android.os.Handler; +import android.os.SystemClock; +import android.util.SparseBooleanArray; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -28,6 +31,7 @@ import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.Advanceable; import android.widget.RemoteViews; import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener; @@ -39,6 +43,13 @@ import java.util.ArrayList; */ public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener { + // Related to the auto-advancing of widgets + private static final long ADVANCE_INTERVAL = 20000; + private static final long ADVANCE_STAGGER = 250; + + // Maintains a list of widget ids which are supposed to be auto advanced. + private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray(); + LayoutInflater mInflater; private CheckLongPressHelper mLongPressHelper; @@ -54,6 +65,10 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc protected int mErrorViewId = R.layout.appwidget_error; + private boolean mIsAttachedToWindow; + private boolean mIsAutoAdvanceRegistered; + private Runnable mAutoAdvanceRunnable; + public LauncherAppWidgetHostView(Context context) { super(context); mContext = context; @@ -78,6 +93,9 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc // Store the orientation in which the widget was inflated updateLastInflationOrientation(); super.updateAppWidget(remoteViews); + + // The provider info or the views might have changed. + checkIfAutoAdvance(); } public boolean isReinflateRequired() { @@ -153,6 +171,19 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc protected void onAttachedToWindow() { super.onAttachedToWindow(); mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + + mIsAttachedToWindow = true; + checkIfAutoAdvance(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // We can't directly use isAttachedToWindow() here, as this is called before the internal + // state is updated. So isAttachedToWindow() will return true until next frame. + mIsAttachedToWindow = false; + checkIfAutoAdvance(); } @Override @@ -171,10 +202,6 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc return info; } - public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() { - return (LauncherAppWidgetProviderInfo) getAppWidgetInfo(); - } - @Override public void onTouchComplete() { if (!mLongPressHelper.hasPerformedLongPress()) { @@ -296,4 +323,79 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc super.onInitializeAccessibilityNodeInfo(info); info.setClassName(getClass().getName()); } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + maybeRegisterAutoAdvance(); + } + + private void checkIfAutoAdvance() { + boolean isAutoAdvance = false; + Advanceable target = getAdvanceable(); + if (target != null) { + isAutoAdvance = true; + target.fyiWillBeAdvancedByHostKThx(); + } + + boolean wasAutoAdvance = sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId()) >= 0; + if (isAutoAdvance != wasAutoAdvance) { + if (isAutoAdvance) { + sAutoAdvanceWidgetIds.put(getAppWidgetId(), true); + } else { + sAutoAdvanceWidgetIds.delete(getAppWidgetId()); + } + maybeRegisterAutoAdvance(); + } + } + + private Advanceable getAdvanceable() { + AppWidgetProviderInfo info = getAppWidgetInfo(); + if (info == null || info.autoAdvanceViewId == NO_ID || !mIsAttachedToWindow) { + return null; + } + View v = findViewById(info.autoAdvanceViewId); + return (v instanceof Advanceable) ? (Advanceable) v : null; + } + + private void maybeRegisterAutoAdvance() { + Handler handler = getHandler(); + boolean shouldRegisterAutoAdvance = getWindowVisibility() == VISIBLE && handler != null + && (sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId()) >= 0); + if (shouldRegisterAutoAdvance != mIsAutoAdvanceRegistered) { + mIsAutoAdvanceRegistered = shouldRegisterAutoAdvance; + if (mAutoAdvanceRunnable == null) { + mAutoAdvanceRunnable = new Runnable() { + @Override + public void run() { + runAutoAdvance(); + } + }; + } + + handler.removeCallbacks(mAutoAdvanceRunnable); + scheduleNextAdvance(); + } + } + + private void scheduleNextAdvance() { + if (!mIsAutoAdvanceRegistered) { + return; + } + long now = SystemClock.uptimeMillis(); + long advanceTime = now + (ADVANCE_INTERVAL - (now % ADVANCE_INTERVAL)) + + ADVANCE_STAGGER * sAutoAdvanceWidgetIds.indexOfKey(getAppWidgetId()); + Handler handler = getHandler(); + if (handler != null) { + handler.postAtTime(mAutoAdvanceRunnable, advanceTime); + } + } + + private void runAutoAdvance() { + Advanceable target = getAdvanceable(); + if (target != null) { + target.advance(); + } + scheduleNextAdvance(); + } } -- cgit v1.2.3