/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tencent.tws.assistant.preference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import com.tencent.tws.assistant.preference.Preference.OnPreferenceChangeInternalListener;
import com.tencent.tws.assistant.widget.ListView;
/**
* An adapter that returns the {@link Preference} contained in this group.
* In most cases, this adapter should be the base class for any custom
* adapters from {@link Preference#getAdapter()}.
* <p>
* This adapter obeys the
* {@link Preference}'s adapter rule (the
* {@link Adapter#getView(int, View, ViewGroup)} should be used instead of
* {@link Preference#getView(ViewGroup)} if a {@link Preference} has an
* adapter via {@link Preference#getAdapter()}).
* <p>
* This adapter also propagates data change/invalidated notifications upward.
* <p>
* This adapter does not include this {@link PreferenceGroup} in the returned
* adapter, use {@link PreferenceCategoryAdapter} instead.
*
* @see PreferenceCategoryAdapter
*/
class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeInternalListener {
private static final String TAG = "PreferenceGroupAdapter";
/**
* The group that we are providing data from.
*/
private PreferenceGroup mPreferenceGroup;
/**
* Maps a position into this adapter -> {@link Preference}. These
* {@link Preference}s don't have to be direct children of this
* {@link PreferenceGroup}, they can be grand children or younger)
*/
private List<Preference> mPreferenceList;
/**
* List of unique Preference and its subclasses' names. This is used to find
* out how many types of views this adapter can return. Once the count is
* returned, this cannot be modified (since the ListView only checks the
* count once--when the adapter is being set). We will not recycle views for
* Preference subclasses seen after the count has been returned.
*/
private ArrayList<PreferenceLayout> mPreferenceLayouts;
private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
/**
* Blocks the mPreferenceClassNames from being changed anymore.
*/
private boolean mHasReturnedViewTypeCount = false;
private volatile boolean mIsSyncing = false;
private Handler mHandler = new Handler();
private Runnable mSyncRunnable = new Runnable() {
public void run() {
syncMyPreferences();
}
};
private int mHighlightedPosition = -1;
private Drawable mHighlightedDrawable;
private static ViewGroup.LayoutParams sWrapperLayoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
private static class PreferenceLayout implements Comparable<PreferenceLayout> {
private int resId;
private int widgetResId;
private String name;
public int compareTo(PreferenceLayout other) {
int compareNames = name.compareTo(other.name);
if (compareNames == 0) {
if (resId == other.resId) {
if (widgetResId == other.widgetResId) {
return 0;
} else {
return widgetResId - other.widgetResId;
}
} else {
return resId - other.resId;
}
} else {
return compareNames;
}
}
}
public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
// If this group gets or loses any children, let us know
mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
mPreferenceList = new ArrayList<Preference>();
mPreferenceLayouts = new ArrayList<PreferenceLayout>();
syncMyPreferences();
}
private void syncMyPreferences() {
synchronized(this) {
if (mIsSyncing) {
return;
}
mIsSyncing = true;
}
List<Preference> newPreferenceList = new ArrayList<Preference>(mPreferenceList.size());
flattenPreferenceGroup(newPreferenceList, mPreferenceGroup);
mPreferenceList = newPreferenceList;
notifyDataSetChanged();
synchronized(this) {
mIsSyncing = false;
notifyAll();
}
}
//tws-start 20140721::preference
boolean isBeginAll = true;
boolean isBeginNewGroup = false;
boolean mPreviousIsLastIndex = false;
boolean mPreviousIsScreen = false;
boolean mPreviousIsCategory = false;
boolean mNextIsScreen = false;
boolean mNextIsCategory = false;
boolean hasNextPreference = false;
//tws-end 20140721::preference
private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
// TODO: shouldn't always?
group.sortPreferences();
//tws-start 20140721::preference
final int groupSize = group.getPreferenceCount();
for (int i = 0; i < groupSize; i++) {//begin for
final Preference preference = group.getPreference(i);
if (i < groupSize && i != 0) {
final Preference mPrevious = group.getPreference(i - 1);
if(mPrevious instanceof PreferenceScreen){
mPreviousIsCategory = false;
mPreviousIsScreen = true;
}else if(mPrevious instanceof PreferenceCategory){
mPreviousIsScreen = false;
mPreviousIsCategory = true;
}else{
mPreviousIsScreen = false;
mPreviousIsCategory = false;
}
}
if(i < groupSize - 1){
final Preference mNext = group.getPreference(i + 1);
if(mNext instanceof PreferenceScreen){
mNextIsCategory = false;
mNextIsScreen = true;
hasNextPreference = false;
}else if(mNext instanceof PreferenceCategory){
mNextIsScreen = false;
mNextIsCategory = true;
hasNextPreference = false;
}else{
mNextIsScreen = false;
mNextIsCategory = false;
hasNextPreference = true;
}
}else{
mNextIsScreen = false;
mNextIsCategory = true;
hasNextPreference = false;
}
preferences.add(preference);
if (!mHasReturnedViewTypeCount && preference.canRecycleLayout()) {
addPreferenceClassName(preference);
}
if(preference instanceof PreferenceScreen){
final PreferenceScreen preferenceAsScreen = (PreferenceScreen) preference;
final int screenSize = preferenceAsScreen.getPreferenceCount();
if (preferenceAsScreen.isOnSameScreenAsChildren()) {
isBeginNewGroup = true;
mPreviousIsLastIndex = false;
flattenPreferenceGroup(preferences, preferenceAsScreen);
}
else{
if (isBeginAll || isBeginNewGroup || mPreviousIsCategory) {
if (groupSize == 1 || i == groupSize - 1) {
if (isBeginNewGroup ) {// isBeginAll ||mPreviousIsCategory
if(hasNextPreference){
preferenceAsScreen.setIndex(Preference.FRIST_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preferenceAsScreen.setIndex(Preference.ONE_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}
} else {
if(hasNextPreference){
preferenceAsScreen.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preferenceAsScreen.setIndex(Preference.SINGLE_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}
}
} else {
if ((isBeginNewGroup ||mPreviousIsCategory) ) { //||mPreviousIsCategory
if(mPreviousIsLastIndex){
preferenceAsScreen.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
if(hasNextPreference || !mNextIsCategory){
preferenceAsScreen.setIndex(Preference.FRIST_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preferenceAsScreen.setIndex(Preference.ONE_PREFERENCE);
mPreviousIsLastIndex = true;
}
}
} else {
if(hasNextPreference || !mNextIsCategory){
preferenceAsScreen.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preferenceAsScreen.setIndex(Preference.SINGLE_PREFERENCE);
mPreviousIsLastIndex = true;
}
}
}
}else{
if(mNextIsCategory){
preferenceAsScreen.setIndex(Preference.LAST_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}else{
preferenceAsScreen.setIndex(Preference.NORMAL_PREFERENCE);
mPreviousIsLastIndex = false;
}
}
}
}//end if PreferenceScreen
else if(preference instanceof PreferenceCategory){//add if PreferenceCategory
final PreferenceCategory preferenceCategory = (PreferenceCategory) preference;
if (preferenceCategory.isOnSameScreenAsChildren()) {
isBeginNewGroup = true;
mPreviousIsLastIndex = false;
flattenPreferenceGroup(preferences, preferenceCategory);
}
}//end if PreferenceCategory
else if (preference instanceof PreferenceGroup) { //original if PreferenceGroup
final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
if (preferenceAsGroup.isOnSameScreenAsChildren()) {
isBeginNewGroup = true;
mPreviousIsLastIndex = false;
flattenPreferenceGroup(preferences, preferenceAsGroup);
}else{
}
//now
}//end if PreferenceGroup
else{
if (isBeginAll || isBeginNewGroup || mPreviousIsCategory) {
if (groupSize == 1 || i == groupSize - 1) {
if (isBeginNewGroup ) {// isBeginAll ||mPreviousIsCategory
//if(mNextIsPreference){
if(hasNextPreference){
preference.setIndex(Preference.FRIST_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preference.setIndex(Preference.ONE_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}
} else {
// if(mNextIsPreference){
if(hasNextPreference){
preference.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preference.setIndex(Preference.SINGLE_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}
}
} else {
if ((isBeginNewGroup || mPreviousIsCategory ) ) {// //||mPreviousIsCategory
if(mPreviousIsLastIndex){
preference.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
if(hasNextPreference || !mNextIsCategory){
preference.setIndex(Preference.FRIST_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preference.setIndex(Preference.ONE_PREFERENCE);
mPreviousIsLastIndex = true;
}
}
} else {
if(hasNextPreference || !mNextIsCategory){
preference.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preference.setIndex(Preference.SINGLE_PREFERENCE);
mPreviousIsLastIndex = true;
}
}
}
}else{
if(mNextIsCategory){
preference.setIndex(Preference.LAST_PREFERENCE);
mPreviousIsLastIndex = true;
isBeginNewGroup = true;
}else{
if(mPreviousIsCategory||mPreviousIsLastIndex||isBeginAll||isBeginNewGroup){
preference.setIndex(Preference.TOP_PREFERENCE);
mPreviousIsLastIndex = false;
}else{
preference.setIndex(Preference.NORMAL_PREFERENCE);
mPreviousIsLastIndex = false;
}
}
}
}
isBeginAll = false;
isBeginNewGroup = false;
preference.setOnPreferenceChangeInternalListener(this);
}//end for
isBeginAll = true;
isBeginNewGroup = false;
mPreviousIsLastIndex = false;
mPreviousIsScreen = false;
mPreviousIsCategory = false;
mNextIsScreen = false;
mNextIsCategory = false;
hasNextPreference = false;
}
//tws-end 20140721::preference
/**
* Creates a string that includes the preference name, layout id and widget layout id.
* If a particular preference type uses 2 different resources, they will be treated as
* different view types.
*/
private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
PreferenceLayout pl = in != null? in : new PreferenceLayout();
pl.name = preference.getClass().getName();
pl.resId = preference.getLayoutResource();
pl.widgetResId = preference.getWidgetLayoutResource();
return pl;
}
private void addPreferenceClassName(Preference preference) {
final PreferenceLayout pl = createPreferenceLayout(preference, null);
int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);
// Only insert if it doesn't exist (when it is negative).
if (insertPos < 0) {
// Convert to insert index
insertPos = insertPos * -1 - 1;
mPreferenceLayouts.add(insertPos, pl);
}
}
public int getCount() {
return mPreferenceList.size();
}
public Preference getItem(int position) {
if (position < 0 || position >= getCount()) return null;
return mPreferenceList.get(position);
}
public long getItemId(int position) {
if (position < 0 || position >= getCount()) return ListView.INVALID_ROW_ID;
return this.getItem(position).getId();
}
/**
* @hide
*/
public void setHighlighted(int position) {
mHighlightedPosition = position;
}
/**
* @hide
*/
public void setHighlightedDrawable(Drawable drawable) {
mHighlightedDrawable = drawable;
}
public View getView(int position, View convertView, ViewGroup parent) {
final Preference preference = this.getItem(position);
// Build a PreferenceLayout to compare with known ones that are cacheable.
mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
// If it's not one of the cached ones, set the convertView to null so that
// the layout gets re-created by the Preference.
if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||
(getItemViewType(position) == getHighlightItemViewType())) {
convertView = null;
}
View result = preference.getView(convertView, parent);
if (position == mHighlightedPosition && mHighlightedDrawable != null) {
ViewGroup wrapper = new FrameLayout(parent.getContext());
wrapper.setLayoutParams(sWrapperLayoutParams);
wrapper.setBackgroundDrawable(mHighlightedDrawable);
wrapper.addView(result);
result = wrapper;
}
return result;
}
@Override
public boolean isEnabled(int position) {
if (position < 0 || position >= getCount()) return true;
return this.getItem(position).isSelectable();
}
@Override
public boolean areAllItemsEnabled() {
// There should always be a preference group, and these groups are always
// disabled
return false;
}
public void onPreferenceChange(Preference preference) {
notifyDataSetChanged();
}
public void onPreferenceHierarchyChange(Preference preference) {
mHandler.removeCallbacks(mSyncRunnable);
mHandler.post(mSyncRunnable);
}
@Override
public boolean hasStableIds() {
return true;
}
private int getHighlightItemViewType() {
return getViewTypeCount() - 1;
}
@Override
public int getItemViewType(int position) {
if (position == mHighlightedPosition) {
return getHighlightItemViewType();
}
if (!mHasReturnedViewTypeCount) {
mHasReturnedViewTypeCount = true;
}
final Preference preference = this.getItem(position);
if (!preference.canRecycleLayout()) {
return IGNORE_ITEM_VIEW_TYPE;
}
mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);
if (viewType < 0) {
// This is a class that was seen after we returned the count, so
// don't recycle it.
return IGNORE_ITEM_VIEW_TYPE;
} else {
return viewType;
}
}
@Override
public int getViewTypeCount() {
if (!mHasReturnedViewTypeCount) {
mHasReturnedViewTypeCount = true;
}
return Math.max(1, mPreferenceLayouts.size()) + 1;
}
}