Introduce font picker

Change-Id: I9d39af91caf2b69f0082111d2fca50784725d2ec
Reviewed-on: https://review.statixos.com/c/android_packages_apps_Statix_ThemePicker/+/7711
Reviewed-by: Sourajit Karmakar <sourajit@live.com>
Reviewed-by: Anay Wadhera <anay1018@gmail.com>
Tested-by: Sourajit Karmakar <sourajit@live.com>
This commit is contained in:
Anay Wadhera 2022-05-12 09:54:19 +09:00 committed by StatiXOS Gerrit
parent d615c2d371
commit 319ea20521
16 changed files with 911 additions and 6 deletions

View File

@ -0,0 +1,34 @@
<?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.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/FullContentPreviewCard"
android:id="@+id/font_preview_card"
android:contentDescription="@string/font_preview_card_content_description"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<FrameLayout
android:id="@+id/theme_preview_card_body_container"
android:layout_width="match_parent"
android:layout_height="@dimen/preview_theme_content_max_height"
android:layout_marginTop="@dimen/preview_theme_content_margin"
android:clipChildren="false"
android:importantForAccessibility="noHideDescendants">
<include layout="@layout/preview_card_font_content" />
</FrameLayout>
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<com.statix.android.customization.picker.font.FontSectionView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:paddingBottom="@dimen/section_bottom_padding"
android:paddingHorizontal="@dimen/section_horizontal_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/font_title"
style="@style/SectionTitleTextStyle" />
<TextView
android:id="@+id/font_section_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/SectionSubtitleTextStyle"/>
</LinearLayout>
<FrameLayout
android:id="@+id/font_section_tile"
android:layout_width="@dimen/option_tile_width"
android:layout_height="@dimen/option_tile_width"
android:scaleType="center"
android:background="@drawable/option_border_color">
<TextView
android:id="@+id/thumbnail_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/font_comonent_option_thumbnail_size"
android:textAlignment="center"
android:textColor="?android:attr/colorForeground"
android:text="@string/font_component_option_thumbnail"/>
</FrameLayout>
</com.statix.android.customization.picker.font.FontSectionView>

View File

@ -0,0 +1,85 @@
<?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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/section_header"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content_section"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/preview_card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingTop="@dimen/preview_page_top_margin"
android:paddingBottom="@dimen/preview_page_bottom_margin"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@+id/options_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<include layout="@layout/font_preview_card" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/options_container"
android:layout_width="match_parent"
android:layout_height="@dimen/options_container_height"
android:layout_marginBottom="@dimen/grid_options_container_bottom_margin"
android:paddingHorizontal="@dimen/grid_options_container_horizontal_margin"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/loading_indicator"
style="@android:style/Widget.DeviceDefault.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:layout_gravity="center_horizontal|top"
android:indeterminate="true"/>
<FrameLayout
android:id="@+id/error_section"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:id="@+id/error_message"
style="@style/TitleTextAppearance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/something_went_wrong"/>
</FrameLayout>
</FrameLayout>
</LinearLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<com.android.customization.picker.iconpack.IconPackSectionView
<com.statix.android.customization.picker.iconpack.IconPackSectionView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:paddingBottom="@dimen/section_bottom_padding"
@ -33,4 +33,4 @@
android:scaleType="center"
android:background="@drawable/option_border_color" />
</com.android.customization.picker.iconpack.IconPackSectionView>
</com.statix.android.customization.picker.iconpack.IconPackSectionView>

View File

@ -0,0 +1,50 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:id="@+id/option_tile"
android:layout_width="@dimen/option_tile_width"
android:layout_height="@dimen/option_tile_width"
android:layout_gravity="center_horizontal"
android:paddingHorizontal="@dimen/option_tile_padding_horizontal"
android:paddingVertical="@dimen/option_tile_padding_vertical"
android:layout_marginHorizontal="@dimen/component_options_margin_horizontal"
android:background="@drawable/option_border">
<TextView
android:id="@+id/thumbnail_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/font_comonent_option_thumbnail_size"
android:textAlignment="center"
android:textColor="?android:attr/colorForeground"
android:text="@string/font_component_option_thumbnail"/>
</FrameLayout>
<TextView
android:id="@+id/option_label"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/theme_option_label_margin"
android:gravity="center"
android:textAppearance="@style/OptionTitleTextAppearance" />
</LinearLayout>

