diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index d4a3582cfbf67..73fa01e592011 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -27,12 +27,14 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.text.TextUtils; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnKeyListener; import android.view.ViewGroup; import android.widget.ListView; +import android.widget.TextView; /** * Shows a hierarchy of {@link Preference} objects as @@ -366,6 +368,20 @@ public abstract class PreferenceFragment extends Fragment implements private void bindPreferences() { final PreferenceScreen preferenceScreen = getPreferenceScreen(); if (preferenceScreen != null) { + View root = getView(); + if (root != null) { + View titleView = root.findViewById(android.R.id.title); + if (titleView instanceof TextView) { + CharSequence title = preferenceScreen.getTitle(); + if (TextUtils.isEmpty(title)) { + titleView.setVisibility(View.GONE); + } else { + ((TextView) titleView).setText(title); + titleView.setVisibility(View.VISIBLE); + } + } + } + preferenceScreen.bind(getListView()); } onBindPreferences(); diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index b1317e656e3e4..2305b0564e499 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -19,6 +19,8 @@ package android.preference; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -31,6 +33,7 @@ import android.widget.Adapter; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; +import android.widget.TextView; /** * Represents a top-level {@link Preference} that @@ -91,13 +94,33 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi private Dialog mDialog; private ListView mListView; - + + private int mLayoutResId = com.android.internal.R.layout.preference_list_fragment; + private Drawable mDividerDrawable; + private boolean mDividerSpecified; + /** * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}. * @hide- */ public PreferenceScreen(Context context, AttributeSet attrs) { super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle); + + TypedArray a = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.PreferenceScreen, + com.android.internal.R.attr.preferenceScreenStyle, + 0); + + mLayoutResId = a.getResourceId( + com.android.internal.R.styleable.PreferenceScreen_screenLayout, + mLayoutResId); + if (a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceScreen_divider)) { + mDividerDrawable = + a.getDrawable(com.android.internal.R.styleable.PreferenceScreen_divider); + mDividerSpecified = true; + } + + a.recycle(); } /** @@ -163,18 +186,30 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View childPrefScreen = inflater.inflate( - com.android.internal.R.layout.preference_list_fragment, null); + View childPrefScreen = inflater.inflate(mLayoutResId, null); + View titleView = childPrefScreen.findViewById(android.R.id.title); mListView = (ListView) childPrefScreen.findViewById(android.R.id.list); + if (mDividerSpecified) { + mListView.setDivider(mDividerDrawable); + } + bind(mListView); // Set the title bar if title is available, else no title bar final CharSequence title = getTitle(); Dialog dialog = mDialog = new Dialog(context, context.getThemeResId()); if (TextUtils.isEmpty(title)) { + if (titleView != null) { + titleView.setVisibility(View.GONE); + } dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); } else { - dialog.setTitle(title); + if (titleView instanceof TextView) { + ((TextView) titleView).setText(title); + titleView.setVisibility(View.VISIBLE); + } else { + dialog.setTitle(title); + } } dialog.setContentView(childPrefScreen); dialog.setOnDismissListener(this); diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 8f908fb854444..b0f19d7a0b49f 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -112,8 +112,8 @@ public class ListView extends AbsListView { public boolean isSelectable; } - private ArrayList mHeaderViewInfos = Lists.newArrayList(); - private ArrayList mFooterViewInfos = Lists.newArrayList(); + ArrayList mHeaderViewInfos = Lists.newArrayList(); + ArrayList mFooterViewInfos = Lists.newArrayList(); Drawable mDivider; int mDividerHeight; @@ -279,7 +279,7 @@ public class ListView extends AbsListView { // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); + wrapHeaderListAdapterInternal(); } // In the case of re-adding a header view, or adding one later on, @@ -373,7 +373,7 @@ public class ListView extends AbsListView { // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof HeaderViewListAdapter)) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter); + wrapHeaderListAdapterInternal(); } // In the case of re-adding a footer view, or adding one later on, @@ -476,7 +476,7 @@ public class ListView extends AbsListView { mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { - mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); + mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } @@ -2228,7 +2228,7 @@ public class ListView extends AbsListView { * after the header views. */ public void setSelectionAfterHeaderView() { - final int count = mHeaderViewInfos.size(); + final int count = getHeaderViewsCount(); if (count > 0) { mNextSelectedPosition = 0; return; @@ -3356,7 +3356,7 @@ public class ListView extends AbsListView { bounds.right = mRight - mLeft - mPaddingRight; final int count = getChildCount(); - final int headerCount = mHeaderViewInfos.size(); + final int headerCount = getHeaderViewsCount(); final int itemCount = mItemCount; final int footerLimit = (itemCount - mFooterViewInfos.size()); final boolean headerDividers = mHeaderDividersEnabled; @@ -3940,7 +3940,7 @@ public class ListView extends AbsListView { if (drawDividers) { final boolean fillForMissingDividers = isOpaque() && !super.isOpaque(); final int itemCount = mItemCount; - final int headerCount = mHeaderViewInfos.size(); + final int headerCount = getHeaderViewsCount(); final int footerLimit = (itemCount - mFooterViewInfos.size()); final boolean isHeader = (itemIndex < headerCount); final boolean isFooter = (itemIndex >= footerLimit); @@ -4052,4 +4052,24 @@ public class ListView extends AbsListView { encoder.addProperty("recycleOnMeasure", recycleOnMeasure()); } + + /** @hide */ + protected HeaderViewListAdapter wrapHeaderListAdapterInternal( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter); + } + + /** @hide */ + protected void wrapHeaderListAdapterInternal() { + mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter); + } + + /** @hide */ + protected void dispatchDataSetObserverOnChangedInternal() { + if (mDataSetObserver != null) { + mDataSetObserver.onChanged(); + } + } } diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java new file mode 100644 index 0000000000000..3d32d86cd49c4 --- /dev/null +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.annotation.IdRes; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.HeaderViewListAdapter; + +import java.util.ArrayList; + +import com.android.internal.util.Predicate; + +public class WatchHeaderListView extends ListView { + private View mTopPanel; + + public WatchHeaderListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public WatchHeaderListView( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected HeaderViewListAdapter wrapHeaderListAdapterInternal( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + return new WatchHeaderListAdapter(headerViewInfos, footerViewInfos, adapter); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (mTopPanel == null) { + setTopPanel(child); + } else { + throw new IllegalStateException("WatchHeaderListView can host only one header"); + } + } + + public void setTopPanel(View v) { + mTopPanel = v; + wrapAdapterIfNecessary(); + } + + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(adapter); + wrapAdapterIfNecessary(); + } + + @Override + protected View findViewTraversal(@IdRes int id) { + View v = super.findViewTraversal(id); + if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewById(id); + } + return v; + } + + @Override + protected View findViewWithTagTraversal(Object tag) { + View v = super.findViewWithTagTraversal(tag); + if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewWithTag(tag); + } + return v; + } + + @Override + protected View findViewByPredicateTraversal(Predicate predicate, View childToSkip) { + View v = super.findViewByPredicateTraversal(predicate, childToSkip); + if (v == null && mTopPanel != null && mTopPanel != childToSkip + && !mTopPanel.isRootNamespace()) { + return mTopPanel.findViewByPredicate(predicate); + } + return v; + } + + @Override + public int getHeaderViewsCount() { + return mTopPanel == null ? super.getHeaderViewsCount() : super.getHeaderViewsCount() + 1; + } + + private void wrapAdapterIfNecessary() { + ListAdapter adapter = getAdapter(); + if (adapter != null && mTopPanel != null) { + if (!(adapter instanceof WatchHeaderListAdapter)) { + wrapHeaderListAdapterInternal(); + } + + ((WatchHeaderListAdapter) getAdapter()).setTopPanel(mTopPanel); + dispatchDataSetObserverOnChangedInternal(); + } + } + + private static class WatchHeaderListAdapter extends HeaderViewListAdapter { + private View mTopPanel; + + public WatchHeaderListAdapter( + ArrayList headerViewInfos, + ArrayList footerViewInfos, + ListAdapter adapter) { + super(headerViewInfos, footerViewInfos, adapter); + } + + public void setTopPanel(View v) { + mTopPanel = v; + } + + private int getTopPanelCount() { + return mTopPanel == null ? 0 : 1; + } + + @Override + public int getCount() { + return super.getCount() + getTopPanelCount(); + } + + @Override + public boolean areAllItemsEnabled() { + return mTopPanel == null && super.areAllItemsEnabled(); + } + + @Override + public boolean isEnabled(int position) { + if (mTopPanel != null) { + if (position == 0) { + return false; + } else { + return super.isEnabled(position - 1); + } + } + + return super.isEnabled(position); + } + + @Override + public Object getItem(int position) { + if (mTopPanel != null) { + if (position == 0) { + return null; + } else { + return super.getItem(position - 1); + } + } + + return super.getItem(position); + } + + @Override + public long getItemId(int position) { + int numHeaders = getHeadersCount() + getTopPanelCount(); + if (getWrappedAdapter() != null && position >= numHeaders) { + int adjPosition = position - numHeaders; + int adapterCount = getWrappedAdapter().getCount(); + if (adjPosition < adapterCount) { + return getWrappedAdapter().getItemId(adjPosition); + } + } + return -1; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (mTopPanel != null) { + if (position == 0) { + return mTopPanel; + } else { + return super.getView(position - 1, convertView, parent); + } + } + + return super.getView(position, convertView, parent); + } + + @Override + public int getItemViewType(int position) { + int numHeaders = getHeadersCount() + getTopPanelCount(); + if (getWrappedAdapter() != null && position >= numHeaders) { + int adjPosition = position - numHeaders; + int adapterCount = getWrappedAdapter().getCount(); + if (adjPosition < adapterCount) { + return getWrappedAdapter().getItemViewType(adjPosition); + } + } + + return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + } + } +} diff --git a/core/res/res/layout-watch/preference_list_fragment_material.xml b/core/res/res/layout-watch/preference_list_fragment_material.xml new file mode 100644 index 0000000000000..ae8f203a7ce61 --- /dev/null +++ b/core/res/res/layout-watch/preference_list_fragment_material.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + +