Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a88d902d authored by Roman Birg's avatar Roman Birg
Browse files

SystemUI: fix some qs tile race conditions



Whenever setTiles() was called, we assumed all the tile animations had
finished, but if the eager beaver had grabbed a tile and dropped it on the
remove target before waiting for all the animations to complete, a lot of
assumptions are broken and crashes happen. Mainly because tiles aren't removed
from pages and we try to add them to a different page (like if they
needed to be placed a page back).

Implemented cancel for ongoing animations, which we call whenever
setTiles comes in. So all tiles should be in their proper state before
we try to cache/re-add them. Also cleaned up and documented setTiles().

Also, don't try to place the edit tile in the proper place, just add it
to the list if it's not present.

Ticket: CYNGNOS-2472

Change-Id: I5c066abbc16f1fe7173525ea6a8a8b39460461ae
Signed-off-by: default avatarRoman Birg <roman@cyngn.com>
parent 0053be36
Loading
Loading
Loading
Loading
+175 −80
Original line number Original line Diff line number Diff line
@@ -19,10 +19,8 @@ package com.android.systemui.qs;
import android.animation.Animator;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Configuration;
@@ -32,12 +30,10 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserHandle;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager;
import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -49,7 +45,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.BaseExpandableListAdapter;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout;
@@ -59,17 +54,16 @@ import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.cm.UserContentObserver;
import com.android.systemui.cm.UserContentObserver;
import com.android.systemui.qs.tiles.EditTile;
import com.android.systemui.qs.tiles.EditTile;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
import com.android.systemui.settings.ToggleSlider;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.QsTuner;
import com.android.systemui.tuner.QsTuner;
import com.viewpagerindicator.CirclePageIndicator;
import com.viewpagerindicator.CirclePageIndicator;
import cyanogenmod.app.StatusBarPanelCustomTile;
import cyanogenmod.app.StatusBarPanelCustomTile;
import cyanogenmod.providers.CMSettings;
import cyanogenmod.providers.CMSettings;
import org.cyanogenmod.internal.logging.CMMetricsLogger;
import org.cyanogenmod.internal.logging.CMMetricsLogger;
import org.cyanogenmod.internal.util.QSConstants;
import org.cyanogenmod.internal.util.QSUtils;
import org.cyanogenmod.internal.util.QSUtils;


import java.util.ArrayList;
import java.util.ArrayList;
@@ -355,7 +349,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On


    @Override
    @Override
    public boolean hasOverlappingRendering() {
    public boolean hasOverlappingRendering() {
        return mClipper.isAnimating() || mEditing;
        return mClipper.isAnimating() || mEditing || !mCurrentlyAnimating.isEmpty();
    }
    }


    @Override
    @Override
