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

Commit dec4305d authored by Pinyao Ting's avatar Pinyao Ting
Browse files

Refactor GridSizeMigrationTaskV2

The majority of the logic in grid size migration should be implemented
as static functions in the first place since (1) they only runs once
after instantiation and (2) they are executed immediately after they are
instantiated.

This CL removes most of the instance variables from grid size migration
in favor of static methods since the later is more efficient, more
testable and therefore considered better programming practice in
general.

Bug: 256859723
Test: atest GridSizeMigrationUtilTest
Change-Id: Ief4654d79ad5cfd636e6145fdcc9dfe5a39cdf62
parent 7b516983
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.GridSizeMigrationUtil;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
@@ -241,10 +241,10 @@ public class PreviewSurfaceRenderer {

    @WorkerThread
    private boolean doGridMigrationIfNecessary() {
        if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) {
        if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
            return false;
        }
        return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp);
        return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
    }

    @UiThread
+116 −196
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -64,42 +63,13 @@ import java.util.stream.Collectors;
 * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
 * result of restoring from a larger device or device density change.
 */
public class GridSizeMigrationTaskV2 {
public class GridSizeMigrationUtil {

    private static final String TAG = "GridSizeMigrationTaskV2";
    private static final String TAG = "GridSizeMigrationUtil";
    private static final boolean DEBUG = false;

    private final Context mContext;
    private final SQLiteDatabase mDb;
    private final DbReader mSrcReader;
    private final DbReader mDestReader;

    private final List<DbEntry> mHotseatItems;
    private final List<DbEntry> mWorkspaceItems;

    private final List<DbEntry> mHotseatDiff;
    private final List<DbEntry> mWorkspaceDiff;

    private final int mDestHotseatSize;
    private final int mTrgX, mTrgY;

    @VisibleForTesting
    protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
            DbReader destReader, int destHotseatSize, Point targetSize) {
        mContext = context;
        mDb = db;
        mSrcReader = srcReader;
        mDestReader = destReader;

        mHotseatItems = destReader.loadHotseatEntries();
        mWorkspaceItems = destReader.loadAllWorkspaceEntries();

        mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
        mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
        mDestHotseatSize = destHotseatSize;

        mTrgX = targetSize.x;
        mTrgY = targetSize.y;
    private GridSizeMigrationUtil() {
        // Util class should not be instantiated
    }

    /**
@@ -187,9 +157,8 @@ public class GridSizeMigrationTaskV2 {
                    context, validPackages);

            Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
            GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
                    srcReader, destReader, destDeviceState.getNumHotseat(), targetSize);
            task.migrate(srcDeviceState, destDeviceState);
            migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
                    targetSize, srcDeviceState, destDeviceState);

            if (!migrateForPreview) {
                dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -212,25 +181,39 @@ public class GridSizeMigrationTaskV2 {
        }
    }

    @VisibleForTesting
    protected boolean migrate(DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
        if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
    public static boolean migrate(
            @NonNull final Context context, @NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            final int destHotseatSize, @NonNull final Point targetSize,
            @NonNull final DeviceGridState srcDeviceState,
            @NonNull final DeviceGridState destDeviceState) {

        final List<DbEntry> hotseatItems = destReader.loadHotseatEntries();
        final List<DbEntry> workspaceItems = destReader.loadAllWorkspaceEntries();
        final List<DbEntry> hotseatDiff =
                calcDiff(srcReader.loadHotseatEntries(), hotseatItems);
        final List<DbEntry> workspaceDiff =
                calcDiff(srcReader.loadAllWorkspaceEntries(), workspaceItems);

        final int trgX = targetSize.x;
        final int trgY = targetSize.y;

        if (hotseatDiff.isEmpty() && workspaceDiff.isEmpty()) {
            return false;
        }

        // Sort the items by the reading order.
        Collections.sort(mHotseatDiff);
        Collections.sort(mWorkspaceDiff);
        Collections.sort(hotseatDiff);
        Collections.sort(workspaceDiff);

        // Migrate hotseat
        HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
                mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
        hotseatSolution.find();
        solveHotseatPlacement(db, srcReader,
                destReader, context, destHotseatSize, hotseatItems, hotseatDiff);

        // Migrate workspace.
        // First we create a collection of the screens
        List<Integer> screens = new ArrayList<>();
        for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
        for (int screenId = 0; screenId <= destReader.mLastScreenId; screenId++) {
            screens.add(screenId);
        }

@@ -245,22 +228,19 @@ public class GridSizeMigrationTaskV2 {
            if (DEBUG) {
                Log.d(TAG, "Migrating " + screenId);
            }
            GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff, false);
            workspaceSolution.find();
            if (mWorkspaceDiff.isEmpty()) {
            solveGridPlacement(db, srcReader,
                    destReader, context, screenId, trgX, trgY, workspaceDiff, false);
            if (workspaceDiff.isEmpty()) {
                break;
            }
        }

        // In case the new grid is smaller, there might be some leftover items that don't fit on
        // any of the screens, in this case we add them to new screens until all of them are placed.
        int screenId = mDestReader.mLastScreenId + 1;
        while (!mWorkspaceDiff.isEmpty()) {
            GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff,
                    preservePages);
            workspaceSolution.find();
        int screenId = destReader.mLastScreenId + 1;
        while (!workspaceDiff.isEmpty()) {
            solveGridPlacement(db, srcReader,
                    destReader, context, screenId, trgX, trgY, workspaceDiff, preservePages);
            screenId++;
        }

@@ -365,59 +345,31 @@ public class GridSizeMigrationTaskV2 {
        return validPackages;
    }

    protected static class GridPlacementSolution {

        private final SQLiteDatabase mDb;
        private final DbReader mSrcReader;
        private final DbReader mDestReader;
        private final Context mContext;
        private final GridOccupancy mOccupied;
        private final int mScreenId;
        private final int mTrgX;
        private final int mTrgY;
        private final List<DbEntry> mSortedItemsToPlace;
        private final boolean mMatchingScreenIdOnly;

        private int mNextStartX;
        private int mNextStartY;

        GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
                Context context, int screenId, int trgX, int trgY, List<DbEntry> sortedItemsToPlace,
                boolean matchingScreenIdOnly) {
            mDb = db;
            mSrcReader = srcReader;
            mDestReader = destReader;
            mContext = context;
            mOccupied = new GridOccupancy(trgX, trgY);
            mScreenId = screenId;
            mTrgX = trgX;
            mTrgY = trgY;
            mNextStartX = 0;
            mNextStartY = mScreenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
                    ? 1 /* smartspace */ : 0;
            List<DbEntry> existedEntries = mDestReader.mWorkspaceEntriesByScreenId.get(screenId);
    private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            @NonNull final Context context, final int screenId, final int trgX, final int trgY,
            @NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
        final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
        final Point trg = new Point(trgX, trgY);
        final Point next = new Point(0, screenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
                ? 1 /* smartspace */ : 0);
        List<DbEntry> existedEntries = destReader.mWorkspaceEntriesByScreenId.get(screenId);
        if (existedEntries != null) {
            for (DbEntry entry : existedEntries) {
                    mOccupied.markCells(entry, true);
                occupied.markCells(entry, true);
            }
        }
            mSortedItemsToPlace = sortedItemsToPlace;
            mMatchingScreenIdOnly = matchingScreenIdOnly;
        }

