WidgetTray revamp work

- RecyclerView is rendering
- Animation is connected
- Drag and drop is now handled
- UI tweaking: background, margins, more to come.
- Flicker and preview not loading issue:
  fixed height for the horizontal scroll view.
- Shortcuts are added
- Widget Preview loading should support shortPress for drop
- UI tweaks left: overlay of arrow when there are more items to scroll
- icons are added in the section header
- Sorting of widget sections and widget horizontal list
- Adding all the padding constants to dimen.xml file
- RecyclerView should only support one view type

For items to be addressed in follow up patches OR CLs,
TODO is added to the comment.

b/19897708

Change-Id: Ibfc4da1696a23d20bada93db46e126706eb13cdc
This commit is contained in:
Hyunyoung Song 2015-04-08 19:01:34 -07:00
parent 85e3d4cc5e
commit 3f471440a8
46 changed files with 1332 additions and 2069 deletions

View File

@ -57,8 +57,8 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />

View File

@ -66,8 +66,8 @@
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />

View File

@ -66,8 +66,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
<include layout="@layout/widgets_view"
android:id="@+id/widget_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<com.android.launcher3.AppsCustomizeTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:clipChildren="false">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipChildren="false">
<FrameLayout
android:id="@+id/fake_page_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
<FrameLayout
android:id="@+id/fake_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
android:clipToPadding="false" />
</FrameLayout>
<com.android.launcher3.AppsCustomizePagedView
android:id="@+id/apps_customize_pane_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
launcher:maxGap="@dimen/workspace_max_gap"
launcher:pageIndicator="@+id/apps_customize_page_indicator" />
</FrameLayout>
<include
android:id="@+id/apps_customize_page_indicator"
layout="@layout/page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</com.android.launcher3.AppsCustomizeTabHost>

View File

@ -30,4 +30,4 @@
android:textColor="@color/apps_view_section_text_color"
android:textSize="@dimen/apps_view_section_text_size"
android:focusable="false" />
</LinearLayout>
</LinearLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project
<!-- Copyright (C) 2015 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.
@ -13,58 +13,26 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.PagedViewWidget
<com.android.launcher3.widget.WidgetCell
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:background="@drawable/focusable_view_bg"
android:focusable="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<FrameLayout
android:id="@+id/left_border"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/widget_text_panel"
android:visibility="gone" />
<!-- The preview of the widget or shortcut. -->
<com.android.launcher3.PagedViewWidgetImageView
android:id="@+id/widget_preview"
style="@style/PagedViewWidgetImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingTop="@dimen/app_widget_preview_padding_top"
android:paddingEnd="@dimen/app_widget_preview_padding_right"
android:paddingRight="@dimen/app_widget_preview_padding_right"
android:scaleType="matrix" />
<FrameLayout
android:id="@+id/right_border"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/widget_text_panel"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/app_widget_preview_label_vertical_padding"
android:paddingBottom="@dimen/app_widget_preview_label_vertical_padding"
android:paddingLeft="@dimen/app_widget_preview_label_horizontal_padding"
android:paddingRight="@dimen/app_widget_preview_label_horizontal_padding"
android:background="@color/widget_text_panel"
android:paddingTop="@dimen/widget_preview_label_vertical_padding"
android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
android:orientation="horizontal">
<!-- The name of the widget. -->
<TextView
android:id="@+id/widget_name"
@ -91,6 +59,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_weight="0"
android:gravity="start"
@ -101,5 +70,16 @@
android:shadowColor="#B0000000" />
</LinearLayout>
<!-- The image of the widget. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
style="@style/WidgetImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="@dimen/widget_preview_padding_top"
android:paddingEnd="@dimen/widget_preview_padding_right"
android:paddingRight="@dimen/widget_preview_padding_right"
android:scaleType="matrix" />
</com.android.launcher3.PagedViewWidget>
</com.android.launcher3.widget.WidgetCell>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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:id="@+id/widgets_cell_list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:focusable="true"
android:background="@drawable/focusable_view_bg"
android:descendantFocusability="afterDescendants">
<!-- Section info -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:focusable="true"
android:background="@drawable/focusable_view_bg"
android:descendantFocusability="afterDescendants">
<ImageView
android:id="@+id/section_image"
android:layout_width="@dimen/widget_section_height"
android:layout_height="@dimen/widget_section_height"
android:paddingLeft="@dimen/widget_section_icon_padding"
android:paddingRight="@dimen/widget_section_icon_padding"
android:paddingTop="@dimen/widget_section_icon_padding"
android:paddingBottom="@dimen/widget_section_icon_padding"
android:background="@color/widget_text_panel"
/>
<TextView
android:id="@+id/section"
android:layout_width="match_parent"
android:layout_height="@dimen/widget_section_height"
android:paddingTop="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="start|center_vertical"
android:textColor="@color/widgets_view_section_text_color"
android:background="@color/widget_text_panel"
android:textSize="20sp"
android:focusable="false" />
</LinearLayout>
<!-- Widget list -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end"
>
<!-- TODO(hyunyoungs): replace the indicator with actual assets. -->
<FrameLayout
android:id="@+id/scrollable_indicator"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_pageindicator_default"
android:visibility="invisible"
/>
<HorizontalScrollView
android:id="@+id/widgets_scroll_container"
android:layout_width="match_parent"
android:layout_height="@dimen/widget_cell_height"
android:scrollbars="none" >
<LinearLayout
android:id="@+id/widgets_cell_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<!-- The top and bottom paddings are defined in this container, but since we want
the list view to span the full width (for touch interception purposes), we
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.widget.WidgetsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/widget_container_inset"
android:paddingBottom="@dimen/widget_container_inset"
android:descendantFocusability="afterDescendants">
<!-- Temporary until finalizing on animation. -->
<include
android:id="@+id/widgets_reveal_view"
layout="@layout/apps_reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/quantum_panel_dark"/>
</LinearLayout>
</com.android.launcher3.widget.WidgetsContainerView>

View File

@ -18,9 +18,4 @@
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">8dip</dimen>
<dimen name="toolbar_button_horizontal_padding">0dip</dimen>
<!-- AppsCustomize -->
<dimen name="apps_customize_tab_bar_height">42dp</dimen>
<integer name="apps_customize_widget_cell_count_x">3</integer>
<integer name="apps_customize_widget_cell_count_y">2</integer>
</resources>

View File

@ -24,11 +24,9 @@
<dimen name="apps_view_row_height">76dp</dimen>
<!-- AppsCustomize -->
<dimen name="apps_customize_tab_bar_height">60dp</dimen>
<dimen name="apps_customize_tab_bar_margin_top">8dp</dimen>
<dimen name="app_widget_preview_label_margin_top">8dp</dimen>
<dimen name="app_widget_preview_label_margin_left">@dimen/app_widget_preview_padding_left</dimen>
<dimen name="app_widget_preview_label_margin_right">@dimen/app_widget_preview_padding_right</dimen>
<dimen name="widget_preview_label_margin_top">8dp</dimen>
<dimen name="widget_preview_label_margin_left">@dimen/widget_preview_padding_left</dimen>
<dimen name="widget_preview_label_margin_right">@dimen/widget_preview_padding_right</dimen>
<!-- Cling -->
<dimen name="cling_migration_logo_height">400dp</dimen>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="PagedViewWidgetImageView">
<item name="android:paddingStart">@dimen/app_widget_preview_padding_left</item>
<style name="WidgetImageView">
<item name="android:paddingStart">@dimen/widget_preview_padding_left</item>
</style>
</resources>

View File

@ -36,8 +36,10 @@
<color name="outline_color">#FFFFFFFF</color>
<color name="widget_text_panel">#FF374248</color>
<!-- Apps view -->
<!-- Apps view -->
<color name="apps_view_scrollbar_thumb_color">#009688</color>
<color name="apps_view_section_text_color">#009688</color>
<!-- Widgetss view -->
<color name="widgets_view_section_text_color">#009688</color>
</resources>

View File