View File

@ -21,6 +21,9 @@
<string name="icon_pack_title">System Icon Packs</string>
<string name="icon_preview_card_content_description">Icon Preview</string>
<string name="font_title">System Fonts</string>
<string name="font_preview_card_content_description">Font Preview</string>
<string name="wallpaper_color_tab">Wallpaper colors</string>
<string name="wallpaper_color_title">Wallpaper color</string>
<string name="preset_color_tab">Basic colors</string>

View File

@ -0,0 +1,149 @@
/*
* 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.statix.android.customization.model.font;
import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.theme.OverlayManagerCompat;
import java.util.Map;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
public class FontManager implements CustomizationManager<FontOption> {
private static FontManager sFontOptionManager;
private Context mContext;
private FontOption mActiveOption;
private OverlayManagerCompat mOverlayManager;
private FontOptionProvider mProvider;
private static final String TAG = "FontManager";
private static final String KEY_STATE_CURRENT_SELECTION = "FontManager.currentSelection";
FontManager(Context context, OverlayManagerCompat overlayManager, FontOptionProvider provider) {
mContext = context;
mProvider = provider;
mOverlayManager = overlayManager;
}
@Override
public boolean isAvailable() {
return mOverlayManager.isAvailable();
}
@Override
public void apply(FontOption option, @Nullable Callback callback) {
if (!persistOverlay(option)) {
Toast failed = Toast.makeText(mContext, "Failed to apply font, reboot to try again.", Toast.LENGTH_SHORT);
failed.show();
if (callback != null) {
callback.onError(null);
}
return;
}
if (option.getPackageName() == null) {
if (mActiveOption.getPackageName() == null) return;
for (String overlay : mOverlayManager.getOverlayPackagesForCategory(
OVERLAY_CATEGORY_FONT, UserHandle.myUserId(), ANDROID_PACKAGE)) {
mOverlayManager.disableOverlay(overlay, UserHandle.myUserId());
}
} else {
mOverlayManager.setEnabledExclusiveInCategory(option.getPackageName(), UserHandle.myUserId());
}
if (callback != null) {
callback.onSuccess();
}
mActiveOption = option;
}
@Override
public void fetchOptions(OptionsFetchedListener<FontOption> callback, boolean reload) {
List<FontOption> options = mProvider.getOptions(reload);
for (FontOption option : options) {
if (isActive(option)) {
mActiveOption = option;
break;
}
}
callback.onOptionsLoaded(options);
}
public OverlayManagerCompat getOverlayManager() {
return mOverlayManager;
}
public boolean isActive(FontOption option) {
String enabledPkg = mOverlayManager.getEnabledPackageName(ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT);
if (enabledPkg != null) {
return enabledPkg.equals(option.getPackageName());
} else {
return option.getPackageName() == null;
}
}
private boolean persistOverlay(FontOption toPersist) {
String value = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, UserHandle.USER_CURRENT);
JSONObject json;
if (value == null) {
json = new JSONObject();
} else {
try {
json = new JSONObject(value);
} catch (JSONException e) {
Log.e(TAG, "Error parsing current settings value:\n" + e.getMessage());
return false;
}
}
// removing all currently enabled overlays from the json
json.remove(OVERLAY_CATEGORY_FONT);
// adding the new ones
try {
json.put(OVERLAY_CATEGORY_FONT, toPersist.getPackageName());
} catch (JSONException e) {
Log.e(TAG, "Error adding new settings value:\n" + e.getMessage());
return false;
}
// updating the setting
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
json.toString(), UserHandle.USER_CURRENT);
return true;
}
public static FontManager getInstance(Context context, OverlayManagerCompat overlayManager) {
if (sFontOptionManager == null) {
Context applicationContext = context.getApplicationContext();
sFontOptionManager = new FontManager(context, overlayManager, new FontOptionProvider(applicationContext, overlayManager));
}
return sFontOptionManager;
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.statix.android.customization.model.font;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.wallpaper.R;
import com.android.wallpaper.util.ResourceUtils;
import com.android.customization.model.CustomizationManager;
import com.android.customization.model.CustomizationOption;
import com.android.customization.model.ResourceConstants;
import com.android.customization.model.theme.OverlayManagerCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class FontOption implements CustomizationOption<FontOption> {
private final Typeface mHeadlineFont;
private final Typeface mBodyFont;
private String mTitle;
private String mOverlayPackage;
public FontOption(String overlayPackage, String label, Typeface headlineFont, Typeface bodyFont) {
mTitle = label;
mHeadlineFont = headlineFont;
mBodyFont = bodyFont;
mOverlayPackage = overlayPackage;
}
@Override
public void bindThumbnailTile(View view) {
Resources res = view.getContext().getResources();
((TextView) view.findViewById(R.id.thumbnail_text)).setTypeface(
mHeadlineFont);
int colorFilter = ResourceUtils.getColorAttr(view.getContext(),
view.isActivated() || view.getId() == R.id.font_section_tile
? android.R.attr.textColorPrimary
: android.R.attr.textColorTertiary);
((TextView) view.findViewById(R.id.thumbnail_text)).setTextColor(colorFilter);
view.setContentDescription(mTitle);
}
@Override
public boolean isActive(CustomizationManager<FontOption> manager) {
FontManager fontManager = (FontManager) manager;
return fontManager.isActive(this);
}
@Override
public int getLayoutResId() {
return R.layout.theme_font_option;
}
@Override
public String getTitle() {
return mTitle;
}
public String getPackageName() {
return mOverlayPackage;
}
public void bindPreview(ViewGroup container) {
ViewGroup cardBody = container.findViewById(R.id.theme_preview_card_body_container);
if (cardBody.getChildCount() == 0) {
LayoutInflater.from(container.getContext()).inflate(
R.layout.preview_card_font_content, cardBody, true);
}
TextView title = container.findViewById(R.id.font_card_title);
title.setTypeface(mHeadlineFont);
TextView bodyText = container.findViewById(R.id.font_card_body);
bodyText.setTypeface(mBodyFont);
container.findViewById(R.id.font_card_divider).setBackgroundColor(
title.getCurrentTextColor());
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.statix.android.customization.model.font;
import static com.android.customization.model.ResourceConstants.ANDROID_PACKAGE;
import static com.android.customization.model.ResourceConstants.CONFIG_BODY_FONT_FAMILY;
import static com.android.customization.model.ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY;
import static com.android.customization.model.ResourceConstants.OVERLAY_CATEGORY_FONT;
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;
import android.graphics.Typeface;
import android.os.UserHandle;
import android.util.Log;
import com.android.customization.model.ResourceConstants;
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.wallpaper.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FontOptionProvider {
private static final String TAG = "FontOptionProvider";
private Context mContext;
private PackageManager mPm;
private final List<String> mOverlayPackages;
private final List<FontOption> mOptions = new ArrayList<>();
private String mActiveOverlay;
public FontOptionProvider(Context context, OverlayManagerCompat manager) {
mContext = context;
mPm = context.getPackageManager();
mOverlayPackages = new ArrayList<>();
mOverlayPackages.addAll(manager.getOverlayPackagesForCategory(OVERLAY_CATEGORY_FONT,
UserHandle.myUserId(), ResourceConstants.getPackagesToOverlay(mContext)));
mActiveOverlay = manager.getEnabledPackageName(ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT);
}
public List<FontOption> getOptions(boolean reload) {
if (reload) mOptions.clear();
if (mOptions.isEmpty()) loadOptions();
return mOptions;
}
private void loadOptions() {
addDefault();
for (String overlayPackage : mOverlayPackages) {
try {
Resources overlayRes = mPm.getResourcesForApplication(overlayPackage);
Typeface headlineFont = Typeface.create(
getFontFamily(overlayPackage, overlayRes, CONFIG_HEADLINE_FONT_FAMILY),
Typeface.NORMAL);
Typeface bodyFont = Typeface.create(
getFontFamily(overlayPackage, overlayRes, CONFIG_BODY_FONT_FAMILY),
Typeface.NORMAL);
String label = mPm.getApplicationInfo(overlayPackage, 0).loadLabel(mPm).toString();
mOptions.add(new FontOption(overlayPackage, label, headlineFont, bodyFont));
} catch (NameNotFoundException | NotFoundException e) {
Log.w(TAG, String.format("Couldn't load font overlay %s, will skip it",
overlayPackage), e);
}
}
}
private void addDefault() {
Resources system = Resources.getSystem();
Typeface headlineFont = Typeface.create(system.getString(system.getIdentifier(
ResourceConstants.CONFIG_HEADLINE_FONT_FAMILY,"string", ANDROID_PACKAGE)),
Typeface.NORMAL);
Typeface bodyFont = Typeface.create(system.getString(system.getIdentifier(
ResourceConstants.CONFIG_BODY_FONT_FAMILY,
"string", ANDROID_PACKAGE)),
Typeface.NORMAL);
mOptions.add(new FontOption(null, mContext.getString(R.string.default_theme_title),
headlineFont, bodyFont));
}
private String getFontFamily(String overlayPackage, Resources overlayRes, String configName) {
return overlayRes.getString(overlayRes.getIdentifier(configName, "string", overlayPackage));
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2021 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.statix.android.customization.model.font;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
import com.android.customization.model.CustomizationOption;
import com.android.customization.widget.OptionSelectorController;
import com.android.customization.widget.OptionSelectorController.OptionSelectedListener;
import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
import com.android.wallpaper.util.LaunchUtils;
import com.statix.android.customization.picker.font.FontFragment;
import com.statix.android.customization.picker.font.FontSectionView;
import java.util.List;
/** A {@link CustomizationSectionController} for system fonts. */
public class FontSectionController implements CustomizationSectionController<FontSectionView> {
private static final String TAG = "FontSectionController";
private final FontManager mFontOptionsManager;
private final CustomizationSectionNavigationController mSectionNavigationController;
private final Callback mApplyFontCallback = new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(@Nullable Throwable throwable) {
}
};
public FontSectionController(FontManager fontOptionsManager,
CustomizationSectionNavigationController sectionNavigationController) {
mFontOptionsManager = fontOptionsManager;
mSectionNavigationController = sectionNavigationController;
}
@Override
public boolean isAvailable(Context context) {
return mFontOptionsManager.isAvailable();
}
@Override
public FontSectionView createView(Context context) {
FontSectionView fontSectionView = (FontSectionView) LayoutInflater.from(context)
.inflate(R.layout.font_section_view, /* root= */ null);
TextView sectionDescription = fontSectionView.findViewById(R.id.font_section_description);
View sectionTile = fontSectionView.findViewById(R.id.font_section_tile);
mFontOptionsManager.fetchOptions(new OptionsFetchedListener<FontOption>() {
@Override
public void onOptionsLoaded(List<FontOption> options) {
FontOption activeOption = getActiveOption(options);
sectionDescription.setText(activeOption.getTitle());
activeOption.bindThumbnailTile(sectionTile);
}
@Override
public void onError(@Nullable Throwable throwable) {
if (throwable != null) {
Log.e(TAG, "Error loading font options", throwable);
}
sectionDescription.setText(R.string.something_went_wrong);
sectionTile.setVisibility(View.GONE);
}
}, /* reload= */ true);
fontSectionView.setOnClickListener(v -> mSectionNavigationController.navigateTo(
FontFragment.newInstance(context.getString(R.string.font_title))));
return fontSectionView;
}
private FontOption getActiveOption(List<FontOption> options) {
return options.stream()
.filter(option -> mFontOptionsManager.isActive(option))
.findAny()
// For development only, as there should always be a grid set.
.orElse(options.get(0));
}
}