        public void find() {
            Iterator<DbEntry> iterator = mSortedItemsToPlace.iterator();
        Iterator<DbEntry> iterator = sortedItemsToPlace.iterator();
        while (iterator.hasNext()) {
            final DbEntry entry = iterator.next();
                if (mMatchingScreenIdOnly && entry.screenId < mScreenId) continue;
                if (mMatchingScreenIdOnly && entry.screenId > mScreenId) break;
                if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
            if (matchingScreenIdOnly && entry.screenId < screenId) continue;
            if (matchingScreenIdOnly && entry.screenId > screenId) break;
            if (entry.minSpanX > trgX || entry.minSpanY > trgY) {
                iterator.remove();
                continue;
            }
                if (findPlacement(entry)) {
                    insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
                            mDestReader.mTableName);
            if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                iterator.remove();
            }
        }
@@ -428,81 +380,53 @@ public class GridSizeMigrationTaskV2 {
     * a memoization of last placement, we can start our search for next placement from there
     * to speed up the search.
     */
        private boolean findPlacement(DbEntry entry) {
            for (int y = mNextStartY; y <  mTrgY; y++) {
                for (int x = mNextStartX; x < mTrgX; x++) {
                    boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
                    boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
    private static boolean findPlacementForEntry(@NonNull final DbEntry entry,
            @NonNull final Point next, @NonNull final Point trg,
            @NonNull final GridOccupancy occupied, final int screenId) {
        for (int y = next.y; y <  trg.y; y++) {
            for (int x = next.x; x < trg.x; x++) {
                boolean fits = occupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
                boolean minFits = occupied.isRegionVacant(x, y, entry.minSpanX,
                        entry.minSpanY);
                if (minFits) {
                    entry.spanX = entry.minSpanX;
                    entry.spanY = entry.minSpanY;
                }
                if (fits || minFits) {
                        entry.screenId = mScreenId;
                    entry.screenId = screenId;
                    entry.cellX = x;
                    entry.cellY = y;
                        mOccupied.markCells(entry, true);
                        mNextStartX = x + entry.spanX;
                        mNextStartY = y;
                    occupied.markCells(entry, true);
                    next.set(x + entry.spanX, y);
                    return true;
                }
            }
                mNextStartX = 0;
            next.set(0, next.y);
        }
        return false;
    }
    }

    protected static class HotseatPlacementSolution {

        private final SQLiteDatabase mDb;
        private final DbReader mSrcReader;
        private final DbReader mDestReader;
        private final Context mContext;
        private final HotseatOccupancy mOccupied;
        private final List<DbEntry> mItemsToPlace;
    private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            @NonNull final Context context, final int hotseatSize,
            @NonNull final  List<DbEntry> placedHotseatItems,
            @NonNull final List<DbEntry> itemsToPlace) {

        HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
                Context context, int hotseatSize, List<DbEntry> placedHotseatItems,
                List<DbEntry> itemsToPlace) {
            mDb = db;
            mSrcReader = srcReader;
            mDestReader = destReader;
            mContext = context;
            mOccupied = new HotseatOccupancy(hotseatSize);
        final boolean[] occupied = new boolean[hotseatSize];
        for (DbEntry entry : placedHotseatItems) {
                mOccupied.markCells(entry, true);
            }
            mItemsToPlace = itemsToPlace;
            occupied[entry.screenId] = true;
        }

        public void find() {
            for (int i = 0; i < mOccupied.mCells.length; i++) {
                if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
                    DbEntry entry = mItemsToPlace.remove(0);
        for (int i = 0; i < occupied.length; i++) {
            if (!occupied[i] && !itemsToPlace.isEmpty()) {
                DbEntry entry = itemsToPlace.remove(0);
                entry.screenId = i;
                // These values does not affect the item position, but we should set them
                // to something other than -1.
                entry.cellX = i;
                entry.cellY = 0;
                    insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
                            mDestReader.mTableName);
                    mOccupied.markCells(entry, true);
                }
            }
        }

        private class HotseatOccupancy {

            private final boolean[] mCells;

            private HotseatOccupancy(int hotseatSize) {
                mCells = new boolean[hotseatSize];
            }

            private void markCells(ItemInfo item, boolean value) {
                mCells[item.screenId] = value;
                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                occupied[entry.screenId] = true;
            }
        }
    }
@@ -515,8 +439,6 @@ public class GridSizeMigrationTaskV2 {
        private final Set<String> mValidPackages;
        private int mLastScreenId = -1;

        private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
        private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
        private final Map<Integer, ArrayList<DbEntry>> mWorkspaceEntriesByScreenId =
                new ArrayMap<>();

@@ -528,7 +450,8 @@ public class GridSizeMigrationTaskV2 {
            mValidPackages = validPackages;
        }

        protected ArrayList<DbEntry> loadHotseatEntries() {
        protected List<DbEntry> loadHotseatEntries() {
            final List<DbEntry> hotseatEntries = new ArrayList<>();
            Cursor c = queryWorkspace(
                    new String[]{
                            LauncherSettings.Favorites._ID,                  // 0
@@ -577,14 +500,15 @@ public class GridSizeMigrationTaskV2 {
                    entriesToRemove.add(entry.id);
                    continue;
                }
                mHotseatEntries.add(entry);
                hotseatEntries.add(entry);
            }
            removeEntryFromDb(mDb, mTableName, entriesToRemove);
            c.close();
            return mHotseatEntries;
            return hotseatEntries;
        }

        protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
        protected List<DbEntry> loadAllWorkspaceEntries() {
            final List<DbEntry> workspaceEntries = new ArrayList<>();
            Cursor c = queryWorkspace(
                    new String[]{
                            LauncherSettings.Favorites._ID,                  // 0
@@ -599,10 +523,6 @@ public class GridSizeMigrationTaskV2 {
                            LauncherSettings.Favorites.APPWIDGET_ID},        // 9
                        LauncherSettings.Favorites.CONTAINER + " = "
                            + LauncherSettings.Favorites.CONTAINER_DESKTOP);
            return loadWorkspaceEntries(c);
        }

        private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
            final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
            final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
            final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
@@ -678,7 +598,7 @@ public class GridSizeMigrationTaskV2 {
                    entriesToRemove.add(entry.id);
                    continue;
                }
                mWorkspaceEntries.add(entry);
                workspaceEntries.add(entry);
                if (!mWorkspaceEntriesByScreenId.containsKey(entry.screenId)) {
                    mWorkspaceEntriesByScreenId.put(entry.screenId, new ArrayList<>());
                }
@@ -686,7 +606,7 @@ public class GridSizeMigrationTaskV2 {
            }
            removeEntryFromDb(mDb, mTableName, entriesToRemove);
            c.close();
            return mWorkspaceEntries;
            return workspaceEntries;
        }

        private int getFolderItemsCount(DbEntry entry) {
+1 −1
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ public class LoaderTask implements Runnable {
        final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);

        boolean clearDb = false;
        if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) {
        if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
            // Migration failed. Clear workspace.
            clearDb = true;
        }
+57 −51
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherFiles
import com.android.launcher3.LauncherSettings.Favorites.*
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.model.GridSizeMigrationTaskV2.DbReader
import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.LauncherDbUtils
import com.android.launcher3.util.LauncherModelHelper
@@ -37,10 +37,10 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

/** Unit tests for [GridSizeMigrationTaskV2]  */
/** Unit tests for [GridSizeMigrationUtil]  */
@SmallTest
@RunWith(AndroidJUnit4::class)
class GridSizeMigrationTaskV2Test {
class GridSizeMigrationUtilTest {
    private lateinit var modelHelper: LauncherModelHelper
    private lateinit var context: Context
    private lateinit var db: SQLiteDatabase
@@ -122,15 +122,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        var c = context.contentResolver.query(
@@ -207,15 +208,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        val c = context.contentResolver.query(
@@ -262,15 +264,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        val c = context.contentResolver.query(
@@ -327,15 +330,16 @@ class GridSizeMigrationTaskV2Test {

        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(
@@ -387,15 +391,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 5
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(
@@ -448,15 +453,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(