1 package com.android.launcher3.graphics; 2 3 import android.content.ContentProvider; 4 import android.content.ContentValues; 5 import android.content.pm.PackageManager; 6 import android.content.res.XmlResourceParser; 7 import android.database.Cursor; 8 import android.database.MatrixCursor; 9 import android.net.Uri; 10 import android.os.Binder; 11 import android.os.Bundle; 12 import android.util.Log; 13 import android.util.Xml; 14 15 import com.android.launcher3.InvariantDeviceProfile; 16 import com.android.launcher3.InvariantDeviceProfile.GridOption; 17 import com.android.launcher3.R; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 22 import java.io.IOException; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.List; 26 27 /** 28 * Exposes various launcher grid options and allows the caller to change them. 29 * APIs: 30 * /list_options: List the various available grip options, has following columns 31 * name: name of the grid 32 * rows: number of rows in the grid 33 * cols: number of columns in the grid 34 * preview_count: number of previews available for this grid option. The preview uri 35 * looks like /preview/<grid-name>/<preview index starting with 0> 36 * is_default: true if this grid is currently active 37 * 38 * /preview: Opens a file stream for the grid preview 39 * 40 * /default_grid: Call update to set the current grid, with values 41 * name: name of the grid to apply 42 */ 43 public class GridOptionsProvider extends ContentProvider { 44 45 private static final String TAG = "GridOptionsProvider"; 46 47 private static final String KEY_NAME = "name"; 48 private static final String KEY_ROWS = "rows"; 49 private static final String KEY_COLS = "cols"; 50 private static final String KEY_PREVIEW_COUNT = "preview_count"; 51 private static final String KEY_IS_DEFAULT = "is_default"; 52 53 private static final String KEY_LIST_OPTIONS = "/list_options"; 54 private static final String KEY_DEFAULT_GRID = "/default_grid"; 55 56 private static final String METHOD_GET_PREVIEW = "get_preview"; 57 58 @Override onCreate()59 public boolean onCreate() { 60 return true; 61 } 62 63 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)64 public Cursor query(Uri uri, String[] projection, String selection, 65 String[] selectionArgs, String sortOrder) { 66 if (!KEY_LIST_OPTIONS.equals(uri.getPath())) { 67 return null; 68 } 69 MatrixCursor cursor = new MatrixCursor(new String[] { 70 KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT}); 71 InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext()); 72 for (GridOption gridOption : parseAllGridOptions()) { 73 cursor.newRow() 74 .add(KEY_NAME, gridOption.name) 75 .add(KEY_ROWS, gridOption.numRows) 76 .add(KEY_COLS, gridOption.numColumns) 77 .add(KEY_PREVIEW_COUNT, 1) 78 .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns 79 && idp.numRows == gridOption.numRows); 80 } 81 return cursor; 82 } 83 parseAllGridOptions()84 private List<GridOption> parseAllGridOptions() { 85 List<GridOption> result = new ArrayList<>(); 86 try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) { 87 final int depth = parser.getDepth(); 88 int type; 89 while (((type = parser.next()) != XmlPullParser.END_TAG || 90 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 91 if ((type == XmlPullParser.START_TAG) 92 && GridOption.TAG_NAME.equals(parser.getName())) { 93 result.add(new GridOption(getContext(), Xml.asAttributeSet(parser))); 94 } 95 } 96 } catch (IOException | XmlPullParserException e) { 97 Log.e(TAG, "Error parsing device profile", e); 98 return Collections.emptyList(); 99 } 100 return result; 101 } 102 103 @Override getType(Uri uri)104 public String getType(Uri uri) { 105 return "vnd.android.cursor.dir/launcher_grid"; 106 } 107 108 @Override insert(Uri uri, ContentValues initialValues)109 public Uri insert(Uri uri, ContentValues initialValues) { 110 return null; 111 } 112 113 @Override delete(Uri uri, String selection, String[] selectionArgs)114 public int delete(Uri uri, String selection, String[] selectionArgs) { 115 return 0; 116 } 117 118 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)119 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 120 if (!KEY_DEFAULT_GRID.equals(uri.getPath())) { 121 return 0; 122 } 123 124 String gridName = values.getAsString(KEY_NAME); 125 // Verify that this is a valid grid option 126 GridOption match = null; 127 for (GridOption option : parseAllGridOptions()) { 128 if (option.name.equals(gridName)) { 129 match = option; 130 break; 131 } 132 } 133 if (match == null) { 134 return 0; 135 } 136 137 InvariantDeviceProfile.INSTANCE.get(getContext()).setCurrentGrid(getContext(), gridName); 138 return 1; 139 } 140 141 @Override call(String method, String arg, Bundle extras)142 public Bundle call(String method, String arg, Bundle extras) { 143 if (getContext().checkPermission("android.permission.BIND_WALLPAPER", 144 Binder.getCallingPid(), Binder.getCallingUid()) 145 != PackageManager.PERMISSION_GRANTED) { 146 return null; 147 } 148 149 if (!METHOD_GET_PREVIEW.equals(method)) { 150 return null; 151 } 152 153 return new PreviewSurfaceRenderer(getContext(), extras).render(); 154 } 155 } 156