View File

@ -27,8 +27,6 @@ import androidx.annotation.Nullable;
import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
import com.android.customization.model.CustomizationOption;
import com.android.customization.picker.iconpack.IconPackFragment;
import com.android.customization.picker.iconpack.IconPackSectionView;
import com.android.customization.widget.OptionSelectorController;
import com.android.customization.widget.OptionSelectorController.OptionSelectedListener;
@ -36,6 +34,9 @@ import com.android.wallpaper.R;
import com.android.wallpaper.model.CustomizationSectionController;
import com.android.wallpaper.util.LaunchUtils;
import com.statix.android.customization.picker.iconpack.IconPackFragment;
import com.statix.android.customization.picker.iconpack.IconPackSectionView;
import java.util.List;
/** A {@link CustomizationSectionController} for system icons. */

View File

@ -22,6 +22,8 @@ import com.android.wallpaper.model.WorkspaceViewModel;
import com.android.wallpaper.module.CustomizationSections;
import com.statix.android.customization.model.color.ColorSectionController;
import com.statix.android.customization.model.font.FontManager;
import com.statix.android.customization.model.font.FontSectionController;
import com.statix.android.customization.model.iconpack.IconPackManager;
import com.statix.android.customization.model.iconpack.IconPackSectionController;
@ -68,6 +70,9 @@ public final class StatixCustomizationSections implements CustomizationSections
sectionControllers.add(new IconPackSectionController(
IconPackManager.getInstance(activity, new OverlayManagerCompat(activity)), sectionNavigationController));
// Font selection section.
sectionControllers.add(new FontSectionController(
FontManager.getInstance(activity, new OverlayManagerCompat(activity)), sectionNavigationController));
return sectionControllers;
}
}