@@ -465,7 +459,6 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
        mLastRightShift = -1;
        mLastRightShift = -1;


        mQsPanelTop.onStopDrag();
        mQsPanelTop.onStopDrag();
        requestLayout();
    }
    }


    protected View getDropTarget() {
    protected View getDropTarget() {
@@ -501,14 +494,16 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
    }
    }


    public void setTiles(final Collection<QSTile<?>> tilesCollection) {
    public void setTiles(final Collection<QSTile<?>> tilesCollection) {
        final List<QSTile<?>> tiles = new ArrayList<>(tilesCollection);
        // we try to be as efficient as possible here because this can happen while the user
        // is in edit mode, or maybe even while tiles are animating
        // step 1: stop all animations
        // step 2: remove tiles no longer to be used, cache ones that are still valid
        // step 3: remove empty viewpager pages
        // step 4: generate new tiles, re-add cached ones

        if (DEBUG_TILES) {
        if (DEBUG_TILES) {
            Log.i(TAG, "setTiles() called with " + "tiles = ["
            Log.i(TAG, "setTiles() called with tiles = [" + tilesCollection + "]");
                    + tiles + "]");
        }
        }

        int currentViewPagerPage = mViewPager.getCurrentItem();

        if (mLastDragRecord != null && mRecords.indexOf(mLastDragRecord) == -1) {
        if (mLastDragRecord != null && mRecords.indexOf(mLastDragRecord) == -1) {
            // the last removed record might be stored in mLastDragRecord if we just shifted
            // the last removed record might be stored in mLastDragRecord if we just shifted
            // re-add it to the list so we'll clean it up below
            // re-add it to the list so we'll clean it up below
@@ -516,25 +511,41 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
            mLastDragRecord = null;
            mLastDragRecord = null;
        }
        }


        Map<QSTile<?>, DragTileRecord> recordMap = new ArrayMap<>();
        // step kinda-1
        if (mDraggingRecord != null) {
            // dragging record might be animating back, force it to finished position
            mDraggingRecord.tileView.animate().cancel();
        }

        int currentViewPagerPage = mViewPager.getCurrentItem();
        int removedPages = 0;

        Map<QSTile<?>, DragTileRecord> cachedRecords = new ArrayMap<>();
        ListIterator<TileRecord> iterator = mRecords.listIterator(mRecords.size());
        ListIterator<TileRecord> iterator = mRecords.listIterator(mRecords.size());


        int recordsRemoved = 0;
        int recordsRemoved = 0;
        // cleanup current records
        // cleanup current records
        while (iterator.hasPrevious()) {
        while (iterator.hasPrevious()) { // mRecords
            DragTileRecord dr = (DragTileRecord) iterator.previous();
            DragTileRecord dr = (DragTileRecord) iterator.previous();


            if (dr.page >= 0) {
            // step 1
                // clean up view
            dr.tileView.animate().cancel();
                mPages.get(dr.page).removeView(dr.tileView);
            }


            if (tiles.contains(dr.tile)) {
            // step 2
            if (tilesCollection.contains(dr.tile)) {
                if (DEBUG_TILES) {
                if (DEBUG_TILES) {
                    Log.i(TAG, "caching tile: " + dr.tile);
                    Log.i(TAG, "caching tile: " + dr.tile);
                }
                }
                recordMap.put(dr.tile, dr);
                cachedRecords.put(dr.tile, dr);
            } else {
            } else {
                if (dr.page >= 0) {
                    if (DEBUG_TILES) {
                        Log.w(TAG, "removed dr.tileView: " + dr.tileView + " from page: "
                                + dr.page + " (dest page: " + dr.destinationPage + ")");
                    }

                    removeTileView(dr.tileView);
                }
                if (DEBUG_TILES) {
                if (DEBUG_TILES) {
                    Log.i(TAG, "removing tile: " + dr.tile);
                    Log.i(TAG, "removing tile: " + dr.tile);
                }
                }
@@ -543,74 +554,92 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                iterator.remove();
                iterator.remove();
                recordsRemoved++;
                recordsRemoved++;


                if (dr.page >= getCurrentMaxPageCount() - 1) {
                    final int childCount = mPages.get(dr.page).getChildCount();

                    if (childCount == 0) {
                        final int currentIndex = mViewPager.getCurrentItem();
                        if (currentIndex > 0 && currentViewPagerPage == currentIndex) {
                            // if we are about to remove the page we are currently on, move back
                            currentViewPagerPage--;
                        }
                        final int pageIndex = dr.page + (mEditing ? 1 : 0);
                        mPagerAdapter.startUpdate(mViewPager);
                        mPagerAdapter.destroyItem(mViewPager, pageIndex, mPages.get(dr.page));
                        mPagerAdapter.finishUpdate(mViewPager);
                        mPagerAdapter.notifyDataSetChanged();
                    }
                }
            }
                dr.page = -1;
                dr.page = -1;
                dr.destinationPage = -1;
                dr.destinationPage = -1;
            }
            }
        }


        // at this point recordMap should have all retained tiles, no new or old tiles
        // at this point cachedRecords should have all retained tiles, no new or old tiles
        int delta = tiles.size() - recordMap.size() - recordsRemoved;
        int delta = tilesCollection.size() - cachedRecords.size() - recordsRemoved;
        if (DEBUG_TILES) {
        if (DEBUG_TILES) {
            Log.i(TAG, "record map delta: " + delta);
            Log.i(TAG, "record map delta: " + delta);
        }
        }
        mRecords.ensureCapacity(tiles.size());


        // step 3
        final Iterator<QSPage> pageIterator = mPages.iterator();
        while (pageIterator.hasNext()) {
            final QSPage page = pageIterator.next();
            final int viewpagerIndex = page.getPageIndex() + (mEditing ? 1 : 0);
            final int childCount = page.getChildCount();

            if (DEBUG_TILES) {
                Log.d(TAG, "page " + viewpagerIndex + " has " + childCount);
            }
            if (page.getPageIndex() >= getCurrentMaxPageCount() - 1) {
                if (DEBUG_TILES) {
                    Log.d(TAG, "page : " + page + " has " + childCount + " children");
                }
                if (childCount == 0) {
                    removedPages++;

                    page.removeAllViews();
                    mPagerAdapter.startUpdate(mViewPager);
                    mPagerAdapter.destroyItem(mViewPager, viewpagerIndex, page);
                    mPagerAdapter.finishUpdate(mViewPager);
                    mPagerAdapter.notifyDataSetChanged();
                    mPagerAdapter.notifyDataSetChanged();
                }
            }
        }


        if (removedPages > 0) {
            // even though we explicitly destroy old pages, without this call,
            // even though we explicitly destroy old pages, without this call,
            // the viewpager doesn't seem to want to pick up the fact that we have less pages
            // the viewpager doesn't seem to want to pick up the fact that we have less pages
            // and allows "empty" scrolls to the right where there is no page.
            // and allows "empty" scrolls to the right where there is no page.
            if (DEBUG_TILES) {
                Log.d(TAG, "re-setting adapter, page: " + currentViewPagerPage);
            }
            mViewPager.setAdapter(mPagerAdapter);
            mViewPager.setAdapter(mPagerAdapter);
            mViewPager.setCurrentItem(Math.min(currentViewPagerPage, mPagerAdapter.getCount()),
                    false);
            mPagerAdapter.notifyDataSetChanged();
        }


        // add new tiles
        // step 4
        for (int i = 0; i < tiles.size(); i++) {
        mRecords.ensureCapacity(tilesCollection.size());
            QSTile<?> tile = tiles.get(i);
        int runningCount = 0;
            final int tileDestPage = getPagesForCount(i + 1) - 1;

        final Iterator<QSTile<?>> newTileIterator = tilesCollection.iterator();
        while (newTileIterator.hasNext()) {
            QSTile<?> tile = newTileIterator.next();
            final int tileDestPage = getPagesForCount(runningCount + 1) - 1;


            if (DEBUG_TILES) {
            if (DEBUG_TILES) {
                Log.d(TAG, "tile at : " + i + ": " + tile + " to dest page: " + tileDestPage);
                Log.d(TAG, "tile at : " + runningCount + ": " + tile
                        + " to dest page: " + tileDestPage);
            }
            }
            DragTileRecord record;
            DragTileRecord record;
            if (!recordMap.containsKey(tile)) {
            if (!cachedRecords.containsKey(tile)) {
                if (DEBUG_TILES) {
                if (DEBUG_TILES) {
                    Log.d(TAG, "tile at: " + i + " not cached, adding it to records");
                    Log.d(TAG, "tile at: " + runningCount + " not cached, adding it to records");
                }
                }
                record = makeRecord(tile);
                record = makeRecord(tile);
                record.destinationPage = tileDestPage;
                record.destinationPage = tileDestPage;
                recordMap.put(tile, record);
                mRecords.add(runningCount, record);
                mRecords.add(i, record);
                mPagerAdapter.notifyDataSetChanged();
                mPagerAdapter.notifyDataSetChanged();
            } else {
            } else {
                record = recordMap.get(tile);
                record = cachedRecords.get(tile);
                if (DEBUG_TILES) {
                    Log.d(TAG, "tile at : " + i + ": cached, restoring: " + record);
                }
                int indexOf = mRecords.indexOf(record);
                if (indexOf != i) {
                if (DEBUG_TILES) {
                if (DEBUG_TILES) {
                        Log.w(TAG, "moving index of " + record + " from "
                    Log.d(TAG, "tile at : " + runningCount + ": cached, restoring: " + record);
                                + indexOf + " to " + i);
                }
                }
                    Collections.swap(mRecords, indexOf, i);


                }
                mPages.get(record.page).removeView(record.tileView);

                record.page = -1;
                record.destinationPage = tileDestPage;
                record.destinationPage = tileDestPage;

                mRecords.remove(record);
                mRecords.add(runningCount, record);
                mPagerAdapter.notifyDataSetChanged();
            }
            }
            if (record.page == -1) {
            if (record.page == -1) {
                // add the view
                // add the view
@@ -620,11 +649,9 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                    Log.d(TAG, "added view " + record);
                    Log.d(TAG, "added view " + record);
                }
                }
            }
            }
            runningCount++;
        }
        }


        // restore the visible page
        mViewPager.setCurrentItem(currentViewPagerPage, false);

        if (isShowingDetail()) {
        if (isShowingDetail()) {
            mDetail.bringToFront();
            mDetail.bringToFront();
        }
        }
@@ -718,10 +745,18 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
        return r;
        return r;
    }
    }


    private void removeTileView(QSTileView v) {
        for (QSPage page : mPages) {
            page.removeView(v);
            page.removeTransientView(v);
        }

    }

    private void removeDraggingRecord() {
    private void removeDraggingRecord() {
        // what spec is this tile?
        // what spec is this tile?
        String spec = mHost.getSpec(mDraggingRecord.tile);
        String spec = mHost.getSpec(mDraggingRecord.tile);
        if (DEBUG_DRAG) {
        if (DEBUG_TILES) {
            Log.w(TAG, "removing tile: " + mDraggingRecord + " with spec: " + spec);
            Log.w(TAG, "removing tile: " + mDraggingRecord + " with spec: " + spec);
        }
        }
        onStopDrag();
        onStopDrag();
@@ -1014,7 +1049,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                }
                }
                if (originatingTileEvent && !event.getResult()) {
                if (originatingTileEvent && !event.getResult()) {
                    // view pager probably ate the event
                    // view pager probably ate the event
                    restoreDraggingTilePosition(v);
                    restoreDraggingTilePosition(v, null);
                }
                }


                break;
                break;
@@ -1032,15 +1067,27 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                        Log.d(TAG, "dropping on delete target!!");
                        Log.d(TAG, "dropping on delete target!!");
                    }
                    }
                    if (mDraggingRecord.tile instanceof EditTile) {
                    if (mDraggingRecord.tile instanceof EditTile) {
                        final QSTileView editTileView = mDraggingRecord.tileView;

                        mQsPanelTop.toast(R.string.quick_settings_cannot_delete_edit_tile);
                        mQsPanelTop.toast(R.string.quick_settings_cannot_delete_edit_tile);
                        restoreDraggingTilePosition(v);
                        restoreDraggingTilePosition(v, new Runnable() {
                            @Override
                            public void run() {
                                // move edit tile to the back
                                final TileRecord editTile = getRecord(editTileView);
                                if (mRecords.remove(editTile)) {
                                    // we depend on mHost.setTiles() placing it on the end
                                    persistRecords();
                                }
                            }
                        });
                        break;
                        break;
                    } else {
                    } else {
                        mRestored = true;
                        mRestored = true;
                        removeDraggingRecord();
                        removeDraggingRecord();
                    }
                    }
                } else {
                } else {
                    restoreDraggingTilePosition(v);
                    restoreDraggingTilePosition(v, null);
                }
                }
                break;
                break;


