From eff44f331ce53b40fbfd2fad632643a0a29246af Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 9 Jan 2019 17:29:49 -0800 Subject: [PATCH] Adding content provider for exposing launcher grid settings Bug: 122262084 Change-Id: I3e89e0a9400fb3e81f932af3606eb49c36d34894 --- AndroidManifest-common.xml | 9 + .../launcher3/InvariantDeviceProfile.java | 20 ++- .../graphics/GridOptionsProvider.java | 157 ++++++++++++++++++ .../graphics/LauncherPreviewRenderer.java | 6 +- 4 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 src/com/android/launcher3/graphics/GridOptionsProvider.java diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index d7c16e650f..979096ce18 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -152,6 +152,15 @@ android:writePermission="${packageName}.permission.WRITE_SETTINGS" android:readPermission="${packageName}.permission.READ_SETTINGS" /> + + + diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 3c0ef79f1e..45bdea88a0 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -139,6 +139,13 @@ public class InvariantDeviceProfile { APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess); } + public InvariantDeviceProfile(Context context, String gridName) { + String newName = initGrid(context, gridName); + if (newName == null || !newName.equals(gridName)) { + throw new IllegalArgumentException("Unknown grid name"); + } + } + /** * Retrieve system defined or RRO overriden icon shape. */ @@ -150,7 +157,7 @@ public class InvariantDeviceProfile { return context.getResources().getString(CONFIG_ICON_MASK_RES_ID); } - private void initGrid(Context context, String gridName) { + private String initGrid(Context context, String gridName) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); @@ -218,6 +225,7 @@ public class InvariantDeviceProfile { } else { defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide); } + return closestProfile.name; } @Nullable @@ -441,11 +449,11 @@ public class InvariantDeviceProfile { } - private static final class GridOption { + public static final class GridOption { - private final String name; - private final int numRows; - private final int numColumns; + public final String name; + public final int numRows; + public final int numColumns; private final int numFolderRows; private final int numFolderColumns; @@ -457,7 +465,7 @@ public class InvariantDeviceProfile { private final SparseArray extraAttrs; - GridOption(Context context, AttributeSet attrs) { + public GridOption(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.GridDisplayOption); name = a.getString(R.styleable.GridDisplayOption_name); diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java new file mode 100644 index 0000000000..9b907ba6e9 --- /dev/null +++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java @@ -0,0 +1,157 @@ +package com.android.launcher3.graphics; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.res.XmlResourceParser; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import android.text.TextUtils; +import android.util.Log; +import android.util.Xml; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.InvariantDeviceProfile.GridOption; +import com.android.launcher3.R; +import com.android.launcher3.util.LooperExecutor; +import com.android.launcher3.util.UiThreadHelper; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Future; + +/** + * Exposes various launcher grid options and allows the caller to change them. + * APIs: + * /list_options: List the various available grip options, has following columns + * name: name of the grid + * rows: number of rows in the grid + * cols: number of columns in the grid + * preview_count: number of previews available for this grid option. The preview uri + * looks like /preview// + * is_default: true if this grid is currently active + * + * /preview: Opens a file stream for the grid preview + */ +public class GridOptionsProvider extends ContentProvider { + + private static final String TAG = "GridOptionsProvider"; + + private static final String KEY_NAME = "name"; + private static final String KEY_ROWS = "rows"; + private static final String KEY_COLS = "cols"; + private static final String KEY_PREVIEW_COUNT = "preview_count"; + private static final String KEY_IS_DEFAULT = "is_default"; + + private static final String KEY_PREVIEW = "preview"; + private static final String MIME_TYPE_PNG = "image/png"; + + public static final PipeDataWriter> BITMAP_WRITER = + new PipeDataWriter>() { + @Override + public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String s, + Bundle bundle, Future bitmap) { + try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) { + bitmap.get().compress(Bitmap.CompressFormat.PNG, 100, os); + } catch (Exception e) { + Log.w(TAG, "fail to write to pipe", e); + } + } + }; + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + // TODO: Validate the query uri + MatrixCursor cursor = new MatrixCursor(new String[] { + KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT}); + InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext()); + try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) { + final int depth = parser.getDepth(); + int type; + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if ((type == XmlPullParser.START_TAG) && "grid-option".equals(parser.getName())) { + GridOption gridOption = new GridOption( + getContext(), Xml.asAttributeSet(parser)); + + cursor.newRow() + .add(KEY_NAME, gridOption.name) + .add(KEY_ROWS, gridOption.numRows) + .add(KEY_COLS, gridOption.numColumns) + .add(KEY_PREVIEW_COUNT, 1) + .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns + && idp.numRows == gridOption.numRows); + } + } + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Error parsing device profile", e); + } + + return cursor; + } + + @Override + public String getType(Uri uri) { + List segments = uri.getPathSegments(); + if (segments.size() > 0 && KEY_PREVIEW.equals(segments.get(0))) { + return MIME_TYPE_PNG; + } + return "vnd.android.cursor.dir/launcher_grid"; + } + + @Override + public Uri insert(Uri uri, ContentValues initialValues) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + List segments = uri.getPathSegments(); + if (segments.size() < 2 || !KEY_PREVIEW.equals(segments.get(0))) { + throw new FileNotFoundException("Invalid preview url"); + } + String profileName = segments.get(1); + if (TextUtils.isEmpty(profileName)) { + throw new FileNotFoundException("Invalid preview url"); + } + + InvariantDeviceProfile idp; + try { + idp = new InvariantDeviceProfile(getContext(), profileName); + } catch (Exception e) { + throw new FileNotFoundException(e.getMessage()); + } + + LooperExecutor executor = new LooperExecutor(UiThreadHelper.getBackgroundLooper()); + try { + return openPipeHelper(uri, MIME_TYPE_PNG, null, + executor.submit(new LauncherPreviewRenderer(getContext(), idp)), BITMAP_WRITER); + } catch (Exception e) { + throw new FileNotFoundException(e.getMessage()); + } + } +} diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index dc6f50fdbe..e52fe66f78 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -61,6 +61,7 @@ import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; /** @@ -72,7 +73,7 @@ import java.util.concurrent.CountDownLatch; * 4) Measure and draw the view on a canvas */ @TargetApi(Build.VERSION_CODES.O) -public class LauncherPreviewRenderer { +public class LauncherPreviewRenderer implements Callable { private static final String TAG = "LauncherPreviewRenderer"; @@ -110,7 +111,8 @@ public class LauncherPreviewRenderer { context.getString(R.string.label_application); } - public Bitmap createScreenShot() { + @Override + public Bitmap call() { return BitmapRenderer.createHardwareBitmap(mDp.widthPx, mDp.heightPx, c -> { if (Looper.myLooper() == Looper.getMainLooper()) {