View File

@ -0,0 +1,206 @@
/*
* Copyright (C) 2018 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.statix.android.customization.picker.font;
import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY_TEXT;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.widget.ContentLoadingProgressBar;
import androidx.recyclerview.widget.RecyclerView;
import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
import com.android.customization.model.CustomizationOption;
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.customization.module.ThemesUserEventLogger;
import com.android.customization.picker.WallpaperPreviewer;
import com.android.customization.widget.OptionSelectorController;
import com.android.customization.widget.OptionSelectorController.CheckmarkStyle;
import com.android.wallpaper.R;
import com.android.wallpaper.picker.AppbarFragment;
import com.android.wallpaper.widget.BottomActionBar;
import com.statix.android.customization.model.font.FontOption;
import com.statix.android.customization.model.font.FontManager;
import java.util.List;
/**
* Fragment that contains the UI for selecting and applying a FontOption.
*/
public class FontFragment extends AppbarFragment {
private static final String TAG = "FontFragment";
private static final String KEY_STATE_SELECTED_OPTION = "FontFragment.selectedOption";
private static final String KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE =
"FontFragment.bottomActionBarVisible";
public static FontFragment newInstance(CharSequence title) {
FontFragment fragment = new FontFragment();
fragment.setArguments(AppbarFragment.createArguments(title));
return fragment;
}
private RecyclerView mOptionsContainer;
private OptionSelectorController<FontOption> mOptionsController;
private FontManager mFontManager;
private FontOption mSelectedOption;
private ContentLoadingProgressBar mLoading;
private ViewGroup mContent;
private View mError;
private BottomActionBar mBottomActionBar;
private final Callback mApplyFontCallback = new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(@Nullable Throwable throwable) {
// Since we disabled it when clicked apply button.
mBottomActionBar.enableActions();
mBottomActionBar.hide();
//TODO(chihhangchuang): handle
}
};
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(
R.layout.fragment_font_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
mContent = view.findViewById(R.id.content_section);
mOptionsContainer = view.findViewById(R.id.options_container);
mLoading = view.findViewById(R.id.loading_indicator);
mError = view.findViewById(R.id.error_section);
// For nav bar edge-to-edge effect.
view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
v.setPadding(
v.getPaddingLeft(),
windowInsets.getSystemWindowInsetTop(),
v.getPaddingRight(),
windowInsets.getSystemWindowInsetBottom());
return windowInsets.consumeSystemWindowInsets();
});
mFontManager = FontManager.getInstance(getContext(), new OverlayManagerCompat(getContext()));
setUpOptions(savedInstanceState);
return view;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (mBottomActionBar != null) {
outState.putBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE, mBottomActionBar.isVisible());
}
}
@Override
protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
super.onBottomActionBarReady(bottomActionBar);
mBottomActionBar = bottomActionBar;
mBottomActionBar.showActionsOnly(APPLY_TEXT);
mBottomActionBar.setActionClickListener(APPLY_TEXT, v -> applyFontOption(mSelectedOption));
}
private void applyFontOption(FontOption fontOption) {
mBottomActionBar.disableActions();
mFontManager.apply(fontOption, mApplyFontCallback);
}
private void setUpOptions(@Nullable Bundle savedInstanceState) {
hideError();
mLoading.show();
mFontManager.fetchOptions(new OptionsFetchedListener<FontOption>() {
@Override
public void onOptionsLoaded(List<FontOption> options) {
mLoading.hide();
mOptionsController = new OptionSelectorController<>(
mOptionsContainer, options, /* useGrid= */ false, CheckmarkStyle.CORNER);
mOptionsController.initOptions(mFontManager);
mSelectedOption = getActiveOption(options);
mOptionsController.setSelectedOption(mSelectedOption);
onOptionSelected(mSelectedOption);
restoreBottomActionBarVisibility(savedInstanceState);
mOptionsController.addListener(selectedOption -> {
onOptionSelected(selectedOption);
mBottomActionBar.show();
});
}
@Override
public void onError(@Nullable Throwable throwable) {
if (throwable != null) {
Log.e(TAG, "Error loading Font options", throwable);
}
showError();
}
}, /*reload= */ true);
}
private FontOption getActiveOption(List<FontOption> options) {
return options.stream()
.filter(option -> mFontManager.isActive(option))
.findAny()
// For development only, as there should always be an Font set.
.orElse(options.get(0));
}
private void hideError() {
mContent.setVisibility(View.VISIBLE);
mError.setVisibility(View.GONE);
}
private void showError() {
mLoading.hide();
mContent.setVisibility(View.GONE);
mError.setVisibility(View.VISIBLE);
}
private void onOptionSelected(CustomizationOption selectedOption) {
mSelectedOption = (FontOption) selectedOption;
refreshPreview();
}
private void refreshPreview() {
mSelectedOption.bindPreview(mContent);
}
private void restoreBottomActionBarVisibility(@Nullable Bundle savedInstanceState) {
boolean isBottomActionBarVisible = savedInstanceState != null
&& savedInstanceState.getBoolean(KEY_STATE_BOTTOM_ACTION_BAR_VISIBLE);
if (mBottomActionBar == null) return;
if (isBottomActionBarVisible) {
mBottomActionBar.show();
} else {
mBottomActionBar.hide();
}
}
}

View File

@ -0,0 +1,12 @@
package com.statix.android.customization.picker.font;
import android.content.Context;
import android.util.AttributeSet;
import com.android.wallpaper.picker.SectionView;
public final class FontSectionView extends SectionView {
public FontSectionView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.customization.picker.iconpack;
package com.statix.android.customization.picker.iconpack;
import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY_TEXT;

View File

@ -1,4 +1,4 @@
package com.android.customization.picker.iconpack;
package com.statix.android.customization.picker.iconpack;
import android.content.Context;
import android.util.AttributeSet;