@ -60,18 +60,9 @@
<dimen name="apps_view_fast_scroll_text_size">40dp</dimen>
<!-- AllApps/Customize/AppsCustomize -->
<!-- The height of the tab bar - if this changes, we should update the
external icon width/height above to compensate -->
<dimen name="apps_customize_tab_bar_height">52dp</dimen>
<dimen name="apps_customize_tab_bar_margin_top">0dp</dimen>
<dimen name="app_icon_size">48dp</dimen>
<dimen name="apps_customize_horizontal_padding">0dp</dimen>
<!-- The AppsCustomize page indicator -->
<dimen name="apps_customize_page_indicator_height">12dp</dimen>
<dimen name="apps_customize_page_indicator_margin">4dp</dimen>
<dimen name="apps_customize_page_indicator_offset">16dp</dimen>
<!-- Drag padding to add to the bottom of drop targets -->
<dimen name="drop_target_drag_padding">14dp</dimen>
<dimen name="drop_target_text_size">14sp</dimen>
@ -85,12 +76,19 @@
should be. If 0, it will not be scaled at all. -->
<dimen name="dragViewScale">12dp</dimen>
<!-- Padding applied to AppWidget previews -->
<dimen name="app_widget_preview_padding_left">16dp</dimen>
<dimen name="app_widget_preview_padding_right">16dp</dimen>
<dimen name="app_widget_preview_padding_top">32dp</dimen>
<dimen name="app_widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="app_widget_preview_label_horizontal_padding">8dp</dimen>
<!-- Widget tray -->
<dimen name="widget_container_inset">8dp</dimen>
<dimen name="widget_preview_size">140dp</dimen>
<dimen name="widget_preview_padding_left">16dp</dimen>
<dimen name="widget_preview_padding_right">16dp</dimen>
<dimen name="widget_preview_padding_top">8dp</dimen>
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
<dimen name="widget_section_height">52dp</dimen>
<dimen name="widget_section_icon_padding">8dp</dimen>
<dimen name="widget_cell_height">160dp</dimen>
<!-- Padding applied to shortcut previews -->
<dimen name="shortcut_preview_padding_left">0dp</dimen>

View File

@ -91,8 +91,8 @@
</style>
<!-- Overridden in device overlays -->
<style name="PagedViewWidgetImageView">
<item name="android:paddingLeft">@dimen/app_widget_preview_padding_left</item>
<style name="WidgetImageView">
<item name="android:paddingLeft">@dimen/widget_preview_padding_left</item>
</style>
</resources>

View File

@ -43,7 +43,7 @@ public class AppInfo extends ItemInfo {
/**
* A bitmap version of the application icon.
*/
Bitmap iconBitmap;
public Bitmap iconBitmap;
/**
* Indicates whether we're using a low res icon
@ -55,7 +55,7 @@ public class AppInfo extends ItemInfo {
*/
long firstInstallTime;
ComponentName componentName;
public ComponentName componentName;
static final int DOWNLOADED_FLAG = 1;
static final int UPDATED_SYSTEM_APP_FLAG = 2;
@ -121,12 +121,15 @@ public class AppInfo extends ItemInfo {
+ " user=" + user + ")";
}
/**
* Helper method used for debugging.
*/
public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
Log.d(tag, label + " size=" + list.size());
for (AppInfo info: list) {
Log.d(tag, " title=\"" + info.title + "\" iconBitmap="
+ info.iconBitmap + " firstInstallTime="
+ info.firstInstallTime);
Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
+ " firstInstallTime=" + info.firstInstallTime
+ " componentName=" + info.componentName.getPackageName());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,228 +0,0 @@
/*
* Copyright (C) 2011 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.launcher3;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable {
static final String LOG_TAG = "AppsCustomizeTabHost";
private static final String WIDGETS_TAB_TAG = "WIDGETS";
private AppsCustomizePagedView mPagedView;
private View mContent;
private boolean mInTransition = false;
private final Rect mInsets = new Rect();
public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Convenience methods to select specific tabs. We want to set the content type immediately
* in these cases, but we note that we still call setCurrentTabByTag() so that the tab view
* reflects the new content (but doesn't do the animation and logic associated with changing
* tabs manually).
*/
void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) {
mPagedView.setContentType(type);
}
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
flp.topMargin = insets.top;
flp.bottomMargin = insets.bottom;
flp.leftMargin = insets.left;
flp.rightMargin = insets.right;
mContent.setLayoutParams(flp);
}
/**
* Setup the tab host and create all necessary tabs.
*/
@Override
protected void onFinishInflate() {
mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content);
mContent = findViewById(R.id.content);
}
public String getContentTag() {
return getTabTagForContentType(mPagedView.getContentType());
}
/**
* Returns the content view used for the launcher transitions.
*/
public View getContentView() {
return findViewById(R.id.apps_customize_pane_content);
}
/**
* Returns the reveal view used for the launcher transitions.
*/
public View getRevealView() {
return findViewById(R.id.fake_page);
}
/**
* Returns the page indicators view.
*/
public View getPageIndicators() {
return findViewById(R.id.apps_customize_page_indicator);
}
/**
* Returns the content type for the specified tab tag.
*/
public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) {
return AppsCustomizePagedView.ContentType.Widgets;
}
/**
* Returns the tab tag for a given content type.
*/
public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) {
return WIDGETS_TAB_TAG;
}
/**
* Disable focus on anything under this view in the hierarchy if we are not visible.
*/
@Override
public int getDescendantFocusability() {
if (getVisibility() != View.VISIBLE) {
return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
}
return super.getDescendantFocusability();
}
void reset() {
// Reset immediately
mPagedView.reset();
}
void trimMemory() {
mPagedView.trimMemory();
}
public void onWindowVisible() {
if (getVisibility() == VISIBLE) {
mContent.setVisibility(VISIBLE);
// We unload the widget previews when the UI is hidden, so need to reload pages
// Load the current page synchronously, and the neighboring pages asynchronously
mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
}
}
@Override
public ViewGroup getContent() {
return mPagedView;
}
public boolean isInTransition() {
return mInTransition;
}
/* LauncherTransitionable overrides */
@Override
public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace);
mInTransition = true;
if (toWorkspace) {
// Going from All Apps -> Workspace
setVisibilityOfSiblingsWithLowerZOrder(VISIBLE);
} else {
// Going from Workspace -> All Apps
mContent.setVisibility(VISIBLE);
// Make sure the current page is loaded (we start loading the side pages after the
// transition to prevent slowing down the animation)
// TODO: revisit this
mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
}
}
@Override
public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
mPagedView.onLauncherTransitionStart(l, animated, toWorkspace);
}
@Override
public void onLauncherTransitionStep(Launcher l, float t) {
mPagedView.onLauncherTransitionStep(l, t);
}
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace);
mInTransition = false;
if (!toWorkspace) {
// Make sure adjacent pages are loaded (we wait until after the transition to
// prevent slowing down the animation)
mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
// Opening apps, need to announce what page we are on.
AccessibilityManager am = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (am.isEnabled()) {
// Notify the user when the page changes
announceForAccessibility(mPagedView.getCurrentPageDescription());
}
// Going from Workspace -> All Apps
// NOTE: We should do this at the end since we check visibility state in some of the
// cling initialization/dismiss code above.
setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE);
}
}
private void setVisibilityOfSiblingsWithLowerZOrder(int visibility) {
ViewGroup parent = (ViewGroup) getParent();
if (parent == null) return;
View appsView = ((Launcher) getContext()).getAppsView();
View overviewPanel = ((Launcher) getContext()).getOverviewPanel();
final int count = parent.getChildCount();
if (!isChildrenDrawingOrderEnabled()) {
for (int i = 0; i < count; i++) {
final View child = parent.getChildAt(i);
if (child == this) {
break;
} else {
if (child.getVisibility() == GONE || child == overviewPanel ||
child == appsView) {
continue;
}
child.setVisibility(visibility);
}
}
} else {
throw new RuntimeException("Failed; can't get z-order of views");
}
}
}

View File

@ -45,7 +45,6 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@ -56,6 +55,7 @@ import android.view.animation.LayoutAnimationController;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.LauncherAccessibilityDelegate.DragType;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.Arrays;
@ -3025,7 +3025,7 @@ public class CellLayout extends ViewGroup {
*
* @return True if a vacant cell of the specified dimension was found, false otherwise.
*/
boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied);
}

View File

