Add full preview for Grid (part 1)
- Add new Activity and Fragment for full preview
- Add grid full preview page
- Screenshot: https://screenshot.googleplex.com/thKsXZgStcX.png
Test: Manually
Bug: 151287994
Change-Id: I5a88c9c60a616abf8a8a2a1dd82dbcbb7ef72139
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c0b2e8d..619d745 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,6 +34,10 @@
<activity android:name="com.android.customization.picker.theme.CustomThemeActivity"
android:resizeableActivity="false"
android:theme="@style/CustomizationTheme.NoActionBar"/>
+
+ <activity android:name="com.android.customization.picker.ViewOnlyFullPreviewActivity"
+ android:resizeableActivity="false"
+ android:theme="@style/CustomizationTheme.NoActionBar"/>
</application>
</manifest>
diff --git a/res/layout/activity_full_preview.xml b/res/layout/activity_full_preview.xml
new file mode 100644
index 0000000..b1e0584
--- /dev/null
+++ b/res/layout/activity_full_preview.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/preview_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginBottom="@dimen/bottom_navbar_height" />
+
+ <include layout="@layout/bottom_action_bar" />
+</FrameLayout>
diff --git a/res/layout/fragment_grid_full_preview.xml b/res/layout/fragment_grid_full_preview.xml
new file mode 100644
index 0000000..80733ab
--- /dev/null
+++ b/res/layout/fragment_grid_full_preview.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/section_header"/>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/fullscreen_preview_background">
+
+ <androidx.cardview.widget.CardView
+ style="@style/FullContentPreviewCard"
+ android:id="@+id/grid_full_preview_card"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="32dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/grid_full_preview_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <SurfaceView
+ android:id="@+id/grid_full_preview_surface"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </androidx.cardview.widget.CardView>
+ </FrameLayout>
+</LinearLayout>
diff --git a/src/com/android/customization/model/grid/GridOption.java b/src/com/android/customization/model/grid/GridOption.java
index e3b90f4..2f32ce1 100644
--- a/src/com/android/customization/model/grid/GridOption.java
+++ b/src/com/android/customization/model/grid/GridOption.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.graphics.PorterDuff.Mode;
import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.view.View;
import android.widget.ImageView;
@@ -29,10 +31,23 @@
/**
* Represents a grid layout option available in the current launcher.
*/
-public class GridOption implements CustomizationOption<GridOption> {
+// TODO(chihhangchuang): Consider moving Parcelable into CustomizationOption.
+public class GridOption implements CustomizationOption<GridOption>, Parcelable {
+ public static final Creator<GridOption> CREATOR = new Creator<GridOption>() {
+ @Override
+ public GridOption createFromParcel(Parcel in) {
+ return new GridOption(in);
+ }
+
+ @Override
+ public GridOption[] newArray(int size) {
+ return new GridOption[size];
+ }
+ };
private final String mTitle;
private final boolean mIsCurrent;
+ private final String mIconShapePath;
private final GridTileDrawable mTileDrawable;
public final String name;
public final int rows;
@@ -44,7 +59,8 @@
Uri previewImageUri, int previewPagesCount, String iconShapePath) {
mTitle = title;
mIsCurrent = isCurrent;
- mTileDrawable = new GridTileDrawable(rows, cols, iconShapePath);
+ mIconShapePath = iconShapePath;
+ mTileDrawable = new GridTileDrawable(rows, cols, mIconShapePath);
this.name = name;
this.rows = rows;
this.cols = cols;
@@ -52,6 +68,18 @@
this.previewPagesCount = previewPagesCount;
}
+ protected GridOption(Parcel in) {
+ mTitle = in.readString();
+ mIsCurrent = in.readByte() != 0;
+ mIconShapePath = in.readString();
+ name = in.readString();
+ rows = in.readInt();
+ cols = in.readInt();
+ previewImageUri = in.readParcelable(Uri.class.getClassLoader());
+ previewPagesCount = in.readInt();
+ mTileDrawable = new GridTileDrawable(rows, cols, mIconShapePath);
+ }
+
@Override
public String getTitle() {
return mTitle;
@@ -76,4 +104,21 @@
public int getLayoutResId() {
return R.layout.grid_option;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(mTitle);
+ parcel.writeByte((byte) (mIsCurrent ? 1 : 0));
+ parcel.writeString(mIconShapePath);
+ parcel.writeString(name);
+ parcel.writeInt(rows);
+ parcel.writeInt(cols);
+ parcel.writeParcelable(previewImageUri, i);
+ parcel.writeInt(previewPagesCount);
+ }
}
diff --git a/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
new file mode 100644
index 0000000..19324da
--- /dev/null
+++ b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.customization.picker.grid.GridFullPreviewFragment;
+import com.android.wallpaper.R;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.BottomActionBar.BottomActionBarHost;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Activity for full preview. */
+public class ViewOnlyFullPreviewActivity extends FragmentActivity implements BottomActionBarHost {
+
+ private static final String EXTRA_PREVIEW_SECTION = "preview_section";
+ private static final String EXTRA_PREVIEW_BUNDLE = "preview_bundle";
+
+ public static final int SECTION_STYLE = 0;
+ public static final int SECTION_GRID = 1;
+ public static final int SECTION_CLOCK = 2;
+
+ @IntDef({SECTION_STYLE, SECTION_GRID, SECTION_CLOCK})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Section {}
+
+ /** Returns a new Intent with the provided data in the extra. */
+ public static Intent newIntent(Context packageContext, @Section int section, Bundle bundle) {
+ Intent intent = new Intent(packageContext, ViewOnlyFullPreviewActivity.class);
+ intent.putExtra(EXTRA_PREVIEW_SECTION, section);
+ intent.putExtra(EXTRA_PREVIEW_BUNDLE, bundle);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_full_preview);
+
+ final Intent intent = getIntent();
+ @Section final int section = intent.getIntExtra(EXTRA_PREVIEW_SECTION, 0);
+ final Bundle bundle = intent.getBundleExtra(EXTRA_PREVIEW_BUNDLE);
+ if (section == SECTION_GRID) {
+ showFragment(
+ GridFullPreviewFragment.newInstance(getString(R.string.grid_title), bundle));
+ }
+ }
+
+ @Override
+ public BottomActionBar getBottomActionBar() {
+ return findViewById(R.id.bottom_actionbar);
+ }
+
+ private void showFragment(Fragment fragment) {
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.preview_fragment_container, fragment)
+ .commitNow();
+ }
+}
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index 4bf29f8..b3495c6 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -15,10 +15,16 @@
*/
package com.android.customization.picker.grid;
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.customization.picker.ViewOnlyFullPreviewActivity.SECTION_GRID;
+import static com.android.customization.picker.grid.GridFullPreviewFragment.EXTRA_GRID_OPTION;
+import static com.android.customization.picker.grid.GridFullPreviewFragment.EXTRA_WALLPAPER_INFO;
import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
@@ -50,6 +56,7 @@
import com.android.customization.module.ThemesUserEventLogger;
import com.android.customization.picker.BasePreviewAdapter;
import com.android.customization.picker.BasePreviewAdapter.PreviewPage;
+import com.android.customization.picker.ViewOnlyFullPreviewActivity;
import com.android.customization.widget.OptionSelectorController;
import com.android.wallpaper.R;
import com.android.wallpaper.asset.Asset;
@@ -72,7 +79,9 @@
*/
public class GridFragment extends AppbarFragment {
- private static final int PREVIEW_FADE_DURATION_MS = 100;
+ static final int PREVIEW_FADE_DURATION_MS = 100;
+
+ private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
private static final String TAG = "GridFragment";
@@ -192,13 +201,24 @@
}
@Override
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
+ applyGridOption(data.getParcelableExtra(EXTRA_GRID_OPTION));
+ }
+ }
+
+
+ @Override
protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
mBottomActionBar = bottomActionBar;
mBottomActionBar.showActionsOnly(APPLY);
- mBottomActionBar.setActionClickListener(APPLY, unused -> {
- mBottomActionBar.disableActions();
- mGridManager.apply(mSelectedOption, mApplyGridCallback);
- });
+ mBottomActionBar.setActionClickListener(APPLY, unused -> applyGridOption(mSelectedOption));
+ }
+
+ private void applyGridOption(GridOption gridOption) {
+ mBottomActionBar.disableActions();
+ mGridManager.apply(gridOption, mApplyGridCallback);
}
private void loadWallpaperBackground() {
@@ -273,6 +293,14 @@
mError.setVisibility(View.VISIBLE);
}
+ private void showFullPreview() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_WALLPAPER_INFO, mHomeWallpaper);
+ bundle.putParcelable(EXTRA_GRID_OPTION, mSelectedOption);
+ Intent intent = ViewOnlyFullPreviewActivity.newIntent(getContext(), SECTION_GRID, bundle);
+ startActivityForResult(intent, FULL_PREVIEW_REQUEST_CODE);
+ }
+
private class GridPreviewPage extends PreviewPage {
private final int mPageId;
private final Asset mPreviewAsset;
@@ -339,6 +367,7 @@
super.setCard(card);
mPreview = card.findViewById(R.id.grid_preview_image);
mPreviewSurface = card.findViewById(R.id.grid_preview_surface);
+ card.setOnClickListener(view -> showFullPreview());
}
public void bindPreviewContent() {
diff --git a/src/com/android/customization/picker/grid/GridFullPreviewFragment.java b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
new file mode 100644
index 0000000..9e469a3
--- /dev/null
+++ b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker.grid;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.customization.picker.grid.GridFragment.PREVIEW_FADE_DURATION_MS;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.cardview.widget.CardView;
+
+import com.android.customization.model.grid.GridOption;
+import com.android.customization.model.grid.GridOptionsManager;
+import com.android.customization.model.grid.LauncherGridOptionsProvider;
+import com.android.customization.module.CustomizationInjector;
+import com.android.customization.module.ThemesUserEventLogger;
+import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.ContentUriAsset;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.picker.AppbarFragment;
+import com.android.wallpaper.util.SurfaceViewUtils;
+import com.android.wallpaper.util.TileSizeCalculator;
+import com.android.wallpaper.widget.BottomActionBar;
+
+import com.bumptech.glide.request.RequestOptions;
+
+/** A Fragment for grid full preview page. */
+public class GridFullPreviewFragment extends AppbarFragment {
+
+ static final String EXTRA_WALLPAPER_INFO = "wallpaper_info";
+ static final String EXTRA_GRID_OPTION = "grid_option";
+
+ private GridOptionsManager mGridManager;
+ private WallpaperInfo mWallpaper;
+ private GridOption mGridOption;
+
+ private CardView mCardView;
+ private ImageView mPreview;
+ private SurfaceView mPreviewSurface;
+ private BitmapDrawable mCardBackground;
+
+ /**
+ * Returns a new {@link GridFullPreviewFragment} with the provided title and bundle arguments
+ * set.
+ */
+ public static GridFullPreviewFragment newInstance(CharSequence title, Bundle intentBundle) {
+ GridFullPreviewFragment fragment = new GridFullPreviewFragment();
+ Bundle bundle = new Bundle();
+ bundle.putAll(AppbarFragment.createArguments(title));
+ bundle.putAll(intentBundle);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
+ mGridOption = getArguments().getParcelable(EXTRA_GRID_OPTION);
+
+ CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+ ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
+ getContext());
+
+ mGridManager = new GridOptionsManager(new LauncherGridOptionsProvider(getContext(),
+ getString(R.string.grid_control_metadata_name)),
+ eventLogger);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(
+ R.layout.fragment_grid_full_preview, container, /* attachToRoot */ false);
+ setUpToolbar(view);
+
+ mCardView = view.findViewById(R.id.grid_full_preview_card);
+ mPreview = view.findViewById(R.id.grid_full_preview_image);
+ mPreviewSurface = view.findViewById(R.id.grid_full_preview_surface);
+
+ final DisplayMetrics dm = getResources().getDisplayMetrics();
+ float screenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
+
+ view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ int cardWidth = (int) (mCardView.getMeasuredHeight() / screenAspectRatio);
+ ViewGroup.LayoutParams layoutParams = mCardView.getLayoutParams();
+ layoutParams.width = cardWidth;
+ mCardView.setLayoutParams(layoutParams);
+ mCardView.setRadius(TileSizeCalculator.getPreviewCornerRadius(
+ getActivity(), mCardView.getMeasuredWidth()));
+ view.removeOnLayoutChangeListener(this);
+ loadWallpaperBackground();
+ }
+ });
+
+ // Needs to fetch for the result of #usesSurfaceView.
+ mGridManager.fetchOptions(grids -> bindPreviewContent(), false);
+ return view;
+ }
+
+ @Override
+ protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+ bottomActionBar.bindBackButtonToSystemBackKey(getActivity());
+ bottomActionBar.showActionsOnly(APPLY);
+ bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
+ bottomActionBar.show();
+ }
+
+ private void finishActivityWithResultOk() {
+ Activity activity = requireActivity();
+ activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_GRID_OPTION, mGridOption);
+ activity.setResult(RESULT_OK, intent);
+ activity.finish();
+ }
+
+ private void loadWallpaperBackground() {
+ if (mWallpaper != null && mCardView.getMeasuredWidth() > 0
+ && mCardView.getMeasuredHeight() > 0) {
+ mWallpaper.getThumbAsset(getContext()).decodeBitmap(mCardView.getMeasuredWidth(),
+ mCardView.getMeasuredHeight(),
+ bitmap -> {
+ mCardBackground = new BitmapDrawable(getResources(), bitmap);
+ bindWallpaperIfAvailable();
+ });
+ }
+ }
+
+ private void bindPreviewContent() {
+ bindWallpaperIfAvailable();
+ final boolean usesSurfaceViewForPreview = mGridManager.usesSurfaceView();
+ mPreview.setVisibility(usesSurfaceViewForPreview ? View.GONE : View.VISIBLE);
+ mPreviewSurface.setVisibility(usesSurfaceViewForPreview ? View.VISIBLE : View.GONE);
+ if (usesSurfaceViewForPreview) {
+ mPreviewSurface.setZOrderOnTop(true);
+ mPreviewSurface.getHolder().addCallback(mSurfaceCallback);
+ } else {
+ final Asset previewAsset = new ContentUriAsset(
+ getContext(),
+ mGridOption.previewImageUri,
+ RequestOptions.fitCenterTransform());
+ previewAsset.loadDrawableWithTransition(getContext(),
+ mPreview /* imageView */,
+ PREVIEW_FADE_DURATION_MS /* duration */,
+ null /* drawableLoadedListener */,
+ getResources().getColor(android.R.color.transparent,
+ null) /* placeHolderColorJ */);
+ }
+ }
+
+ private void bindWallpaperIfAvailable() {
+ if (mCardBackground != null) {
+ mPreview.setBackground(mCardBackground);
+ mPreviewSurface.setBackground(mCardBackground);
+ }
+ }
+
+ private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+
+ private Surface mLastSurface;
+ private Message mCallback;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mLastSurface != holder.getSurface()) {
+ mLastSurface = holder.getSurface();
+ Bundle result = mGridManager.renderPreview(
+ SurfaceViewUtils.createSurfaceViewRequest(mPreviewSurface),
+ mGridOption.name);
+ if (result != null) {
+ mPreviewSurface.setChildSurfacePackage(
+ SurfaceViewUtils.getSurfacePackage(result));
+ mCallback = SurfaceViewUtils.getCallback(result);
+ }
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mCallback != null) {
+ try {
+ mCallback.replyTo.send(mCallback);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } finally {
+ mCallback = null;
+ }
+ }
+ }
+ };
+}