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;
+                }
+            }
+        }
+    };
+}