Improve error handling when loading customizations
Add a loading indicator and an error message if there's
an issue retrieving content for any of the customization
tabs.
Bug: 133326909
Change-Id: I2da6309f5c2b369caffeec4e21c3aa856ddb4e4a
diff --git a/res/layout/fragment_clock_picker.xml b/res/layout/fragment_clock_picker.xml
index 630d37a..1528666 100644
--- a/res/layout/fragment_clock_picker.xml
+++ b/res/layout/fragment_clock_picker.xml
@@ -23,60 +23,89 @@
android:background="?android:colorPrimary">
<include layout="@layout/section_header"/>
- <androidx.constraintlayout.widget.ConstraintLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.customization.widget.PreviewPager
- android:id="@+id/clock_preview_pager"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content_section"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:background="@color/secondary_color"
- app:layout_constrainedHeight="true"
- app:layout_constraintBottom_toTopOf="@id/options_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
- app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
- app:layout_constraintVertical_bias="0.0"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
+ android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/options_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="10dp"
- app:layout_constraintBottom_toTopOf="@id/placeholder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/clock_preview_pager"
- app:layout_constraintVertical_bias="1.0"/>
+ <com.android.customization.widget.PreviewPager
+ android:id="@+id/clock_preview_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/secondary_color"
+ app:layout_constrainedHeight="true"
+ app:layout_constraintBottom_toTopOf="@id/options_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
+ app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
+ app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="spread_inside"/>
- <Space
- android:id="@+id/placeholder"
- android:layout_width="match_parent"
- android:layout_height="@dimen/min_taptarget_height"
- app:layout_constraintBottom_toTopOf="@id/apply_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/options_container"
- app:layout_constraintVertical_bias="1.0"/>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/options_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/options_container_height"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="10dp"
+ app:layout_constraintBottom_toTopOf="@id/placeholder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/clock_preview_pager"
+ app:layout_constraintVertical_bias="1.0"/>
- <Button
- android:id="@+id/apply_button"
- style="@style/ActionPrimaryButton"
+ <Space
+ android:id="@+id/placeholder"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/min_taptarget_height"
+ app:layout_constraintBottom_toTopOf="@id/apply_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHeight_min="@dimen/min_taptarget_height"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/options_container"
+ app:layout_constraintVertical_bias="1.0"/>
+
+ <Button
+ android:id="@+id/apply_button"
+ style="@style/ActionPrimaryButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginEnd="10dp"
+ android:layout_marginVertical="10dp"
+ android:layout_weight="1"
+ android:text="@string/apply_theme_btn"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="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_gravity="end"
- android:layout_marginEnd="10dp"
- android:layout_marginVertical="10dp"
- android:layout_weight="1"
- android:text="@string/apply_theme_btn"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/placeholder"/>
-
- </androidx.constraintlayout.widget.ConstraintLayout>
+ 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/HeaderTextAppearance"
+ 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>
diff --git a/res/layout/fragment_grid_picker.xml b/res/layout/fragment_grid_picker.xml
index d55b5c9..467a620 100644
--- a/res/layout/fragment_grid_picker.xml
+++ b/res/layout/fragment_grid_picker.xml
@@ -23,61 +23,88 @@
android:background="?android:colorPrimary">
<include layout="@layout/section_header"/>
- <androidx.constraintlayout.widget.ConstraintLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.customization.widget.PreviewPager
- android:id="@+id/grid_preview_pager"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content_section"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:background="@color/secondary_color"
- app:card_style="screen_aspect_ratio"
- app:layout_constrainedHeight="true"
- app:layout_constraintBottom_toTopOf="@id/options_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
- app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
- app:layout_constraintVertical_bias="0.0"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
+ android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/options_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="10dp"
- app:layout_constraintBottom_toTopOf="@id/placeholder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/grid_preview_pager"
- app:layout_constraintVertical_bias="1.0"/>
+ <com.android.customization.widget.PreviewPager
+ android:id="@+id/grid_preview_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/secondary_color"
+ app:card_style="screen_aspect_ratio"
+ app:layout_constrainedHeight="true"
+ app:layout_constraintBottom_toTopOf="@id/options_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
+ app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
+ app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_chainStyle="spread_inside"/>
- <Space
- android:id="@+id/placeholder"
- android:layout_width="match_parent"
- android:layout_height="@dimen/min_taptarget_height"
- app:layout_constraintBottom_toTopOf="@id/apply_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/options_container"
- app:layout_constraintVertical_bias="1.0"/>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/options_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/options_container_height"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="10dp"
+ app:layout_constraintBottom_toTopOf="@id/placeholder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/grid_preview_pager"
+ app:layout_constraintVertical_bias="1.0"/>
- <Button
- android:id="@+id/apply_button"
- style="@style/ActionPrimaryButton"
+ <Space
+ android:id="@+id/placeholder"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/min_taptarget_height"
+ app:layout_constraintBottom_toTopOf="@id/apply_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/options_container"
+ app:layout_constraintVertical_bias="1.0"/>
+
+ <Button
+ android:id="@+id/apply_button"
+ style="@style/ActionPrimaryButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginEnd="10dp"
+ android:layout_marginVertical="10dp"
+ android:layout_weight="1"
+ android:text="@string/apply_theme_btn"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="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_gravity="end"
- android:layout_marginEnd="10dp"
- android:layout_marginVertical="10dp"
- android:layout_weight="1"
- android:text="@string/apply_theme_btn"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/placeholder"/>
-
- </androidx.constraintlayout.widget.ConstraintLayout>
+ 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/HeaderTextAppearance"
+ 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>
diff --git a/res/layout/fragment_theme_picker.xml b/res/layout/fragment_theme_picker.xml
index 84c5516..dbb633e 100644
--- a/res/layout/fragment_theme_picker.xml
+++ b/res/layout/fragment_theme_picker.xml
@@ -23,66 +23,95 @@
android:background="?android:colorPrimary">
<include layout="@layout/section_header"/>
- <androidx.constraintlayout.widget.ConstraintLayout
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.customization.widget.PreviewPager
- android:id="@+id/theme_preview_pager"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/content_section"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/secondary_color"
- app:layout_constrainedHeight="true"
- app:layout_constraintBottom_toTopOf="@id/options_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
- app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
+ android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/options_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/options_container_height"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginTop="10dp"
- android:layout_weight="1"
- app:layout_constraintBottom_toTopOf="@id/use_my_wallpaper"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/theme_preview_pager"
- app:layout_constraintVertical_bias="1.0"/>
+ <com.android.customization.widget.PreviewPager
+ android:id="@+id/theme_preview_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/secondary_color"
+ app:layout_constrainedHeight="true"
+ app:layout_constraintBottom_toTopOf="@id/options_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
+ app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintVertical_chainStyle="spread_inside"/>
- <CheckBox
- android:id="@+id/use_my_wallpaper"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/min_taptarget_height"
- android:layout_marginStart="10dp"
- android:ellipsize="end"
- android:gravity="start|center_vertical"
- android:paddingLeft="4dp"
- android:text="@string/keep_my_wallpaper"
- app:layout_constraintBottom_toTopOf="@id/apply_button"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHeight_min="@dimen/min_taptarget_height"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/options_container"
- app:layout_constraintVertical_bias="1.0"/>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/options_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/options_container_height"
+ android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="10dp"
+ android:layout_weight="1"
+ app:layout_constraintBottom_toTopOf="@id/use_my_wallpaper"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/theme_preview_pager"
+ app:layout_constraintVertical_bias="1.0"/>
- <Button
- android:id="@+id/apply_button"
- style="@style/ActionPrimaryButton"
+ <CheckBox
+ android:id="@+id/use_my_wallpaper"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/min_taptarget_height"
+ android:layout_marginStart="10dp"
+ android:ellipsize="end"
+ android:gravity="start|center_vertical"
+ android:paddingLeft="4dp"
+ android:text="@string/keep_my_wallpaper"
+ app:layout_constraintBottom_toTopOf="@id/apply_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHeight_min="@dimen/min_taptarget_height"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/options_container"
+ app:layout_constraintVertical_bias="1.0"/>
+
+ <Button
+ android:id="@+id/apply_button"
+ style="@style/ActionPrimaryButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="10dp"
+ android:text="@string/apply_theme_btn"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="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_gravity="end"
- android:layout_marginEnd="10dp"
- android:layout_marginBottom="10dp"
- android:text="@string/apply_theme_btn"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"/>
+ android:layout_marginTop="200dp"
+ android:layout_gravity="center_horizontal|top"
+ android:indeterminate="true"/>
- </androidx.constraintlayout.widget.ConstraintLayout>
+ <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/HeaderTextAppearance"
+ 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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 46de146..f571b6b 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -187,4 +187,7 @@
<!-- Content description for a screen showing the preview of a clock face. [CHAR_LIMIT=NONE] -->
<string name="clock_preview_content_description"><xliff:g name="clock_name">%1$s</xliff:g> clock preview</string>
+
+ <!-- Generic error message [CHAR_LIMIT=NONE] -->
+ <string name="something_went_wrong">Oops! Something went wrong.</string>
</resources>
diff --git a/src/com/android/customization/model/CustomizationManager.java b/src/com/android/customization/model/CustomizationManager.java
index 3296ca1..7b9f463 100644
--- a/src/com/android/customization/model/CustomizationManager.java
+++ b/src/com/android/customization/model/CustomizationManager.java
@@ -15,6 +15,9 @@
*/
package com.android.customization.model;
+import android.util.Log;
+import android.widget.Toast;
+
import androidx.annotation.Nullable;
import java.util.List;
@@ -49,6 +52,15 @@
* Called when the options have been retrieved.
*/
void onOptionsLoaded(List<T> options);
+
+ /**
+ * Called if there was an error loading grid options
+ */
+ default void onError(@Nullable Throwable throwable) {
+ if (throwable != null) {
+ Log.e("OptionsFecthedListener", "Error loading options", throwable);
+ }
+ }
}
/**
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java
index ba5968e..1599dde 100644
--- a/src/com/android/customization/model/grid/GridOptionsManager.java
+++ b/src/com/android/customization/model/grid/GridOptionsManager.java
@@ -78,8 +78,19 @@
@Override
protected void onPostExecute(List<GridOption> gridOptions) {
if (mCallback != null) {
- mCallback.onOptionsLoaded(gridOptions != null ? gridOptions
- : Collections.emptyList());
+ if (gridOptions != null && !gridOptions.isEmpty()) {
+ mCallback.onOptionsLoaded(gridOptions);
+ } else {
+ mCallback.onError(null);
+ }
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ super.onCancelled();
+ if (mCallback != null) {
+ mCallback.onError(null);
}
}
}
diff --git a/src/com/android/customization/picker/clock/ClockFragment.java b/src/com/android/customization/picker/clock/ClockFragment.java
index c3e1c97..14dbc16 100644
--- a/src/com/android/customization/picker/clock/ClockFragment.java
+++ b/src/com/android/customization/picker/clock/ClockFragment.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -26,9 +27,11 @@
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.clock.BaseClockManager;
import com.android.customization.model.clock.Clockface;
import com.android.customization.module.ThemesUserEventLogger;
@@ -41,11 +44,15 @@
import com.android.wallpaper.module.InjectorProvider;
import com.android.wallpaper.picker.ToolbarFragment;
+import java.util.List;
+
/**
* Fragment that contains the main UI for selecting and applying a Clockface.
*/
public class ClockFragment extends ToolbarFragment {
+ private static final String TAG = "ClockFragment";
+
/**
* Interface to be implemented by an Activity hosting a {@link ClockFragment}
*/
@@ -64,6 +71,9 @@
private Clockface mSelectedOption;
private BaseClockManager mClockManager;
private PreviewPager mPreviewPager;
+ private ContentLoadingProgressBar mLoading;
+ private View mContent;
+ private View mError;
private ThemesUserEventLogger mEventLogger;
@Override
@@ -81,8 +91,11 @@
View view = inflater.inflate(
R.layout.fragment_clock_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
+ mContent = view.findViewById(R.id.content_section);
mPreviewPager = view.findViewById(R.id.clock_preview_pager);
mOptionsContainer = view.findViewById(R.id.options_container);
+ mLoading = view.findViewById(R.id.loading_indicator);
+ mError = view.findViewById(R.id.error_section);
setUpOptions();
view.findViewById(R.id.apply_button).setOnClickListener(v -> {
mClockManager.apply(mSelectedOption, new Callback() {
@@ -95,6 +108,9 @@
@Override
public void onError(@Nullable Throwable throwable) {
+ if (throwable != null) {
+ Log.e(TAG, "Error loading clockfaces", throwable);
+ }
//TODO(santie): handle
}
});
@@ -108,26 +124,50 @@
}
private void setUpOptions() {
- mClockManager.fetchOptions(options -> {
- mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
+ hideError();
+ mLoading.show();
+ mClockManager.fetchOptions(new OptionsFetchedListener<Clockface>() {
+ @Override
+ public void onOptionsLoaded(List<Clockface> options) {
+ mLoading.hide();
+ mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
- mOptionsController.addListener(selected -> {
- mSelectedOption = (Clockface) selected;
- mEventLogger.logClockSelected(mSelectedOption);
- createAdapter();
- });
- mOptionsController.initOptions(mClockManager);
- for (Clockface option : options) {
- if (option.isActive(mClockManager)) {
- mSelectedOption = option;
+ mOptionsController.addListener(selected -> {
+ mSelectedOption = (Clockface) selected;
+ mEventLogger.logClockSelected(mSelectedOption);
+ createAdapter();
+ });
+ mOptionsController.initOptions(mClockManager);
+ for (Clockface option : options) {
+ if (option.isActive(mClockManager)) {
+ mSelectedOption = option;
+ }
+ }
+ // For development only, as there should always be a grid set.
+ if (mSelectedOption == null) {
+ mSelectedOption = options.get(0);
+ }
+ createAdapter();
+ }
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ if (throwable != null) {
+ Log.e(TAG, "Error loading clockfaces", throwable);
}
+ showError();
}
- // For development only, as there should always be a grid set.
- if (mSelectedOption == null) {
- mSelectedOption = options.get(0);
- }
- createAdapter();
- }, false);
+ }, false);
+ }
+
+ 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 static class ClockfacePreviewPage extends PreviewPage {
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index d50c7a8..3c395a1 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -22,6 +22,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
@@ -31,9 +32,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
+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.grid.GridOption;
import com.android.customization.model.grid.GridOptionsManager;
import com.android.customization.module.ThemesUserEventLogger;
@@ -51,6 +54,8 @@
import com.bumptech.glide.request.RequestOptions;
+import java.util.List;
+
/**
* Fragment that contains the UI for selecting and applying a GridOption.
*/
@@ -58,6 +63,8 @@
private static final int PREVIEW_FADE_DURATION_MS = 100;
+ private static final String TAG = "GridFragment";
+
/**
* Interface to be implemented by an Activity hosting a {@link GridFragment}
*/
@@ -82,6 +89,9 @@
private GridOptionsManager mGridManager;
private GridOption mSelectedOption;
private PreviewPager mPreviewPager;
+ private ContentLoadingProgressBar mLoading;
+ private View mContent;
+ private View mError;
private ThemesUserEventLogger mEventLogger;
@Override
@@ -99,8 +109,11 @@
View view = inflater.inflate(
R.layout.fragment_grid_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
+ mContent = view.findViewById(R.id.content_section);
mPreviewPager = view.findViewById(R.id.grid_preview_pager);
mOptionsContainer = view.findViewById(R.id.options_container);
+ mLoading = view.findViewById(R.id.loading_indicator);
+ mError = view.findViewById(R.id.error_section);
final Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
mScreenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
@@ -161,28 +174,53 @@
}
private void setUpOptions() {
- mGridManager.fetchOptions(options -> {
- mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
+ hideError();
+ mLoading.show();
+ mGridManager.fetchOptions(new OptionsFetchedListener<GridOption>() {
+ @Override
+ public void onOptionsLoaded(List<GridOption> options) {
+ mLoading.hide();
+ mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
- mOptionsController.addListener(selected -> {
- mSelectedOption = (GridOption) selected;
- mEventLogger.logGridSelected(mSelectedOption);
- createAdapter();
- });
- mOptionsController.initOptions(mGridManager);
- for (GridOption option : options) {
- if (option.isActive(mGridManager)) {
- mSelectedOption = option;
+ mOptionsController.addListener(selected -> {
+ mSelectedOption = (GridOption) selected;
+ mEventLogger.logGridSelected(mSelectedOption);
+ createAdapter();
+ });
+ mOptionsController.initOptions(mGridManager);
+ for (GridOption option : options) {
+ if (option.isActive(mGridManager)) {
+ mSelectedOption = option;
+ }
}
+ // For development only, as there should always be a grid set.
+ if (mSelectedOption == null) {
+ mSelectedOption = options.get(0);
+ }
+ createAdapter();
}
- // For development only, as there should always be a grid set.
- if (mSelectedOption == null) {
- mSelectedOption = options.get(0);
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ if (throwable != null) {
+ Log.e(TAG, "Error loading grid options", throwable);
+ }
+ showError();
}
- createAdapter();
}, false);
}
+ 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 class GridPreviewPage extends PreviewPage {
private final int mPageId;
private final Asset mPreviewAsset;
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index 227dae5..bec3cf8 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -43,9 +43,12 @@
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.grid.GridOption;
import com.android.customization.model.theme.ThemeBundle;
import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
import com.android.customization.model.theme.ThemeManager;
@@ -94,6 +97,9 @@
private ThemeBundle mSelectedTheme;
private ThemePreviewAdapter mAdapter;
private PreviewPager mPreviewPager;
+ private ContentLoadingProgressBar mLoading;
+ private View mContent;
+ private View mError;
private boolean mUseMyWallpaper;
private WallpaperInfo mCurrentHomeWallpaper;
private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
@@ -115,6 +121,9 @@
R.layout.fragment_theme_picker, container, /* attachToRoot */ false);
setUpToolbar(view);
+ mContent = view.findViewById(R.id.content_section);
+ mLoading = view.findViewById(R.id.loading_indicator);
+ mError = view.findViewById(R.id.error_section);
mCurrentWallpaperFactory = InjectorProvider.getInjector()
.getCurrentWallpaperFactory(getActivity().getApplicationContext());
mPreviewPager = view.findViewById(R.id.theme_preview_pager);
@@ -237,45 +246,70 @@
? View.INVISIBLE : View.VISIBLE);
}
+ 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 setUpOptions(@Nullable Bundle savedInstanceState) {
- mThemeManager.fetchOptions(options -> {
- mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
- mOptionsController.addListener(selected -> {
- if (selected instanceof CustomTheme && !((CustomTheme) selected).isDefined()) {
- navigateToCustomTheme((CustomTheme) selected);
- } else {
- mSelectedTheme = (ThemeBundle) selected;
- if (mUseMyWallpaper || mSelectedTheme instanceof CustomTheme) {
- mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
+ hideError();
+ mLoading.show();
+ mThemeManager.fetchOptions(new OptionsFetchedListener<ThemeBundle>() {
+ @Override
+ public void onOptionsLoaded(List<ThemeBundle> options) {
+ mOptionsController = new OptionSelectorController<>(mOptionsContainer, options);
+ mOptionsController.addListener(selected -> {
+ mLoading.hide();
+ if (selected instanceof CustomTheme && !((CustomTheme) selected).isDefined()) {
+ navigateToCustomTheme((CustomTheme) selected);
} else {
- mSelectedTheme.setOverrideThemeWallpaper(null);
+ mSelectedTheme = (ThemeBundle) selected;
+ if (mUseMyWallpaper || mSelectedTheme instanceof CustomTheme) {
+ mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
+ } else {
+ mSelectedTheme.setOverrideThemeWallpaper(null);
+ }
+ mEventLogger.logThemeSelected(mSelectedTheme,
+ selected instanceof CustomTheme);
+ createAdapter(options);
+ updateButtonsVisibility();
}
- mEventLogger.logThemeSelected(mSelectedTheme, selected instanceof CustomTheme);
- createAdapter(options);
- updateButtonsVisibility();
+ });
+ mOptionsController.initOptions(mThemeManager);
+ String previouslySelected = savedInstanceState != null
+ ? savedInstanceState.getString(KEY_SELECTED_THEME) : null;
+ for (ThemeBundle theme : options) {
+ if (previouslySelected != null
+ && previouslySelected.equals(theme.getSerializedPackages())) {
+ mSelectedTheme = theme;
+ } else if (theme.isActive(mThemeManager)) {
+ mSelectedTheme = theme;
+ break;
+ }
}
- });
- mOptionsController.initOptions(mThemeManager);
- String previouslySelected = savedInstanceState != null
- ? savedInstanceState.getString(KEY_SELECTED_THEME) : null;
- for (ThemeBundle theme : options) {
- if (previouslySelected != null
- && previouslySelected.equals(theme.getSerializedPackages())) {
- mSelectedTheme = theme;
- } else if (theme.isActive(mThemeManager)) {
- mSelectedTheme = theme;
- break;
+ if (mSelectedTheme == null) {
+ // Select the default theme if there is no matching custom enabled theme
+ // TODO(b/124796742): default to custom if there is no matching theme bundle
+ mSelectedTheme = options.get(0);
+ } else {
+ // Only show show checkmark if we found a matching theme
+ mOptionsController.setAppliedOption(mSelectedTheme);
}
+ mOptionsController.setSelectedOption(mSelectedTheme);
}
- if (mSelectedTheme == null) {
- // Select the default theme if there is no matching custom enabled theme
- // TODO(b/124796742): default to custom if there is no matching theme bundle
- mSelectedTheme = options.get(0);
- } else {
- // Only show show checkmark if we found a matching theme
- mOptionsController.setAppliedOption(mSelectedTheme);
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ if (throwable != null) {
+ Log.e(TAG, "Error loading theme bundles", throwable);
+ }
+ showError();
}
- mOptionsController.setSelectedOption(mSelectedTheme);
}, false);
}