diff --git a/src/api-impl/android/app/ListActivity.java b/src/api-impl/android/app/ListActivity.java new file mode 100644 index 00000000..599e60ca --- /dev/null +++ b/src/api-impl/android/app/ListActivity.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2006 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 android.app; + +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; + +/** + * An activity that displays a list of items by binding to a data source such as + * an array or Cursor, and exposes event handlers when the user selects an item. + *

+ * ListActivity hosts a {@link android.widget.ListView ListView} object that can + * be bound to different data sources, typically either an array or a Cursor + * holding query results. Binding, screen layout, and row layout are discussed + * in the following sections. + *

+ * Screen Layout + *

+ *

+ * ListActivity has a default layout that consists of a single, full-screen list + * in the center of the screen. However, if you desire, you can customize the + * screen layout by setting your own view layout with setContentView() in + * onCreate(). To do this, your own view MUST contain a ListView object with the + * id "@android:id/list" (or {@link android.R.id#list} if it's in code) + *

+ * Optionally, your custom view can contain another view object of any type to + * display when the list view is empty. This "empty list" notifier must have an + * id "android:id/empty". Note that when an empty view is present, the list view + * will be hidden when there is no data to display. + *

+ * The following code demonstrates an (ugly) custom screen layout. It has a list + * with a green background, and an alternate red "no data" message. + *

+ * + *
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ *         android:orientation="vertical"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="match_parent"
+ *         android:paddingLeft="8dp"
+ *         android:paddingRight="8dp">
+ *
+ *     <ListView android:id="@android:id/list"
+ *               android:layout_width="match_parent"
+ *               android:layout_height="match_parent"
+ *               android:background="#00FF00"
+ *               android:layout_weight="1"
+ *               android:drawSelectorOnTop="false"/>
+ *
+ *     <TextView android:id="@android:id/empty"
+ *               android:layout_width="match_parent"
+ *               android:layout_height="match_parent"
+ *               android:background="#FF0000"
+ *               android:text="No data"/>
+ * </LinearLayout>
+ * 
+ * + *

+ * Row Layout + *

+ *

+ * You can specify the layout of individual rows in the list. You do this by + * specifying a layout resource in the ListAdapter object hosted by the activity + * (the ListAdapter binds the ListView to the data; more on this later). + *

+ * A ListAdapter constructor takes a parameter that specifies a layout resource + * for each row. It also has two additional parameters that let you specify + * which data field to associate with which object in the row layout resource. + * These two parameters are typically parallel arrays. + *

+ *

+ * Android provides some standard row layout resources. These are in the + * {@link android.R.layout} class, and have names such as simple_list_item_1, + * simple_list_item_2, and two_line_list_item. The following layout XML is the + * source for the resource two_line_list_item, which displays two data + * fields,one above the other, for each list row. + *

+ * + *
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ *     android:layout_width="match_parent"
+ *     android:layout_height="wrap_content"
+ *     android:orientation="vertical">
+ *
+ *     <TextView android:id="@+id/text1"
+ *         android:textSize="16sp"
+ *         android:textStyle="bold"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="wrap_content"/>
+ *
+ *     <TextView android:id="@+id/text2"
+ *         android:textSize="16sp"
+ *         android:layout_width="match_parent"
+ *         android:layout_height="wrap_content"/>
+ * </LinearLayout>
+ * 
+ * + *

+ * You must identify the data bound to each TextView object in this layout. The + * syntax for this is discussed in the next section. + *

+ *

+ * Binding to Data + *

+ *

+ * You bind the ListActivity's ListView object to data using a class that + * implements the {@link android.widget.ListAdapter ListAdapter} interface. + * Android provides two standard list adapters: + * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps), + * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor + * query results. + *

+ *

+ * The following code from a custom ListActivity demonstrates querying the + * Contacts provider for all contacts, then binding the Name and Company fields + * to a two line row layout in the activity's ListView. + *

+ * + *
+ * public class MyListAdapter extends ListActivity {
+ *
+ *     @Override
+ *     protected void onCreate(Bundle savedInstanceState){
+ *         super.onCreate(savedInstanceState);
+ *
+ *         // We'll define a custom screen layout here (the one shown above), but
+ *         // typically, you could just use the standard ListActivity layout.
+ *         setContentView(R.layout.custom_list_activity_view);
+ *
+ *         // Query for all people contacts using the {@link android.provider.Contacts.People} convenience class.
+ *         // Put a managed wrapper around the retrieved cursor so we don't have to worry about
+ *         // requerying or closing it as the activity changes state.
+ *         mCursor = this.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
+ *         startManagingCursor(mCursor);
+ *
+ *         // Now create a new list adapter bound to the cursor.
+ *         // SimpleListAdapter is designed for binding to a Cursor.
+ *         ListAdapter adapter = new SimpleCursorAdapter(
+ *                 this, // Context.
+ *                 android.R.layout.two_line_list_item,  // Specify the row template to use (here, two columns bound to the two retrieved cursor
+ * rows).
+ *                 mCursor,                                              // Pass in the cursor to bind to.
+ *                 new String[] {People.NAME, People.COMPANY},           // Array of cursor columns to bind to.
+ *                 new int[] {android.R.id.text1, android.R.id.text2});  // Parallel array of which template objects to bind to those columns.
+ *
+ *         // Bind to our new adapter.
+ *         setListAdapter(adapter);
+ *     }
+ * }
+ * 
+ * + * @see #setListAdapter + * @see android.widget.ListView + */ +public class ListActivity extends Activity { + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected ListAdapter mAdapter; + /** + * This field should be made private, so it is hidden from the SDK. + * {@hide} + */ + protected ListView mList; + + private Handler mHandler = new Handler(); + private boolean mFinishedStart = false; + + private Runnable mRequestFocus = new Runnable() { + public void run() { + mList.focusableViewAvailable(mList); + } + }; + + /** + * This method will be called when an item in the list is selected. + * Subclasses should override. Subclasses can call + * getListView().getItemAtPosition(position) if they need to access the + * data associated with the selected item. + * + * @param l The ListView where the click happened + * @param v The view that was clicked within the ListView + * @param position The position of the view in the list + * @param id The row id of the item that was clicked + */ + protected void onListItemClick(ListView l, View v, int position, long id) { + } + + /** + * Ensures the list view has been created before Activity restores all + * of the view states. + * + *@see Activity#onRestoreInstanceState(Bundle) + */ + /*@Override + protected void onRestoreInstanceState(Bundle state) { + ensureList(); + super.onRestoreInstanceState(state); + }*/ + + /** + * @see Activity#onDestroy() + */ + @Override + protected void onDestroy() { + mHandler.removeCallbacks(mRequestFocus); + super.onDestroy(); + } + + /** + * Updates the screen state (current list and other views) when the + * content changes. + * + * @see Activity#onContentChanged() + */ + @Override + public void onContentChanged() { + super.onContentChanged(); + View emptyView = findViewById(com.android.internal.R.id.empty); + mList = (ListView)findViewById(com.android.internal.R.id.list); + if (mList == null) { + throw new RuntimeException( + "Your content must have a ListView whose id attribute is " + + + "'android.R.id.list'"); + } + if (emptyView != null) { + // FIXME + //mList.setEmptyView(emptyView); + } + mList.setOnItemClickListener(mOnClickListener); + if (mFinishedStart) { + setListAdapter(mAdapter); + } + mHandler.post(mRequestFocus); + mFinishedStart = true; + } + + /** + * Provide the cursor for the list view. + */ + public void setListAdapter(ListAdapter adapter) { + synchronized (this) { + ensureList(); + mAdapter = adapter; + mList.setAdapter(adapter); + } + } + + /** + * Set the currently selected list item to the specified + * position with the adapter's data + * + * @param position + */ + public void setSelection(int position) { + mList.setSelection(position); + } + + /** + * Get the position of the currently selected list item. + */ + public int getSelectedItemPosition() { + //return mList.getSelectedItemPosition(); + return 0; // FIXME + } + + /** + * Get the cursor row ID of the currently selected list item. + */ + public long getSelectedItemId() { + //return mList.getSelectedItemId(); + return 0; // FIXME + } + + /** + * Get the activity's list view widget. + */ + public ListView getListView() { + ensureList(); + return mList; + } + + /** + * Get the ListAdapter associated with this activity's ListView. + */ + public ListAdapter getListAdapter() { + return mAdapter; + } + + private void ensureList() { + if (mList != null) { + return; + } + try { + setContentView(com.android.internal.R.layout.list_content_simple); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + //onListItemClick((ListView)parent, v, position, id); + } + }; +} diff --git a/src/api-impl/android/content/SearchRecentSuggestionsProvider.java b/src/api-impl/android/content/SearchRecentSuggestionsProvider.java new file mode 100644 index 00000000..fb805d38 --- /dev/null +++ b/src/api-impl/android/content/SearchRecentSuggestionsProvider.java @@ -0,0 +1,5 @@ +package android.content; + +public class SearchRecentSuggestionsProvider extends ContentProvider { + public void setupSuggestions(String s, int i) {} +} diff --git a/src/api-impl/android/preference/PreferenceManager.java b/src/api-impl/android/preference/PreferenceManager.java index f96c7d92..81d84bf3 100644 --- a/src/api-impl/android/preference/PreferenceManager.java +++ b/src/api-impl/android/preference/PreferenceManager.java @@ -8,4 +8,8 @@ public class PreferenceManager { public static SharedPreferences getDefaultSharedPreferences(Context context) { return Context.this_application.getSharedPreferences("default", 0); // is this correct? } + + public static void setDefaultValues(Context context, int i, boolean b) { + System.out.println("android.preference.PrefereceManager.setDefaultValues: STUB"); + } } diff --git a/src/api-impl/android/view/LayoutInflater.java b/src/api-impl/android/view/LayoutInflater.java index 98c66e4a..44574cf6 100644 --- a/src/api-impl/android/view/LayoutInflater.java +++ b/src/api-impl/android/view/LayoutInflater.java @@ -98,6 +98,7 @@ public class LayoutInflater { public View inflate(int layoutResID, ViewGroup root, boolean attachToRoot) { + Slog.v(TAG, "inflating view from id: " + String.format("%x", layoutResID)); XmlResourceParser xpp = Context.this_application.getResources().getLayout(layoutResID); try { diff --git a/src/api-impl/android/widget/ListView.java b/src/api-impl/android/widget/ListView.java index 14ff0c31..c17dec0a 100644 --- a/src/api-impl/android/widget/ListView.java +++ b/src/api-impl/android/widget/ListView.java @@ -18,4 +18,6 @@ public class ListView extends AbsListView { public Drawable getDivider() {return null;} + public void setTextFilterEnabled(boolean enabled) {} + } diff --git a/src/api-impl/android/widget/SimpleAdapter.java b/src/api-impl/android/widget/SimpleAdapter.java new file mode 100644 index 00000000..3e0b6624 --- /dev/null +++ b/src/api-impl/android/widget/SimpleAdapter.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2006 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 android.widget; + +import android.content.Context; +import android.net.Uri; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * An easy adapter to map static data to views defined in an XML file. You can specify the data + * backing the list as an ArrayList of Maps. Each entry in the ArrayList corresponds to one row + * in the list. The Maps contain the data for each row. You also specify an XML file that + * defines the views used to display the row, and a mapping from keys in the Map to specific + * views. + * + * Binding data to views occurs in two phases. First, if a + * {@link android.widget.SimpleAdapter.ViewBinder} is available, + * {@link ViewBinder#setViewValue(android.view.View, Object, String)} + * is invoked. If the returned value is true, binding has occurred. + * If the returned value is false, the following views are then tried in order: + * + * If no appropriate binding can be found, an {@link IllegalStateException} is thrown. + */ +public class SimpleAdapter extends BaseAdapter /*implements Filterable*/ { + private int[] mTo; + private String[] mFrom; + private ViewBinder mViewBinder; + + private List> mData; + + private int mResource; + private int mDropDownResource; + private LayoutInflater mInflater; + + private SimpleFilter mFilter; + private ArrayList> mUnfilteredData; + + /** + * Constructor + * + * @param context The context where the View associated with this SimpleAdapter is running + * @param data A List of Maps. Each entry in the List corresponds to one row in the list. The + * Maps contain the data for each row, and should include all the entries specified in + * "from" + * @param resource Resource identifier of a view layout that defines the views for this list + * item. The layout file should include at least those named views defined in "to" + * @param from A list of column names that will be added to the Map associated with each + * item. + * @param to The views that should display column in the "from" parameter. These should all be + * TextViews. The first N views in this list are given the values of the first N columns + * in the from parameter. + */ + public SimpleAdapter(Context context, List> data, + int resource, String[] from, int[] to) { + mData = data; + mResource = mDropDownResource = resource; + mFrom = from; + mTo = to; + mInflater = (LayoutInflater)context.getSystemService("layout_inflater"); + } + + /** + * @see android.widget.Adapter#getCount() + */ + public int getCount() { + return mData.size(); + } + + /** + * @see android.widget.Adapter#getItem(int) + */ + public Object getItem(int position) { + return mData.get(position); + } + + /** + * @see android.widget.Adapter#getItemId(int) + */ + public long getItemId(int position) { + return position; + } + + /** + * @see android.widget.Adapter#getView(int, View, ViewGroup) + */ + public View getView(int position, View convertView, ViewGroup parent) { + return createViewFromResource(position, convertView, parent, mResource); + } + + private View createViewFromResource(int position, View convertView, + ViewGroup parent, int resource) { + View v; + if (convertView == null) { + v = mInflater.inflate(resource, parent, false); + } else { + v = convertView; + } + + bindView(position, v); + + return v; + } + + /** + *

Sets the layout resource to create the drop down views.

+ * + * @param resource the layout resource defining the drop down views + * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) + */ + public void setDropDownViewResource(int resource) { + this.mDropDownResource = resource; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return createViewFromResource(position, convertView, parent, mDropDownResource); + } + + private void bindView(int position, View view) { + final Map dataSet = mData.get(position); + if (dataSet == null) { + return; + } + + final ViewBinder binder = mViewBinder; + final String[] from = mFrom; + final int[] to = mTo; + final int count = to.length; + + for (int i = 0; i < count; i++) { + final View v = view.findViewById(to[i]); + if (v != null) { + final Object data = dataSet.get(from[i]); + String text = data == null ? "" : data.toString(); + if (text == null) { + text = ""; + } + + boolean bound = false; + if (binder != null) { + bound = binder.setViewValue(v, data, text); + } + + if (!bound) { + if (v instanceof Checkable) { + if (data instanceof Boolean) { + ((Checkable)v).setChecked((Boolean)data); + } else if (v instanceof TextView) { + // Note: keep the instanceof TextView check at the bottom of these + // ifs since a lot of views are TextViews (e.g. CheckBoxes). + setViewText((TextView)v, text); + } else { + throw new IllegalStateException(v.getClass().getName() + + " should be bound to a Boolean, not a " + + (data == null ? "" : data.getClass())); + } + } else if (v instanceof TextView) { + // Note: keep the instanceof TextView check at the bottom of these + // ifs since a lot of views are TextViews (e.g. CheckBoxes). + setViewText((TextView)v, text); + } else if (v instanceof ImageView) { + if (data instanceof Integer) { + setViewImage((ImageView)v, (Integer)data); + } else { + setViewImage((ImageView)v, text); + } + } else { + throw new IllegalStateException(v.getClass().getName() + " is not a " + + + " view that can be bounds by this SimpleAdapter"); + } + } + } + } + } + + /** + * Returns the {@link ViewBinder} used to bind data to views. + * + * @return a ViewBinder or null if the binder does not exist + * + * @see #setViewBinder(android.widget.SimpleAdapter.ViewBinder) + */ + public ViewBinder getViewBinder() { + return mViewBinder; + } + + /** + * Sets the binder used to bind data to views. + * + * @param viewBinder the binder used to bind data to views, can be null to + * remove the existing binder + * + * @see #getViewBinder() + */ + public void setViewBinder(ViewBinder viewBinder) { + mViewBinder = viewBinder; + } + + /** + * Called by bindView() to set the image for an ImageView but only if + * there is no existing ViewBinder or if the existing ViewBinder cannot + * handle binding to an ImageView. + * + * This method is called instead of {@link #setViewImage(ImageView, String)} + * if the supplied data is an int or Integer. + * + * @param v ImageView to receive an image + * @param value the value retrieved from the data set + * + * @see #setViewImage(ImageView, String) + */ + public void setViewImage(ImageView v, int value) { + v.setImageResource(value); + } + + /** + * Called by bindView() to set the image for an ImageView but only if + * there is no existing ViewBinder or if the existing ViewBinder cannot + * handle binding to an ImageView. + * + * By default, the value will be treated as an image resource. If the + * value cannot be used as an image resource, the value is used as an + * image Uri. + * + * This method is called instead of {@link #setViewImage(ImageView, int)} + * if the supplied data is not an int or Integer. + * + * @param v ImageView to receive an image + * @param value the value retrieved from the data set + * + * @see #setViewImage(ImageView, int) + */ + public void setViewImage(ImageView v, String value) { + try { + v.setImageResource(Integer.parseInt(value)); + } catch (NumberFormatException nfe) { + //v.setImageURI(Uri.parse(value)); + } + } + + /** + * Called by bindView() to set the text for a TextView but only if + * there is no existing ViewBinder or if the existing ViewBinder cannot + * handle binding to a TextView. + * + * @param v TextView to receive text + * @param text the text to be set for the TextView + */ + public void setViewText(TextView v, String text) { + v.setText(text); + } + + public Filter getFilter() { + if (mFilter == null) { + mFilter = new SimpleFilter(); + } + return mFilter; + } + + /** + * This class can be used by external clients of SimpleAdapter to bind + * values to views. + * + * You should use this class to bind values to views that are not + * directly supported by SimpleAdapter or to change the way binding + * occurs for views supported by SimpleAdapter. + * + * @see SimpleAdapter#setViewImage(ImageView, int) + * @see SimpleAdapter#setViewImage(ImageView, String) + * @see SimpleAdapter#setViewText(TextView, String) + */ + public static interface ViewBinder { + /** + * Binds the specified data to the specified view. + * + * When binding is handled by this ViewBinder, this method must return true. + * If this method returns false, SimpleAdapter will attempts to handle + * the binding on its own. + * + * @param view the view to bind the data to + * @param data the data to bind to the view + * @param textRepresentation a safe String representation of the supplied data: + * it is either the result of data.toString() or an empty String but it + * is never null + * + * @return true if the data was bound to the view, false otherwise + */ + boolean setViewValue(View view, Object data, String textRepresentation); + } + + /** + *

An array filters constrains the content of the array adapter with + * a prefix. Each item that does not start with the supplied prefix + * is removed from the list.

+ */ + private class SimpleFilter extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence prefix) { + FilterResults results = new FilterResults(); + + if (mUnfilteredData == null) { + mUnfilteredData = new ArrayList>(mData); + } + + if (prefix == null || prefix.length() == 0) { + ArrayList> list = mUnfilteredData; + results.values = list; + results.count = list.size(); + } else { + String prefixString = prefix.toString().toLowerCase(); + + ArrayList> unfilteredValues = mUnfilteredData; + int count = unfilteredValues.size(); + + ArrayList> newValues = new ArrayList>(count); + + for (int i = 0; i < count; i++) { + Map h = unfilteredValues.get(i); + if (h != null) { + + int len = mTo.length; + + for (int j = 0; j < len; j++) { + String str = (String)h.get(mFrom[j]); + + String[] words = str.split(" "); + int wordCount = words.length; + + for (int k = 0; k < wordCount; k++) { + String word = words[k]; + + if (word.toLowerCase().startsWith(prefixString)) { + newValues.add(h); + break; + } + } + } + } + } + + results.values = newValues; + results.count = newValues.size(); + } + + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + // noinspection unchecked + mData = (List>)results.values; + if (results.count > 0) { + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); + } + } + } +} diff --git a/src/api-impl/meson.build b/src/api-impl/meson.build index 64666234..1eb56349 100644 --- a/src/api-impl/meson.build +++ b/src/api-impl/meson.build @@ -30,6 +30,7 @@ hax_jar = jar('hax', [ 'android/app/FragmentTransaction.java', 'android/app/IntentService.java', 'android/app/KeyguardManager.java', + 'android/app/ListActivity.java', 'android/app/NativeActivity.java', 'android/app/Notification.java', 'android/app/NotificationManager.java', @@ -62,6 +63,7 @@ hax_jar = jar('hax', [ 'android/content/Intent.java', 'android/content/IntentSender.java', 'android/content/OperationApplicationException.java', + 'android/content/SearchRecentSuggestionsProvider.java', 'android/content/RestrictionsManager.java', 'android/content/UriMatcher.java', 'android/content/pm/ActivityInfo.java', @@ -387,10 +389,10 @@ hax_jar = jar('hax', [ 'android/view/animation/Animation.java', 'android/view/animation/AnimationUtils.java', 'android/view/animation/LinearInterpolator.java', - 'android/view/accessibility/AccessibilityManager.java', - 'android/view/accessibility/AccessibilityNodeInfo.java', - 'android/view/accessibility/CaptioningManager.java', - 'android/view/textservice/TextServicesManager.java', + 'android/view/accessibility/AccessibilityManager.java', + 'android/view/accessibility/AccessibilityNodeInfo.java', + 'android/view/accessibility/CaptioningManager.java', + 'android/view/textservice/TextServicesManager.java', 'android/webkit/DownloadListener.java', 'android/webkit/MimeTypeMap.java', 'android/webkit/WebSettings.java', @@ -432,6 +434,7 @@ hax_jar = jar('hax', [ 'android/widget/Scroller.java', 'android/widget/ScrollView.java', 'android/widget/SeekBar.java', + 'android/widget/SimpleAdapter.java', 'android/widget/Space.java', 'android/widget/Spinner.java', 'android/widget/SpinnerAdapter.java',