1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher3;
18
19import android.appwidget.AppWidgetProviderInfo;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.content.res.Resources;
24import android.graphics.Rect;
25import android.util.AttributeSet;
26import android.util.TypedValue;
27import android.view.MotionEvent;
28import android.view.View;
29import android.widget.ImageView;
30import android.widget.LinearLayout;
31import android.widget.TextView;
32
33import com.android.launcher3.R;
34
35/**
36 * The linear layout used strictly for the widget/wallpaper tab of the customization tray
37 */
38public class PagedViewWidget extends LinearLayout {
39    static final String TAG = "PagedViewWidgetLayout";
40
41    private static boolean sDeletePreviewsWhenDetachedFromWindow = true;
42    private static boolean sRecyclePreviewsWhenDetachedFromWindow = true;
43
44    private String mDimensionsFormatString;
45    CheckForShortPress mPendingCheckForShortPress = null;
46    ShortPressListener mShortPressListener = null;
47    boolean mShortPressTriggered = false;
48    static PagedViewWidget sShortpressTarget = null;
49    boolean mIsAppWidget;
50    private final Rect mOriginalImagePadding = new Rect();
51    private Object mInfo;
52    private WidgetPreviewLoader mWidgetPreviewLoader;
53
54    public PagedViewWidget(Context context) {
55        this(context, null);
56    }
57
58    public PagedViewWidget(Context context, AttributeSet attrs) {
59        this(context, attrs, 0);
60    }
61
62    public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) {
63        super(context, attrs, defStyle);
64
65        final Resources r = context.getResources();
66        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
67
68        setWillNotDraw(false);
69        setClipToPadding(false);
70    }
71
72    @Override
73    protected void onFinishInflate() {
74        super.onFinishInflate();
75
76        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
77        mOriginalImagePadding.left = image.getPaddingLeft();
78        mOriginalImagePadding.top = image.getPaddingTop();
79        mOriginalImagePadding.right = image.getPaddingRight();
80        mOriginalImagePadding.bottom = image.getPaddingBottom();
81
82        // Ensure we are using the right text size
83        LauncherAppState app = LauncherAppState.getInstance();
84        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
85        TextView name = (TextView) findViewById(R.id.widget_name);
86        if (name != null) {
87            name.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
88        }
89        TextView dims = (TextView) findViewById(R.id.widget_dims);
90        if (dims != null) {
91            dims.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
92        }
93    }
94
95    public static void setDeletePreviewsWhenDetachedFromWindow(boolean value) {
96        sDeletePreviewsWhenDetachedFromWindow = value;
97    }
98
99    public static void setRecyclePreviewsWhenDetachedFromWindow(boolean value) {
100        sRecyclePreviewsWhenDetachedFromWindow = value;
101    }
102
103    @Override
104    protected void onDetachedFromWindow() {
105        super.onDetachedFromWindow();
106
107        if (sDeletePreviewsWhenDetachedFromWindow) {
108            final ImageView image = (ImageView) findViewById(R.id.widget_preview);
109            if (image != null) {
110                FastBitmapDrawable preview = (FastBitmapDrawable) image.getDrawable();
111                if (sRecyclePreviewsWhenDetachedFromWindow &&
112                        mInfo != null && preview != null && preview.getBitmap() != null) {
113                    mWidgetPreviewLoader.recycleBitmap(mInfo, preview.getBitmap());
114                }
115                image.setImageDrawable(null);
116            }
117        }
118    }
119
120    public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
121            int maxWidth, int[] cellSpan, WidgetPreviewLoader loader) {
122        LauncherAppState app = LauncherAppState.getInstance();
123        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
124
125        mIsAppWidget = true;
126        mInfo = info;
127        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
128        if (maxWidth > -1) {
129            image.setMaxWidth(maxWidth);
130        }
131        final TextView name = (TextView) findViewById(R.id.widget_name);
132        name.setText(info.label);
133        final TextView dims = (TextView) findViewById(R.id.widget_dims);
134        if (dims != null) {
135            int hSpan = Math.min(cellSpan[0], (int) grid.numColumns);
136            int vSpan = Math.min(cellSpan[1], (int) grid.numRows);
137            dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
138        }
139        mWidgetPreviewLoader = loader;
140    }
141
142    public void applyFromResolveInfo(
143            PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
144        mIsAppWidget = false;
145        mInfo = info;
146        CharSequence label = info.loadLabel(pm);
147        final TextView name = (TextView) findViewById(R.id.widget_name);
148        name.setText(label);
149        final TextView dims = (TextView) findViewById(R.id.widget_dims);
150        if (dims != null) {
151            dims.setText(String.format(mDimensionsFormatString, 1, 1));
152        }
153        mWidgetPreviewLoader = loader;
154    }
155
156    public int[] getPreviewSize() {
157        final ImageView i = (ImageView) findViewById(R.id.widget_preview);
158        int[] maxSize = new int[2];
159        maxSize[0] = i.getWidth() - mOriginalImagePadding.left - mOriginalImagePadding.right;
160        maxSize[1] = i.getHeight() - mOriginalImagePadding.top;
161        return maxSize;
162    }
163
164    void applyPreview(FastBitmapDrawable preview, int index) {
165        final PagedViewWidgetImageView image =
166            (PagedViewWidgetImageView) findViewById(R.id.widget_preview);
167        if (preview != null) {
168            image.mAllowRequestLayout = false;
169            image.setImageDrawable(preview);
170            if (mIsAppWidget) {
171                // center horizontally
172                int[] imageSize = getPreviewSize();
173                int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2;
174                image.setPadding(mOriginalImagePadding.left + centerAmount,
175                        mOriginalImagePadding.top,
176                        mOriginalImagePadding.right,
177                        mOriginalImagePadding.bottom);
178            }
179            image.setAlpha(1f);
180            image.mAllowRequestLayout = true;
181        }
182    }
183
184    void setShortPressListener(ShortPressListener listener) {
185        mShortPressListener = listener;
186    }
187
188    interface ShortPressListener {
189        void onShortPress(View v);
190        void cleanUpShortPress(View v);
191    }
192
193    class CheckForShortPress implements Runnable {
194        public void run() {
195            if (sShortpressTarget != null) return;
196            if (mShortPressListener != null) {
197                mShortPressListener.onShortPress(PagedViewWidget.this);
198                sShortpressTarget = PagedViewWidget.this;
199            }
200            mShortPressTriggered = true;
201        }
202    }
203
204    private void checkForShortPress() {
205        if (sShortpressTarget != null) return;
206        if (mPendingCheckForShortPress == null) {
207            mPendingCheckForShortPress = new CheckForShortPress();
208        }
209        postDelayed(mPendingCheckForShortPress, 120);
210    }
211
212    /**
213     * Remove the longpress detection timer.
214     */
215    private void removeShortPressCallback() {
216        if (mPendingCheckForShortPress != null) {
217          removeCallbacks(mPendingCheckForShortPress);
218        }
219    }
220
221    private void cleanUpShortPress() {
222        removeShortPressCallback();
223        if (mShortPressTriggered) {
224            if (mShortPressListener != null) {
225                mShortPressListener.cleanUpShortPress(PagedViewWidget.this);
226            }
227            mShortPressTriggered = false;
228        }
229    }
230
231    static void resetShortPressTarget() {
232        sShortpressTarget = null;
233    }
234
235    @Override
236    public boolean onTouchEvent(MotionEvent event) {
237        super.onTouchEvent(event);
238
239        switch (event.getAction()) {
240            case MotionEvent.ACTION_UP:
241                cleanUpShortPress();
242                break;
243            case MotionEvent.ACTION_DOWN:
244                checkForShortPress();
245                break;
246            case MotionEvent.ACTION_CANCEL:
247                cleanUpShortPress();
248                break;
249            case MotionEvent.ACTION_MOVE:
250                break;
251        }
252
253        // We eat up the touch events here, since the PagedView (which uses the same swiping
254        // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
255        // the user is scrolling between pages.  This means that if the pages themselves don't
256        // handle touch events, it gets forwarded up to PagedView itself, and it's own
257        // onTouchEvent() handling will prevent further intercept touch events from being called
258        // (it's the same view in that case).  This is not ideal, but to prevent more changes,
259        // we just always mark the touch event as handled.
260        return true;
261    }
262}
263