diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a900209f738a..202f4d645634 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2673,6 +2673,62 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean
*/
public static final String POINTER_SPEED = "pointer_speed";
+ /**
+ * Whether or not to use the app sidebar
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_ENABLED = "app_sidebar_enabled";
+
+ /**
+ * User defined transparency level for sidebar
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_TRANSPARENCY = "app_sidebar_transparency";
+
+ /**
+ * Disable text labels for app sidebar items
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_DISABLE_LABELS = "app_sidebar_disable_labels";
+
+ /**
+ * Position of app sidebar
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_POSITION = "app_sidebar_position";
+
+ /**
+ * Width of the appbar trigger
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_TRIGGER_WIDTH = "app_sidebar_trigger_width";
+
+ /**
+ * Position of appbar trigger
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_TRIGGER_TOP = "app_sidebar_trigger_top";
+
+ /**
+ * Height of the appbar trigger
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_TRIGGER_HEIGHT = "app_sidebar_trigger_height";
+
+ /**
+ * Whether to display the trigger region or not
+ *
+ * @hide
+ */
+ public static final String APP_SIDEBAR_SHOW_TRIGGER = "app_sidebar_show_trigger";
+
/**
* Whether to enable pie controls
* The value is 1, 2 or 0).
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index baa2436888fc..8b58f9be5c77 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -77,6 +77,9 @@
+
+
+
@@ -239,6 +242,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/sidebar_drag_enter.xml b/packages/SystemUI/res/drawable/sidebar_drag_enter.xml
new file mode 100644
index 000000000000..dab369373855
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sidebar_drag_enter.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/sidebar_drag_exit.xml b/packages/SystemUI/res/drawable/sidebar_drag_exit.xml
new file mode 100644
index 000000000000..8b96818255b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sidebar_drag_exit.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/trigger_region.xml b/packages/SystemUI/res/drawable/trigger_region.xml
new file mode 100644
index 000000000000..dcbef4a5ab9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/trigger_region.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/app_sidebar.xml b/packages/SystemUI/res/layout/app_sidebar.xml
new file mode 100644
index 000000000000..f802bb221e2a
--- /dev/null
+++ b/packages/SystemUI/res/layout/app_sidebar.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/folder_icon.xml b/packages/SystemUI/res/layout/folder_icon.xml
new file mode 100644
index 000000000000..bee6bf22c388
--- /dev/null
+++ b/packages/SystemUI/res/layout/folder_icon.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/setup_user_folder.xml b/packages/SystemUI/res/layout/setup_user_folder.xml
new file mode 100644
index 000000000000..55772546ab09
--- /dev/null
+++ b/packages/SystemUI/res/layout/setup_user_folder.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/sidebar_configuration_layout.xml b/packages/SystemUI/res/layout/sidebar_configuration_layout.xml
new file mode 100644
index 000000000000..259dc51b250d
--- /dev/null
+++ b/packages/SystemUI/res/layout/sidebar_configuration_layout.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/sidebar_user_folder.xml b/packages/SystemUI/res/layout/sidebar_user_folder.xml
new file mode 100644
index 000000000000..e6ff9367921d
--- /dev/null
+++ b/packages/SystemUI/res/layout/sidebar_user_folder.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/menu/sidebar_configuration.xml b/packages/SystemUI/res/menu/sidebar_configuration.xml
new file mode 100644
index 000000000000..e62d43155b1c
--- /dev/null
+++ b/packages/SystemUI/res/menu/sidebar_configuration.xml
@@ -0,0 +1,10 @@
+
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 464f39ec0cb5..8d946a343ae3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -53,4 +53,7 @@
@android:color/holo_blue_light
#ff8ad5f0
#ff0099cc
+
+
+ #8015a0e5
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 92df9b9d5ced..31018bdb1400 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -105,5 +105,8 @@
true
+
+
+ 8dip
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4b29e7688a94..313066ed7a16 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -271,4 +271,20 @@
255dp
85sp
+
+ 60dp
+ 2dp
+ 16dp
+ 32dp
+
+ 80dp
+ 48dp
+ 12sp
+ 16dp
+ 0dp
+ 10dp
+ 290dp
+ 480dp
+ 90dp
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 129a27e44d2e..398a196e8372 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -680,4 +680,12 @@
Airplane mode on
Emergency calls only
+
+ Folder
+ Appbar content setup
+ Save
+ Appbar items have been saved.
+ Add items to the Appbar by long pressing any item on the right and drag it over to the Appbar.\n\nCombine items into folders by droping an item onto any existing item in the Appbar.\n\nTo remove an item simply drag it out of the \@appbar.
+ Continue
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppSidebar.java b/packages/SystemUI/src/com/android/systemui/statusbar/AppSidebar.java
new file mode 100644
index 000000000000..b9abd961918d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AppSidebar.java
@@ -0,0 +1,806 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar;
+
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_BACK;
+
+import android.app.AlarmManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.animation.Animation;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.TranslateAnimation;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.sidebar.*;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+public class AppSidebar extends FrameLayout {
+ private static final String TAG = "AppSidebar";
+ private static final boolean DEBUG_LAYOUT = false;
+ private static final long AUTO_HIDE_DELAY = 3000;
+
+ // Sidebar positions
+ public static final int SIDEBAR_POSITION_LEFT = 0;
+ public static final int SIDEBAR_POSITION_RIGHT = 1;
+
+ private static final String ACTION_HIDE_APP_CONTAINER
+ = "com.android.internal.policy.statusbar.HIDE_APP_CONTAINER";
+
+ public static final String ACTION_SIDEBAR_ITEMS_CHANGED
+ = "com.android.internal.policy.statusbar.SIDEBAR_ITEMS_CHANGED";
+
+ private static enum SIDEBAR_STATE { OPENING, OPENED, CLOSING, CLOSED };
+ private SIDEBAR_STATE mState = SIDEBAR_STATE.CLOSED;
+
+ private static final LinearLayout.LayoutParams SCROLLVIEW_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ 1.0f );
+
+ private static LinearLayout.LayoutParams ITEM_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1.0f
+ );
+
+ private int mTriggerWidth;
+ private int mTriggerTop;
+ private int mTriggerBottom;
+ private int mTriggerColor;
+ private LinearLayout mAppContainer;
+ private SnappingScrollView mScrollView;
+ private List mContainerItems;
+ private Rect mIconBounds;
+ private int mItemTextSize;
+ private float mBarAlpha = 1f;
+ private int mFolderWidth;
+ private Folder mFolder;
+ private boolean mFirstTouch = false;
+ private boolean mHideTextLabels = false;
+ private boolean mUseTab = false;
+ private int mPosition = SIDEBAR_POSITION_RIGHT;
+ private int mBarHeight;
+
+ private TranslateAnimation mSlideIn;
+ private TranslateAnimation mSlideOut;
+
+ private Context mContext;
+ private SettingsObserver mSettingsObserver;
+ private PackageManager mPm;
+ private WindowManager mWm;
+
+ public AppSidebar(Context context) {
+ this(context, null);
+ }
+
+ public AppSidebar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppSidebar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mTriggerWidth = context.getResources().getDimensionPixelSize(R.dimen.config_app_sidebar_trigger_width);
+ mContext = context;
+ Resources resources = context.getResources();
+ mItemTextSize = resources.getDimensionPixelSize(R.dimen.item_title_text_size);
+ mFolderWidth = resources.getDimensionPixelSize(R.dimen.folder_width);
+ int iconSize = resources.getDimensionPixelSize(R.dimen.app_sidebar_item_size) - mItemTextSize;
+ mIconBounds = new Rect(0, 0, iconSize, iconSize);
+ mTriggerColor = resources.getColor(R.color.trigger_region_color);
+ mPm = context.getPackageManager();
+ mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mBarHeight = getWindowHeight();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ if (DEBUG_LAYOUT)
+ setBackgroundColor(0xffff0000);
+ setupAppContainer();
+ mSettingsObserver = new SettingsObserver(new Handler());
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSettingsObserver.observe();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_HIDE_APP_CONTAINER);
+ filter.addAction(ACTION_SIDEBAR_ITEMS_CHANGED);
+ getContext().registerReceiver(mBroadcastReceiver, filter);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ getContext().registerReceiver(mAppChangeReceiver, filter);
+ createSidebarAnimations(mPosition);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mSettingsObserver.unobserve();
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mContext.unregisterReceiver(mAppChangeReceiver);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_OUTSIDE:
+ if (mState == SIDEBAR_STATE.OPENED)
+ showAppContainer(false);
+ break;
+ case MotionEvent.ACTION_DOWN:
+ if (isKeyguardEnabled())
+ return false;
+ if (ev.getX() <= mTriggerWidth && mState == SIDEBAR_STATE.CLOSED) {
+ showAppContainer(true);
+ cancelAutoHideTimer();
+ mScrollView.onTouchEvent(ev);
+ mFirstTouch = true;
+ } else
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ cancelAutoHideTimer();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ if (mState != SIDEBAR_STATE.CLOSED)
+ mState = SIDEBAR_STATE.OPENED;
+ if (mFirstTouch) {
+ mFirstTouch = false;
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ private void showTriggerRegion() {
+ setBackgroundResource(R.drawable.trigger_region);
+ }
+
+ private void hideTriggerRegion() {
+ setBackgroundColor(0x00000000);
+ }
+
+ private void setTopPercentage(float value) {
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams)this.getLayoutParams();
+ mTriggerTop = (int)(mBarHeight * value);
+ params.y = mTriggerTop;
+ params.height = mTriggerBottom;
+ try {
+ mWm.updateViewLayout(this, params);
+ } catch (Exception e) {
+ }
+ }
+
+ private void setBottomPercentage(float value) {
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams)this.getLayoutParams();
+ mTriggerBottom = (int)(mBarHeight * value);
+ params.height = mTriggerBottom;
+ try {
+ mWm.updateViewLayout(this, params);
+ } catch (Exception e) {
+ }
+ }
+
+ private void setTriggerWidth(int value) {
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams)this.getLayoutParams();
+ mTriggerWidth = value;
+ params.width = mTriggerWidth;
+ try {
+ mWm.updateViewLayout(this, params);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_OUTSIDE:
+ if (mState == SIDEBAR_STATE.OPENED)
+ showAppContainer(false);
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ default:
+ cancelAutoHideTimer();
+ }
+ return mScrollView.onTouchEvent(ev);
+ }
+
+ private void createSidebarAnimations(int position) {
+ if (position == SIDEBAR_POSITION_LEFT) {
+ mSlideIn = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, -1.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f);
+
+ mSlideOut = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, -1.0f,
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f);
+ } else {
+ mSlideIn = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, 1.0f, Animation.RELATIVE_TO_PARENT, 0.0f,
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f);
+
+ mSlideOut = new TranslateAnimation(
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 1.0f,
+ Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f);
+ }
+ mSlideIn.setDuration(300);
+ mSlideIn.setInterpolator(new DecelerateInterpolator());
+ mSlideIn.setFillAfter(true);
+ mSlideIn.setAnimationListener(mAnimListener);
+ mSlideOut.setDuration(300);
+ mSlideOut.setInterpolator(new DecelerateInterpolator());
+ mSlideOut.setFillAfter(true);
+ mSlideOut.setAnimationListener(mAnimListener);
+ }
+
+ private void showAppContainer(boolean show) {
+ if (mScrollView == null)
+ return;
+ mState = show ? SIDEBAR_STATE.OPENING : SIDEBAR_STATE.CLOSING;
+ if (show) {
+ mScrollView.setVisibility(View.VISIBLE);
+ expandFromTriggerRegion();
+ } else {
+ cancelAutoHideTimer();
+ dismissFolderView();
+ }
+ mScrollView.startAnimation(show ? mSlideIn : mSlideOut);
+ }
+
+ private Animation.AnimationListener mAnimListener = new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ animation.cancel();
+ mScrollView.clearAnimation();
+ switch (mState) {
+ case CLOSING:
+ mState = SIDEBAR_STATE.CLOSED;
+ mScrollView.setVisibility(View.GONE);
+ reduceToTriggerRegion();
+ break;
+ case OPENING:
+ mState = SIDEBAR_STATE.OPENED;
+ mScrollView.setVisibility(View.VISIBLE);
+ break;
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ };
+
+ private boolean isKeyguardEnabled() {
+ KeyguardManager km = (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ return km.inKeyguardRestrictedInputMode();
+ }
+
+ public void updateAutoHideTimer(long delay) {
+ Context ctx = getContext();
+ AlarmManager am = (AlarmManager)ctx.getSystemService(Context.ALARM_SERVICE);
+ Intent i = new Intent(ACTION_HIDE_APP_CONTAINER);
+
+ PendingIntent pi = PendingIntent.getBroadcast(ctx, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
+ try {
+ am.cancel(pi);
+ } catch (Exception e) {
+ }
+ Calendar time = Calendar.getInstance();
+ time.setTimeInMillis(System.currentTimeMillis() + delay);
+ am.set(AlarmManager.RTC, time.getTimeInMillis(), pi);
+ }
+
+ public void cancelAutoHideTimer() {
+ Context ctx = getContext();
+ AlarmManager am = (AlarmManager)ctx.getSystemService(Context.ALARM_SERVICE);
+ Intent i = new Intent(ACTION_HIDE_APP_CONTAINER);
+
+ PendingIntent pi = PendingIntent.getBroadcast(ctx, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
+ try {
+ am.cancel(pi);
+ } catch (Exception e) {
+ }
+ }
+
+ private final BroadcastReceiver mAppChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
+ Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ setupAppContainer();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_HIDE_APP_CONTAINER.equals(action)) {
+ dismissFolderView();
+ showAppContainer(false);
+ } else if (ACTION_SIDEBAR_ITEMS_CHANGED.equals(action)) {
+ if (mContainerItems != null) {
+ mContainerItems.clear();
+ setupAppContainer();
+ }
+ }
+ }
+ };
+
+ private int enableKeyEvents() {
+ return (0
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
+ }
+
+ private int disableKeyEvents() {
+ return (0
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
+ }
+
+ private void expandFromTriggerRegion() {
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
+ params.y = 0;
+ Rect r = new Rect();
+ getWindowVisibleDisplayFrame(r);
+ mBarHeight = r.bottom - r.top;
+ params.height = mBarHeight;
+ params.width = LayoutParams.WRAP_CONTENT;
+ params.flags = enableKeyEvents();
+ mWm.updateViewLayout(this, params);
+ }
+
+ private void reduceToTriggerRegion() {
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams) getLayoutParams();
+ params.y = mTriggerTop;
+ params.height = mTriggerBottom;
+ params.width = mTriggerWidth;
+ params.flags = disableKeyEvents();
+ mWm.updateViewLayout(this, params);
+ }
+
+ private void setupAppContainer() {
+ post(new Runnable() {
+ public void run() {
+ mContainerItems = new ArrayList();
+ loadSidebarContents();
+ layoutItems();
+ }
+ });
+ }
+
+ private int getWindowHeight() {
+ Rect r = new Rect();
+ getWindowVisibleDisplayFrame(r);
+ return r.bottom - r.top;
+ }
+
+ private void layoutItems() {
+ int windowHeight = getWindowHeight();
+ if (mScrollView != null)
+ removeView(mScrollView);
+
+ // create a linearlayout to hold our items
+ if (mAppContainer == null) {
+ mAppContainer = new LinearLayout(mContext);
+ mAppContainer.setOrientation(LinearLayout.VERTICAL);
+ mAppContainer.setGravity(Gravity.CENTER);
+ }
+ mAppContainer.removeAllViews();
+
+ // set the layout height based on the item height we would like and the
+ // number of items that would fit at on screen at once given the height
+ // of the app sidebar
+ int padding = mContext.getResources().getDimensionPixelSize(R.dimen.app_sidebar_item_padding);
+ int desiredHeight = mContext.getResources().
+ getDimensionPixelSize(R.dimen.app_sidebar_item_size) +
+ padding * 2;
+ int numItems = (int)Math.floor(windowHeight / desiredHeight);
+ ITEM_LAYOUT_PARAMS.height = windowHeight / numItems;
+ ITEM_LAYOUT_PARAMS.width = desiredHeight;
+
+ for (View icon : mContainerItems) {
+ ItemInfo ai = (ItemInfo)icon.getTag();
+ if (ai instanceof AppItemInfo) {
+ icon.setOnClickListener(mItemClickedListener);
+ if (mHideTextLabels)
+ ((TextView)icon).setTextSize(0);
+ } else {
+ icon.setOnClickListener(new FolderClickListener());
+ if (mHideTextLabels) {
+ ((FolderIcon)icon).setTextVisible(false);
+ ((FolderIcon)icon).setPreviewSize(mIconBounds.right);
+ }
+ }
+ icon.setClickable(true);
+ icon.setPadding(0, padding, 0, padding);
+ mAppContainer.addView(icon, ITEM_LAYOUT_PARAMS);
+ }
+
+ // we need our horizontal scroll view to wrap the linear layout
+ if (mScrollView == null) {
+ mScrollView = new SnappingScrollView(mContext);
+ // make the fading edge the size of a button (makes it more noticible that we can scroll
+ mScrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
+ mScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
+ mScrollView.setBackgroundResource(R.drawable.app_sidebar_background);
+ }
+ mScrollView.removeAllViews();
+ mScrollView.addView(mAppContainer, SCROLLVIEW_LAYOUT_PARAMS);
+ addView(mScrollView, SCROLLVIEW_LAYOUT_PARAMS);
+ mScrollView.setAlpha(mBarAlpha);
+ mScrollView.setVisibility(View.GONE);
+ mAppContainer.setFocusable(true);
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KEYCODE_BACK && event.getAction() == ACTION_DOWN &&
+ mState == SIDEBAR_STATE.OPENED)
+ showAppContainer(false);
+ return super.dispatchKeyEventPreIme(event);
+ }
+
+ private void launchApplication(AppItemInfo ai) {
+ dismissFolderView();
+ updateAutoHideTimer(500);
+ ComponentName cn = new ComponentName(ai.packageName, ai.className);
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(cn);
+ mContext.startActivity(intent);
+ }
+
+ private OnClickListener mItemClickedListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mState != SIDEBAR_STATE.OPENED || mFirstTouch) {
+ mFirstTouch = false;
+ return;
+ }
+
+ launchApplication((AppItemInfo)view.getTag());
+ }
+ };
+
+ class SnappingScrollView extends ScrollView {
+
+ private boolean mSnapTrigger = false;
+
+ public SnappingScrollView(Context context) {
+ super(context);
+ }
+
+ Runnable mSnapRunnable = new Runnable(){
+ @Override
+ public void run() {
+ int mSelectedItem = ((getScrollY() + (ITEM_LAYOUT_PARAMS.height / 2)) / ITEM_LAYOUT_PARAMS.height);
+ int scrollTo = mSelectedItem * ITEM_LAYOUT_PARAMS.height;
+ smoothScrollTo(0, scrollTo);
+ mSnapTrigger = false;
+ }
+ };
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ if (Math.abs(oldt - t) <= 1 && mSnapTrigger) {
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ removeCallbacks(mSnapRunnable);
+ postDelayed(mSnapRunnable, 100);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mSnapTrigger = true;
+ mFirstTouch = false;
+ if (mState != SIDEBAR_STATE.OPENED)
+ return false;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mSnapTrigger = false;
+ cancelAutoHideTimer();
+ }
+ return super.onTouchEvent(ev);
+ }
+ }
+
+ public void setPosition(int position) {
+ mPosition = position;
+ createSidebarAnimations(position);
+ }
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_ENABLED), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_TRANSPARENCY), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_POSITION), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_DISABLE_LABELS), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_TRIGGER_WIDTH), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_TRIGGER_TOP), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_TRIGGER_HEIGHT), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_SHOW_TRIGGER), false, this);
+ update();
+ }
+
+ void unobserve() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ boolean enabled = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_ENABLED, 0) == 1;
+ setVisibility(enabled ? View.VISIBLE : View.GONE);
+
+ float barAlpha = (float)(100 - Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_TRANSPARENCY, 0)) / 100f;
+ if (barAlpha != mBarAlpha) {
+ if (mScrollView != null)
+ mScrollView.setAlpha(barAlpha);
+ mBarAlpha = barAlpha;
+ }
+
+ int position = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_POSITION, SIDEBAR_POSITION_LEFT);
+ if (position != mPosition) {
+ mPosition = position;
+ createSidebarAnimations(position);
+ }
+
+ boolean hideLabels = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_DISABLE_LABELS, 0) == 1;
+ if (hideLabels != mHideTextLabels) {
+ mHideTextLabels = hideLabels;
+ if (mScrollView != null)
+ setupAppContainer();
+ }
+
+ int width = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_TRIGGER_WIDTH, 10);
+ if (mTriggerWidth != width)
+ setTriggerWidth(width);
+ setTopPercentage(Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_TRIGGER_TOP, 0) / 100f);
+ setBottomPercentage(Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_TRIGGER_HEIGHT, 100) / 100f);
+ if (Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_SHOW_TRIGGER, 0) == 1)
+ showTriggerRegion();
+ else
+ hideTriggerRegion();
+ }
+ }
+
+ private final class FolderClickListener implements OnClickListener {
+
+ @Override
+ public void onClick(View v) {
+ if (mFirstTouch)
+ return;
+ if (mFolder != null) {
+ dismissFolderView();
+ return;
+ }
+ final Folder folder = mFolder = ((FolderIcon)v).getFolder();
+ int iconY = v.getTop() - mScrollView.getScrollY();
+ mWm.addView(mFolder, getFolderLayoutParams(iconY, folder.getHeight()));
+ mFolder.setVisibility(View.VISIBLE);
+ ArrayList items = folder.getItemsInReadingOrder();
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ for (View item : items)
+ item.setOnClickListener(mItemClickedListener);
+ folder.setOnTouchListener(new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissFolderView();
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+ }
+
+ private void dismissFolderView() {
+ if (mFolder != null) {
+ mWm.removeView(mFolder);
+ mFolder = null;
+ updateAutoHideTimer(AUTO_HIDE_DELAY);
+ }
+ }
+
+ private void loadSidebarContents() {
+ String[] projection = {
+ SidebarTable.COLUMN_ITEM_ID,
+ SidebarTable.COLUMN_ITEM_TYPE,
+ SidebarTable.COLUMN_CONTAINER,
+ SidebarTable.COLUMN_TITLE,
+ SidebarTable.COLUMN_COMPONENT
+ };
+ ArrayList items = new ArrayList();
+ Cursor cursor = mContext.getContentResolver().query(SidebarContentProvider.CONTENT_URI,
+ projection, null, null, null);
+ while (cursor.moveToNext()) {
+ ItemInfo item;
+ int type = cursor.getInt(cursor.getColumnIndex(SidebarTable.COLUMN_ITEM_TYPE));
+ if (type == ItemInfo.TYPE_APPLICATION) {
+ item = new AppItemInfo();
+ String component = cursor.getString(4);
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ ((AppItemInfo)item).packageName = cn.getPackageName();
+ ((AppItemInfo)item).className = cn.getClassName();
+ } else {
+ item = new FolderInfo();
+ }
+ item.id = cursor.getInt(0);
+ item.itemType = type;
+ item.container = cursor.getInt(2);
+ item.title = cursor.getString(3);
+ if (item.container == ItemInfo.CONTAINER_SIDEBAR) {
+ if (item instanceof AppItemInfo) {
+ TextView tv = createAppItem((AppItemInfo) item);
+ mContainerItems.add(tv);
+ } else {
+ FolderIcon icon = FolderIcon.fromXml(R.layout.folder_icon,
+ mAppContainer, null, (FolderInfo)item, mContext, true);
+ mContainerItems.add(icon);
+ }
+ } else {
+ try {
+ ((AppItemInfo)item).setIcon(mPm.getActivityIcon(
+ new ComponentName(((AppItemInfo)item).packageName, ((AppItemInfo)item).className)));
+ } catch (NameNotFoundException e) {
+ ((AppItemInfo)item).setIcon(mPm.getDefaultActivityIcon());
+ }
+ ((AppItemInfo)item).icon.setBounds(mIconBounds);
+ FolderInfo info = (FolderInfo) items.get(item.container);
+ info.add((AppItemInfo) item);
+ }
+ items.add(item);
+ }
+ }
+
+ private TextView createAppItem(AppItemInfo info) {
+ TextView tv = new TextView(mContext);
+ try {
+ info.setIcon(mPm.getActivityIcon(new ComponentName(info.packageName, info.className)));
+ } catch (NameNotFoundException e) {
+ info.setIcon(mPm.getDefaultActivityIcon());
+ }
+ info.icon.setBounds(mIconBounds);
+ tv.setCompoundDrawables(null, info.icon, null, null);
+ tv.setTag(info);
+ tv.setText(info.title);
+ tv.setSingleLine(true);
+ tv.setEllipsize(TruncateAt.END);
+ tv.setGravity(Gravity.CENTER);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mItemTextSize);
+
+ return tv;
+ }
+
+ private WindowManager.LayoutParams getFolderLayoutParams(int iconY, int folderHeight) {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mFolderWidth,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
+ 0
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+ if (mPosition == SIDEBAR_POSITION_LEFT)
+ lp.x = getWidth();
+ else
+ lp.x = mWm.getDefaultDisplay().getWidth() - getWidth() - mFolderWidth;
+ if (iconY < 0)
+ lp.y = 0;
+ else {
+ if (iconY + folderHeight < getHeight())
+ lp.y = iconY;
+ else
+ lp.y = iconY - (getHeight() - (iconY + folderHeight));
+ }
+ lp.setTitle("SidebarFolder");
+ return lp;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 5549008a23cc..096201a194a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -29,6 +29,7 @@
import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsActivity;
import com.android.systemui.recent.TaskDescription;
+import com.android.systemui.statusbar.AppSidebar;
import com.android.systemui.statusbar.pie.PieLayout;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
import com.android.systemui.statusbar.policy.PieController;
@@ -306,6 +307,9 @@ public boolean onTouch(View v, MotionEvent event) {
private boolean mDeviceProvisioned = false;
+ protected AppSidebar mAppSidebar;
+ protected int mSidebarPosition;
+
public IStatusBarService getStatusBarService() {
return mBarService;
}
@@ -470,6 +474,9 @@ public void onReceive(Context context, Intent intent) {
mSettingsObserver.observe();
mLocale = mContext.getResources().getConfiguration().locale;
+
+ SidebarObserver observer = new SidebarObserver(mHandler);
+ observer.observe();
}
public void userSwitched(int newUserId) {
@@ -1464,6 +1471,63 @@ public boolean inKeyguardRestrictedInputMode() {
return km.inKeyguardRestrictedInputMode();
}
+ class SidebarObserver extends ContentObserver {
+ SidebarObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_POSITION), false, this);
+ update();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ int sidebarPosition = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_POSITION, AppSidebar.SIDEBAR_POSITION_LEFT);
+ if (sidebarPosition != mSidebarPosition) {
+ mSidebarPosition = sidebarPosition;
+ mWindowManager.updateViewLayout(mAppSidebar, getAppSidebarLayoutParams(sidebarPosition));
+ }
+ }
+ }
+
+ protected void addSidebarView() {
+ mAppSidebar = (AppSidebar)View.inflate(mContext, R.layout.app_sidebar, null);
+ mWindowManager.addView(mAppSidebar, getAppSidebarLayoutParams(mSidebarPosition));
+ }
+
+ protected void removeSidebarView() {
+ if (mAppSidebar != null)
+ mWindowManager.removeView(mAppSidebar);
+ }
+
+ protected WindowManager.LayoutParams getAppSidebarLayoutParams(int position) {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
+ 0
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.TOP;// | Gravity.FILL_VERTICAL;
+ lp.gravity |= position == AppSidebar.SIDEBAR_POSITION_LEFT ? Gravity.LEFT : Gravity.RIGHT;
+ lp.setTitle("AppSidebar");
+
+ return lp;
+ }
+
public void addNavigationBarCallback(NavigationBarCallback callback) {
mNavigationCallbacks.add(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5cdd0d15e94c..e8a685a84f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -103,6 +103,7 @@
import com.android.internal.util.slim.ButtonsHelper;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.statusbar.AppSidebar;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
@@ -407,6 +408,8 @@ void observe() {
Settings.System.EXPANDED_DESKTOP_STATE), false, this);
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.NOTIFICATION_SETTINGS_BUTTON), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SIDEBAR_POSITION), false, this);
update();
}
@@ -444,6 +447,12 @@ public void update() {
updateStatusBarVisibility();
}
showClock(true);
+ int sidebarPosition = Settings.System.getInt(
+ resolver, Settings.System.APP_SIDEBAR_POSITION, AppSidebar.SIDEBAR_POSITION_LEFT);
+ if (sidebarPosition != mSidebarPosition) {
+ mSidebarPosition = sidebarPosition;
+ mWindowManager.updateViewLayout(mAppSidebar, getAppSidebarLayoutParams(sidebarPosition));
+ }
}
}
@@ -603,6 +612,11 @@ public boolean onTouch(View v, MotionEvent event) {
addNavigationBarCallback(mNavigationBarView);
}
+ if (mRecreating) {
+ removeSidebarView();
+ }
+ addSidebarView();
+
// figure out which pixel-format to use for the status bar.
mPixelFormat = PixelFormat.OPAQUE;
@@ -3182,6 +3196,7 @@ public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
+ Configuration config = mContext.getResources().getConfiguration();
mDisplay.getSize(mCurrentDisplaySize);
updateResources();
@@ -3189,7 +3204,23 @@ public void onReceive(Context context, Intent intent) {
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
updateSwapXY();
updateShowSearchHoldoff();
- } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ try {
+ // position app sidebar on left if in landscape orientation and device has a navbar
+ if (mWindowManagerService.hasNavigationBar() &&
+ config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mWindowManager.updateViewLayout(mAppSidebar,
+ getAppSidebarLayoutParams(AppSidebar.SIDEBAR_POSITION_LEFT));
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mAppSidebar.setPosition(AppSidebar.SIDEBAR_POSITION_LEFT);
+ }
+ }, 500);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ else if (Intent.ACTION_SCREEN_ON.equals(action)) {
// work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
repositionNavigationBar();
notifyNavigationBarScreenOn(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppContainer.java
new file mode 100644
index 000000000000..b28bb5d6a6ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppContainer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import com.android.systemui.R;
+
+public class AppContainer extends LinearLayout {
+ private float mInsertDelta;
+ private ItemInfo mAddToItem = null;
+
+ public AppContainer(Context context) {
+ this(context, null);
+ }
+
+ public AppContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mInsertDelta = context.getResources().getDimensionPixelSize(R.dimen.item_above_below_delta);
+ }
+
+ public void repositionView(View view, float x, float y, boolean isFolder) {
+ int index = indexOfChild(view);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ float mid = v.getY() + v.getHeight()/2;
+ if (i == index){
+ if((index < childCount-1 && y > mid+mInsertDelta && y < v.getY()+v.getHeight()) &&
+ view.getVisibility() != View.GONE) {
+ removeView(view);
+ view.setVisibility(View.GONE);
+ addView(view, i+1);
+ }
+ continue;
+ }
+ if (v != view)
+ v.setBackgroundResource(0);
+ if ((i < index && (y > v.getY() && y < mid-mInsertDelta)) ||
+ (i > index && (y > mid+mInsertDelta && y < v.getY()+v.getHeight()))) {
+ removeView(view);
+ addView(view, i);
+ view.setVisibility(View.VISIBLE);
+ mAddToItem = null;
+ return;
+ } else if (!isFolder && v != view && y >= mid-mInsertDelta && y <= mid+mInsertDelta) {
+ view.setVisibility(View.GONE);
+ v.setBackgroundResource(R.drawable.item_placeholder);
+ mAddToItem = (ItemInfo) v.getTag();
+ return;
+ } else
+ view.setVisibility(View.VISIBLE);
+ mAddToItem = null;
+ }
+ }
+
+ public ItemInfo getAddToItem() {
+ return mAddToItem;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemAdapter.java
new file mode 100644
index 000000000000..f03ceeaf17fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import java.util.List;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class AppItemAdapter extends BaseAdapter {
+ List mInstalledApps;
+
+ public AppItemAdapter(List installedApps) {
+ mInstalledApps = installedApps;
+ }
+
+ @Override
+ public int getCount() {
+ return mInstalledApps.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mInstalledApps.get(position).getTag();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mInstalledApps.get(position);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemInfo.java
new file mode 100644
index 000000000000..fca7d7f7b381
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/AppItemInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.graphics.drawable.Drawable;
+
+public class AppItemInfo extends ItemInfo {
+ public String packageName;
+ public String className;
+ public Drawable icon;
+
+ public AppItemInfo() {
+ itemType = TYPE_APPLICATION;
+ container = CONTAINER_SIDEBAR;
+ }
+
+ public void setIcon(Drawable d) {
+ icon = d;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s, packageName: %s, className: %s",
+ super.toString(), packageName, className);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/Folder.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/Folder.java
new file mode 100644
index 000000000000..b85df94bab47
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/Folder.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.text.InputType;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.GridView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.sidebar.FolderInfo.FolderListener;
+
+public class Folder extends LinearLayout implements FolderListener,
+ View.OnFocusChangeListener, TextView.OnEditorActionListener {
+ protected FolderInfo mInfo;
+
+ private FolderIcon mIcon;
+ private Context mContext;
+ private ArrayList mContents = new ArrayList();
+ private boolean mIsEditingName = false;
+ FolderEditText mFolderName;
+
+ private static int sTextSize;
+
+ private GridView mContent;
+ private FrameLayout mFolderFooter;
+ private int mFolderNameHeight;
+
+ private InputMethodManager mInputMethodManager;
+
+ public Folder(Context context) {
+ this(context, null);
+ }
+
+ public Folder(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public Folder(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mContext = context;
+ mInputMethodManager = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ sTextSize = context.getResources().getDimensionPixelSize(R.dimen.item_title_text_size);
+ }
+
+ /**
+ * Creates a new UserFolder, inflated from R.layout.setup_user_folder.
+ *
+ * @param context The application's context.
+ *
+ * @return A new UserFolder.
+ */
+ static Folder fromXml(Context context, boolean isSidebar) {
+ return (Folder) LayoutInflater.from(context).inflate(
+ isSidebar ? R.layout.sidebar_user_folder : R.layout.setup_user_folder,
+ null);
+ }
+
+ public void setFolderIcon(FolderIcon icon) {
+ mIcon = icon;
+ }
+
+ public ArrayList getItemsInReadingOrder() {
+ return mContents;
+ }
+
+ void bind(FolderInfo info) {
+ mInfo = info;
+ ArrayList children = info.contents;
+ ArrayList overflow = new ArrayList();
+ int count = 0;
+ for (AppItemInfo child : children) {
+ if (!createAndAddShortcut(child)) {
+ overflow.add(child);
+ } else {
+ count++;
+ }
+ }
+ mFolderName.setText(mInfo.title);
+
+ mInfo.addListener(this);
+ }
+
+ protected boolean createAndAddShortcut(AppItemInfo item) {
+ final TextView textView = new TextView(mContext);
+
+ textView.setCompoundDrawables(null,
+ item.icon, null, null);
+ textView.setText(item.title);
+ textView.setTag(item);
+ textView.setSingleLine(true);
+ textView.setEllipsize(TruncateAt.END);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, sTextSize);
+
+ GridView.LayoutParams lp =
+ new GridView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ mContents.add(textView);
+ return true;
+ }
+
+ public int getItemCount() {
+ return mContents.size();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContent = (GridView) findViewById(R.id.folder_content);
+ mContent.setAdapter(new ContentsAdapter());
+ mFolderFooter = (FrameLayout) findViewById(R.id.folder_footer);
+ mFolderName = (FolderEditText) findViewById(R.id.folder_name);
+ mFolderName.setFolder(this);
+ mFolderName.setOnFocusChangeListener(this);
+
+ // We find out how tall the text view wants to be (it is set to wrap_content), so that
+ // we can allocate the appropriate amount of space for it.
+ int measureSpec = MeasureSpec.UNSPECIFIED;
+ mFolderFooter.measure(measureSpec, measureSpec);
+ mFolderNameHeight = mFolderFooter.getMeasuredHeight();
+
+ // We disable action mode for now since it messes up the view on phones
+ mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback);
+ mFolderName.setOnEditorActionListener(this);
+ mFolderName.setSelectAllOnFocus(true);
+ mFolderName.setInputType(mFolderName.getInputType() |
+ InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+
+ }
+
+ /**
+ * @return the FolderInfo object associated with this folder
+ */
+ FolderInfo getInfo() {
+ return mInfo;
+ }
+
+ @Override
+ public void onAdd(AppItemInfo item) {
+ // make sure this is not a duplicate
+ for (View v : mContents) {
+ AppItemInfo ai = (AppItemInfo) v.getTag();
+ if (ai.packageName.equals(item.packageName) && ai.className.equals(item.className))
+ return;
+ }
+
+ createAndAddShortcut(item);
+ ContentsAdapter adapter = (ContentsAdapter)mContent.getAdapter();
+ adapter.notifyDataSetChanged();
+ mContent.setAdapter(adapter);
+ }
+
+ @Override
+ public void onRemove(AppItemInfo item) {
+ mContents.remove(item);
+ View v = mContent.findViewWithTag(item);
+ if (v != null)
+ removeView(v);
+ }
+
+ public void removeView(View v) {
+ mContents.remove(v);
+ ContentsAdapter adapter = (ContentsAdapter)mContent.getAdapter();
+ adapter.notifyDataSetChanged();
+ mContent.setAdapter(adapter);
+ }
+
+ @Override
+ public void onTitleChanged(CharSequence title) {
+ }
+
+ @Override
+ public void onItemsChanged() {
+ }
+
+ public void startEditingFolderName() {
+ mFolderName.setHint("");
+ mIsEditingName = true;
+ }
+
+ public void doneEditingFolderName(boolean commit) {
+ // Convert to a string here to ensure that no other state associated with the text field
+ // gets saved.
+ String newTitle = mFolderName.getText().toString();
+ mInfo.setTitle(newTitle);
+ // In order to clear the focus from the text field, we set the focus on ourself. This
+ // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
+ requestFocus();
+
+ Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
+ mIsEditingName = false;
+ }
+
+ public void dismissEditingName() {
+ mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ doneEditingFolderName(true);
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (v == mFolderName && hasFocus) {
+ startEditingFolderName();
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ dismissEditingName();
+ return true;
+ }
+ return false;
+ }
+
+ private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+ };
+
+ private class ContentsAdapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return mContents.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mContents.get(position).getTag();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mContents.get(position);
+ }
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderEditText.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderEditText.java
new file mode 100644
index 000000000000..c342aada2ea0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderEditText.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+public class FolderEditText extends EditText {
+
+ private Folder mFolder;
+
+ public FolderEditText(Context context) {
+ super(context);
+ }
+
+ public FolderEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FolderEditText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setFolder(Folder folder) {
+ mFolder = folder;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderIcon.java
new file mode 100644
index 000000000000..6e8e0b7c8c16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderIcon.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.sidebar.FolderInfo.FolderListener;
+
+/**
+ * An icon that can appear on in the workspace representing an {@link Folder}.
+ */
+public class FolderIcon extends LinearLayout implements FolderListener {
+ private static boolean sStaticValuesDirty = true;
+
+ // The number of icons to display in the
+ private static final int NUM_ITEMS_IN_PREVIEW = 3;
+
+ private static final int STYLE_STACKED = 0;
+ private static final int STYLE_GRID = 1;
+ private static final int STYLE_CAROUSEL = 2;
+
+ // The amount of vertical spread between items in the stack [0...1]
+ private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
+
+ // The degree to which the item in the back of the stack is scaled [0...1]
+ // (0 means it's not scaled at all, 1 means it's scaled to nothing)
+ private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+
+ public static Drawable sSharedFolderLeaveBehind = null;
+
+ private ImageView mPreviewBackground;
+ private TextView mFolderName;
+ private FolderInfo mInfo;
+ private Folder mFolder;
+
+ private int mNumItemsInPreview = NUM_ITEMS_IN_PREVIEW;
+ private int mFolderIconStyle = STYLE_GRID;
+
+ // These variables are all associated with the drawing of the preview; they are stored
+ // as member variables for shared usage and to avoid computation on each frame
+ private int mIntrinsicIconSize;
+ private float mBaselineIconScale;
+ private int mBaselineIconSize;
+ private int mAvailableSpaceInPreview;
+ private int mTotalWidth = -1;
+ private int mPreviewOffsetX;
+ private int mPreviewOffsetY;
+ private float mMaxPerspectiveShift;
+ boolean mAnimating = false;
+ private static LayoutInflater sInflater;
+ private static int sPreviewSize;
+ private static int sPreviewPadding;
+
+ private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+ private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+ private ArrayList mHiddenItems = new ArrayList();
+
+ public FolderIcon(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public FolderIcon(Context context) {
+ super(context);
+ init(context);
+ }
+
+ private void init(Context context) {
+ //mFolderIconStyle = PreferencesProvider.Interface.Homescreen.FolderIconStyle.getFolderIconStyle(getContext());
+ mNumItemsInPreview = (mFolderIconStyle == STYLE_GRID) ? 4 : 3;
+ sPreviewSize = context.getResources().getDimensionPixelSize(R.dimen.icon_size);
+ }
+
+ public static FolderIcon fromXml(int resId, ViewGroup group, OnClickListener listener,
+ FolderInfo folderInfo, Context context, boolean isSidebar) {
+ if (sInflater == null)
+ sInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ FolderIcon icon = (FolderIcon) sInflater.inflate(resId, group, false);
+
+ int textSize = context.getResources().getDimensionPixelSize(R.dimen.item_title_text_size);
+ icon.mFolderName = (TextView) icon.findViewById(R.id.folder_icon_name);
+ icon.mFolderName.setText(folderInfo.title);
+ icon.mFolderName.setGravity(Gravity.CENTER);
+ icon.mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+ icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+
+ icon.setTag(folderInfo);
+ icon.setOnClickListener(listener);
+ icon.setDrawingCacheEnabled(true);
+ icon.mInfo = folderInfo;
+ Folder folder = Folder.fromXml(context, isSidebar);
+ folder.setFolderIcon(icon);
+ folder.bind(folderInfo);
+ icon.mFolder = folder;
+
+ folderInfo.addListener(icon);
+
+ return icon;
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ sStaticValuesDirty = true;
+ return super.onSaveInstanceState();
+ }
+
+ public Folder getFolder() {
+ return mFolder;
+ }
+
+ FolderInfo getFolderInfo() {
+ return mInfo;
+ }
+
+ private boolean willAcceptItem(ItemInfo item) {
+ final int itemType = item.itemType;
+ return (itemType == ItemInfo.TYPE_APPLICATION ||
+ item != mInfo && !mInfo.opened);
+ }
+
+ public void addItem(AppItemInfo item) {
+ mInfo.add(item);
+ }
+
+ public void setPreviewSize(int previewSize) {
+ sPreviewSize = previewSize;
+ }
+
+ private void computePreviewDrawingParams(int drawableSize, int totalSize) {
+ if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
+ mIntrinsicIconSize = drawableSize;
+ mTotalWidth = totalSize;
+
+ final int previewSize = sPreviewSize;
+ final int previewPadding = sPreviewPadding;
+
+ mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
+ // cos(45) = 0.707 + ~= 0.1) = 0.8f
+ int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
+
+ int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
+ mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
+
+ mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
+ mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
+
+ mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
+ mPreviewOffsetY = previewPadding;
+ }
+ }
+
+ private void computePreviewDrawingParams(Drawable d) {
+ Rect bounds = d.getBounds();
+ computePreviewDrawingParams(bounds.right - bounds.left, getMeasuredWidth());
+ }
+
+ class PreviewItemDrawingParams {
+ PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
+ this.transX = transX;
+ this.transY = transY;
+ this.scale = scale;
+ this.overlayAlpha = overlayAlpha;
+ }
+ float transX;
+ float transY;
+ float scale;
+ int overlayAlpha;
+ Drawable drawable;
+ }
+
+ private float getLocalCenterForIndex(int index, int[] center) {
+ mParams = computePreviewItemDrawingParams(Math.min(mNumItemsInPreview, index), mParams);
+
+ mParams.transX += mPreviewOffsetX;
+ mParams.transY += mPreviewOffsetY;
+ float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
+ float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
+
+ center[0] = Math.round(offsetX);
+ center[1] = Math.round(offsetY);
+ return mParams.scale;
+ }
+
+ private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
+ PreviewItemDrawingParams params) {
+ switch (mFolderIconStyle) {
+ case STYLE_STACKED:
+ return computePreviewItemDrawingParamsStacked(index, params);
+ case STYLE_GRID:
+ return computePreviewItemDrawingParamsGrid(index, params);
+ case STYLE_CAROUSEL:
+ return computePreviewItemDrawingParamsCarousel(index, params);
+ }
+ return params;
+ }
+
+ private PreviewItemDrawingParams computePreviewItemDrawingParamsStacked(int index,
+ PreviewItemDrawingParams params) {
+ index = mNumItemsInPreview - index - 1;
+ float r = (index * 1.0f) / (mNumItemsInPreview - 1);
+ float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
+
+ float offset = (1 - r) * mMaxPerspectiveShift;
+ float scaledSize = scale * mBaselineIconSize;
+ float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
+
+ // We want to imagine our coordinates from the bottom left, growing up and to the
+ // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
+ float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
+ float transX = offset + scaleOffsetCorrection;
+ float totalScale = mBaselineIconScale * scale;
+ final int overlayAlpha = (int) (80 * (1 - r));
+
+ if (params == null) {
+ params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+ } else {
+ params.transX = transX;
+ params.transY = transY;
+ params.scale = totalScale;
+ params.overlayAlpha = overlayAlpha;
+ }
+ return params;
+ }
+
+ private PreviewItemDrawingParams computePreviewItemDrawingParamsGrid(int index,
+ PreviewItemDrawingParams params) {
+ //index = mNumItemsInPreview - index - 1;
+ float iconScale = 0.45f;
+
+ // We want to imagine our coordinates from the bottom left, growing up and to the
+ // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
+ float totalCellSize = mAvailableSpaceInPreview / 2;
+ float iconSize = mIntrinsicIconSize * iconScale;
+ float cellOffset = (totalCellSize - iconSize) / 2f;
+ int cellX = index % (mNumItemsInPreview / 2);
+ int cellY = index / (mNumItemsInPreview / 2);
+ float xOffset = (totalCellSize * cellX) + cellOffset;
+ float yOffset = (totalCellSize * cellY) + cellOffset + mPreviewOffsetY;
+ float transX = xOffset;
+ float transY = yOffset;
+ final int overlayAlpha = 0;
+
+ if (params == null) {
+ params = new PreviewItemDrawingParams(transX, transY, iconScale, overlayAlpha);
+ } else {
+ params.transX = transX;
+ params.transY = transY;
+ params.scale = iconScale;
+ params.overlayAlpha = overlayAlpha;
+ }
+ return params;
+ }
+
+ private PreviewItemDrawingParams computePreviewItemDrawingParamsCarousel(int index,
+ PreviewItemDrawingParams params) {
+ float r = (index == 0) ? ((mNumItemsInPreview - 2) * 1.0f) / (mNumItemsInPreview - 1) :
+ 0;
+ float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
+
+ float yOffset;
+ float xOffset;
+ int alpha;
+ float scaledSize = scale * mBaselineIconSize;
+ if (index > 0 ) {
+ yOffset = scaledSize/3;
+ xOffset = index == 1 ? 0f : mAvailableSpaceInPreview - scaledSize;
+ alpha = 80;
+ } else {
+ yOffset = mMaxPerspectiveShift + scaledSize/3;
+ xOffset = (mAvailableSpaceInPreview - scaledSize) / 2;
+ alpha = 0;
+ }
+ //float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
+
+ // We want to imagine our coordinates from the bottom left, growing up and to the
+ // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
+ float transY = yOffset;// + scaledSize + scaleOffsetCorrection);
+ float transX = xOffset;// + scaleOffsetCorrection;
+ float totalScale = mBaselineIconScale * scale;
+ final int overlayAlpha = alpha;//(int) (80 * (1 - r));
+
+ if (params == null) {
+ params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+ } else {
+ params.transX = transX;
+ params.transY = transY;
+ params.scale = totalScale;
+ params.overlayAlpha = overlayAlpha;
+ }
+ return params;
+ }
+
+ private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
+ canvas.save();
+ canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
+ canvas.scale(params.scale, params.scale);
+ Drawable d = params.drawable;
+
+ if (d != null) {
+ d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
+ d.setFilterBitmap(true);
+ d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
+ d.draw(canvas);
+ d.clearColorFilter();
+ d.setFilterBitmap(false);
+ }
+ canvas.restore();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mFolder == null) return;
+ if (mFolder.getItemCount() == 0) return;
+
+ ArrayList items = mFolder.getItemsInReadingOrder();
+ Drawable d;
+ TextView v;
+
+ // Update our drawing parameters if necessary
+ if (mAnimating) {
+ computePreviewDrawingParams(mAnimParams.drawable);
+ } else {
+ v = (TextView) items.get(0);
+ d = v.getCompoundDrawables()[1];
+ computePreviewDrawingParams(d);
+ }
+
+ int nItemsInPreview = Math.min(items.size(), mNumItemsInPreview);
+ if (!mAnimating) {
+ for (int i = nItemsInPreview - 1; i >= 0; i--) {
+ v = (TextView) items.get(i);
+ if (!mHiddenItems.contains(v.getTag())) {
+ d = v.getCompoundDrawables()[1];
+ mParams = computePreviewItemDrawingParams(i, mParams);
+ mParams.drawable = d;
+ drawPreviewItem(canvas, mParams);
+ }
+ }
+ } else {
+ drawPreviewItem(canvas, mAnimParams);
+ }
+ }
+
+ public void setTextVisible(boolean visible) {
+ if (visible) {
+ mFolderName.setVisibility(VISIBLE);
+ } else {
+ mFolderName.setVisibility(GONE);
+ }
+ }
+
+ public boolean getTextVisible() {
+ return mFolderName.getVisibility() == VISIBLE;
+ }
+
+ public void onItemsChanged() {
+ invalidate();
+ requestLayout();
+ }
+
+ public void onAdd(AppItemInfo item) {
+ invalidate();
+ requestLayout();
+ }
+
+ public void onRemove(AppItemInfo item) {
+ invalidate();
+ requestLayout();
+ }
+
+ public void onTitleChanged(CharSequence title) {
+ mFolderName.setText(title.toString());
+ setContentDescription(title);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderInfo.java
new file mode 100644
index 000000000000..f8f29dde3c67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/FolderInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a folder containing shortcuts or apps.
+ */
+public class FolderInfo extends ItemInfo {
+
+ /**
+ * Whether this folder has been opened
+ */
+ boolean opened;
+
+ /**
+ * The apps
+ */
+ ArrayList contents = new ArrayList();
+
+ ArrayList listeners = new ArrayList();
+
+ public FolderInfo() {
+ itemType = ItemInfo.TYPE_FOLDER;
+ container = CONTAINER_SIDEBAR;
+ }
+
+ /**
+ * Add an app or shortcut
+ *
+ * @param item
+ */
+ public void add(AppItemInfo item) {
+ item.container = ItemInfo.CONTAINER_FOLDER;
+ contents.add(item);
+ for (FolderListener listener : listeners) {
+ listener.onAdd(item);
+ }
+ itemsChanged();
+ }
+
+ /**
+ * Remove an app or shortcut. Does not change the DB.
+ *
+ * @param item
+ */
+ public void remove(AppItemInfo item) {
+ contents.remove(item);
+ for (FolderListener listener : listeners) {
+ listener.onRemove(item);
+ }
+ itemsChanged();
+ }
+
+ public void setTitle(CharSequence title) {
+ this.title = title;
+ for (FolderListener listener : listeners) {
+ listener.onTitleChanged(title);
+ }
+ }
+/*
+ @Override
+ void onAddToDatabase(ContentValues values) {
+ super.onAddToDatabase(values);
+ values.put(LauncherSettings.Favorites.TITLE, title.toString());
+ }
+*/
+ public void addListener(FolderListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(FolderListener listener) {
+ if (listeners.contains(listener)) {
+ listeners.remove(listener);
+ }
+ }
+
+ void itemsChanged() {
+ for (FolderListener listener : listeners) {
+ listener.onItemsChanged();
+ }
+ }
+
+ public void unbind() {
+ listeners.clear();
+ }
+
+ interface FolderListener {
+ public void onAdd(AppItemInfo item);
+ public void onRemove(AppItemInfo item);
+ public void onTitleChanged(CharSequence title);
+ public void onItemsChanged();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/ItemInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/ItemInfo.java
new file mode 100644
index 000000000000..ff53122a9790
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/ItemInfo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+public class ItemInfo {
+
+ public static final int TYPE_APPLICATION = 0;
+
+ public static final int TYPE_FOLDER = 1;
+
+ public static final int CONTAINER_SIDEBAR = -100;
+
+ public static final int CONTAINER_FOLDER = -101;
+
+ public int itemType;
+
+ public CharSequence title;
+
+ public int container;
+
+ public int id;
+
+ @Override
+ public String toString() {
+ return String.format("itemType: %d, title: %s, container: %d, id: %d",
+ itemType, title, container, id);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarConfigurationActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarConfigurationActivity.java
new file mode 100644
index 000000000000..10737c5f0096
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarConfigurationActivity.java
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils.TruncateAt;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.DragEvent;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.DragShadowBuilder;
+import android.view.View.OnClickListener;
+import android.view.View.OnDragListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
+import android.widget.GridView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.AppSidebar;
+
+public class SidebarConfigurationActivity extends Activity {
+ private static final String TAG = "SidebarConfiguration";
+
+ private List mInstalledPackages;
+ private static final Collator sCollator = Collator.getInstance();
+ private AscendingComparator mAscendingComparator;
+ private GridView mAppGridView;
+ private AppContainer mSidebarContents;
+ private FrameLayout mMainLayout;
+ private ScrollView mSideBar;
+ private Activity mContext;
+ private Rect mIconBounds;
+ private int mDragItemSize;
+ private float mItemTextSize;
+ private Folder mFolder;
+ private FolderIcon mFolderIcon;
+ private ItemDragListener mDragListener;
+ private ItemLongClickListener mLongClickListener;
+ private int mFolderWidth;
+ private int mSidebarWidth;
+ private PackageManager mPm;
+
+ private static LinearLayout.LayoutParams DUMMY_VIEW_PARAMS = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ 1.0f
+ );
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.sidebar_configuration_layout);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ setProgressBarIndeterminateVisibility(true);
+ setProgressBarIndeterminate(true);
+
+ mDragListener = new ItemDragListener();
+ mLongClickListener = new ItemLongClickListener();
+ mAscendingComparator = new AscendingComparator();
+ mPm = getPackageManager();
+
+ mMainLayout = (FrameLayout)findViewById(R.id.frame_layout);
+ mAppGridView = (GridView) findViewById(R.id.available_apps);
+ mAppGridView.setOnDragListener(mDragListener);
+ mAppGridView.setOnTouchListener(mTouchOutsideListener);
+ mSidebarContents = (AppContainer) findViewById(R.id.contents);
+ mSidebarContents.setOnTouchListener(mTouchOutsideListener);
+ mSideBar = (ScrollView) findViewById(R.id.sidebar);
+ mSideBar.setOnDragListener(mDragListener);
+ mSideBar.setOnTouchListener(mTouchOutsideListener);
+ mContext = this;
+ mFolderWidth = getResources().getDimensionPixelSize(R.dimen.folder_width);
+ mSidebarWidth = getResources().getDimensionPixelSize(R.dimen.setup_sidebar_width);
+
+ Resources res = getResources();
+ int size = (int) res.getDimensionPixelSize(R.dimen.icon_size);
+ mIconBounds = new Rect(0, 0, size, size);
+ mDragItemSize = (int) res.getDimensionPixelSize(R.dimen.drag_item_size);
+ mItemTextSize = res.getDimensionPixelSize(R.dimen.item_title_text_size);
+ DUMMY_VIEW_PARAMS.height = mDragItemSize;
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean firstRun = prefs.getBoolean("first_run", true);
+ if (!firstRun) {
+ findViewById(R.id.first_use).setVisibility(View.GONE);
+ } else {
+ prefs.edit().putBoolean("first_run", false).commit();
+ findViewById(R.id.dismiss).setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ findViewById(R.id.first_use).setVisibility(View.GONE);
+ }
+ });
+ }
+
+ mRefreshAppsTask.execute(null, null, null);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.sidebar_configuration, menu);
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_save:
+ SidebarSQLiteHelper helper = new SidebarSQLiteHelper(this);
+ SQLiteDatabase db = helper.getWritableDatabase();
+ helper.resetDatabase(db);
+ db.close();
+ helper.close();
+ List items = getSidebarItems();
+ for (ItemInfo i : items) {
+ if (i.container >= ItemInfo.CONTAINER_SIDEBAR) {
+ ContentValues values = new ContentValues();
+ values.put(SidebarTable.COLUMN_ITEM_ID, i.id);
+ values.put(SidebarTable.COLUMN_ITEM_TYPE, i.itemType);
+ values.put(SidebarTable.COLUMN_CONTAINER, i.container);
+ values.put(SidebarTable.COLUMN_TITLE, i.title.toString());
+ if (i instanceof AppItemInfo) {
+ ComponentName cn = new ComponentName(((AppItemInfo)i).packageName,
+ ((AppItemInfo)i).className);
+ values.put(SidebarTable.COLUMN_COMPONENT, cn.flattenToString());
+ }
+ getContentResolver().insert(SidebarContentProvider.CONTENT_URI, values);
+ }
+ }
+ sendBroadcast(new Intent(AppSidebar.ACTION_SIDEBAR_ITEMS_CHANGED));
+ Toast.makeText(this, R.string.toast_items_saved, Toast.LENGTH_SHORT).show();
+ return true;
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void getInstalledAppsList() {
+ Intent localIntent = new Intent("android.intent.action.MAIN",
+ null);
+ localIntent.addCategory("android.intent.category.LAUNCHER");
+ List apps = mPm.queryIntentActivities(localIntent,
+ 0);
+ mInstalledPackages = new ArrayList();
+ ResolveInfo ri;
+ AppItemInfo ai;
+ ai = new AppItemInfo();
+ ai.packageName = "com.android.systemui";
+ ai.className = "com.android.systemui.statusbar.sidebar.SidebarConfigurationActivity";
+ try {
+ ActivityInfo info = mPm.getActivityInfo(new ComponentName(ai.packageName, ai.className), 0);
+ ai.title = info.loadLabel(mPm);
+ ai.icon = info.loadIcon(mPm);
+ } catch (NameNotFoundException e) {
+ }
+ TextView tv = createAppItem(ai);
+ tv.setOnLongClickListener(mLongClickListener);
+ mInstalledPackages.add(tv);
+ for (int i = 0; i < apps.size(); i++) {
+ ri = apps.get(i);
+ ai = new AppItemInfo();
+ ai.className = ri.activityInfo.name;
+ ai.packageName = ri.activityInfo.packageName;
+ ai.title = ri.activityInfo.loadLabel(mPm).toString();
+ tv = new TextView(mContext);
+ ai.setIcon(ri.activityInfo.loadIcon(mPm));
+ ai.icon.setBounds(mIconBounds);
+ tv.setCompoundDrawables(null, ai.icon, null, null);
+ tv.setTag(ai);
+ tv.setText(ai.title);
+ tv.setSingleLine(true);
+ tv.setEllipsize(TruncateAt.END);
+ tv.setGravity(Gravity.CENTER);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mItemTextSize);
+ mInstalledPackages.add(tv);
+ tv.setOnLongClickListener(mLongClickListener);
+ }
+ Collections.sort(mInstalledPackages, mAscendingComparator);
+ }
+
+ AsyncTask mRefreshAppsTask = new AsyncTask(){
+
+ @Override
+ protected void onPostExecute(Void result) {
+ AppItemAdapter adapter = new AppItemAdapter(mInstalledPackages);
+ mAppGridView.setAdapter(adapter);
+ setProgressBarIndeterminateVisibility(false);
+ setProgressBarIndeterminate(false);
+ populateSidebar();
+ super.onPostExecute(result);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ getInstalledAppsList();
+ return null;
+ }
+ };
+
+ public static class AscendingComparator implements Comparator {
+ public final int compare(TextView a, TextView b) {
+ String alabel = ((AppItemInfo) a.getTag()).title.toString();
+ String blabel = ((AppItemInfo) b.getTag()).title.toString();
+ return sCollator.compare(alabel, blabel);
+ }
+ }
+
+ private OnTouchListener mTouchOutsideListener = new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN)
+ dismissFolderView();
+ return false;
+ }
+ };
+
+ private void dismissFolderView() {
+ if (mFolder != null) {
+ mMainLayout.removeView(mFolder);
+ mFolder = null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see android.app.Activity#onBackPressed()
+ */
+ @Override
+ public void onBackPressed() {
+ if (mFolder != null) {
+ dismissFolderView();
+ } else
+ super.onBackPressed();
+ }
+
+ private final class ItemLongClickListener implements OnLongClickListener {
+
+ @Override
+ public boolean onLongClick(View view) {
+ if (view.getParent() == mSidebarContents)
+ view.setVisibility(View.INVISIBLE);
+ ClipData data = ClipData.newPlainText("", "");
+ DragShadowBuilder shadowBuilder = new IconShadowBuilder(view);
+ view.startDrag(data, shadowBuilder, view, 0);
+ if (mFolder != null) {
+ mFolderIcon.invalidate();
+ mFolder.setVisibility(View.GONE);
+ ItemInfo ai = (ItemInfo) view.getTag();
+ if (ai.container == ItemInfo.CONTAINER_FOLDER) {
+ mFolder.getInfo().remove((AppItemInfo) ai);
+ ai.container = ItemInfo.CONTAINER_SIDEBAR;
+ if (mFolder.getItemCount() == 1) {
+ int folderPos = mSidebarContents.indexOfChild(mFolderIcon);
+ AppItemInfo finalItem = (AppItemInfo)mFolder.getItemsInReadingOrder().get(0).getTag();
+ mFolder.getInfo().remove(finalItem);
+ mSidebarContents.removeViewAt(folderPos);
+ finalItem.container = ItemInfo.CONTAINER_SIDEBAR;
+ mSidebarContents.addView(createAppItem(finalItem), folderPos);
+ }
+ }
+ mSidebarContents.invalidate();
+ }
+ return true;
+ }
+
+ }
+
+ private final class FolderClickListener implements OnClickListener {
+
+ @Override
+ public void onClick(View v) {
+ if (mFolder != null) {
+ dismissFolderView();
+ return;
+ }
+ mFolderIcon = (FolderIcon) v;
+ final Folder folder = mFolder = ((FolderIcon)v).getFolder();
+ mFolder.setOnDragListener(mDragListener);
+ mMainLayout.addView(mFolder, getFolderLayoutParams());
+ mFolder.setVisibility(View.VISIBLE);
+ ArrayList items = folder.getItemsInReadingOrder();
+ for (View item : items)
+ item.setOnLongClickListener(mLongClickListener);
+ folder.setOnTouchListener(new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissFolderView();
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+ }
+
+ class ItemDragListener implements OnDragListener {
+ private View mDummyView;
+ @Override
+ public boolean onDrag(View v, DragEvent event) {
+ int action = event.getAction();
+ View view = (View) event.getLocalState();
+ ViewGroup owner = (ViewGroup) view.getParent();
+ Object tag = view.getTag();
+ if (mDummyView == null) {
+ mDummyView = new View(mContext);
+ DUMMY_VIEW_PARAMS.height = view.getHeight();
+ }
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ if (v == mSideBar) {
+ int pos = mSidebarContents.indexOfChild(view);
+ if (pos >= 0) {
+ mSidebarContents.addView(mDummyView, pos);
+ mSideBar.setBackgroundResource(R.drawable.sidebar_drag_enter);
+ }
+ mSidebarContents.removeView(view);
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENTERED:
+ if (v == mSideBar) {
+ v.setBackgroundResource(R.drawable.sidebar_drag_enter);
+ mDummyView.setBackgroundResource(R.drawable.item_placeholder);
+ if (mDummyView.getParent() == null)
+ mSidebarContents.addView(mDummyView, DUMMY_VIEW_PARAMS);
+ }
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ if (v == mSideBar) {
+ float x = event.getX();
+ float y = event.getY();
+ mSidebarContents.repositionView(mDummyView, x, y + mSideBar.getScrollY(), tag instanceof FolderInfo);
+ if (y < mDummyView.getHeight() / 2)
+ mSideBar.scrollBy((int)x, (int)-5);
+ else if (y > mSideBar.getHeight() - mDummyView.getHeight()/2)
+ mSideBar.scrollBy((int)x, 5);
+ if (mSidebarContents.indexOfChild(mDummyView) == mSidebarContents.getChildCount()-1)
+ mSideBar.scrollTo((int)x, mDummyView.getBottom());
+ }
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ if (v == mSideBar) {
+ v.setBackgroundResource(R.drawable.sidebar_drag_exit);
+ if (mDummyView.getParent() == mSidebarContents)
+ mSidebarContents.removeView(mDummyView);
+ for (int i = 0; i < mSidebarContents.getChildCount(); i++)
+ mSidebarContents.getChildAt(i).setBackgroundResource(0);
+ }
+ break;
+ case DragEvent.ACTION_DROP:
+ // Dropped, reassign View to ViewGroup
+ if (v == mSideBar) {
+ if (mSidebarContents.findViewWithTag(view.getTag()) != null) {
+ mSidebarContents.removeView(mDummyView);
+ break;
+ }
+ ItemInfo addTo = mSidebarContents.getAddToItem();
+ View addToView = mSidebarContents.findViewWithTag(addTo);
+ View tv = cloneItem(view);
+ tv.setOnLongClickListener(mLongClickListener);
+ tv.setBackgroundResource(0);
+ tv.setVisibility(View.VISIBLE);
+ if (addTo != null && addToView != null) {
+ addToView.setBackgroundResource(0);
+ FolderInfo info;
+ FolderIcon icon;
+ if (addTo instanceof FolderInfo) {
+ info = (FolderInfo)addTo;
+ icon = (FolderIcon)addToView;
+ } else {
+ info = new FolderInfo();
+ info.title = getString(R.string.default_folder_text);
+ icon = FolderIcon.fromXml(R.layout.folder_icon,
+ mSidebarContents, null, info, mContext, false);
+ info.add((AppItemInfo)addToView.getTag());
+ icon.setOnLongClickListener(mLongClickListener);
+ icon.setOnClickListener(new FolderClickListener());
+ }
+
+ info.add((AppItemInfo)tv.getTag());
+ int pos = mSidebarContents.indexOfChild(addToView);
+ mSidebarContents.removeView(addToView);
+ mSidebarContents.addView(icon, pos);
+ } else {
+ int pos = mSidebarContents.indexOfChild(mDummyView);
+ mSidebarContents.addView(tv, pos);
+ }
+ mSidebarContents.removeView(mDummyView);
+ } else {
+ if (owner != null && owner != v) {
+ owner.removeView(view);
+ } else
+ view.setVisibility(View.VISIBLE);
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ if (v == mSideBar)
+ v.setBackgroundResource(R.drawable.sidebar_drag_exit);
+ dismissFolderView();
+ default:
+ break;
+ }
+ return true;
+ }
+
+ private View cloneItem(View original) {
+ ItemInfo ai = (ItemInfo) original.getTag();
+ View v;
+ if (ai instanceof AppItemInfo) {
+ v = new TextView(mContext);
+ TextView tv = (TextView)v;
+ ((AppItemInfo)ai).icon.setBounds(mIconBounds);
+ tv.setCompoundDrawables(null, ((AppItemInfo)ai).icon, null, null);
+ tv.setTag(ai);
+ tv.setSingleLine(true);
+ tv.setEllipsize(TruncateAt.END);
+ tv.setText(ai.title);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mItemTextSize);
+ tv.setGravity(Gravity.CENTER);
+ } else {
+ v = original;
+ }
+
+ return v;
+ }
+ }
+
+ private class IconShadowBuilder extends DragShadowBuilder {
+ private Drawable shadow;
+ private int mShadowWidth;
+ private int mShadowHeight;
+
+ @SuppressWarnings("deprecation")
+ public IconShadowBuilder(View v) {
+ super(v);
+ mShadowWidth = (int)(v.getWidth() * 1.5f);
+ mShadowHeight = (int)(v.getHeight() * 1.5f);
+ v.setDrawingCacheEnabled(true);
+ v.buildDrawingCache();
+ shadow = new BitmapDrawable(v.getDrawingCache(true));
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.View.DragShadowBuilder#onDrawShadow(android.graphics.Canvas)
+ */
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ shadow.draw(canvas);
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.View.DragShadowBuilder#onProvideShadowMetrics(android.graphics.Point, android.graphics.Point)
+ */
+ @Override
+ public void onProvideShadowMetrics(Point size,
+ Point touch) {
+ // The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
+ // Canvas that the system will provide. As a result, the drag shadow will fill the
+ // Canvas.
+ shadow.setBounds(0, 0, mShadowWidth, mShadowHeight);
+
+ // Sets the size parameter's width and height values. These get back to the system
+ // through the size parameter.
+ size.set(mShadowWidth, mShadowHeight);
+
+ // Sets the touch point's position to be in the middle of the drag shadow
+ touch.set(mShadowWidth / 2, mShadowHeight / 2);
+ }
+ }
+
+ private LayoutParams getFolderLayoutParams() {
+ LayoutParams lp = new LayoutParams(
+ mFolderWidth,
+ LayoutParams.WRAP_CONTENT,
+ Gravity.TOP | Gravity.LEFT);
+ lp.leftMargin = mSidebarWidth;
+ return lp;
+ }
+
+ private List getSidebarItems() {
+ int id = 0;
+ ArrayList items = new ArrayList();
+ for (int i = 0; i < mSidebarContents.getChildCount(); i++) {
+ ItemInfo info = (ItemInfo) mSidebarContents.getChildAt(i).getTag();
+ info.id = id++;
+ items.add(info);
+ if (info instanceof FolderInfo) {
+ List contents = ((FolderInfo)info).contents;
+ for (AppItemInfo item : contents) {
+ item.container = info.id;
+ item.id = id++;
+ items.add(item);
+ }
+ }
+ }
+
+ return items;
+ }
+
+ private void populateSidebar() {
+ String[] projection = {
+ SidebarTable.COLUMN_ITEM_ID,
+ SidebarTable.COLUMN_ITEM_TYPE,
+ SidebarTable.COLUMN_CONTAINER,
+ SidebarTable.COLUMN_TITLE,
+ SidebarTable.COLUMN_COMPONENT
+ };
+ ArrayList items = new ArrayList();
+ Cursor cursor = getContentResolver().query(SidebarContentProvider.CONTENT_URI,
+ projection, null, null, null);
+ while (cursor.moveToNext()) {
+ ItemInfo item;
+ int type = cursor.getInt(cursor.getColumnIndex(SidebarTable.COLUMN_ITEM_TYPE));
+ if (type == ItemInfo.TYPE_APPLICATION) {
+ item = new AppItemInfo();
+ String component = cursor.getString(4);
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ ((AppItemInfo)item).packageName = cn.getPackageName();
+ ((AppItemInfo)item).className = cn.getClassName();
+ } else {
+ item = new FolderInfo();
+ }
+ item.id = cursor.getInt(0);
+ item.itemType = type;
+ item.container = cursor.getInt(2);
+ item.title = cursor.getString(3);
+ if (item.container == ItemInfo.CONTAINER_SIDEBAR) {
+ if (item instanceof AppItemInfo) {
+ TextView tv = createAppItem((AppItemInfo) item);
+ mSidebarContents.addView(tv);
+ tv.setOnLongClickListener(mLongClickListener);
+ } else {
+ FolderIcon icon = FolderIcon.fromXml(R.layout.folder_icon,
+ mSidebarContents, null, (FolderInfo)item, mContext, false);
+ icon.setOnLongClickListener(mLongClickListener);
+ icon.setOnClickListener(new FolderClickListener());
+ mSidebarContents.addView(icon);
+ }
+ } else {
+ try {
+ ((AppItemInfo)item).setIcon(mPm.getActivityIcon(
+ new ComponentName(((AppItemInfo)item).packageName, ((AppItemInfo)item).className)));
+ } catch (NameNotFoundException e) {
+ ((AppItemInfo)item).setIcon(mPm.getDefaultActivityIcon());
+ }
+ ((AppItemInfo)item).icon.setBounds(mIconBounds);
+ FolderInfo info = (FolderInfo) items.get(item.container);
+ info.add((AppItemInfo) item);
+ }
+ items.add(item);
+ }
+ }
+
+ private TextView createAppItem(AppItemInfo info) {
+ TextView tv = new TextView(mContext);
+ try {
+ info.setIcon(mPm.getActivityIcon(new ComponentName(info.packageName, info.className)));
+ } catch (NameNotFoundException e) {
+ info.setIcon(mPm.getDefaultActivityIcon());
+ }
+ info.icon.setBounds(mIconBounds);
+ tv.setCompoundDrawables(null, info.icon, null, null);
+ tv.setTag(info);
+ tv.setText(info.title);
+ tv.setSingleLine(true);
+ tv.setEllipsize(TruncateAt.END);
+ tv.setGravity(Gravity.CENTER);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mItemTextSize);
+
+ return tv;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarContentProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarContentProvider.java
new file mode 100644
index 000000000000..e0d59d87dc0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarContentProvider.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.text.TextUtils;
+
+public class SidebarContentProvider extends ContentProvider {
+
+ private SidebarSQLiteHelper database;
+
+ // Used for the UriMatcher
+ private static final int ITEMS = 10;
+ private static final int ITEM_ID = 20;
+
+ private static final String AUTHORITY = "org.chameleonos.sidebar.contentprovider";
+
+ private static final String BASE_PATH = "sidebar_items";
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ + "/" + BASE_PATH);
+
+ public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
+ + "/sidebar_items";
+ public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ + "/sidebar_item";
+
+ private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ static {
+ sURIMatcher.addURI(AUTHORITY, BASE_PATH, ITEMS);
+ sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", ITEM_ID);
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#delete(android.net.Uri, java.lang.String, java.lang.String[])
+ */
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ int uriType = sURIMatcher.match(uri);
+ SQLiteDatabase sqlDB = database.getWritableDatabase();
+ int rowsDeleted = 0;
+ switch (uriType) {
+ case ITEMS:
+ rowsDeleted = sqlDB.delete(SidebarTable.TABLE_SIDEBAR, selection,
+ selectionArgs);
+ break;
+ case ITEM_ID:
+ String id = uri.getLastPathSegment();
+ if (TextUtils.isEmpty(selection)) {
+ rowsDeleted = sqlDB.delete(SidebarTable.TABLE_SIDEBAR,
+ SidebarTable.COLUMN_ITEM_ID + "=" + id,
+ null);
+ } else {
+ rowsDeleted = sqlDB.delete(SidebarTable.TABLE_SIDEBAR,
+ SidebarTable.COLUMN_ITEM_ID + "=" + id
+ + " and " + selection,
+ selectionArgs);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+ getContext().getContentResolver().notifyChange(uri, null);
+ return rowsDeleted;
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#getType(android.net.Uri)
+ */
+ @Override
+ public String getType(Uri uri) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#insert(android.net.Uri, android.content.ContentValues)
+ */
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ int uriType = sURIMatcher.match(uri);
+ SQLiteDatabase db = database.getWritableDatabase();
+ long id = 0;
+ switch (uriType) {
+ case ITEMS:
+ db.insert(SidebarTable.TABLE_SIDEBAR, null, values);
+ id = values.getAsLong(SidebarTable.COLUMN_ITEM_ID);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+ getContext().getContentResolver().notifyChange(uri, null);
+ return Uri.parse(BASE_PATH + "/" + id);
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#onCreate()
+ */
+ @Override
+ public boolean onCreate() {
+ database = new SidebarSQLiteHelper(getContext());
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String)
+ */
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+
+ // Using SQLiteQueryBuilder instead of query() method
+ SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
+
+ // Check if the caller has requested a column which does not exist
+ checkColumns(projection);
+
+ // Set the table
+ queryBuilder.setTables(SidebarTable.TABLE_SIDEBAR);
+
+ int uriType = sURIMatcher.match(uri);
+ switch (uriType) {
+ case ITEMS:
+ break;
+ case ITEM_ID:
+ // Add the ID to the original query
+ queryBuilder.appendWhere(SidebarTable.COLUMN_ITEM_ID + "="
+ + uri.getLastPathSegment());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+
+ SQLiteDatabase db = database.getWritableDatabase();
+ Cursor cursor = queryBuilder.query(db, projection, selection,
+ selectionArgs, null, null, sortOrder);
+ // Make sure that potential listeners are notified
+ cursor.setNotificationUri(getContext().getContentResolver(), uri);
+
+ return cursor;
+ }
+
+ /* (non-Javadoc)
+ * @see android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[])
+ */
+ @Override
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ int uriType = sURIMatcher.match(uri);
+ SQLiteDatabase sqlDB = database.getWritableDatabase();
+ int rowsUpdated = 0;
+ switch (uriType) {
+ case ITEMS:
+ rowsUpdated = sqlDB.update(SidebarTable.TABLE_SIDEBAR,
+ values,
+ selection,
+ selectionArgs);
+ break;
+ case ITEM_ID:
+ String id = uri.getLastPathSegment();
+ if (TextUtils.isEmpty(selection)) {
+ rowsUpdated = sqlDB.update(SidebarTable.TABLE_SIDEBAR,
+ values,
+ SidebarTable.COLUMN_ITEM_ID + "=" + id,
+ null);
+ } else {
+ rowsUpdated = sqlDB.update(SidebarTable.TABLE_SIDEBAR,
+ values,
+ SidebarTable.COLUMN_ITEM_ID + "=" + id
+ + " and "
+ + selection,
+ selectionArgs);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+ getContext().getContentResolver().notifyChange(uri, null);
+ return rowsUpdated;
+ }
+
+ private void checkColumns(String[] projection) {
+ String[] available = {
+ SidebarTable.COLUMN_ITEM_ID,
+ SidebarTable.COLUMN_ITEM_TYPE,
+ SidebarTable.COLUMN_CONTAINER,
+ SidebarTable.COLUMN_TITLE,
+ SidebarTable.COLUMN_COMPONENT
+ };
+ if (projection != null) {
+ HashSet requestedColumns = new HashSet(Arrays.asList(projection));
+ HashSet availableColumns = new HashSet(Arrays.asList(available));
+ // Check if all columns which are requested are available
+ if (!availableColumns.containsAll(requestedColumns)) {
+ throw new IllegalArgumentException("Unknown columns in projection");
+ }
+ }
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarSQLiteHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarSQLiteHelper.java
new file mode 100644
index 000000000000..046508b3b8cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarSQLiteHelper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class SidebarSQLiteHelper extends SQLiteOpenHelper {
+
+ private static final String DATABASE_NAME = "sidebar.db";
+ private static final int DATABASE_VERSION = 1;
+
+ public SidebarSQLiteHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ /* (non-Javadoc)
+ * @see android.database.sqlite.SQLiteOpenHelper#onCreate(android.database.sqlite.SQLiteDatabase)
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ SidebarTable.onCreate(db);
+ }
+
+ /* (non-Javadoc)
+ * @see android.database.sqlite.SQLiteOpenHelper#onUpgrade(android.database.sqlite.SQLiteDatabase, int, int)
+ */
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ SidebarTable.onUpgrade(db, oldVersion, newVersion);
+ }
+
+ public void resetDatabase(SQLiteDatabase db) {
+ onUpgrade(db, DATABASE_VERSION - 1, DATABASE_VERSION);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarTable.java b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarTable.java
new file mode 100644
index 000000000000..e366f9c4aa2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/sidebar/SidebarTable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 The ChameleonOS 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.systemui.statusbar.sidebar;
+
+import android.database.sqlite.SQLiteDatabase;
+
+public class SidebarTable {
+ public static final String TABLE_SIDEBAR = "sidebar_items";
+ public static final String COLUMN_ITEM_ID = "_id";
+ public static final String COLUMN_ITEM_TYPE = "itemType";
+ public static final String COLUMN_CONTAINER = "container";
+ public static final String COLUMN_TITLE = "title";
+ public static final String COLUMN_COMPONENT = "component";
+
+ private static final String DATABASE_CREATE =
+ "create table "
+ + TABLE_SIDEBAR + "("
+ + COLUMN_ITEM_ID + " integer primary key, "
+ + COLUMN_ITEM_TYPE + " integer, "
+ + COLUMN_CONTAINER + " integer, "
+ + COLUMN_TITLE + " text, "
+ + COLUMN_COMPONENT + " text);";
+
+ public static void onCreate(SQLiteDatabase database) {
+ database.execSQL(DATABASE_CREATE);
+ }
+
+ public static void onUpgrade(SQLiteDatabase database, int oldVersion,
+ int newVersion) {
+ database.execSQL("DROP TABLE IF EXISTS " + TABLE_SIDEBAR);
+ onCreate(database);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index c7153a17f2d1..314d5ed91049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -662,6 +662,11 @@ public boolean onTouch(View v, MotionEvent ev) {
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mBroadcastReceiver, filter);
+ if (mRecreating) {
+ removeSidebarView();
+ }
+ addSidebarView();
+
return sb;
}