@@ -1171,7 +1218,7 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
        return false;
        return false;
    }
    }


    private void restoreDraggingTilePosition(View v) {
    private void restoreDraggingTilePosition(View v, final Runnable onAnimationFinishedRunnable) {
        if (mRestored) {
        if (mRestored) {
            return;
            return;
        }
        }
@@ -1250,6 +1297,20 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                        mDraggingRecord.tileView.setAlpha(1);
                        mDraggingRecord.tileView.setAlpha(1);
                    }
                    }


                    @Override
                    public void onAnimationCancel(Animator animation) {
                        mViewPager.requestDisallowInterceptTouchEvent(false);
                        removeTransientView(mDraggingRecord.tileView);
                        mCurrentlyAnimating.remove(mDraggingRecord);
                        mRestoring = false;
                        mPagerAdapter.notifyDataSetChanged();
                        onStopDrag();

                        if (onAnimationFinishedRunnable != null) {
                            postOnAnimation(onAnimationFinishedRunnable);
                        }
                    }

                    @Override
                    @Override
                    public void onAnimationEnd(Animator animation) {
                    public void onAnimationEnd(Animator animation) {
                        mViewPager.requestDisallowInterceptTouchEvent(false);
                        mViewPager.requestDisallowInterceptTouchEvent(false);
@@ -1265,8 +1326,8 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                                Log.i(TAG, "drag record was attached");
                                Log.i(TAG, "drag record was attached");
                            }
                            }
                        }
                        }
                        mDraggingRecord.page = mDraggingRecord.destinationPage;
                        targetP.addView(mDraggingRecord.tileView);
                        targetP.addView(mDraggingRecord.tileView);
                        mDraggingRecord.page = mDraggingRecord.destinationPage;


                        mDraggingRecord.tileView.setX(mDraggingRecord.destination.x);
                        mDraggingRecord.tileView.setX(mDraggingRecord.destination.x);
                        // reset this to be in the coords of the page, not viewpager anymore
                        // reset this to be in the coords of the page, not viewpager anymore
