Merge tag 'android-11.0.0_r38' of https://android.googlesource.com/platform//packages/apps/ThemePicker into r
Android 11.0.0 Release 38 (RQ3A.210605.005)
Change-Id: If964d283cf218c890880e9a2a4c9c74f352764ac
diff --git a/Android.mk b/Android.mk
index 987c875..5a443d2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -49,8 +49,27 @@
LOCAL_MANIFEST_FILE := AndroidManifest.xml
+LOCAL_REQUIRED_MODULES := default_permissions_com.android.wallpaper.xml privapp_whitelist_com.android.wallpaper.xml
+
include $(BUILD_PACKAGE)
+include $(CLEAR_VARS)
+LOCAL_MODULE := default_permissions_com.android.wallpaper.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT_ETC)/default-permissions
+LOCAL_SYSTEM_EXT_MODULE := true
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := privapp_whitelist_com.android.wallpaper.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM_EXT_ETC)/permissions
+LOCAL_SYSTEM_EXT_MODULE := true
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
# ==================================================
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1bcacab..d106de0 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -9,6 +9,10 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
+ <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
<application
tools:replace="android:icon,android:name"
@@ -25,7 +29,8 @@
android:name="com.android.customization.picker.CustomizationPickerActivity"
android:label="@string/app_name"
android:resizeableActivity="false"
- android:theme="@style/CustomizationTheme.NoActionBar">
+ android:exported="true"
+ android:theme="@style/CustomizationTheme.NoActionBar.CustomizationPicker">
<intent-filter>
<action android:name="android.intent.action.SET_WALLPAPER"/>
@@ -39,6 +44,16 @@
<activity android:name="com.android.customization.picker.ViewOnlyFullPreviewActivity"
android:resizeableActivity="false"
android:theme="@style/CustomizationTheme.NoActionBar"/>
+ <activity
+ android:name="com.android.customization.picker.LockClockPickerActivity"
+ android:label="@string/app_name"
+ android:resizeableActivity="true"
+ android:theme="@style/CustomizationTheme.NoActionBar">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/default_permissions_com.android.wallpaper.xml b/default_permissions_com.android.wallpaper.xml
new file mode 100644
index 0000000..41b23ce
--- /dev/null
+++ b/default_permissions_com.android.wallpaper.xml
@@ -0,0 +1,37 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<!--
+ Copyright (C) 2019-2020 The LineageOS Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This file contains permissions to be granted by default. Default
+ permissions are granted to special platform components and to apps
+ that are approved to get default grants. The special components
+ are apps that are expected to work out-of-the-box as they provide
+ core use cases such as default dialer, default email, etc. These
+ grants are managed by the platform. The apps that are additionally
+ approved for default grants are ones that provide carrier specific
+ functionality, ones legally required at some location, ones providing
+ alternative disclosure and opt-out UI, ones providing highlight features
+ of a dedicated device, etc. This file contains only the latter exceptions.
+ Fixed permissions cannot be controlled by the user and need a special
+ approval. Typically these are to ensure either legally mandated functions
+ or the app is considered a part of the OS.
+-->
+<exceptions>
+ <exception package="com.android.wallpaper">
+ <permission name="android.permission.READ_EXTERNAL_STORAGE" fixed="false"/>
+ </exception>
+</exceptions>
diff --git a/privapp_whitelist_com.android.wallpaper.xml b/privapp_whitelist_com.android.wallpaper.xml
new file mode 100644
index 0000000..abcec3d
--- /dev/null
+++ b/privapp_whitelist_com.android.wallpaper.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.wallpaper">
+ <permission name="android.permission.BIND_WALLPAPER"/>
+ <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
+ <permission name="android.permission.READ_WALLPAPER_INTERNAL"/>
+ <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/res/layout/fragment_clock_picker.xml b/res/layout/fragment_clock_picker.xml
index 6b9f94c..5a0ebad 100644
--- a/res/layout/fragment_clock_picker.xml
+++ b/res/layout/fragment_clock_picker.xml
@@ -35,7 +35,7 @@
<com.android.wallpaper.widget.PreviewPager
android:id="@+id/clock_preview_pager"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:background="@color/preview_pager_background"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@id/options_container"
@@ -50,7 +50,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/options_container"
android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
+ android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toTopOf="@id/placeholder"
diff --git a/res/layout/fragment_custom_theme_name.xml b/res/layout/fragment_custom_theme_name.xml
index 532e904..28dffd3 100644
--- a/res/layout/fragment_custom_theme_name.xml
+++ b/res/layout/fragment_custom_theme_name.xml
@@ -80,6 +80,7 @@
android:layout_gravity="center|top"
android:importantForAutofill="no"
android:minWidth="300dp"
+ android:inputType="textCapWords"
style="@style/CustomThemeNameEditText"/>
</LinearLayout>
</ScrollView>
diff --git a/res/layout/theme_component_preview.xml b/res/layout/theme_component_preview.xml
index bf3255d..fc3e474 100644
--- a/res/layout/theme_component_preview.xml
+++ b/res/layout/theme_component_preview.xml
@@ -48,6 +48,7 @@
android:layout_gravity="center_horizontal"
android:drawablePadding="@dimen/theme_preview_header_drawable_padding"
android:textAppearance="@style/CardTitleTextAppearance"
+ android:gravity="center_horizontal"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="@id/theme_preview_card_body_container"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/res/values/override.xml b/res/values/override.xml
index a070dbe..b0723e1 100644
--- a/res/values/override.xml
+++ b/res/values/override.xml
@@ -17,7 +17,6 @@
-->
<resources>
<string name="themes_stub_package" translatable="false"/>
- <string name="clocks_stub_package" translatable="false"/>
<!-- Authority of a provider in System UI that will provide preview info for available clockfaces. -->
<string name="clocks_provider_authority" translatable="false">com.android.keyguard.clock</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index b4e1971..0948012 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -38,7 +38,9 @@
<item name="android:windowNoTitle">true</item>
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ </style>
+ <style name="CustomizationTheme.NoActionBar.CustomizationPicker" parent="CustomizationTheme.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowDisablePreview">true</item>
diff --git a/src/com/android/customization/model/clock/ContentProviderClockProvider.java b/src/com/android/customization/model/clock/ContentProviderClockProvider.java
index 8f4c031..ad60ddc 100644
--- a/src/com/android/customization/model/clock/ContentProviderClockProvider.java
+++ b/src/com/android/customization/model/clock/ContentProviderClockProvider.java
@@ -2,9 +2,7 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
@@ -27,7 +25,6 @@
private final Context mContext;
private final ProviderInfo mProviderInfo;
private List<Clockface> mClocks;
- private boolean mClockContentAvailable;
public ContentProviderClockProvider(Context context) {
mContext = context;
@@ -36,25 +33,11 @@
mProviderInfo = TextUtils.isEmpty(providerAuthority) ? null
: mContext.getPackageManager().resolveContentProvider(providerAuthority,
PackageManager.MATCH_SYSTEM_ONLY);
-
- if (TextUtils.isEmpty(mContext.getString(R.string.clocks_stub_package))) {
- mClockContentAvailable = false;
- } else {
- try {
- ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
- mContext.getString(R.string.clocks_stub_package),
- PackageManager.MATCH_SYSTEM_ONLY);
- mClockContentAvailable = applicationInfo != null;
- } catch (NameNotFoundException e) {
- mClockContentAvailable = false;
- }
- }
}
@Override
public boolean isAvailable() {
- return mProviderInfo != null && mClockContentAvailable
- && (mClocks == null || !mClocks.isEmpty());
+ return mProviderInfo != null && (mClocks == null || !mClocks.isEmpty());
}
@Override
diff --git a/src/com/android/customization/model/grid/GridOption.java b/src/com/android/customization/model/grid/GridOption.java
index 43afee4..b62a4ee 100644
--- a/src/com/android/customization/model/grid/GridOption.java
+++ b/src/com/android/customization/model/grid/GridOption.java
@@ -62,7 +62,7 @@
mTitle = title;
mIsCurrent = isCurrent;
mIconShapePath = iconShapePath;
- mTileDrawable = new GridTileDrawable(rows, cols, mIconShapePath);
+ mTileDrawable = new GridTileDrawable(cols, rows, iconShapePath);
this.name = name;
this.rows = rows;
this.cols = cols;
diff --git a/src/com/android/customization/model/theme/OverlayThemeExtractor.java b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
index 816176e..395734a 100644
--- a/src/com/android/customization/model/theme/OverlayThemeExtractor.java
+++ b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
@@ -149,8 +149,12 @@
void addIconOverlay(Builder builder, String packageName, String... previewIcons)
throws NameNotFoundException {
builder.addOverlayPackage(getOverlayCategory(packageName), packageName);
- for (String iconName : previewIcons) {
- builder.addIcon(loadIconPreviewDrawable(iconName, packageName, false));
+ try {
+ for (String iconName : previewIcons) {
+ builder.addIcon(loadIconPreviewDrawable(iconName, packageName, false));
+ }
+ } catch (NameNotFoundException | NotFoundException e) {
+ Log.w(TAG, "Didn't find overlay icons, will skip preview", e);
}
}
diff --git a/src/com/android/customization/model/theme/ThemeManager.java b/src/com/android/customization/model/theme/ThemeManager.java
index 533fbd0..0fae521 100644
--- a/src/com/android/customization/model/theme/ThemeManager.java
+++ b/src/com/android/customization/model/theme/ThemeManager.java
@@ -74,7 +74,7 @@
@Override
public boolean isAvailable() {
- return mOverlayManagerCompat.isAvailable() && mProvider.isAvailable();
+ return false;
}
@Override
diff --git a/src/com/android/customization/model/theme/custom/IconOptionsProvider.java b/src/com/android/customization/model/theme/custom/IconOptionsProvider.java
index f7b669b..1457d54 100644
--- a/src/com/android/customization/model/theme/custom/IconOptionsProvider.java
+++ b/src/com/android/customization/model/theme/custom/IconOptionsProvider.java
@@ -24,6 +24,7 @@
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_ICON_THEMEPICKER;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -71,6 +72,7 @@
protected void loadOptions() {
addDefault();
+ PackageManager pm = mContext.getPackageManager();
Map<String, IconOption> optionsByPrefix = new HashMap<>();
for (String overlayPackage : mOverlayPackages) {
IconOption option = addOrUpdateOption(optionsByPrefix, overlayPackage,
@@ -79,6 +81,8 @@
for (String iconName : ICONS_FOR_PREVIEW) {
option.addIcon(loadIconPreviewDrawable(iconName, overlayPackage));
}
+
+ option.setLabel(pm.getApplicationInfo(overlayPackage, 0).loadLabel(pm).toString());
} catch (NotFoundException | NameNotFoundException e) {
Log.w(TAG, String.format("Couldn't load icon overlay details for %s, will skip it",
overlayPackage), e);
@@ -104,7 +108,6 @@
for (IconOption option : optionsByPrefix.values()) {
if (option.isValid(mContext)) {
mOptions.add(option);
- option.setLabel(mContext.getString(R.string.icon_component_label, mOptions.size()));
}
}
}
diff --git a/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java b/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java
index f93b892..633c1f5 100644
--- a/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java
+++ b/src/com/android/customization/model/theme/custom/ShapeOptionsProvider.java
@@ -55,6 +55,8 @@
public class ShapeOptionsProvider extends ThemeComponentOptionProvider<ShapeOption> {
private static final String TAG = "ShapeOptionsProvider";
+ private static final int MAX_ICON_SHAPE_PREVIEWS = 6;
+
private final String[] mShapePreviewIconPackages;
private int mThumbSize;
@@ -106,6 +108,10 @@
private List<ShapeAppIcon> getShapedAppIcons(Path path) {
List<ShapeAppIcon> shapedAppIcons = new ArrayList<>();
for (String packageName : mShapePreviewIconPackages) {
+ if (shapedAppIcons.size() == MAX_ICON_SHAPE_PREVIEWS) {
+ break;
+ }
+
Drawable icon = null;
CharSequence name = null;
try {
diff --git a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
index 5922f5c..327ff65 100644
--- a/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
+++ b/src/com/android/customization/model/theme/custom/ThemeComponentOption.java
@@ -151,6 +151,9 @@
}
TextView title = container.findViewById(R.id.font_card_title);
title.setTypeface(mHeadlineFont);
+ TextView header = container.findViewById(R.id.theme_preview_card_header);
+ header.setText(String.format("%s\n(%s)",
+ container.getContext().getString(R.string.preview_name_font), mLabel));
TextView bodyText = container.findViewById(R.id.font_card_body);
bodyText.setTypeface(mBodyFont);
container.findViewById(R.id.font_card_divider).setBackgroundColor(
@@ -233,6 +236,9 @@
bindPreviewHeader(container, R.string.preview_name_icon, R.drawable.ic_wifi_24px);
+ TextView header = container.findViewById(R.id.theme_preview_card_header);
+ header.setText(String.format("%s\n(%s)",
+ container.getContext().getString(R.string.preview_name_icon), mLabel));
ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
if (cardBody.getChildCount() == 0) {
LayoutInflater.from(container.getContext()).inflate(
@@ -366,6 +372,9 @@
bindPreviewHeader(container, R.string.preview_name_color, R.drawable.ic_colorize_24px);
+ TextView header = container.findViewById(R.id.theme_preview_card_header);
+ header.setText(String.format("%s\n(%s)",
+ container.getContext().getString(R.string.preview_name_color), mLabel));
ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
if (cardBody.getChildCount() == 0) {
LayoutInflater.from(container.getContext()).inflate(
@@ -509,6 +518,9 @@
bindPreviewHeader(container, R.string.preview_name_shape, R.drawable.ic_shapes_24px);
+ TextView header = container.findViewById(R.id.theme_preview_card_header);
+ header.setText(String.format("%s\n(%s)",
+ container.getContext().getString(R.string.preview_name_shape), mLabel));
ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
if (cardBody.getChildCount() == 0) {
LayoutInflater.from(container.getContext()).inflate(
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index 786bebd..f24a7c7 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -91,7 +91,7 @@
*/
public class CustomizationPickerActivity extends FragmentActivity implements WallpapersUiContainer,
CategoryFragmentHost, ThemeFragmentHost, GridFragmentHost, ClockFragmentHost,
- BottomActionBarHost, FragmentTransactionChecker {
+ BottomActionBarHost, FragmentTransactionChecker, PermissionChangedListener {
public static final String WALLPAPER_FLAVOR_EXTRA =
"com.android.launcher3.WALLPAPER_FLAVOR";
@@ -154,6 +154,9 @@
WALLPAPER_FOCUS.equals(getIntent().getStringExtra(WALLPAPER_FLAVOR_EXTRA))
? R.id.nav_wallpaper : R.id.nav_theme);
}
+ if (!isReadExternalStoragePermissionGranted()) {
+ requestExternalStoragePermission(this);
+ }
}
@Override
@@ -463,6 +466,14 @@
return mIsSafeToCommitFragmentTransaction;
}
+ @Override
+ public void onPermissionsGranted() {
+ }
+
+ @Override
+ public void onPermissionsDenied(boolean dontAskAgain) {
+ }
+
/**
* Represents a section of the Picker (eg "ThemeBundle", "Clock", etc).
* There should be a concrete subclass per available section, providing the corresponding
diff --git a/src/com/android/customization/picker/LockClockPickerActivity.java b/src/com/android/customization/picker/LockClockPickerActivity.java
new file mode 100644
index 0000000..3b61f44
--- /dev/null
+++ b/src/com/android/customization/picker/LockClockPickerActivity.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker;
+
+import android.content.Intent;
+import android.os.Bundle;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import com.android.customization.model.clock.Clockface;
+import com.android.customization.model.clock.ClockManager;
+import com.android.customization.model.clock.ContentProviderClockProvider;
+import com.android.customization.module.CustomizationInjector;
+import com.android.customization.module.ThemesUserEventLogger;
+import com.android.customization.picker.clock.ClockFragment;
+import com.android.customization.picker.clock.ClockFragment.ClockFragmentHost;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.R;
+
+/**
+ * Activity allowing for the clock face picker to be linked to from other setup flows.
+ *
+ * This should be used with startActivityForResult. The resulting intent contains an extra
+ * "clock_face_name" with the id of the picked clock face.
+ */
+public class LockClockPickerActivity extends FragmentActivity implements ClockFragmentHost {
+
+ private ClockManager mClockManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_clock_face_picker);
+
+ CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+ ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
+ this);
+
+ mClockManager = new ClockManager(getContentResolver(),
+ new ContentProviderClockProvider(this), eventLogger);
+ if (!mClockManager.isAvailable()) {
+ finish();
+ } else {
+ final FragmentManager fm = getSupportFragmentManager();
+ final FragmentTransaction fragmentTransaction = fm.beginTransaction();
+ final ClockFragment clockFragment = ClockFragment.newInstance(
+ getString(R.string.clock_title));
+ fragmentTransaction.replace(R.id.fragment_container, clockFragment);
+ fragmentTransaction.commitNow();
+ }
+ }
+
+ @Override
+ public ClockManager getClockManager() {
+ return mClockManager;
+ }
+}
diff --git a/src/com/android/customization/widget/GridTileDrawable.java b/src/com/android/customization/widget/GridTileDrawable.java
index c746aaf..6d58503 100644
--- a/src/com/android/customization/widget/GridTileDrawable.java
+++ b/src/com/android/customization/widget/GridTileDrawable.java
@@ -28,7 +28,8 @@
private final Path mTransformedPath;
private final Matrix mScaleMatrix;
private float mCellSize = -1f;
- private float mSpaceBetweenIcons;
+ private float mMarginTop;
+ private float mMarginLeft;
public GridTileDrawable(int cols, int rows, String path) {
mCols = cols;
@@ -41,9 +42,15 @@
@Override
protected void onBoundsChange(Rect bounds) {
+ float spaceBetweenIcons;
+
super.onBoundsChange(bounds);
- mCellSize = (float) bounds.height() / mRows;
- mSpaceBetweenIcons = mCellSize * ((1 - ICON_SCALE) / 2);
+ mCellSize = Math.min((float) bounds.height() / mRows, (float) bounds.width() / mCols);
+
+ spaceBetweenIcons = mCellSize * ((1 - ICON_SCALE) / 2);
+ mMarginTop = (bounds.height() - mCellSize * mRows) / 2 + spaceBetweenIcons;
+ mMarginLeft = (bounds.width() - mCellSize * mCols) / 2 + spaceBetweenIcons;
+
float scaleFactor = (mCellSize * ICON_SCALE) / PATH_SIZE;
mScaleMatrix.setScale(scaleFactor, scaleFactor);
@@ -55,8 +62,8 @@
for (int r = 0; r < mRows; r++) {
for (int c = 0; c < mCols; c++) {
int saveCount = canvas.save();
- float x = (c * mCellSize) + mSpaceBetweenIcons;
- float y = (r * mCellSize) + mSpaceBetweenIcons;
+ float x = (c * mCellSize) + mMarginLeft;
+ float y = (r * mCellSize) + mMarginTop;
canvas.translate(x, y);
canvas.drawPath(mTransformedPath, mPaint);
canvas.restoreToCount(saveCount);