/*
* Copyright (C) 2008 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.launcher;
import com.android.launcher.FlingGesture.FlingListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import android.widget.Toast;
public class MiniLauncher extends ViewGroup implements View.OnLongClickListener, DropTarget,
DragController.DragListener,DragSource,
FlingListener {
private static final int HORIZONTAL=1;
private static final int VERTICAL=0;
private Launcher mLauncher;
//private View mDeleteView;
private int mOrientation=HORIZONTAL;
private int mNumCells=4;
private int mCellWidth=20;
private int mCellHeight=20;
private TransitionDrawable mBackground;
/**
* ADW: Scrolling vars
*/
private Scroller mScroller;
private float mLastMotionX;
private float mLastMotionY;
private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;
private static final int TOUCH_STATE_DOWN = 2;
private int mTouchState = TOUCH_STATE_REST;
private int mTouchSlop;
private final int mScrollingSpeed=600;
private boolean mScrollAllowed=false;
private DragController mDragger;
private final FlingGesture mFlingGesture = new FlingGesture();
private int mCurrentIndex = 0;
public MiniLauncher(Context context) {
super(context);
}
public MiniLauncher(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MiniLauncher(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setHapticFeedbackEnabled(true);
TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.MiniLauncher,defStyle,0);
mOrientation=a.getInt(R.styleable.MiniLauncher_orientation, mOrientation);
mNumCells=a.getInt(R.styleable.MiniLauncher_cells, mNumCells);
mCellWidth=a.getDimensionPixelSize(R.styleable.MiniLauncher_cellWidth, mCellWidth);
mCellHeight=a.getDimensionPixelSize(R.styleable.MiniLauncher_cellHeight, mCellHeight);
mScroller = new Scroller(getContext());
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mFlingGesture.setListener(this);
}
public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
return true;
}
public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo, Rect recycle) {
return null;
}
private int FindItemDropPos(int x, int y) {
final boolean horizontal = (mOrientation==HORIZONTAL);
final int count=getChildCount();
int margin = horizontal ? ((getMeasuredWidth())/2)-(((count*mCellWidth)/2)) :
((getMeasuredHeight())/2)-(((count*mCellHeight)/2));
if (margin < 0)
margin = 0;
// check for drop on an item
int dropPos = horizontal ? (x + getScrollX()) : (y + getScrollY());
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
ItemInfo item=(ItemInfo) child.getTag();
if (child.getVisibility() != GONE) {
int bound1 = margin + (item.cellX * (horizontal ? mCellWidth : mCellHeight));
int bound2 = bound1 + (horizontal ? mCellWidth : mCellHeight);
if (dropPos < bound1 && item.cellX == 0) // before the first item
return 0;
if (dropPos >= bound2 && item.cellX == (count - 1))
return count;
if (dropPos >= bound1 && dropPos < bound2) {
int pos = item.cellX;
for (int j = 0; j < item.cellX; j++) {
View chld = getChildAt(j);
if (chld.getVisibility() == GONE)
pos++;
}
int middle = bound1 + (((mOrientation==HORIZONTAL) ? mCellWidth : mCellHeight) / 2);
if (dropPos >= middle)
return pos + 1;
return pos;
}
}
}
//something went wrong...
return getChildCount();
}
/**
* Adds the specified child in the specified screen. The position and dimension of
* the child are defined by x, y, spanX and spanY.
*
* @param child The child to add in one of the workspace's screens.
* @param screen The screen in which to add the child.
* @param x The X position of the child in the screen's grid.
* @param y The Y position of the child in the screen's grid.
* @param spanX The number of cells spanned horizontally by the child.
* @param spanY The number of cells spanned vertically by the child.
* @param insert When true, the child is inserted at the beginning of the children list.
*/
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
final LauncherModel model = Launcher.getModel();
ItemInfo info = (ItemInfo) dragInfo;
boolean accept=true;
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
//we do accept those
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Toast t=Toast.makeText(getContext(), R.string.toast_widgets_not_supported, Toast.LENGTH_SHORT);
t.show();
accept=false;
return;
default:
Toast t2=Toast.makeText(getContext(), R.string.toast_unknown_item, Toast.LENGTH_SHORT);
t2.show();
accept=false;
return;
}
info.cellX=FindItemDropPos(x, y);
//add it to launcher database
if (info instanceof LauncherAppWidgetInfo) {
model.removeDesktopAppWidget((LauncherAppWidgetInfo) info);
final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) info;
final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
if (appWidgetHost != null) {
appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
}
}
if(accept){
model.addDesktopItem(info);
LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
LauncherSettings.Favorites.CONTAINER_DOCKBAR, -1, info.cellX, -1);
addItemInDockBar(info);
// Reorder the other items:
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
ItemInfo childInfo = (ItemInfo)child.getTag();
if (childInfo != info && childInfo.cellX >= info.cellX) {
childInfo.cellX += 1;
LauncherModel.moveItemInDatabase(mLauncher, childInfo,
LauncherSettings.Favorites.CONTAINER_DOCKBAR, -1, childInfo.cellX, -1);
}
}
}else{
LauncherModel.deleteItemFromDatabase(mLauncher, info);
}
}
public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
mBackground.startTransition(200);
}
public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
}
public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
Object dragInfo) {
mBackground.resetTransition();
}
public void onDragStart(View v, DragSource source, Object info, int dragAction) {
}
public void onDragEnd() {
}
public void addItemInDockBar(ItemInfo info){
View view=null;
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (info.container == NO_ID) {
// Came from all apps -- make a copy
info = new ApplicationInfo((ApplicationInfo) info);
}
view = mLauncher.createSmallShortcut(R.layout.small_application, this,
(ApplicationInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
view = mLauncher.createSmallLiveFolder(R.layout.small_application, this,
(LiveFolderInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
view = mLauncher.createSmallFolder(R.layout.small_application, this,
(UserFolderInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
//Toast t=Toast.makeText(getContext(), "Widgets not supported... sorry :-)", Toast.LENGTH_SHORT);
//t.show();
return;
default:
//Toast t2=Toast.makeText(getContext(), "Unknown item. We can't add unknown item types :-)", Toast.LENGTH_SHORT);
//t2.show();
return;
//throw new IllegalStateException("Unknown item type: " + info.itemType);
}
view.setLongClickable(true);
view.setOnLongClickListener(this);
view.setBackgroundDrawable(IconHighlights.getDrawable(mLauncher,IconHighlights.TYPE_DOCKBAR));
addView(view);
invalidate();
}
public boolean onLongClick(View v) {
if (!v.isInTouchMode()) {
return false;
}
//ADW Delete the item and reposition the remaining ones
ItemInfo item=(ItemInfo) v.getTag();
//Now we need to update database (and position) for remainint items
final int count=getChildCount();
for(int i=0;i<count;i++){
final View cell=getChildAt(i);
final ItemInfo info = (ItemInfo) cell.getTag();
if(info.cellX>item.cellX){
info.cellX-=1;
cell.setTag(info);
LauncherModel.moveItemInDatabase(mLauncher, info,
LauncherSettings.Favorites.CONTAINER_DOCKBAR, -1, info.cellX, -1);
}
}
requestLayout();
Launcher.getModel().removeDesktopItem(item);
mDragger.startDrag(v, this, item, DragController.DRAG_ACTION_COPY);
detachViewFromParent(v);
removeView(v);
return true;
}
void setLauncher(Launcher launcher) {
mLauncher = launcher;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
child.measure(mCellWidth, mCellHeight);
}
if(mOrientation==HORIZONTAL){
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mCellHeight, MeasureSpec.AT_MOST));
}else{
super.onMeasure(MeasureSpec.makeMeasureSpec(mCellWidth, MeasureSpec.AT_MOST),heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
int count = getChildCount();
int marginLeft=((getMeasuredWidth())/2)-(((count*mCellWidth)/2));
int marginTop=((getMeasuredHeight())/2)-(((count*mCellHeight)/2));
if(getChildCount()>mNumCells){
if(mOrientation==HORIZONTAL){
marginLeft=0;
mCellWidth=(getMeasuredWidth()/mNumCells);
}else{
marginTop=0;
mCellHeight=(getMeasuredHeight()/mNumCells);
}
mScrollAllowed=true;
snapScroll();
}else{
mScrollAllowed=false;
scrollTo(0, 0);
}
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
ItemInfo item=(ItemInfo) child.getTag();
if (child.getVisibility() != GONE) {
int childLeft=(mOrientation==HORIZONTAL)?marginLeft+(item.cellX*mCellWidth):0;
int childTop = (mOrientation==VERTICAL)?marginTop+(item.cellX*mCellHeight):0;
int childRight = childLeft+mCellWidth;
int childBottom = childTop+mCellHeight;
child.layout(childLeft, childTop, childRight, childBottom);
}
}
}
/**
* ADW: Lets add scrolling capabilities :P
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!mScrollAllowed){
return super.onInterceptTouchEvent(ev);
}
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
return true;
}
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
if (xMoved || yMoved) {
if (xMoved && mOrientation==HORIZONTAL) {
mTouchState = TOUCH_STATE_SCROLLING;
}else if(yMoved && mOrientation==VERTICAL){
mTouchState = TOUCH_STATE_SCROLLING;
}
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mLastMotionY = y;
mTouchState=mScroller.isFinished() ? TOUCH_STATE_REST:TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
}
return mTouchState != TOUCH_STATE_REST;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(!mScrollAllowed){
return super.onTouchEvent(ev);
}
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
mFlingGesture.ForwardTouchEvent(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mTouchState = TOUCH_STATE_DOWN;
// Remember where the motion event started
mLastMotionX = x;
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
final int delta = (mOrientation==HORIZONTAL)?(int) (mLastMotionX - x):(int) (mLastMotionY - y);
if(Math.abs(delta)>mTouchSlop || mTouchState == TOUCH_STATE_SCROLLING){
mTouchState = TOUCH_STATE_SCROLLING;
mLastMotionX = x;
mLastMotionY = y;
if (delta < 0) {
if(mOrientation==HORIZONTAL)
scrollBy(delta, 0);
else
scrollBy(0,delta);
} else if (delta > 0) {
if(mOrientation==HORIZONTAL)
scrollBy(delta, 0);
else
scrollBy(0,delta);
}
}
}
break;
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
}
return true;
}
@Override
public void OnFling(int Direction) {
int leftDir = (mOrientation == HORIZONTAL)?FlingGesture.FLING_LEFT:FlingGesture.FLING_DOWN;
int rightDir = (mOrientation == HORIZONTAL)?FlingGesture.FLING_RIGHT:FlingGesture.FLING_UP;
if (Direction == leftDir && mCurrentIndex > 0) {
// Fling hard enough to move left
snapToItem(mCurrentIndex - getItemsPerPage());
} else if (Direction == rightDir && mCurrentIndex < getChildCount() - 1) {
// Fling hard enough to move right
snapToItem(mCurrentIndex + getItemsPerPage());
}
else
snapScroll();
}
private void snapToItem(int index) {
if (index < 0)
index = 0;
if (index > getChildCount() - getItemsPerPage())
index = getChildCount() - getItemsPerPage();
final int actualScroll=(mOrientation==HORIZONTAL)?getScrollX():getScrollY();
final int cellSize=(mOrientation==HORIZONTAL)?mCellWidth:mCellHeight;
final int target = cellSize * index;
final int delta=target-actualScroll;
if(mOrientation==HORIZONTAL){
mScroller.startScroll(actualScroll, 0, delta, 0, mScrollingSpeed);
}else{
mScroller.startScroll(0,actualScroll, 0, delta, mScrollingSpeed);
}
mCurrentIndex = index;
}
private int getItemsPerPage() {
final int actualLimit=(mOrientation==HORIZONTAL)?getWidth():getHeight();
final int cellSize=(mOrientation==HORIZONTAL)?mCellWidth:mCellHeight;
return actualLimit / cellSize;
}
private void snapScroll(){
final int actualScroll=(mOrientation==HORIZONTAL)?getScrollX():getScrollY();
final int cellSize=(mOrientation==HORIZONTAL)?mCellWidth:mCellHeight;
final int actualLimit=(mOrientation==HORIZONTAL)?getWidth():getHeight();
int position = actualScroll-(actualScroll%cellSize);
if(position<0)position=0;
if(position>((getChildCount())*cellSize)-actualLimit) position=(getChildCount()*cellSize)-actualLimit;
mCurrentIndex = position / cellSize;
final int delta=position-actualScroll;
if(mOrientation==HORIZONTAL){
mScroller.startScroll(actualScroll, 0, delta, 0, mScrollingSpeed);
}else{
mScroller.startScroll(0,actualScroll, 0, delta, mScrollingSpeed);
}
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
@Override
public void setBackgroundDrawable(Drawable d) {
// TODO Auto-generated method stub
if(mBackground!=null)mBackground.setCallback(null);
super.setBackgroundDrawable(d);
mBackground=(TransitionDrawable) d;
mBackground.setCrossFadeEnabled(true);
}
public void onDropCompleted(View target, boolean success) {
}
public void setDragger(DragController dragger) {
mDragger=dragger;
}
public void updateCounters(String packageName, int counter, int color){
final int count=getChildCount();
for(int i=0;i<count;i++){
final View view=getChildAt(i);
final Object tag = view.getTag();
if (tag instanceof ApplicationInfo) {
ApplicationInfo info = (ApplicationInfo) tag;
// We need to check for ACTION_MAIN otherwise getComponent() might
// return null for some shortcuts (for instance, for shortcuts to
// web pages.)
final Intent intent = info.intent;
final ComponentName name = intent.getComponent();
if ((info.itemType==LauncherSettings.Favorites.ITEM_TYPE_APPLICATION||
info.itemType==LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
packageName.equals(name.getPackageName())) {
if(view instanceof CounterImageView)
((CounterImageView) view).setCounter(counter, color);
//else if
view.invalidate();
Launcher.getModel().updateCounterDesktopItem(info, counter, color);
}
}
}
}
}