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:
parent
d615c2d371
commit
319ea20521
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue