package com.noshufou.android.su;
import com.noshufou.android.su.preferences.Preferences;
import com.noshufou.android.su.provider.PermissionsProvider.Apps;
import com.noshufou.android.su.provider.PermissionsProvider.Logs;
import com.noshufou.android.su.service.ResultService;
import com.noshufou.android.su.util.Util;
import com.noshufou.android.su.widget.AppListItem;
import com.noshufou.android.su.widget.PinnedHeaderListView;
import com.noshufou.android.su.widget.PinnedHeaderListView.PinnedHeaderCache;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SectionIndexer;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
import java.util.HashMap;
public class AppListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
private static final String TAG = "Su.AppListFragment";
private boolean mShowStatusIcons = true;
private boolean mShowLogData = true;
private String mStatusIconType = null;
private int mPinnedHeaderBackgroundColor;
private AppListAdapter mAdapter;
private LinearLayout mLoadingLayout = null;
public static final String[] PROJECTION = new String[] {
Apps._ID, Apps.UID, Apps.NAME, Apps.ALLOW, Logs.DATE, Logs.TYPE,
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_UID = 1;
private static final int COLUMN_NAME = 2;
private static final int COLUMN_ALLOW = 3;
private static final int COLUMN_LAST_ACCESS = 4;
private static final int COLUMN_LAST_ACCESS_TYPE = 5;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_app_list, container, false);
mLoadingLayout = (LinearLayout) view.findViewById(R.id.loading_layout);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setupListView();
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onResume() {
super.onResume();
boolean refresh = false;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
boolean showStatusIcons = prefs.getBoolean(Preferences.SHOW_STATUS_ICONS, true);
if (mShowStatusIcons != showStatusIcons) {
refresh = true;
}
mShowStatusIcons = showStatusIcons;
boolean showLogData = prefs.getBoolean(Preferences.APPLIST_SHOW_LOG_DATA, true);
if (mShowLogData != showLogData) {
refresh = true;
}
mShowLogData = showLogData;
String statusIconType = prefs.getString(Preferences.STATUS_ICON_TYPE, "emote");
if (mStatusIconType == null) {
// If mStatusIconType is null, that means this is a first run and a refresh is not
// necessary
refresh = false;
} else if (!statusIconType.equals(mStatusIconType)) {
refresh = true;
}
mStatusIconType = statusIconType;
if (refresh) {
getLoaderManager().restartLoader(0, null, this);
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
((HomeActivity)getActivity()).showDetails(id);
}
private void setupListView() {
final ListView list = getListView();
final LayoutInflater inflater = getActivity().getLayoutInflater();
list.setDividerHeight(0);
mAdapter = new AppListAdapter(null, getActivity());
setListAdapter(mAdapter);
if (list instanceof PinnedHeaderListView &&
mAdapter.getDisplaySectionHeadersEnabled()) {
mPinnedHeaderBackgroundColor = 0xffffffff;
PinnedHeaderListView pinnedHeaderExpandableList =
(PinnedHeaderListView) list;
View pinnedHeader = inflater.inflate(R.layout.list_section, list, false);
pinnedHeaderExpandableList.setPinnedHeaderView(pinnedHeader);
}
list.setOnScrollListener(mAdapter);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), Apps.CONTENT_URI, PROJECTION,
Apps.ALLOW + "!=?", new String[] { String.valueOf(Apps.AllowType.ASK) }, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
mLoadingLayout.setVisibility(View.GONE);
final HomeActivity activity = (HomeActivity) getActivity();
Fragment fragment = activity.getSupportFragmentManager().findFragmentById(R.id.fragment_container);
boolean logging = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(Preferences.LOGGING, true);
if (fragment instanceof AppDetailsFragment) {
long shownItem = 0;
if (logging) {
shownItem = ((AppDetailsFragment)fragment).getShownIndex();
} else {
if (data != null && data.moveToFirst()) {
shownItem = data.getLong(COLUMN_ID);
((AppDetailsFragment)fragment).setShownIndex(shownItem);
}
}
getListView().setItemChecked(mAdapter.getPositionForId(shownItem), true);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
private void doToggle(long id, int allow) {
ContentResolver cr = getActivity().getContentResolver();
Uri uri = Uri.withAppendedPath(Apps.CONTENT_URI, String.valueOf(id));
ContentValues values = new ContentValues();
values.put(Apps.ALLOW, allow == 1?0:1);
cr.update(uri, values, null, null);
// Update the log
values.clear();
values.put(Logs.DATE, System.currentTimeMillis());
values.put(Logs.TYPE, Logs.LogType.TOGGLE);
cr.insert(Uri.withAppendedPath(Logs.CONTENT_URI, String.valueOf(id)), values);
Intent intent = new Intent(getActivity(), ResultService.class);
intent.putExtra(ResultService.EXTRA_ACTION, ResultService.ACTION_RECYCLE);
getActivity().startService(intent);
}
public class AppListAdapter extends CursorAdapter
implements OnScrollListener, SectionIndexer,
PinnedHeaderListView.PinnedHeaderAdapter {
private final String[] mSections = { getString(R.string.allow), getString(R.string.deny) };
private final int[] mSectionTypes = { Apps.AllowType.ALLOW, Apps.AllowType.DENY };
private int[] mSectionPositions = { 0, -1 };
private boolean mDisplaySectionHeaders = true;
private Cursor mCursor;
private HashMap<Long, Integer> mPositions;
private int mLastCachedPosition = -1;
public AppListAdapter(Cursor cursor, Context context) {
super(context, cursor, false);
mPositions = new HashMap<Long, Integer>(cursor!=null?
cursor.getCount():0);
mLastCachedPosition = -1;
mCursor = cursor;
}
@Override
public Cursor swapCursor(Cursor newCursor) {
mSectionPositions[1] = -1;
mCursor = newCursor;
mPositions = new HashMap<Long, Integer>(newCursor != null?
newCursor.getCount():0);
mLastCachedPosition = -1;
return super.swapCursor(newCursor);
}
public boolean getDisplaySectionHeadersEnabled() {
return mDisplaySectionHeaders;
}
@Override
public boolean hasStableIds() {
return true;
}
public int getPositionForId(long id) {
if (mPositions.containsKey(id)) {
return mPositions.get(id);
} else {
mCursor.moveToPosition(mLastCachedPosition);
while (mCursor.moveToNext()) {
mLastCachedPosition++;
if (mCursor.getLong(COLUMN_ID) == id) {
mPositions.put(id, mLastCachedPosition);
return mLastCachedPosition;
}
}
}
return -1;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
bindSectionHeader(v, position, true);
return v;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final AppListItem view = new AppListItem(context, null);
// view.setOnStatusButtonClickListener(AppListActivity.this);
return view;
}
@Override
public void bindView(View itemView, Context context, Cursor cursor) {
final AppListItem view = (AppListItem) itemView;
final long id = cursor.getLong(COLUMN_ID);
final int uid = cursor.getInt(COLUMN_UID);
final String nameText = cursor.getString(COLUMN_NAME);
final int allow = cursor.getInt(COLUMN_ALLOW);
// Set app icon
view.setAppIcon(Util.getAppIcon(getActivity(), uid));
// Set name
if (nameText != null && nameText.length() > 0) {
view.setNameText(nameText);
} else {
view.setNameText(getString(R.string.unknown));
}
// Set the status indicator
if (mShowStatusIcons) {
view.setStatusButton(Util.getStatusIconDrawable(getActivity(), allow), 0, 0l);
// view.setOnStatusButtonClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// doToggle(id, allow);
// }
//
// });
} else {
view.setStatusButton(null, 0, 0);
}
// Set log data
long date = cursor.getLong(COLUMN_LAST_ACCESS);
if (mShowLogData && date > 0) {
int lastLogType = cursor.getInt(COLUMN_LAST_ACCESS_TYPE);
int logTextRes = R.string.log_last_accessed;
switch (lastLogType) {
case Logs.LogType.ALLOW: logTextRes = R.string.log_last_allowed; break;
case Logs.LogType.DENY: logTextRes = R.string.log_last_denied; break;
case Logs.LogType.TOGGLE: logTextRes = R.string.log_last_toggled; break;
case Logs.LogType.CREATE: logTextRes = R.string.log_created_on; break;
}
view.setLogText(getString(logTextRes, Util.formatDate(getActivity(), date),
Util.formatTime(getActivity(), date)));
} else {
view.setLogText(null);
}
}
private void bindSectionHeader(View itemView, int position, boolean displaySectionHeaders) {
final AppListItem view = (AppListItem)itemView;
if (!displaySectionHeaders) {
view.setSectionHeader(null);
view.setDividerVisible(true);
} else {
final int section = getSectionForPosition(position);
if (getPositionForSection(section) == position) {
String title = mSections[section];
view.setSectionHeader(title);
} else {
view.setDividerVisible(false);
view.setSectionHeader(null);
}
// move the divider for the last item in a section
if (getPositionForSection(section + 1) - 1 == position) {
view.setDividerVisible(true);
} else {
view.setDividerVisible(true);
}
}
}
@Override
public int getPositionForSection(int sectionIndex) {
if (sectionIndex > 1) {
return mCursor.getCount();
}
if (mSectionPositions[sectionIndex] > -1) {
return mSectionPositions[sectionIndex];
} else {
Uri uri = Uri.withAppendedPath(Apps.COUNT_CONTENT_URI, String.valueOf(mSectionTypes[sectionIndex-1]));
Cursor c = getActivity().getContentResolver().query(uri, null, null, null, null);
int numInSection = 0;
if (c.moveToFirst()) {
numInSection = c.getInt(0);
} else {
numInSection = 0;
}
c.close();
mSectionPositions[sectionIndex] = numInSection;
return numInSection;
}
}
@Override
public int getSectionForPosition(int position) {
if (mCursor == null) {
return -1;
}
mCursor.moveToPosition(position);
int allow = mCursor.getInt(COLUMN_ALLOW);
if (allow == Apps.AllowType.ALLOW) {
return 0;
} else {
return 1;
}
}
@Override
public Object[] getSections() {
return mSections;
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (view instanceof PinnedHeaderListView && visibleItemCount > 0) {
((PinnedHeaderListView)view).configureHeaderView(firstVisibleItem);
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void configurePinnedHeader(View header, int position, int alpha) {
PinnedHeaderCache cache = (PinnedHeaderCache)header.getTag();
if (cache == null) {
cache = new PinnedHeaderCache();
cache.titleView = (TextView)header.findViewById(R.id.header_text);
cache.textColor = cache.titleView.getTextColors();
cache.background = header.getBackground();
header.setTag(cache);
}
int section = getSectionForPosition(position);
String title = mSections[section];
cache.titleView.setText(title);
if (alpha == 255) {
// Opaque, use the default background and original text color
header.setBackgroundDrawable(cache.background);
cache.titleView.setTextColor(cache.textColor);
} else {
// Faded, use a solid color approximation of the background and
// a translucent text color
int red = Color.red(mPinnedHeaderBackgroundColor);
int green = Color.green(mPinnedHeaderBackgroundColor);
int blue = Color.blue(mPinnedHeaderBackgroundColor);
header.setBackgroundColor(Color.rgb(
255 - alpha*(255-red)/255,
255 - alpha*(255-green)/255,
255 - alpha*(255-blue)/255));
int textColor = cache.textColor.getDefaultColor();
cache.titleView.setTextColor(Color.argb(alpha,
Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
}
}
@Override
public int getPinnedHeaderState(int position) {
if (mCursor == null || mCursor.getCount() == 0 || mCursor.isClosed()) {
return PINNED_HEADER_GONE;
}
if (position < 0) {
return PINNED_HEADER_GONE;
}
// The header should get pushed up if the top item shown
// is the last item in a particular section.
int section = getSectionForPosition(position);
int nextSectionPosition = getPositionForSection(section + 1);
if (nextSectionPosition != -1 && position == nextSectionPosition - 1) {
return PINNED_HEADER_PUSHED_UP;
}
return PINNED_HEADER_VISIBLE;
}
}
}