package com.limemobile.app.launcher.wp8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.limemobile.app.launcher.activity.Launcher;
import com.limemobile.app.launcher.anim.Rotate3d;
import com.limemobile.app.launcher.entity.ApplicationInfo;
import com.limemobile.app.launcher.receiver.LauncherModel;
import com.limemobile.app.launcher.util.PinyinUtil;
import com.limemobile.app.launcher.view.AllAppsView;
import com.limemobile.app.launcher.view.DragController;
import com.limemobile.app.launcher.view.DragSource;
import com.limemobile.app.launcher.view.LetterFilterView;
import com.limemobile.app.launcher.view.LetterFilterView.OnTouchingLetterChangedListener;
import com.limemobile.app.launcher.view.SearchAppsView;
import com.limemobile.app.launcher.wp8.AllAppsLetterFilter.AppsAdapter.ViewHolder;
public class AllAppsLetterFilter extends RelativeLayout implements AllAppsView,
AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
View.OnKeyListener, DragSource, OnTouchingLetterChangedListener, OnClickListener {
private static final String TAG = "Launcher.AllAppsLetterFilter";
private static final boolean DEBUG = false;
private Launcher mLauncher;
private DragController mDragController;
private SearchAppsView mSearchView;
private GridView mLetterGrid;
private ListView mList;
private LetterFilterView mLetterFilter;
private TextView mLetterIndicator;
private OverlayThread mOverlayThread;
private ASCIIComparator mASCIIComparator;
private ArrayList<ApplicationInfo> mAllAppsList = new ArrayList<ApplicationInfo>();
private HashMap<String, Integer> mLetterIndex = new HashMap<String, Integer>();
// preserve compatibility with 3D all apps:
// 0.0 -> hidden
// 1.0 -> shown and opaque
// intermediate values -> partially shown & partially opaque
private float mZoom;
private AppsAdapter mAppsAdapter;
private LettersAdapter mLettersAdapter;
private Rotate3d mLeftRotate;
private Rotate3d mRightRotate;
//= new Rotate3d(0, -90, centerX, 0);
// ------------------------------------------------------------
private class ASCIIComparator implements Comparator<ApplicationInfo> {
@Override
public int compare(ApplicationInfo lhs, ApplicationInfo rhs) {
return lhs.compareTo(rhs);
}
}
public class AppsAdapter extends ArrayAdapter<ApplicationInfo> {
private final LayoutInflater mInflater;
public AppsAdapter(Context context, ArrayList<ApplicationInfo> apps) {
super(context, 0, apps);
mInflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ApplicationInfo info = getItem(position);
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.application_wp8, parent, false);
holder = new ViewHolder();
holder.letter = (TextView) convertView.findViewById(R.id.letter_indicator);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.version = (TextView) convertView.findViewById(R.id.version);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
// if (!info.filtered) {
// info.icon = Utilities.createIconThumbnail(info.icon, getContext());
// info.filtered = true;
// }
holder.letter.setVisibility(View.GONE);
holder.letter.setClickable(true);
if (position == 0) {
holder.letter.setVisibility(View.VISIBLE);
holder.letter.setText(PinyinUtil.getFirstLetter(info.ascii));
} else {
String currentLetter = PinyinUtil.getFirstLetter(info.ascii);
String previousLetter = PinyinUtil.getFirstLetter(getItem(position - 1).ascii);
if (!currentLetter.equals(previousLetter)) {
holder.letter.setVisibility(View.VISIBLE);
holder.letter.setText(currentLetter);
}
holder.letter.setOnClickListener(AllAppsLetterFilter.this);
}
info.iconBitmap.setDensity(Bitmap.DENSITY_NONE);
holder.icon.setImageBitmap(info.iconBitmap);
holder.name.setText(info.title);
try {
PackageInfo pi = getContext().getPackageManager().getPackageInfo(info.componentName.getPackageName(), PackageManager.GET_ACTIVITIES);
holder.version.setText(pi.versionName);
holder.version.setVisibility(View.VISIBLE);
} catch (NameNotFoundException e) {
holder.version.setVisibility(View.GONE);
}
return convertView;
}
class ViewHolder {
TextView letter;
ImageView icon;
TextView name;
TextView version;
}
}
public AllAppsLetterFilter(Context context, AttributeSet attrs) {
super(context, attrs);
setVisibility(View.GONE);
setSoundEffectsEnabled(false);
setHapticFeedbackEnabled(false);
mASCIIComparator = new ASCIIComparator();
mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);
mAppsAdapter.setNotifyOnChange(false);
mLettersAdapter = new LettersAdapter(getContext(), LetterFilterView.mLetters);
mLettersAdapter.setNotifyOnChange(false);
mOverlayThread = new OverlayThread();
int centerX = context.getResources().getDisplayMetrics().widthPixels / 2;
mLeftRotate = new Rotate3d(0, -90, centerX, 0);
mRightRotate = new Rotate3d(90, 0, centerX, 0);
mLeftRotate.setFillAfter(false);
mLeftRotate.setDuration(800);
mRightRotate.setFillAfter(false);
mRightRotate.setDuration(800);
}
@Override
protected void onFinishInflate() {
setBackgroundColor(Color.BLACK);
try {
mList = (ListView)findViewWithTag("all_apps_list");
if (mList == null) throw new Resources.NotFoundException();
mLetterFilter = (LetterFilterView) findViewWithTag("letter_filter");
if (mLetterFilter == null) throw new Resources.NotFoundException();
mSearchView = (SearchAppsView) findViewWithTag("search_app");
if (mSearchView == null) throw new Resources.NotFoundException();
mSearchView.setLauncher(mLauncher);
mSearchView.setDragController(mDragController);
mSearchView.setApps(mAllAppsList);
mLetterIndicator = (TextView) findViewWithTag("letter_indicator");
if (mLetterIndicator == null) throw new Resources.NotFoundException();
mLetterIndicator.setVisibility(View.GONE);
mList.setOnItemClickListener(this);
mList.setOnItemLongClickListener(this);
// mList.setBackgroundColor(Color.BLACK);
// mList.setCacheColorHint(Color.BLACK);
mLetterFilter.setOnTouchingLetterChangedListener(this);
mLetterGrid = (GridView) findViewWithTag("letter_grid");
if (mLetterGrid == null) throw new Resources.NotFoundException();
mLetterGrid.setVisibility(View.GONE);
ImageButton searchBtn = (ImageButton) findViewById(R.id.wp8_search);
ImageButton menuBtn = (ImageButton) findViewById(R.id.wp8_menu);
ImageButton arrowBtn = (ImageButton) findViewById(R.id.wp8_arrow);
searchBtn.setOnClickListener(this);
menuBtn.setOnClickListener(this);
arrowBtn.setOnClickListener(this);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Can't find necessary layout elements for AllAppsLetterFilter");
}
setOnKeyListener(this);
}
public void setLauncher(Launcher launcher) {
mLauncher = launcher;
if (mSearchView != null)
mSearchView.setLauncher(mLauncher);
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (!isVisible()) return false;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (mLetterGrid.getVisibility() == View.VISIBLE) {
mLetterGrid.setVisibility(View.GONE);
return true;
}
if (mSearchView.getVisibility() == View.VISIBLE) {
mSearchView.clearAnimation();
mSearchView.startAnimation(mLeftRotate);
mLeftRotate.setAnimationListener(new AnimationListener () {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mSearchView.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
return true;
}
mLauncher.closeAllApps(true);
return false;
default:
return false;
}
}
@Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
mLauncher.startActivitySafely(app.intent, app);
}
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (!view.isInTouchMode()) {
return false;
}
int visibility = View.VISIBLE;
try {
AppsAdapter.ViewHolder holder = (ViewHolder) view.getTag();
visibility = holder.letter.getVisibility();
holder.letter.setVisibility(View.GONE);
} catch (ClassCastException e) {
}
ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
app = new ApplicationInfo(app);
mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);
try {
if (visibility == View.VISIBLE) {
AppsAdapter.ViewHolder holder = (ViewHolder) view.getTag();
holder.letter.setVisibility(View.VISIBLE);
}
} catch (ClassCastException e) {
}
mLauncher.closeAllApps(true);
return true;
}
protected void onFocusChanged(boolean gainFocus, int direction, android.graphics.Rect prev) {
if (gainFocus) {
mList.requestFocus();
}
}
public void setDragController(DragController dragger) {
mDragController = dragger;
if (mSearchView != null)
mSearchView.setDragController(mDragController);
}
public void onDropCompleted(View target, boolean success) {
}
/**
* Zoom to the specifed level.
*
* @param zoom [0..1] 0 is hidden, 1 is open
*/
public void zoom(float zoom, boolean animate) {
// Log.d(TAG, "zooming " + ((zoom == 1.0) ? "open" : "closed"));
cancelLongPress();
mZoom = zoom;
if (isVisible()) {
getParent().bringChildToFront(this);
setVisibility(View.VISIBLE);
mList.setAdapter(mAppsAdapter);
mLetterGrid.setAdapter(mLettersAdapter);
if (animate) {
startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_in));
} else {
onAnimationEnd();
}
} else {
if (animate) {
startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_out));
} else {
onAnimationEnd();
}
}
}
protected void onAnimationEnd() {
if (!isVisible()) {
setVisibility(View.GONE);
mLetterGrid.setVisibility(View.GONE);
mSearchView.setVisibility(View.GONE);
mList.setAdapter(null);
mLetterGrid.setAdapter(null);
mZoom = 0.0f;
} else {
mZoom = 1.0f;
}
mLauncher.zoomed(mZoom);
}
public boolean isVisible() {
return mZoom > 0.001f;
}
@Override
public boolean isOpaque() {
return mZoom > 0.999f;
}
public void setApps(ArrayList<ApplicationInfo> list) {
mAllAppsList.clear();
addApps(list);
if (mSearchView != null)
mSearchView.addApps(mAllAppsList);
}
public void addApps(ArrayList<ApplicationInfo> list) {
// Log.d(TAG, "addApps: " + list.size() + " apps: " + list.toString());
// final int N = list.size();
//
// for (int i=0; i<N; i++) {
// final ApplicationInfo item = list.get(i);
// int index = Collections.binarySearch(mAllAppsList, item,
// LauncherModel.APP_NAME_COMPARATOR);
// if (index < 0) {
// index = -(index+1);
// }
// mAllAppsList.add(index, item);
// }
for (ApplicationInfo info : list)
mAllAppsList.add(info);
Collections.sort(mAllAppsList, mASCIIComparator);
mLetterIndex.clear();
String previousLetter = null;
for (int index = 0; index < mAllAppsList.size(); ++index) {
if (previousLetter == null || !previousLetter.equals(PinyinUtil.getFirstLetter(mAllAppsList.get(index).ascii))) {
previousLetter = PinyinUtil.getFirstLetter(mAllAppsList.get(index).ascii);
mLetterIndex.put(previousLetter, index);
}
}
mAppsAdapter.notifyDataSetChanged();
}
public void removeApps(ArrayList<ApplicationInfo> list) {
final int N = list.size();
for (int i=0; i<N; i++) {
final ApplicationInfo item = list.get(i);
int index = findAppByComponent(mAllAppsList, item);
if (index >= 0) {
mAllAppsList.remove(index);
} else {
if (DEBUG)
Log.w(TAG, "couldn't find a match for item \"" + item + "\"");
// Try to recover. This should keep us from crashing for now.
}
}
mLetterIndex.clear();
String previousLetter = null;
for (int index = 0; index < mAllAppsList.size(); ++index) {
if (previousLetter == null || !previousLetter.equals(PinyinUtil.getFirstLetter(mAllAppsList.get(index).ascii))) {
previousLetter = PinyinUtil.getFirstLetter(mAllAppsList.get(index).ascii);
mLetterIndex.put(previousLetter, index);
}
}
mAppsAdapter.notifyDataSetChanged();
}
public void updateApps(ArrayList<ApplicationInfo> list) {
// Just remove and add, because they may need to be re-sorted.
removeApps(list);
addApps(list);
if (mSearchView != null)
mSearchView.addApps(mAllAppsList);
}
private static int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
ComponentName component = item.intent.getComponent();
final int N = list.size();
for (int i=0; i<N; i++) {
ApplicationInfo x = list.get(i);
if (x.intent.getComponent().equals(component)) {
return i;
}
}
return -1;
}
public void dumpState() {
ApplicationInfo.dumpApplicationInfoList(TAG, "mAllAppsList", mAllAppsList);
}
public void surrender() {
}
@Override
public void onTouchingLetterChanged(String s) {
if (mLetterIndicator != null) {
mLetterIndicator.setText(s);
mLetterIndicator.setVisibility(View.VISIBLE);
mLetterIndicator.removeCallbacks(mOverlayThread);
mLetterIndicator.postDelayed(mOverlayThread, 1500);
if (mLetterIndex.containsKey(s.toLowerCase()))
mList.setSelection(mLetterIndex.get(s.toLowerCase()));
}
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.letter_indicator) {
mLetterGrid.startLayoutAnimation();
mLetterGrid.setVisibility(View.VISIBLE);
} else if (v instanceof TextView && v.getTag() != null && v.getTag() instanceof String) {
String letter = (String) v.getTag();
if (mLetterIndex.containsKey(letter.toLowerCase())) {
mLetterGrid.setVisibility(View.GONE);
mList.setSelection(mLetterIndex.get(letter.toLowerCase()));
}
} else if (v.getId() == R.id.wp8_search) {
mSearchView.clearAnimation();
mSearchView.startAnimation(mRightRotate);
mSearchView.setVisibility(View.VISIBLE);
} else if (v.getId() == R.id.wp8_menu) {
//mLauncher.prepareExpandedMenu();
mLauncher.changeAllAppMode(true);
mLauncher.closeAllApps(true);
mLauncher.showAllApps(true);
} else if (v.getId() == R.id.wp8_arrow) {
mLauncher.closeAllApps(true);
}
}
private class OverlayThread implements Runnable {
@Override
public void run() {
if (mLetterIndicator != null)
mLetterIndicator.setVisibility(View.GONE);
}
}
public class LettersAdapter extends ArrayAdapter<String> {
public LettersAdapter(Context context, String[] objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final String letter = getItem(position).toLowerCase();
if (convertView == null) {
float density = 1.0f;//getContext().getResources().getDisplayMetrics().density;
convertView = new TextView(getContext());
float width = getContext().getResources().getDimension(R.dimen.letter_tile_size);
convertView.setLayoutParams(new GridView.LayoutParams(LayoutParams.FILL_PARENT, (int)(width * density)));
((TextView)convertView).setTextColor(Color.WHITE);
((TextView)convertView).setTextSize(36.0f);
((TextView)convertView).setGravity(Gravity.LEFT | Gravity.BOTTOM);
((TextView)convertView).setPadding(5, 5, 5, 5);
mLetterGrid.setColumnWidth((int)(width * density));
}
convertView.setTag(letter);
((TextView)convertView).setText(letter);
if (mLetterIndex.containsKey(letter)) {
convertView.setBackgroundColor(Color.BLUE);
convertView.setClickable(true);
convertView.setOnClickListener(AllAppsLetterFilter.this);
}
else {
convertView.setBackgroundColor(Color.BLACK);
convertView.setClickable(false);
convertView.setOnClickListener(null);
}
return convertView;
}
}
@Override
public MenuType getOptionsMenuType() {
return MenuType.ExpendedMenu;
}
}