129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung/* 23f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song * Copyright (C) 2015 The Android Open Source Project 329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * 429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * Licensed under the Apache License, Version 2.0 (the "License"); 529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * you may not use this file except in compliance with the License. 629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * You may obtain a copy of the License at 729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * 829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * http://www.apache.org/licenses/LICENSE-2.0 929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * 1029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * Unless required by applicable law or agreed to in writing, software 1129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * distributed under the License is distributed on an "AS IS" BASIS, 1229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1329d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * See the License for the specific language governing permissions and 1429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung * limitations under the License. 1529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung */ 1629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 173f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songpackage com.android.launcher3.widget; 1829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 1929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chungimport android.content.Context; 2059e1f9a07eef87b1d287956d21b8d9c5b27faf9cWinson Chungimport android.content.res.Resources; 215b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyalimport android.graphics.Bitmap; 2229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chungimport android.util.AttributeSet; 233f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport android.util.Log; 24ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellorimport android.view.MotionEvent; 25ed66b2bac7447febe2e405b4ce725cae4f6b5988Adam Cohenimport android.view.View; 265b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyalimport android.view.View.OnLayoutChangeListener; 27f00d02b254d94eeaf52742e0640e9eacd63fafcaHyunyoung Songimport android.view.ViewPropertyAnimator; 2829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chungimport android.widget.LinearLayout; 2929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chungimport android.widget.TextView; 3029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 313f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport com.android.launcher3.DeviceProfile; 323f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport com.android.launcher3.ItemInfo; 332e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohenimport com.android.launcher3.Launcher; 343f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport com.android.launcher3.LauncherAppState; 35bb83520a620919a8f19472ba4426585d007d1abaMady Mellorimport com.android.launcher3.SimpleOnStylusPressListener; 363f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport com.android.launcher3.R; 37ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellorimport com.android.launcher3.StylusEventHelper; 383f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songimport com.android.launcher3.WidgetPreviewLoader; 395b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyalimport com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; 404ddc40130aa0460924364eab3557088421eebe0cSunny Goyalimport com.android.launcher3.model.WidgetItem; 41ffe83f13319cdb833a25596d47cf1af098cc4c60Sunny Goyal 4229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung/** 43f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * Represents the individual cell of the widget inside the widget tray. The preview is drawn 44f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * horizontally centered, and scaled down if needed. 45f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * 46f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * This view does not support padding. Since the image is scaled down to fit the view, padding will 47f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth 48f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * transition from the view to drag view, so when adding padding support, DnD would need to 49f52abd7758b8f245522936e941336e574debabf5Sunny Goyal * consider the appropriate scaling factor. 5029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung */ 513f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Songpublic class WidgetCell extends LinearLayout implements OnLayoutChangeListener { 5229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 534e8fb91cf19b7d621de8cbed2bde2c8dac734121Hyunyoung Song private static final String TAG = "WidgetCell"; 543f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song private static final boolean DEBUG = false; 553f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song 56f17a1c9ad99d46a65de797422fd439382114b348Hyunyoung Song private static final int FADE_IN_DURATION_MS = 90; 574b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song 584b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song /** Widget cell width is calculated by multiplying this factor to grid cell width. */ 595cd1d92f6c40b47eff12859d4e13b6431aa778c0Hyunyoung Song private static final float WIDTH_SCALE = 2.6f; 604b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song 614b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song /** Widget preview width is calculated by multiplying this factor to the widget cell width. */ 625cd1d92f6c40b47eff12859d4e13b6431aa778c0Hyunyoung Song private static final float PREVIEW_SCALE = 0.8f; 634b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song 645cd1d92f6c40b47eff12859d4e13b6431aa778c0Hyunyoung Song private int mPresetPreviewSize; 655cd1d92f6c40b47eff12859d4e13b6431aa778c0Hyunyoung Song int cellSize; 663f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song 67f52abd7758b8f245522936e941336e574debabf5Sunny Goyal private WidgetImageView mWidgetImage; 68b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song private TextView mWidgetName; 69b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song private TextView mWidgetDims; 705b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal 714ddc40130aa0460924364eab3557088421eebe0cSunny Goyal private WidgetItem mItem; 725b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal 733f4e070aa58d51dd136885b4d3e2e6c5d9f93ea0Michael Jurka private WidgetPreviewLoader mWidgetPreviewLoader; 745b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal private PreviewLoadRequest mActiveRequest; 75ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor private StylusEventHelper mStylusEventHelper; 760499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung 774ddc40130aa0460924364eab3557088421eebe0cSunny Goyal private final Launcher mLauncher; 782e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen 793f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song public WidgetCell(Context context) { 8029d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung this(context, null); 8129d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung } 8229d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 833f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song public WidgetCell(Context context, AttributeSet attrs) { 8429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung this(context, attrs, 0); 8529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung } 8629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 873f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song public WidgetCell(Context context, AttributeSet attrs, int defStyle) { 8829d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung super(context, attrs, defStyle); 8929d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 9059e1f9a07eef87b1d287956d21b8d9c5b27faf9cWinson Chung final Resources r = context.getResources(); 91abef55a1c1f1fda4f42e4b4223534dd21f3dc19fAndrew Sapperstein mLauncher = Launcher.getLauncher(context); 92bb83520a620919a8f19472ba4426585d007d1abaMady Mellor mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); 9359e1f9a07eef87b1d287956d21b8d9c5b27faf9cWinson Chung 944b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song setContainerWidth(); 9529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung setWillNotDraw(false); 9629d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung setClipToPadding(false); 97ae50284e0a838139c67caf0064a0977c871497faSunny Goyal setAccessibilityDelegate(mLauncher.getAccessibilityDelegate()); 984b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song } 993f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song 1004b69f2ca91d63a59dfa921ba965b92de7f604f5aHyunyoung Song private void setContainerWidth() { 1012e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen DeviceProfile profile = mLauncher.getDeviceProfile(); 1025cd1d92f6c40b47eff12859d4e13b6431aa778c0Hyunyoung Song cellSize = (int) (profile.cellWidthPx * WIDTH_SCALE); 1032e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen mPresetPreviewSize = (int) (cellSize * PREVIEW_SCALE); 10429d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung } 10529d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung 106c93e5ae12018bb214099ff88a48cc21580aec4c3Winson Chung @Override 107c93e5ae12018bb214099ff88a48cc21580aec4c3Winson Chung protected void onFinishInflate() { 108c93e5ae12018bb214099ff88a48cc21580aec4c3Winson Chung super.onFinishInflate(); 109c93e5ae12018bb214099ff88a48cc21580aec4c3Winson Chung 110f52abd7758b8f245522936e941336e574debabf5Sunny Goyal mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview); 111b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song mWidgetName = ((TextView) findViewById(R.id.widget_name)); 112b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song mWidgetDims = ((TextView) findViewById(R.id.widget_dims)); 1134e8fb91cf19b7d621de8cbed2bde2c8dac734121Hyunyoung Song } 1144e8fb91cf19b7d621de8cbed2bde2c8dac734121Hyunyoung Song 115559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song /** 116559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song * Called to clear the view and free attached resources. (e.g., {@link Bitmap} 117559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song */ 118559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song public void clear() { 119559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song if (DEBUG) { 120559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song Log.d(TAG, "reset called on:" + mWidgetName.getText()); 121559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song } 1222d58471e8d3da621e209cec58a43d159778f8a0eWinson Chung mWidgetImage.animate().cancel(); 123f52abd7758b8f245522936e941336e574debabf5Sunny Goyal mWidgetImage.setBitmap(null); 124b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song mWidgetName.setText(null); 125b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song mWidgetDims.setText(null); 126f17a1c9ad99d46a65de797422fd439382114b348Hyunyoung Song 127559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song if (mActiveRequest != null) { 128559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song mActiveRequest.cleanup(); 129559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song mActiveRequest = null; 130559d90d0dafbac1d97a1e6f18062309831a25d51Hyunyoung Song } 131099459377a737d885bbd8ac4d52e6884a103b1c7Winson Chung } 132099459377a737d885bbd8ac4d52e6884a103b1c7Winson Chung 1334ddc40130aa0460924364eab3557088421eebe0cSunny Goyal public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) { 1344ddc40130aa0460924364eab3557088421eebe0cSunny Goyal mItem = item; 1354ddc40130aa0460924364eab3557088421eebe0cSunny Goyal mWidgetName.setText(mItem.label); 1364ddc40130aa0460924364eab3557088421eebe0cSunny Goyal mWidgetDims.setText(getContext().getString(R.string.widget_dims_format, 1374ddc40130aa0460924364eab3557088421eebe0cSunny Goyal mItem.spanX, mItem.spanY)); 1386c61323998edccbc28dc9f31602594e4f32e1215Jon Miranda mWidgetDims.setContentDescription(getContext().getString( 1396c61323998edccbc28dc9f31602594e4f32e1215Jon Miranda R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY)); 140d31dcbcc7c7901959c65c4e635e59274a1d4c334Hyunyoung Song mWidgetPreviewLoader = loader; 141d31dcbcc7c7901959c65c4e635e59274a1d4c334Hyunyoung Song 1424ddc40130aa0460924364eab3557088421eebe0cSunny Goyal if (item.activityInfo != null) { 1434ddc40130aa0460924364eab3557088421eebe0cSunny Goyal setTag(new PendingAddShortcutInfo(item.activityInfo)); 1444ddc40130aa0460924364eab3557088421eebe0cSunny Goyal } else { 1454ddc40130aa0460924364eab3557088421eebe0cSunny Goyal setTag(new PendingAddWidgetInfo(mLauncher, item.widgetInfo)); 1464ddc40130aa0460924364eab3557088421eebe0cSunny Goyal } 147b44b52439d155f570db7d6d0b80fdd3350e35685Winson Chung } 1480499834db3f9dc6fb0f5f57b5876b8503bce5189Winson Chung 149038f9d8bfb53288e7cf5812f62ec3d5b25fec965Michael Jurka public int[] getPreviewSize() { 150038f9d8bfb53288e7cf5812f62ec3d5b25fec965Michael Jurka int[] maxSize = new int[2]; 1516babf2e27e6260f02751413b8f7a55e46659af27Hyunyoung Song 1523f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song maxSize[0] = mPresetPreviewSize; 1533f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song maxSize[1] = mPresetPreviewSize; 154038f9d8bfb53288e7cf5812f62ec3d5b25fec965Michael Jurka return maxSize; 155038f9d8bfb53288e7cf5812f62ec3d5b25fec965Michael Jurka } 156038f9d8bfb53288e7cf5812f62ec3d5b25fec965Michael Jurka 1575b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal public void applyPreview(Bitmap bitmap) { 158f52abd7758b8f245522936e941336e574debabf5Sunny Goyal if (bitmap != null) { 159f52abd7758b8f245522936e941336e574debabf5Sunny Goyal mWidgetImage.setBitmap(bitmap); 160b99ff3e83270e113f6182e337c4f7b0223bad92bHyunyoung Song mWidgetImage.setAlpha(0f); 161f00d02b254d94eeaf52742e0640e9eacd63fafcaHyunyoung Song ViewPropertyAnimator anim = mWidgetImage.animate(); 162f00d02b254d94eeaf52742e0640e9eacd63fafcaHyunyoung Song anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS); 163ed66b2bac7447febe2e405b4ce725cae4f6b5988Adam Cohen } 164ed66b2bac7447febe2e405b4ce725cae4f6b5988Adam Cohen } 165ed66b2bac7447febe2e405b4ce725cae4f6b5988Adam Cohen 1665b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal public void ensurePreview() { 1675b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal if (mActiveRequest != null) { 1685b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal return; 1695b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal } 1705b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal int[] size = getPreviewSize(); 1713f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song if (DEBUG) { 1723f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):", 1733f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song getTagToString(), size[0], size[1])); 1743f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song } 1754ddc40130aa0460924364eab3557088421eebe0cSunny Goyal mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, size[0], size[1], this); 1765b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal } 1775b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal 1785b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal @Override 1795b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 1805b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal int oldTop, int oldRight, int oldBottom) { 1815b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal removeOnLayoutChangeListener(this); 1825b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal ensurePreview(); 1835b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal } 1845b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal 1855b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal public int getActualItemWidth() { 1865b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal ItemInfo info = (ItemInfo) getTag(); 1875b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal int[] size = getPreviewSize(); 1882e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen int cellWidth = mLauncher.getDeviceProfile().cellWidthPx; 1895b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal 1905b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal return Math.min(size[0], info.spanX * cellWidth); 1915b0e669169ea2c951bf2f6f71faf793b24db3c23Sunny Goyal } 1923f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song 193ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor @Override 194ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor public boolean onTouchEvent(MotionEvent ev) { 195ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor boolean handled = super.onTouchEvent(ev); 196bb83520a620919a8f19472ba4426585d007d1abaMady Mellor if (mStylusEventHelper.onMotionEvent(ev)) { 197ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor return true; 198ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor } 199ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor return handled; 200ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor } 201ef044dd380ac3abf354027750efdc16d5d48ac70Mady Mellor 2023f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song /** 2033f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song * Helper method to get the string info of the tag. 2043f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song */ 2053f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song private String getTagToString() { 20682b016cb56540fe26213e817dd0dd668099c8e20Winson Chung if (getTag() instanceof PendingAddWidgetInfo || 20782b016cb56540fe26213e817dd0dd668099c8e20Winson Chung getTag() instanceof PendingAddShortcutInfo) { 20882b016cb56540fe26213e817dd0dd668099c8e20Winson Chung return getTag().toString(); 2093f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song } 2103f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song return ""; 2113f471440a8b6b71d4c15501a96befd3b715c9e8fHyunyoung Song } 2126d02c7a0337d37e5b1c596a4acec0c292a9b7f93Sunny Goyal 2136d02c7a0337d37e5b1c596a4acec0c292a9b7f93Sunny Goyal @Override 2146d02c7a0337d37e5b1c596a4acec0c292a9b7f93Sunny Goyal public CharSequence getAccessibilityClassName() { 2156d02c7a0337d37e5b1c596a4acec0c292a9b7f93Sunny Goyal return WidgetCell.class.getName(); 2166d02c7a0337d37e5b1c596a4acec0c292a9b7f93Sunny Goyal } 21729d6fea296ebecb607525c8245a54696ad7c5db7Winson Chung} 218