@ -42,6 +42,7 @@ import android.view.animation.LinearInterpolator;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
public class DeleteDropTarget extends ButtonDropTarget {
private static int DELETE_ANIMATION_DURATION = 285;
@ -100,8 +101,9 @@ public class DeleteDropTarget extends ButtonDropTarget {
private boolean isAllAppsApplication(DragSource source, Object info) {
return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
}
private boolean isAllAppsWidget(DragSource source, Object info) {
if (source instanceof AppsCustomizePagedView) {
private boolean isWidget(DragSource source, Object info) {
if (source instanceof WidgetsContainerView) {
if (info instanceof PendingAddItemInfo) {
PendingAddItemInfo addInfo = (PendingAddItemInfo) info;
switch (addInfo.itemType) {
@ -173,7 +175,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
// If we are dragging an application from AppsCustomize, only show the control if we can
// delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
// Hide the delete target if it is a widget from AppsCustomize.
if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
if (!willAcceptDrop(info) || isWidget(source, info)) {
isVisible = false;
}
if (useUninstallLabel) {
@ -489,13 +491,14 @@ public class DeleteDropTarget extends ButtonDropTarget {
}
public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView;
final boolean isWidgets = d.dragSource instanceof WidgetsContainerView;
final boolean isAllapps = d.dragSource instanceof AppsContainerView;
// Don't highlight the icon as it's animating
d.dragView.setColor(0);
d.dragView.updateInitialScaleToCurrentScale();
// Don't highlight the target if we are flinging from AllApps
if (isAllApps) {
if (isWidgets || isAllapps) {
resetHoverColor();
}
@ -545,7 +548,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
public void run() {
// If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up
// itself, otherwise, complete the drop to initiate the deletion process
if (!isAllApps) {
if (!isWidgets || !isAllapps) {
mLauncher.exitSpringLoadedDragMode();
completeDrop(d);
}

View File

@ -106,8 +106,8 @@ public class DeviceProfile {
public int cellWidthPx;
public int cellHeightPx;
int iconSizePx;
int iconTextSizePx;
public int iconSizePx;
public int iconTextSizePx;
int iconDrawablePaddingPx;
int allAppsIconSizePx;
int allAppsIconTextSizePx;
@ -803,64 +803,6 @@ public class DeviceProfile {
}
}
// Layout AllApps
AppsCustomizeTabHost host = (AppsCustomizeTabHost)
launcher.findViewById(R.id.apps_customize_pane);
if (host != null) {
// Center the all apps page indicator
int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f,
(allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
if (pageIndicator != null) {
LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams();
lllp.width = LayoutParams.WRAP_CONTENT;
lllp.height = pageIndicatorHeight;
pageIndicator.setLayoutParams(lllp);
}
AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
host.findViewById(R.id.apps_customize_pane_content);
FrameLayout fakePageContainer = (FrameLayout)
host.findViewById(R.id.fake_page_container);
FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page);
padding = new Rect();
if (pagedView != null) {
// Constrain the dimensions of all apps so that it does not span the full width
int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) /
(2 * (allAppsNumCols + 1));
int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) /
(2 * (allAppsNumRows + 1));
paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f));
paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
// Only adjust the side paddings on landscape phones, or tablets
if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) {
padding.left = padding.right = gridPaddingLR;
}
// The icons are centered, so we can't just offset by the page indicator height
// because the empty space will actually be pageIndicatorHeight + paddingTB
padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel));
// Horizontal padding for the whole paged view
int pagedFixedViewPadding =
res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding);
padding.left += pagedFixedViewPadding;
padding.right += pagedFixedViewPadding;
pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
}
// Layout the Overview Mode
ViewGroup overviewMode = launcher.getOverviewPanel();
if (overviewMode != null) {

View File

@ -22,9 +22,9 @@ import com.android.launcher3.DropTarget.DragObject;
/**
* Interface defining an object that can originate a drag.
*
*/
public interface DragSource {
/**
* @return whether items dragged from this source supports
*/

View File

@ -32,7 +32,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
class FastBitmapDrawable extends Drawable {
public class FastBitmapDrawable extends Drawable {
static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
@ -72,7 +72,7 @@ class FastBitmapDrawable extends Drawable {
private boolean mPressed = false;
private ObjectAnimator mPressedAnimator;
FastBitmapDrawable(Bitmap b) {
public FastBitmapDrawable(Bitmap b) {
mAlpha = 255;
mBitmap = b;
setBounds(0, 0, b.getWidth(), b.getHeight());

View File

@ -44,6 +44,7 @@ import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PackageItemInfo;
import java.util.HashMap;
import java.util.HashSet;
@ -454,12 +455,13 @@ public class IconCache {
* Fill in {@param appInfo} with the icon and label for {@param packageName}
*/
public synchronized void getTitleAndIconForApp(
String packageName, UserHandleCompat user, boolean useLowResIcon, AppInfo appInfoOut) {
String packageName, UserHandleCompat user, boolean useLowResIcon,
PackageItemInfo infoOut) {
CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon);
appInfoOut.iconBitmap = entry.icon;
appInfoOut.title = entry.title;
appInfoOut.usingLowResIcon = entry.isLowResIcon;
appInfoOut.contentDescription = entry.contentDescription;
infoOut.iconBitmap = entry.icon;
infoOut.title = entry.title;
infoOut.usingLowResIcon = entry.isLowResIcon;
infoOut.contentDescription = entry.contentDescription;
}
public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {

View File

@ -18,6 +18,10 @@ package com.android.launcher3;
import android.graphics.Rect;
/**
* Allows the implementing {@link View} to not draw underneath system bars.
* e.g., notification bar on top and home key area on the bottom.
*/
public interface Insettable {
void setInsets(Rect insets);

View File

@ -36,7 +36,7 @@ public class ItemInfo {
*/
static final String EXTRA_PROFILE = "profile";
static final int NO_ID = -1;
public static final int NO_ID = -1;
/**
* The id in the settings database for this item
@ -82,7 +82,7 @@ public class ItemInfo {
/**
* Indicates the Y cell span.
*/
int spanY = 1;
public int spanY = 1;
/**
* Indicates the minimum X cell span.
@ -107,21 +107,21 @@ public class ItemInfo {
/**
* Title of the item
*/
CharSequence title;
public CharSequence title;
/**
* Content description of the item.
*/
CharSequence contentDescription;
public CharSequence contentDescription;
/**
* The position of the item in a drag-and-drop operation.
*/
int[] dropPos = null;
public int[] dropPos = null;
UserHandleCompat user;
public UserHandleCompat user;
ItemInfo() {
public ItemInfo() {
user = UserHandleCompat.myUserHandle();
}

View File

@ -103,6 +103,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetsContainerView;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@ -130,11 +132,11 @@ public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
LauncherStateTransitionAnimation.Callbacks {
static final String TAG = "Launcher";
static final boolean LOGD = false;
static final String TAG = "Launcher - MERONG";
static final boolean LOGD = true;
static final boolean PROFILE_STARTUP = false;
static final boolean DEBUG_WIDGETS = false;
static final boolean DEBUG_WIDGETS = true;
static final boolean DEBUG_STRICT_MODE = false;
static final boolean DEBUG_RESUME_TIME = false;
static final boolean DEBUG_DUMP_LOG = false;
@ -264,9 +266,13 @@ public class Launcher extends Activity
private View mAllAppsButton;
private SearchDropTargetBar mSearchDropTargetBar;
// Main container view for the all apps screen.
@Thunk AppsContainerView mAppsView;
@Thunk AppsCustomizeTabHost mAppsCustomizeTabHost;
private AppsCustomizePagedView mAppsCustomizeContent;
// Main container view for the widget tray screen.
private WidgetsContainerView mWidgetsView;
private boolean mAutoAdvanceRunning = false;
private AppWidgetHostView mQsb;
@ -672,7 +678,7 @@ public class Launcher extends Activity
return mInflater;
}
boolean isDraggingEnabled() {
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
return !mModel.isLoadingWorkspace();
@ -1013,15 +1019,9 @@ public class Launcher extends Activity
startTimeCallbacks = System.currentTimeMillis();
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setBulkBind(true);
}
for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
mBindOnResumeCallbacks.get(i).run();
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setBulkBind(false);
}
mBindOnResumeCallbacks.clear();
if (DEBUG_RESUME_TIME) {
Log.d(TAG, "Time spent processing callbacks in onResume: " +
@ -1213,9 +1213,8 @@ public class Launcher extends Activity
if (mModel.isCurrentCallbacks(this)) {
mModel.stopLoader();
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.surrender();
}
//TODO(hyunyoungs): stop the widgets loader when there is a rotation.
return Boolean.TRUE;
}
@ -1336,19 +1335,6 @@ public class Launcher extends Activity
mRestoring = true;
}
// Restore the AppsCustomize tab
if (mAppsCustomizeTabHost != null) {
String curTab = savedState.getString("apps_customize_currentTab");
if (curTab != null) {
mAppsCustomizeTabHost.setContentTypeImmediate(
mAppsCustomizeTabHost.getContentTypeForTabTag(curTab));
mAppsCustomizeContent.loadAssociatedPages(
mAppsCustomizeContent.getCurrentPage());
}
int currentIndex = savedState.getInt("apps_customize_currentIndex");
mAppsCustomizeContent.restorePageForIndex(currentIndex);
}
mItemIdToViewId = (HashMap<Integer, Integer>)
savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
}
@ -1434,10 +1420,7 @@ public class Launcher extends Activity
mAppsView = (AppsContainerView) findViewById(R.id.apps_view);
// Setup AppsCustomize
mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
mAppsCustomizeContent = (AppsCustomizePagedView)
mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
mAppsCustomizeContent.setup(this, dragController);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
@ -1651,7 +1634,7 @@ public class Launcher extends Activity
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
if (mAppsView != null && mAppsCustomizeTabHost != null &&
if (mAppsView != null && mWidgetsView != null &&
mPendingAddInfo.container == ItemInfo.NO_ID) {
showWorkspace(false);
}
@ -1735,7 +1718,6 @@ public class Launcher extends Activity
// you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
// is a more appropriate event to handle
if (mVisible) {
mAppsCustomizeTabHost.onWindowVisible();
if (!mWorkspaceLoading) {
final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
// We want to let Launcher draw itself at least once before we force it to build
@ -1839,7 +1821,7 @@ public class Launcher extends Activity
launcherInfo.hostView = null;
}
void showOutOfSpaceMessage(boolean isHotseatLayout) {
public void showOutOfSpaceMessage(boolean isHotseatLayout) {
int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
}
@ -1852,8 +1834,8 @@ public class Launcher extends Activity
return mAppsView;
}
public AppsCustomizeTabHost getWidgetsView() {
return mAppsCustomizeTabHost;
public WidgetsContainerView getWidgetsView() {
return mWidgetsView;
}
public Workspace getWorkspace() {
@ -1946,9 +1928,9 @@ public class Launcher extends Activity
mAppsView.scrollToTop();
}
// Reset the apps customize page
if (!alreadyOnHome && mAppsCustomizeTabHost != null) {
mAppsCustomizeTabHost.reset();
// Reset the widgets view
if (!alreadyOnHome && mWidgetsView != null) {
mWidgetsView.scrollToTop();
}
if (mLauncherCallbacks != null) {
@ -2003,16 +1985,8 @@ public class Launcher extends Activity
outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
}
// Save the current AppsCustomize tab
if (mAppsCustomizeTabHost != null) {
AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType();
String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type);
if (currentTabTag != null) {
outState.putString("apps_customize_currentTab", currentTabTag);
}
int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
outState.putInt("apps_customize_currentIndex", currentIndex);
}
// Save the current widgets tray?
// TODO(hyunyoungs)
outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
if (mLauncherCallbacks != null) {
@ -3276,9 +3250,7 @@ public class Launcher extends Activity
SQLiteDatabase.releaseMemory();
// This clears all widget bitmaps from the widget tray
if (mAppsCustomizeTabHost != null) {
mAppsCustomizeTabHost.trimMemory();
}
// TODO(hyunyoungs)
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onTrimMemory(level);
@ -3355,15 +3327,16 @@ public class Launcher extends Activity
* Shows the widgets view.
*/
void showWidgetsView(boolean animated, boolean resetPageToZero) {
Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
if (resetPageToZero) {
mAppsCustomizeTabHost.reset();
mWidgetsView.scrollToTop();
}
showAppsOrWidgets(animated, State.WIDGETS);
mAppsCustomizeTabHost.post(new Runnable() {
mWidgetsView.post(new Runnable() {
@Override
public void run() {
// We post this in-case the all apps view isn't yet constructed.
mAppsCustomizeTabHost.requestFocus();
mWidgetsView.requestFocus();
}
});
}
@ -3394,7 +3367,9 @@ public class Launcher extends Activity
.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
void enterSpringLoadedDragMode() {
public void enterSpringLoadedDragMode() {
Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s",
mState.name()));
if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
mState == State.WIDGETS_SPRING_LOADED) {
return;
@ -3405,7 +3380,7 @@ public class Launcher extends Activity
mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
}
void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
final Runnable onCompleteRunnable) {
if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
@ -3413,10 +3388,12 @@ public class Launcher extends Activity
@Override
public void run() {
if (successfulDrop) {
// TODO(hyunyoungs): verify if this hack is still needed, if not, delete.
//
// Before we show workspace, hide all apps again because
// exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
// clean up our state transition functions
mAppsCustomizeTabHost.setVisibility(View.GONE);
mWidgetsView.setVisibility(View.GONE);
showWorkspace(true, onCompleteRunnable);
} else {
exitSpringLoadedDragMode();
@ -3918,8 +3895,8 @@ public class Launcher extends Activity
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
Bundle options =
AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
Bundle options = null;
// AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
@ -4122,9 +4099,9 @@ public class Launcher extends Activity
if (mAppsView != null) {
mAppsView.setApps(apps);
}
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated(
LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */));
if (mWidgetsView != null) {
mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
getPackageManager());
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
@ -4276,15 +4253,16 @@ public class Launcher extends Activity
mWidgetsAndShortcuts = null;
}
};
public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
mWidgetsAndShortcuts = widgetsAndShortcuts;
return;
}
// Update the widgets pane
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
if (mWidgetsView != null) {
mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false),
getPackageManager());
}
}
@ -4577,10 +4555,8 @@ public class Launcher extends Activity
Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
Log.d(TAG, "sFolders.size=" + sFolders.size());
mModel.dumpState();
// TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.dumpState();
}
Log.d(TAG, "END launcher3 dump state");
}

View File

@ -141,7 +141,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
return mModel;
}
LauncherAccessibilityDelegate getAccessibilityDelegate() {
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
return mAccessibilityDelegate;
}

View File

@ -16,10 +16,10 @@ import android.os.Parcel;
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
public boolean isCustomWidget = false;
int spanX = -1;
int spanY = -1;
int minSpanX = -1;
int minSpanY = -1;
public int spanX = -1;
public int spanY = -1;
public int minSpanX = -1;
public int minSpanY = -1;
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {
@ -78,10 +78,11 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
return super.loadIcon(context, cache.getFullResIconDpi());
}
public String toString() {
public String toString(PackageManager pm) {
if (isCustomWidget) {
return "LauncherAppWidgetProviderInfo(" + provider + ")";
return "WidgetProviderInfo(" + provider + ")";
}
return super.toString();
return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)",
provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY);
}
}

View File

@ -3628,7 +3628,7 @@ public class LauncherModel extends BroadcastReceiver
private final HashMap<Object, String> mLabelCache;
private final Collator mCollator;
WidgetAndShortcutNameComparator(Context context) {
public WidgetAndShortcutNameComparator(Context context) {
mManager = AppWidgetManagerCompat.getInstance(context);
mPackageManager = context.getPackageManager();
mLabelCache = new HashMap<Object, String>();

View File

@ -22,7 +22,7 @@ import android.provider.BaseColumns;
/**
* Settings related utilities.
*/
class LauncherSettings {
public class LauncherSettings {
/** Columns required on table staht will be subject to backup and restore. */
static interface ChangeLogColumns extends BaseColumns {
/**
@ -121,7 +121,7 @@ class LauncherSettings {
/**
* Favorites.
*/
static final class Favorites implements BaseLauncherColumns {
public static final class Favorites implements BaseLauncherColumns {
/**
* The content:// style URL for this table
*/
@ -217,12 +217,12 @@ class LauncherSettings {
/**
* The favorite is a widget
*/
static final int ITEM_TYPE_APPWIDGET = 4;
public static final int ITEM_TYPE_APPWIDGET = 4;
/**
* The favorite is a custom widget provided by the launcher
*/
static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
/**
* The favorite is a clock

View File

@ -23,6 +23,7 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewAnimationUtils;
@ -30,6 +31,7 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
import java.util.HashMap;
@ -179,20 +181,12 @@ public class LauncherStateTransitionAnimation {
* Starts an animation to the widgets view.
*/
public void startAnimationToWidgets(final boolean animated) {
final AppsCustomizeTabHost toView = mLauncher.getWidgetsView();
final WidgetsContainerView toView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public void onRevealViewVisible(View revealView, View contentView,
View allAppsButtonView) {
// Hide the real page background, and swap in the fake one
((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false);
revealView.setBackground(
mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark));
}
@Override
public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
// Show the real page background
((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true);
revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark));
}
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
@ -204,7 +198,7 @@ public class LauncherStateTransitionAnimation {
}
};
startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(),
toView.getRevealView(), toView.getPageIndicators(), animated, cb);
toView.getRevealView(), null, animated, cb);
}
/**
@ -500,44 +494,8 @@ public class LauncherStateTransitionAnimation {
private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
final Workspace.State toWorkspaceState, final boolean animated,
final Runnable onCompleteRunnable) {
AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView();
WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {
AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
// Hide the real page background, and swap in the fake one
pagedView.stopScrolling();
pagedView.setPageBackgroundsVisible(false);
revealView.setBackground(
mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark));
// Hide the side pages of the Widget tray to avoid some ugly edge cases
final View currentPage = pagedView.getPageAt(pagedView.getNextPage());
int count = pagedView.getChildCount();
for (int i = 0; i < count; i++) {
View child = pagedView.getChildAt(i);
if (child != currentPage) {
child.setVisibility(View.INVISIBLE);
}
}
}
@Override
public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {
AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView);
// Show the real page background and force-update the page
pagedView.setPageBackgroundsVisible(true);
pagedView.setCurrentPage(pagedView.getNextPage());
pagedView.updateCurrentPageScroll();
// Unhide the side pages
int count = pagedView.getChildCount();
for (int i = 0; i < count; i++) {
View child = pagedView.getChildAt(i);
child.setVisibility(View.VISIBLE);
}
}
@Override
public float getMaterialRevealViewFinalYDrift(View revealView) {
return revealView.getMeasuredHeight() / 2;
@ -559,7 +517,7 @@ public class LauncherStateTransitionAnimation {
};
startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView,
widgetsView.getContentView(), widgetsView.getRevealView(),
widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb);
null, animated, onCompleteRunnable, cb);
}
/**

View File

@ -1,174 +0,0 @@
/*
* Copyright (C) 2010 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.launcher3;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/* Class that does most of the work of enabling dragging items out of a PagedView by performing a
* vertical drag. Used by both CustomizePagedView and AllAppsPagedView.
* Subclasses must do the following:
* * call setDragSlopeThreshold after making an instance of the PagedViewWithDraggableItems
* * call child.setOnLongClickListener(this) and child.setOnTouchListener(this) on all children
* (good place to do it is in syncPageItems)
* * override beginDragging(View) (but be careful to call super.beginDragging(View)
*
*/
public abstract class PagedViewWithDraggableItems extends PagedView
implements View.OnLongClickListener, View.OnTouchListener {
private View mLastTouchedItem;
private boolean mIsDragging;
private boolean mIsDragEnabled;
private float mDragSlopeThreshold;
private Launcher mLauncher;
public PagedViewWithDraggableItems(Context context) {
this(context, null);
}
public PagedViewWithDraggableItems(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = (Launcher) context;
}
protected boolean beginDragging(View v) {
boolean wasDragging = mIsDragging;
mIsDragging = true;
return !wasDragging;
}
protected void cancelDragging() {
mIsDragging = false;
mLastTouchedItem = null;
mIsDragEnabled = false;
}
private void handleTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
cancelDragging();
mIsDragEnabled = true;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging && mIsDragEnabled) {
determineDraggingStart(ev);
}
break;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
return super.onTouchEvent(ev);
}
public void trimMemory() {
mLastTouchedItem = null;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
mLastTouchedItem = v;
mIsDragEnabled = true;
return false;
}
@Override
public boolean onLongClick(View v) {
// Return early if this is not initiated from a touch
if (!v.isInTouchMode()) return false;
// Return early if we are still animating the pages
if (mNextPage != INVALID_PAGE) return false;
// When we have exited all apps or are in transition, disregard long clicks
if (!mLauncher.isWidgetsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return false;
return beginDragging(v);
}
/*
* Determines if we should change the touch state to start scrolling after the
* user moves their touch point too far.
*/
protected void determineScrollingStart(MotionEvent ev) {
if (!mIsDragging) super.determineScrollingStart(ev);
}
/*
* Determines if we should change the touch state to start dragging after the
* user moves their touch point far enough.
*/
protected void determineDraggingStart(MotionEvent ev) {
/*
* Locally do absolute value. mLastMotionX is set to the y value
* of the down event.
*/
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
final int touchSlop = mTouchSlop;
boolean yMoved = yDiff > touchSlop;
boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold;
if (isUpwardMotion && yMoved && mLastTouchedItem != null) {
// Drag if the user moved far enough along the Y axis
beginDragging(mLastTouchedItem);
// Cancel any pending long press
if (mAllowLongPress) {
mAllowLongPress = false;
// Try canceling the long press. It could also have been scheduled
// by a distant descendant, so use the mAllowLongPress flag to block
// everything
final View currentPage = getPageAt(mCurrentPage);
if (currentPage != null) {
currentPage.cancelLongPress();
}
}
}
}
public void setDragSlopeThreshold(float dragSlopeThreshold) {
mDragSlopeThreshold = dragSlopeThreshold;
}
@Override
protected void onDetachedFromWindow() {
cancelDragging();
super.onDetachedFromWindow();
}
}

View File

@ -16,93 +16,17 @@
package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Parcelable;
/**
* We pass this object with a drag from the customization tray
* Meta data that is used for deferred binding.
* e.g., this object is used to pass information on dragable targets when they are dropped onto
* the workspace from another container.
*/
class PendingAddItemInfo extends ItemInfo {
public class PendingAddItemInfo extends ItemInfo {
/**
* The component that will be created.
*/
ComponentName componentName;
}
class PendingAddShortcutInfo extends PendingAddItemInfo {
ActivityInfo shortcutActivityInfo;
public PendingAddShortcutInfo(ActivityInfo activityInfo) {
shortcutActivityInfo = activityInfo;
}
@Override
public String toString() {
return "Shortcut: " + shortcutActivityInfo.packageName;
}
}
class PendingAddWidgetInfo extends PendingAddItemInfo {
int minWidth;
int minHeight;
int minResizeWidth;
int minResizeHeight;
int previewImage;
int icon;
LauncherAppWidgetProviderInfo info;
AppWidgetHostView boundWidget;
Bundle bindOptions = null;
public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
if (i.isCustomWidget) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
} else {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
}
this.info = i;
componentName = i.provider;
minWidth = i.minWidth;
minHeight = i.minHeight;
minResizeWidth = i.minResizeWidth;
minResizeHeight = i.minResizeHeight;
previewImage = i.previewImage;
icon = i.icon;
spanX = i.spanX;
spanY = i.spanY;
minSpanX = i.minSpanX;
minSpanY = i.minSpanY;
}
public boolean isCustomWidget() {
return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
}
// Copy constructor
public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
minWidth = copy.minWidth;
minHeight = copy.minHeight;
minResizeWidth = copy.minResizeWidth;
minResizeHeight = copy.minResizeHeight;
previewImage = copy.previewImage;
icon = copy.icon;
info = copy.info;
boundWidget = copy.boundWidget;
componentName = copy.componentName;
itemType = copy.itemType;
spanX = copy.spanX;
spanY = copy.spanY;
minSpanX = copy.minSpanX;
minSpanY = copy.minSpanY;
bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
}
@Override
public String toString() {
return "Widget: " + componentName.toShortString();
}
public ComponentName componentName;
}

View File

@ -32,6 +32,7 @@ import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetCell;
import java.lang.ref.WeakReference;
import java.util.Collections;
@ -45,6 +46,7 @@ import java.util.concurrent.ExecutionException;
public class WidgetPreviewLoader {
private static final String TAG = "WidgetPreviewLoader";
private static final boolean DEBUG = false;
private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
@ -78,7 +80,7 @@ public class WidgetPreviewLoader {
* @return a request id which can be used to cancel the request.
*/
public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
PagedViewWidget caller, Bitmap[] immediateResult) {
WidgetCell caller, Bitmap[] immediateResult) {
String size = previewWidth + "x" + previewHeight;
WidgetCacheKey key = getObjectKey(o, size);
@ -576,21 +578,26 @@ public class WidgetPreviewLoader {
private final Object mInfo;
private final int mPreviewHeight;
private final int mPreviewWidth;
private final PagedViewWidget mCaller;
private final WidgetCell mCaller;
PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth,
int previewHeight, PagedViewWidget caller) {
int previewHeight, WidgetCell caller) {
mKey = key;
mInfo = info;
mPreviewHeight = previewHeight;
mPreviewWidth = previewWidth;
mCaller = caller;
if (DEBUG) {
Log.d(TAG, String.format("%s, %s, %d, %d",
mKey, mInfo, mPreviewHeight, mPreviewWidth));
}
}
@Override
protected Bitmap doInBackground(Void... params) {
Bitmap unusedBitmap = null;
// TODO(hyunyoungs): Figure out why this path causes concurrency issue.
synchronized (mUnusedBitmaps) {
// Check if we can use a bitmap
for (Bitmap candidate : mUnusedBitmaps) {
@ -608,7 +615,6 @@ public class WidgetPreviewLoader {
mUnusedBitmaps.remove(unusedBitmap);
}
}
if (isCancelled()) {
return null;
}

View File

@ -1,84 +0,0 @@
package com.android.launcher3;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
class SectionedWidgetsRow {
String section;
List<List<Object>> widgets;
public SectionedWidgetsRow(String sc) {
section = sc;
}
}
class SectionedWidgetsAlgorithm {
public List<SectionedWidgetsRow> computeSectionedWidgetRows(List<Object> sortedWidgets,
int widgetsPerRow) {
List<SectionedWidgetsRow> rows = new ArrayList<>();
LinkedHashMap<String, List<Object>> sections = computeSectionedApps(sortedWidgets);
for (Map.Entry<String, List<Object>> sectionEntry : sections.entrySet()) {
String section = sectionEntry.getKey();
SectionedWidgetsRow row = new SectionedWidgetsRow(section);
List<Object> widgets = sectionEntry.getValue();
int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow);
for (int i = 0; i < numRows; i++) {
List<Object> widgetsInRow = new ArrayList<>();
int offset = i * widgetsPerRow;
for (int j = 0; j < widgetsPerRow; j++) {
widgetsInRow.add(widgets.get(offset + j));
}
row.widgets.add(widgetsInRow);
}
}
return rows;
}
private LinkedHashMap<String, List<Object>> computeSectionedApps(List<Object> sortedWidgets) {
LinkedHashMap<String, List<Object>> sections = new LinkedHashMap<>();
for (Object info : sortedWidgets) {
String section = getSection(info);
List<Object> sectionedWidgets = sections.get(section);
if (sectionedWidgets == null) {
sectionedWidgets = new ArrayList<>();
sections.put(section, sectionedWidgets);
}
sectionedWidgets.add(info);
}
return sections;
}
private String getSection(Object widgetOrShortcut) {
return "UNKNOWN";
}
}
/**
* The widgets list view container.
*/
public class WidgetsContainerView extends FrameLayout {
public WidgetsContainerView(Context context) {
this(context, null);
}
public WidgetsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
}
}

View File

@ -71,6 +71,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperUtils;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.HashMap;

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.content.ComponentName;
import android.graphics.Bitmap;
import com.android.launcher3.ItemInfo;
import java.util.Arrays;
/**
* Represents a {@link Package} in the widget tray section.
*/
public class PackageItemInfo extends ItemInfo {
private static final String TAG = "PackageInfo";
/**
* A bitmap version of the application icon.
*/
public Bitmap iconBitmap;
/**
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon;
public ComponentName componentName;
int flags = 0;
PackageItemInfo() {
}
@Override
public String toString() {
return "PackageItemInfo(title=" + title.toString() + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+ " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ " user=" + user + ")";
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
/**
* Meta data used for late binding of the short cuts.
*
* @see {@link PendingAddItemInfo}
*/
public class PendingAddShortcutInfo extends PendingAddItemInfo {
ActivityInfo activityInfo;
public PendingAddShortcutInfo(ActivityInfo activityInfo) {
this.activityInfo = activityInfo;
componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
@Override
public String toString() {
return String.format("PendingAddShortcutInfo package=%s, name=%s",
activityInfo.packageName, activityInfo.name);
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.appwidget.AppWidgetHostView;
import android.os.Bundle;
import android.os.Parcelable;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
/**
* Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}.
*
* @see {@link PendingAddItemInfo}
*/
public class PendingAddWidgetInfo extends PendingAddItemInfo {
public int minWidth;
public int minHeight;
public int minResizeWidth;
public int minResizeHeight;
public int previewImage;
public int icon;
public LauncherAppWidgetProviderInfo info;
public AppWidgetHostView boundWidget;
public Bundle bindOptions = null;
public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
if (i.isCustomWidget) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
} else {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
}
this.info = i;
componentName = i.provider;
minWidth = i.minWidth;
minHeight = i.minHeight;
minResizeWidth = i.minResizeWidth;
minResizeHeight = i.minResizeHeight;
previewImage = i.previewImage;
icon = i.icon;
spanX = i.spanX;
spanY = i.spanY;
minSpanX = i.minSpanX;
minSpanY = i.minSpanY;
}
public boolean isCustomWidget() {
return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
}
// Copy constructor
public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
minWidth = copy.minWidth;
minHeight = copy.minHeight;
minResizeWidth = copy.minResizeWidth;
minResizeHeight = copy.minResizeHeight;
previewImage = copy.previewImage;
icon = copy.icon;
info = copy.info;
boundWidget = copy.boundWidget;
componentName = copy.componentName;
itemType = copy.itemType;
spanX = copy.spanX;
spanY = copy.spanY;
minSpanX = copy.minSpanX;
minSpanY = copy.minSpanY;
bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
}
@Override
public String toString() {
return String.format("PendingAddWidgetInfo package=%s, name=%s",
componentName.getPackageName(), componentName.getShortClassName());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2015 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.launcher3;
package com.android.launcher3.widget;
import android.content.Context;
import android.content.pm.PackageManager;
@ -23,6 +23,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
@ -31,15 +32,31 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
import com.android.launcher3.compat.AppWidgetManagerCompat;
/**
* The linear layout used strictly for the widget/wallpaper tab of the customization tray
* The linear layout used strictly for the widget tray.
*/
public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListener {
public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
private static PagedViewWidget sShortpressTarget = null;
private static final String TAG = "PagedViewWidget";
private static final boolean DEBUG = false;
// Temporary preset width and height of the image to keep them aligned.
//private static final int PRESET_PREVIEW_HEIGHT = 480;
//private static final int PRESET_PREVIEW_WIDTH = 480;
private int mPresetPreviewSize;
private static WidgetCell sShortpressTarget = null;
private final Rect mOriginalImagePadding = new Rect();
@ -53,23 +70,25 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
private WidgetPreviewLoader mWidgetPreviewLoader;
private PreviewLoadRequest mActiveRequest;
public PagedViewWidget(Context context) {
public WidgetCell(Context context) {
this(context, null);
}
public PagedViewWidget(Context context, AttributeSet attrs) {
public WidgetCell(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) {
public WidgetCell(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources r = context.getResources();
mDimensionsFormatString = r.getString(R.string.widget_dims_format);
mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size);
setWillNotDraw(false);
setClipToPadding(false);
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
}
@Override
@ -97,8 +116,11 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
@Override
protected void onDetachedFromWindow() {
if (DEBUG) {
Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
}
super.onDetachedFromWindow();
deletePreview(true);
deletePreview(false);
}
public void deletePreview(boolean recycleImage) {
@ -154,15 +176,19 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
public int[] getPreviewSize() {
final ImageView i = (ImageView) findViewById(R.id.widget_preview);
int[] maxSize = new int[2];
maxSize[0] = i.getWidth() - mOriginalImagePadding.left - mOriginalImagePadding.right;
maxSize[1] = i.getHeight() - mOriginalImagePadding.top;
maxSize[0] = mPresetPreviewSize;
maxSize[1] = mPresetPreviewSize;
return maxSize;
}
public void applyPreview(Bitmap bitmap) {
FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
final PagedViewWidgetImageView image =
(PagedViewWidgetImageView) findViewById(R.id.widget_preview);
final WidgetImageView image =
(WidgetImageView) findViewById(R.id.widget_preview);
if (DEBUG) {
Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
getTagToString(), preview));
}
if (preview != null) {
image.mAllowRequestLayout = false;
image.setImageDrawable(preview);
@ -177,6 +203,7 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
}
image.setAlpha(1f);
image.mAllowRequestLayout = true;
image.requestLayout();
}
}
@ -193,8 +220,8 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
public void run() {
if (sShortpressTarget != null) return;
if (mShortPressListener != null) {
mShortPressListener.onShortPress(PagedViewWidget.this);
sShortpressTarget = PagedViewWidget.this;
mShortPressListener.onShortPress(WidgetCell.this);
sShortpressTarget = WidgetCell.this;
}
mShortPressTriggered = true;
}
@ -221,7 +248,7 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
removeShortPressCallback();
if (mShortPressTriggered) {
if (mShortPressListener != null) {
mShortPressListener.cleanUpShortPress(PagedViewWidget.this);
mShortPressListener.cleanUpShortPress(WidgetCell.this);
}
mShortPressTriggered = false;
}
@ -264,6 +291,10 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
return;
}
int[] size = getPreviewSize();
if (DEBUG) {
Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
getTagToString(), size[0], size[1]));
}
if (size[0] <= 0 || size[1] <= 0) {
addOnLayoutChangeListener(this);
@ -292,4 +323,16 @@ public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListe
return Math.min(size[0], info.spanX * cellWidth);
}
/**
* Helper method to get the string info of the tag.
*/
private String getTagToString() {
if (getTag() instanceof PendingAddWidgetInfo) {
return ((PendingAddWidgetInfo)getTag()).toString();
} else if (getTag() instanceof PendingAddShortcutInfo) {
return ((PendingAddShortcutInfo)getTag()).toString();
}
return "";
}
}

View File

@ -14,17 +14,17 @@
* limitations under the License.
*/
package com.android.launcher3;
package com.android.launcher3.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.ImageView;
public class PagedViewWidgetImageView extends ImageView {
public class WidgetImageView extends ImageView {
public boolean mAllowRequestLayout = true;
public PagedViewWidgetImageView(Context context, AttributeSet attrs) {
public WidgetImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@ -44,6 +44,5 @@ public class PagedViewWidgetImageView extends ImageView {
super.onDraw(canvas);
canvas.restore();
}
}

View File

@ -0,0 +1,376 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragController;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.Folder;
import com.android.launcher3.IconCache;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.Workspace;
import java.util.ArrayList;
/**
* The widgets list view container.
*/
public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener,
View.OnLongClickListener, DragSource{
private static final String TAG = "WidgetContainerView";
private static final boolean DEBUG = false;
/* {@link RecyclerView} will keep following # of views in cache, before recycling. */
private static final int WIDGET_CACHE_SIZE = 2;
/* Global instances that are used inside this container. */
private Launcher mLauncher;
private DragController mDragController;
private IconCache mIconCache;
/* Data model for the widget */
private WidgetsModel mWidgets;
/* Recycler view related member variables */
private RecyclerView mView;
private WidgetsListAdapter mAdapter;
/* Dragging related. */
private boolean mDraggingWidget = false; // TODO(hyunyoungs): seems not needed? check!
private Point mLastTouchDownPos = new Point();
/* Rendering related. */
private WidgetPreviewLoader mWidgetPreviewLoader;
private Rect mPadding = new Rect();
public WidgetsContainerView(Context context) {
this(context, null);
}
public WidgetsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
mLauncher = (Launcher) context;
mDragController = mLauncher.getDragController();
mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher);
mWidgets = new WidgetsModel(context, mAdapter);
mAdapter.setWidgetsModel(mWidgets);
mIconCache = (LauncherAppState.getInstance()).getIconCache();
if (DEBUG) {
Log.d(TAG, "WidgetsContainerView constructor");
}
}
@Override
protected void onFinishInflate() {
if (DEBUG) {
Log.d(TAG, String.format("onFinishInflate [widgets size=%d]",
mWidgets.getPackageSize()));
}
mView = (RecyclerView) findViewById(R.id.widgets_list_view);
mView.setAdapter(mAdapter);
mView.setLayoutManager(new LinearLayoutManager(getContext()));
mView.setItemViewCacheSize(WIDGET_CACHE_SIZE);
mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
getPaddingBottom());
}
//
// Returns views used for launcher transitions.
//
public View getContentView() {
return findViewById(R.id.widgets_list_view);
}
public View getRevealView() {
// TODO(hyunyoungs): temporarily use apps view transition.
return findViewById(R.id.widgets_reveal_view);
}
public void scrollToTop() {
mView.scrollToPosition(0);
if (DEBUG) {
Log.d(TAG, String.format("scrollToTop, [widgets size=%d]",
mWidgets.getPackageSize()));
}
}
//
// Touch related handling.
//
@Override
public boolean onLongClick(View v) {
if (DEBUG) {
Log.d(TAG, String.format("onLonglick [v=%s]", v));
}
// Return early if this is not initiated from a touch
if (!v.isInTouchMode()) return false;
// When we have exited all apps or are in transition, disregard long clicks
if (!mLauncher.isWidgetsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled
Log.d(TAG, String.format("onLonglick dragging enabled?.", v));
if (!mLauncher.isDraggingEnabled()) return false;
return beginDragging(v);
}
private boolean beginDragging(View v) {
if (v instanceof WidgetCell) {
if (!beginDraggingWidget((WidgetCell) v)) {
return false;
}
} else {
Log.e(TAG, "Unexpected dragging view: " + v);
}
// We delay entering spring-loaded mode slightly to make sure the UI
// thready is free of any work.
postDelayed(new Runnable() {
@Override
public void run() {
// We don't enter spring-loaded mode if the drag has been cancelled
if (mLauncher.getDragController().isDragging()) {
// Go into spring loaded mode (must happen before we startDrag())
mLauncher.enterSpringLoadedDragMode();
}
}
}, 150);
return true;
}
private boolean beginDraggingWidget(WidgetCell v) {
mDraggingWidget = true;
// Get the widget preview as the drag representation
ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
// If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
// we abort the drag.
if (image.getDrawable() == null) {
mDraggingWidget = false;
return false;
}
// Compose the drag image
Bitmap preview;
Bitmap outline;
float scale = 1f;
Point previewPadding = null;
if (createItemInfo instanceof PendingAddWidgetInfo) {
// This can happen in some weird cases involving multi-touch. We can't start dragging
// the widget if this is null, so we break out.
PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
float minScale = 1.25f;
int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
int[] previewSizeBeforeScale = new int[1];
preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
maxWidth, null, previewSizeBeforeScale);
// Compare the size of the drag preview to the preview in the AppsCustomize tray
int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
v.getActualItemWidth());
scale = previewWidthInAppsCustomize / (float) preview.getWidth();
// The bitmap in the AppsCustomize tray is always the the same size, so there
// might be extra pixels around the preview itself - this accounts for that
if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
int padding =
(previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
previewPadding = new Point(padding, 0);
}
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo);
preview = Utilities.createIconBitmap(icon, mLauncher);
createItemInfo.spanX = createItemInfo.spanY = 1;
}
// Don't clip alpha values for the drag outline if we're using the default widget preview
boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
(((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
// Save the preview for the outline generation, then dim the preview
outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
false);
// Start the drag
mLauncher.lockScreenOrientation();
mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
mDragController.startDrag(image, preview, this, createItemInfo,
DragController.DRAG_ACTION_COPY, previewPadding, scale);
outline.recycle();
preview.recycle();
return true;
}
/*
* @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
*/
@Override
public boolean onTouch(View v, MotionEvent ev) {
Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev));
if (ev.getAction() == MotionEvent.ACTION_DOWN ||
ev.getAction() == MotionEvent.ACTION_MOVE) {
mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
}
return false;
}
//
// Drag related handling methods that implement {@link DragSource} interface.
//
@Override
public boolean supportsFlingToDelete() {
return false;
}
@Override
public boolean supportsAppInfoDropTarget() {
return true;
}
/*
* Both this method and {@link #supportsFlingToDelete} has to return {@code false} for the
* {@link DeleteDropTarget} to be invisible.)
*/
@Override
public boolean supportsDeleteDropTarget() {
return false;
}
@Override
public float getIntrinsicIconScaleFactor() {
return 0;
}
@Override
public void onFlingToDeleteCompleted() {
// We just dismiss the drag when we fling, so cleanup here
mLauncher.exitSpringLoadedDragModeDelayed(true,
Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
mLauncher.unlockScreenOrientation(false);
}
@Override
public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
boolean success) {
if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
!(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
// Exit spring loaded mode if we have not successfully dropped or have not handled the
// drop in Workspace
mLauncher.exitSpringLoadedDragModeDelayed(true,
Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
}
mLauncher.unlockScreenOrientation(false);
// Display an error message if the drag failed due to there not being enough space on the
// target layout we were dropping on.
if (!success) {
boolean showOutOfSpaceMessage = false;
if (target instanceof Workspace) {
int currentScreen = mLauncher.getCurrentWorkspaceScreen();
Workspace workspace = (Workspace) target;
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
ItemInfo itemInfo = (ItemInfo) d.dragInfo;
if (layout != null) {
layout.calculateSpans(itemInfo);
showOutOfSpaceMessage =
!layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
}
}
if (showOutOfSpaceMessage) {
mLauncher.showOutOfSpaceMessage(false);
}
d.deferDragViewCleanupPostAnimation = false;
}
}
//
// Container rendering related.
//
/*
* @see Insettable#setInsets(Rect)
*/
@Override
public void setInsets(Rect insets) {
setPadding(mPadding.left + insets.left, mPadding.top + insets.top,
mPadding.right + insets.right, mPadding.bottom + insets.bottom);
}
/**
* Initialize the widget data model.
*/
public void addWidgets(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm);
}
private WidgetPreviewLoader getWidgetPreviewLoader() {
if (mWidgetPreviewLoader == null) {
mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
}
return mWidgetPreviewLoader;
}
}

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.support.v7.widget.RecyclerView.Adapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.launcher3.IconCache;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.compat.UserHandleCompat;
import java.util.List;
/**
* List view adapter for the widget tray.
*
* <p>Memory vs. Performance:
* The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
* happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
* only a single type of view.
*/
public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
private static final String TAG = "WidgetsListAdapter";
private static final boolean DEBUG = false;
private Context mContext;
private Launcher mLauncher;
private LayoutInflater mLayoutInflater;
private IconCache mIconCache;
private WidgetsModel mWidgetsModel;
private WidgetPreviewLoader mWidgetPreviewLoader;
private View.OnTouchListener mTouchListener;
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
public WidgetsListAdapter(Context context,
View.OnTouchListener touchListener,
View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener,
Launcher launcher) {
mLayoutInflater = LayoutInflater.from(context);
mContext = context;
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
mLauncher = launcher;
mIconCache = LauncherAppState.getInstance().getIconCache();
}
public void setWidgetsModel(WidgetsModel w) {
mWidgetsModel = w;
}
@Override
public int getItemCount() {
return mWidgetsModel.getPackageSize();
}
@Override
public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
String packageName = mWidgetsModel.getPackageName(pos);
List<Object> infoList = mWidgetsModel.getSortedWidgets(packageName);
ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
if (DEBUG) {
Log.d(TAG, String.format(
"onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]",
pos, packageName, infoList.size(), row.getChildCount()));
}
// Add more views.
// if there are too many, hide them.
int diff = infoList.size() - row.getChildCount();
if (diff > 0) {
for (int i = 0; i < diff; i++) {
WidgetCell widget = new WidgetCell(mContext);
widget = (WidgetCell) mLayoutInflater.inflate(
R.layout.widget_cell, row, false);
// set up touch.
widget.setOnClickListener(mIconClickListener);
widget.setOnLongClickListener(mIconLongClickListener);
widget.setOnTouchListener(mTouchListener);
row.addView(widget);
}
} else if (diff < 0) {
for (int i=infoList.size() ; i < row.getChildCount(); i++) {
row.getChildAt(i).setVisibility(View.GONE);
}
}
// Bind the views in the application info section.
PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName);
if (infoOut.usingLowResIcon) {
mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
false /* useLowResIcon */, infoOut);
}
((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title);
ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image);
iv.setImageBitmap(infoOut.iconBitmap);
// Bind the view in the widget horizontal tray region.
for (int i=0; i < infoList.size(); i++) {
WidgetCell widget = (WidgetCell) row.getChildAt(i);
if (getWidgetPreviewLoader() == null || widget == null) {
return;
}
if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
widget.setTag(pawi);
widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader);
} else if (infoList.get(i) instanceof ResolveInfo) {
ResolveInfo info = (ResolveInfo) infoList.get(i);
PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo);
widget.setTag(pasi);
widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader);
}
widget.setVisibility(View.VISIBLE);
widget.ensurePreview();
}
// TODO(hyunyoungs): Draw the scrollable indicator.
}
@Override
public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (DEBUG) {
Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType));
}
ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
R.layout.widgets_list_row_view, parent, false);
return new WidgetsRowViewHolder(container);
}
@Override
public long getItemId(int pos) {
return pos;
}
private WidgetPreviewLoader getWidgetPreviewLoader() {
if (mWidgetPreviewLoader == null) {
mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
}
return mWidgetPreviewLoader;
}
/**
* TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell
* and then check if the total sum is longer than the parent width.
*/
private void addScrollableIndicator(int contentSize, ViewGroup parent) {
if (contentSize > 2) {
ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator);
indicator.setVisibility(View.VISIBLE);
}
}
}