@@ -1281,6 +1342,12 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                            mPagerAdapter.notifyDataSetChanged();
                            mPagerAdapter.notifyDataSetChanged();
                        }
                        }
                        onStopDrag();
                        onStopDrag();

                        if (onAnimationFinishedRunnable != null) {
                            postOnAnimation(onAnimationFinishedRunnable);
                        } else {
                            requestLayout();
                        }
                    }
                    }
                });
                });
    }
    }
@@ -1496,11 +1563,17 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                        .x(ti.destination.x + getWidth())
                        .x(ti.destination.x + getWidth())
                        .y(ti.destination.y)
                        .y(ti.destination.y)
                        .setListener(new AnimatorListenerAdapter() {
                        .setListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationCancel(Animator animation) {
                                tilePageSource.removeTransientView(ti.tileView);
                                mCurrentlyAnimating.remove(ti);
                            }

                            @Override
                            @Override
                            public void onAnimationEnd(Animator animation) {
                            public void onAnimationEnd(Animator animation) {
                                tilePageSource.removeTransientView(ti.tileView);
                                tilePageSource.removeTransientView(ti.tileView);
                                ti.page = tilePageTarget.getPageIndex();
                                tilePageTarget.addView(ti.tileView);
                                tilePageTarget.addView(ti.tileView);
                                ti.page = tilePageTarget.getPageIndex();
                                ti.tileView.setX(ti.destination.x);
                                ti.tileView.setX(ti.destination.x);
                                ti.tileView.setY(ti.destination.y);
                                ti.tileView.setY(ti.destination.y);


@@ -1515,6 +1588,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                        .x(ti.destination.x)
                        .x(ti.destination.x)
                        .y(ti.destination.y)
                        .y(ti.destination.y)
                        .setListener(new AnimatorListenerAdapter() {
                        .setListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationCancel(Animator animation) {
                                mCurrentlyAnimating.remove(ti);
                            }

                            @Override
                            @Override
                            public void onAnimationEnd(Animator animation) {
                            public void onAnimationEnd(Animator animation) {
                                mCurrentlyAnimating.remove(ti);
                                mCurrentlyAnimating.remove(ti);
@@ -1550,11 +1628,17 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                    .x(last.destination.x + getWidth())
                    .x(last.destination.x + getWidth())
                    .y(last.destination.y)
                    .y(last.destination.y)
                    .setListener(new AnimatorListenerAdapter() {
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationCancel(Animator animation) {
                            tilePageSource.removeTransientView(last.tileView);
                            mCurrentlyAnimating.remove(last);
                        }

                        @Override
                        @Override
                        public void onAnimationEnd(Animator animation) {
                        public void onAnimationEnd(Animator animation) {
                            tilePageSource.removeTransientView(last.tileView);
                            tilePageSource.removeTransientView(last.tileView);
                            last.page = tilePageTarget.getPageIndex();
                            tilePageTarget.addView(last.tileView);
                            tilePageTarget.addView(last.tileView);
                            last.page = tilePageTarget.getPageIndex();
                            last.tileView.setX(last.destination.x);
                            last.tileView.setX(last.destination.x);
                            last.tileView.setY(last.destination.y);
                            last.tileView.setY(last.destination.y);


@@ -1572,6 +1656,11 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                    .x(last.destination.x)
                    .x(last.destination.x)
                    .y(last.destination.y)
                    .y(last.destination.y)
                    .setListener(new AnimatorListenerAdapter() {
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationCancel(Animator animation) {
                            mCurrentlyAnimating.remove(last);
                        }

                        @Override
                        @Override
                        public void onAnimationEnd(Animator animation) {
                        public void onAnimationEnd(Animator animation) {
                            if (DEBUG_DRAG) {
                            if (DEBUG_DRAG) {
@@ -1653,11 +1742,17 @@ public class QSDragPanel extends QSPanel implements View.OnDragListener, View.On
                                page.addTransientView(ti.tileView, 0);
                                page.addTransientView(ti.tileView, 0);
                            }
                            }


                            @Override
                            public void onAnimationCancel(Animator animation) {
                                page.removeTransientView(ti.tileView);
                                mCurrentlyAnimating.remove(ti);
                            }

                            @Override
                            @Override
                            public void onAnimationEnd(Animator animation) {
                            public void onAnimationEnd(Animator animation) {
                                page.removeTransientView(ti.tileView);
                                page.removeTransientView(ti.tileView);
                                ti.page = page.getPageIndex();
                                page.addView(ti.tileView);
                                page.addView(ti.tileView);
                                ti.page = page.getPageIndex();


                                mCurrentlyAnimating.remove(ti);
                                mCurrentlyAnimating.remove(ti);
                                requestLayout();
                                requestLayout();
+3 −4
Original line number Original line Diff line number Diff line
@@ -399,11 +399,10 @@ public class QSTileHost implements QSTile.Host, Tunable {
                tiles.add(tile);
                tiles.add(tile);
            }
            }
        }
        }
        // ensure edit tile is present
        // ensure edit tile is present, default placement should be handled in the default
        if (tiles.size() < TILES_PER_PAGE && !tiles.contains("edit")) {
        // tile list.
        if (!tiles.contains("edit")) {
            tiles.add("edit");
            tiles.add("edit");
        } else if (tiles.size() > TILES_PER_PAGE && !tiles.contains("edit")) {
            tiles.add((TILES_PER_PAGE - 1), "edit");
        }
        }
        return tiles;
        return tiles;
    }
    }