View File

@ -0,0 +1,136 @@
package com.android.launcher3.widget;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator;
import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
* <p> The widgets and shortcuts are organized using package name as its index.
*/
public class WidgetsModel {
private static final String TAG = "WidgetsModel";
private static final boolean DEBUG = false;
/* List of packages that is tracked by this model. */
private List<String> mPackageNames = new ArrayList<>();
private Map<String, PackageItemInfo> mPackageItemInfoList = new HashMap<>();
/* Map of widgets and shortcuts that are tracked per package. */
private Map<String, ArrayList<Object>> mWidgetsList = new HashMap<>();
/* Notifies the adapter when data changes. */
private RecyclerView.Adapter mAdapter;
private Comparator mWidgetAndShortcutNameComparator;
private IconCache mIconCache;
public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
mAdapter = adapter;
mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context);
mIconCache = LauncherAppState.getInstance().getIconCache();
}
// Access methods that may be deleted if the private fields are made package-private.
public int getPackageSize() {
return mPackageNames.size();
}
// Access methods that may be deleted if the private fields are made package-private.
public String getPackageName(int pos) {
return mPackageNames.get(pos);
}
public PackageItemInfo getPackageItemInfo(String packageName) {
return mPackageItemInfoList.get(packageName);
}
public List<Object> getSortedWidgets(String packageName) {
return mWidgetsList.get(packageName);
}
public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
}
// clear the lists.
mPackageNames.clear();
mWidgetsList.clear();
// add and update.
for (Object o: widgetsShortcuts) {
String packageName = "";
if (o instanceof LauncherAppWidgetProviderInfo) {
LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
packageName = widgetInfo.provider.getPackageName();
} else if (o instanceof ResolveInfo) {
ResolveInfo resolveInfo = (ResolveInfo) o;
packageName = resolveInfo.activityInfo.packageName;
} else {
Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s",
o.getClass().toString()));
}
ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(packageName);
if (widgetsShortcutsList != null) {
widgetsShortcutsList.add(o);
} else {
widgetsShortcutsList = new ArrayList<Object>();
widgetsShortcutsList.add(o);
mWidgetsList.put(packageName, widgetsShortcutsList);
mPackageNames.add(packageName);
}
}
for (String packageName: mPackageNames) {
PackageItemInfo pInfo = mPackageItemInfoList.get(packageName);
if (pInfo == null) {
pInfo = new PackageItemInfo();
mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
true /* useLowResIcon */, pInfo);
mPackageItemInfoList.put(packageName, pInfo);
}
}
// sort.
sortPackageList();
for (String packageName: mPackageNames) {
Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator);
}
// notify.
mAdapter.notifyDataSetChanged();
}
private void sortPackageList() {
Collections.sort(mPackageNames, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
String lhsTitle = mPackageItemInfoList.get(lhs).title.toString();
String rhsTitle = mPackageItemInfoList.get(rhs).title.toString();
return lhsTitle.compareTo(rhsTitle);
}
});
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011 The Android Open Source Project
* Copyright (C) 2015 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.
@ -14,36 +14,31 @@
* limitations under the License.
*/
package com.android.launcher3;
package com.android.launcher3.widget;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.GridLayout;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import com.android.launcher3.R;
/**
* The grid based layout used strictly for the widget/wallpaper tab of the AppsCustomize pane
* Layout used for widget tray rows for each app. For performance, this view can be replaced with
* a {@link RecyclerView} in the future if we settle on scrollable single row for the widgets.
* If we decide on collapsable grid, then HorizontalScrollView can be replaced with a
* {@link GridLayout}.
*/
public class PagedViewGridLayout extends GridLayout implements Page {
static final String TAG = "PagedViewGridLayout";
public class WidgetsRowView extends HorizontalScrollView {
static final String TAG = "WidgetsRow";
private int mCellCountX;
private int mCellCountY;
private Runnable mOnLayoutListener;
private String mAppName;
public PagedViewGridLayout(Context context, int cellCountX, int cellCountY) {
public WidgetsRowView(Context context, String appName) {
super(context, null, 0);
mCellCountX = cellCountX;
mCellCountY = cellCountY;
}
int getCellCountX() {
return mCellCountX;
}
int getCellCountY() {
return mCellCountY;
mAppName = appName;
}
/**
@ -56,6 +51,13 @@ public class PagedViewGridLayout extends GridLayout implements Page {
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
TextView tv = (TextView) findViewById(R.id.widget_name);
tv.setText(mAppName);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
@ -66,6 +68,7 @@ public class PagedViewGridLayout extends GridLayout implements Page {
mOnLayoutListener = r;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mOnLayoutListener != null) {
@ -76,43 +79,9 @@ public class PagedViewGridLayout extends GridLayout implements Page {
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
int count = getPageChildCount();
if (count > 0) {
// We only intercept the touch if we are tapping in empty space after the final row
View child = getChildOnPageAt(count - 1);
int bottom = child.getBottom();
result = result || (event.getY() < bottom);
}
return result;
}
@Override
public void removeAllViewsOnPage() {
removeAllViews();
mOnLayoutListener = null;
setLayerType(LAYER_TYPE_NONE, null);
}
@Override
public void removeViewOnPageAt(int index) {
removeViewAt(index);
}
@Override
public int getPageChildCount() {
return getChildCount();
}
@Override
public View getChildOnPageAt(int i) {
return getChildAt(i);
}
@Override
public int indexOfChildOnPage(View v) {
return indexOfChild(v);
}
public static class LayoutParams extends FrameLayout.LayoutParams {
public LayoutParams(int width, int height) {
super(width, height);

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2015 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.launcher3.widget;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class WidgetsRowViewHolder extends ViewHolder {
ViewGroup mContent;
public WidgetsRowViewHolder(ViewGroup v) {
super(v);
mContent = v;
}
ViewGroup getContent() {
return mContent;
}
}