summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ActionBar.java148
-rw-r--r--core/java/android/app/BackStackEntry.java4
-rw-r--r--core/java/android/app/ContextImpl.java101
-rw-r--r--core/java/android/app/FragmentTransaction.java6
-rw-r--r--core/java/android/app/NativeActivity.java13
-rw-r--r--core/java/android/app/backup/SharedPreferencesBackupHelper.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java15
-rw-r--r--core/java/android/database/sqlite/SQLiteCompiledSql.java25
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java45
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java12
-rw-r--r--core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java31
-rw-r--r--core/java/android/os/BatteryStats.java47
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java14
-rw-r--r--core/java/android/util/TimeUtils.java181
-rw-r--r--core/java/android/view/View.java12
-rw-r--r--core/java/android/view/ViewDebug.java9
-rw-r--r--core/java/android/view/ViewGroup.java5
-rw-r--r--core/java/android/view/ViewRoot.java14
-rw-r--r--core/java/android/webkit/WebSettings.java21
-rw-r--r--core/java/android/webkit/ZoomManager.java3
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java96
-rw-r--r--core/java/android/widget/QuickContactBadge.java3
-rw-r--r--core/java/android/widget/StackView.java384
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java137
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java14
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java82
26 files changed, 885 insertions, 545 deletions
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 38086f0..29f2e30 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -123,72 +123,6 @@ public abstract class ActionBar {
public abstract int getSelectedNavigationItem();
/**
- * Set the action bar into standard navigation mode, supplying a title and subtitle.
- *
- * Standard navigation mode is default. The title is automatically set to the
- * name of your Activity. Subtitles are displayed underneath the title, usually
- * in a smaller font or otherwise less prominently than the title. Subtitles are
- * good for extended descriptions of activity state.
- *
- * @param title The action bar's title. null is treated as an empty string.
- * @param subtitle The action bar's subtitle. null will remove the subtitle entirely.
- *
- * @see #setStandardNavigationMode()
- * @see #setStandardNavigationMode(CharSequence)
- * @see #setStandardNavigationMode(int)
- * @see #setStandardNavigationMode(int, int)
- */
- public abstract void setStandardNavigationMode(CharSequence title, CharSequence subtitle);
-
- /**
- * Set the action bar into standard navigation mode, supplying a title and subtitle.
- *
- * Standard navigation mode is default. The title is automatically set to the
- * name of your Activity. Subtitles are displayed underneath the title, usually
- * in a smaller font or otherwise less prominently than the title. Subtitles are
- * good for extended descriptions of activity state.
- *
- * @param titleResId Resource ID of a title string
- * @param subtitleResId Resource ID of a subtitle string
- *
- * @see #setStandardNavigationMode()
- * @see #setStandardNavigationMode(CharSequence)
- * @see #setStandardNavigationMode(CharSequence, CharSequence)
- * @see #setStandardNavigationMode(int)
- */
- public abstract void setStandardNavigationMode(int titleResId, int subtitleResId);
-
- /**
- * Set the action bar into standard navigation mode, supplying a title and subtitle.
- *
- * Standard navigation mode is default. The title is automatically set to the
- * name of your Activity on startup if an action bar is present.
- *
- * @param title The action bar's title. null is treated as an empty string.
- *
- * @see #setStandardNavigationMode()
- * @see #setStandardNavigationMode(CharSequence, CharSequence)
- * @see #setStandardNavigationMode(int)
- * @see #setStandardNavigationMode(int, int)
- */
- public abstract void setStandardNavigationMode(CharSequence title);
-
- /**
- * Set the action bar into standard navigation mode, supplying a title and subtitle.
- *
- * Standard navigation mode is default. The title is automatically set to the
- * name of your Activity on startup if an action bar is present.
- *
- * @param titleResId Resource ID of a title string
- *
- * @see #setStandardNavigationMode()
- * @see #setStandardNavigationMode(CharSequence)
- * @see #setStandardNavigationMode(CharSequence, CharSequence)
- * @see #setStandardNavigationMode(int, int)
- */
- public abstract void setStandardNavigationMode(int titleResId);
-
- /**
* Set the action bar into standard navigation mode, using the currently set title
* and/or subtitle.
*
@@ -324,18 +258,6 @@ public abstract class ActionBar {
public abstract void setTabNavigationMode();
/**
- * Set the action bar into tabbed navigation mode.
- *
- * @param containerViewId Id of the container view where tab content fragments should appear.
- *
- * @see #addTab(Tab)
- * @see #insertTab(Tab, int)
- * @see #removeTab(Tab)
- * @see #removeTabAt(int)
- */
- public abstract void setTabNavigationMode(int containerViewId);
-
- /**
* Create and return a new {@link Tab}.
* This tab will not be included in the action bar until it is added.
*
@@ -354,13 +276,13 @@ public abstract class ActionBar {
public abstract void addTab(Tab tab);
/**
- * Insert a tab for use in tabbed navigation mode. The tab will be inserted at
+ * Add a tab for use in tabbed navigation mode. The tab will be inserted at
* <code>position</code>.
*
* @param tab The tab to add
* @param position The new position of the tab
*/
- public abstract void insertTab(Tab tab, int position);
+ public abstract void addTab(Tab tab, int position);
/**
* Remove a tab from the action bar.
@@ -384,6 +306,14 @@ public abstract class ActionBar {
public abstract void selectTab(Tab tab);
/**
+ * Returns the currently selected tab if in tabbed navigation mode and there is at least
+ * one tab present.
+ *
+ * @return The currently selected tab or null
+ */
+ public abstract Tab getSelectedTab();
+
+ /**
* Retrieve the current height of the ActionBar.
*
* @return The ActionBar's height
@@ -477,22 +407,68 @@ public abstract class ActionBar {
public abstract void setText(CharSequence text);
/**
- * Returns the fragment that will be shown when this tab is selected.
+ * Set a custom view to be used for this tab. This overrides values set by
+ * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
*
- * @return Fragment associated with this tab
+ * @param view Custom view to be used as a tab.
*/
- public abstract Fragment getFragment();
+ public abstract void setCustomView(View view);
/**
- * Set the fragment that will be shown when this tab is selected.
+ * Retrieve a previously set custom view for this tab.
*
- * @param fragment Fragment to associate with this tab
+ * @return The custom view set by {@link #setCustomView(View)}.
*/
- public abstract void setFragment(Fragment fragment);
+ public abstract View getCustomView();
+
+ /**
+ * Give this Tab an arbitrary object to hold for later use.
+ *
+ * @param obj Object to store
+ */
+ public abstract void setTag(Object obj);
+
+ /**
+ * @return This Tab's tag object.
+ */
+ public abstract Object getTag();
+
+ /**
+ * Set the {@link TabListener} that will handle switching to and from this tab.
+ * All tabs must have a TabListener set before being added to the ActionBar.
+ *
+ * @param listener Listener to handle tab selection events
+ */
+ public abstract void setTabListener(TabListener listener);
/**
* Select this tab. Only valid if the tab has been added to the action bar.
*/
public abstract void select();
}
+
+ /**
+ * Callback interface invoked when a tab is focused, unfocused, added, or removed.
+ */
+ public interface TabListener {
+ /**
+ * Called when a tab enters the selected state.
+ *
+ * @param tab The tab that was selected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. The previous tab's unselect and this tab's select will be
+ * executed in a single transaction.
+ */
+ public void onTabSelected(Tab tab, FragmentTransaction ft);
+
+ /**
+ * Called when a tab exits the selected state.
+ *
+ * @param tab The tab that was unselected
+ * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+ * during a tab switch. This tab's unselect and the newly selected tab's select
+ * will be executed in a single transaction.
+ */
+ public void onTabUnselected(Tab tab, FragmentTransaction ft);
+ }
}
diff --git a/core/java/android/app/BackStackEntry.java b/core/java/android/app/BackStackEntry.java
index 00c2fc4..71fd5e5 100644
--- a/core/java/android/app/BackStackEntry.java
+++ b/core/java/android/app/BackStackEntry.java
@@ -490,4 +490,8 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
public int getTransitionStyle() {
return mTransitionStyle;
}
+
+ public boolean isEmpty() {
+ return mNumOp == 0;
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6886aba..7497136 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -323,54 +323,54 @@ class ContextImpl extends Context {
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
+ File prefsFile;
+ boolean needInitialLoad = false;
synchronized (sSharedPrefs) {
sp = sSharedPrefs.get(name);
- if (sp != null && !sp.hasFileChanged()) {
- //Log.i(TAG, "Returning existing prefs " + name + ": " + sp);
+ if (sp != null && !sp.hasFileChangedUnexpectedly()) {
return sp;
}
- }
- File f = getSharedPrefsFile(name);
- FileInputStream str = null;
- File backup = makeBackupFile(f);
- if (backup.exists()) {
- f.delete();
- backup.renameTo(f);
+ prefsFile = getSharedPrefsFile(name);
+ if (sp == null) {
+ sp = new SharedPreferencesImpl(prefsFile, mode, null);
+ sSharedPrefs.put(name, sp);
+ needInitialLoad = true;
+ }
}
- // Debugging
- if (f.exists() && !f.canRead()) {
- Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
- }
+ synchronized (sp) {
+ if (needInitialLoad && sp.isLoaded()) {
+ // lost the race to load; another thread handled it
+ return sp;
+ }
+ File backup = makeBackupFile(prefsFile);
+ if (backup.exists()) {
+ prefsFile.delete();
+ backup.renameTo(prefsFile);
+ }
- Map map = null;
- if (f.exists() && f.canRead()) {
- try {
- str = new FileInputStream(f);
- map = XmlUtils.readMapXml(str);
- str.close();
- } catch (org.xmlpull.v1.XmlPullParserException e) {
- Log.w(TAG, "getSharedPreferences", e);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "getSharedPreferences", e);
- } catch (IOException e) {
- Log.w(TAG, "getSharedPreferences", e);
+ // Debugging
+ if (prefsFile.exists() && !prefsFile.canRead()) {
+ Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission");
}
- }
- synchronized (sSharedPrefs) {
- if (sp != null) {
- //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);
- sp.replace(map);
- } else {
- sp = sSharedPrefs.get(name);
- if (sp == null) {
- sp = new SharedPreferencesImpl(f, mode, map);
- sSharedPrefs.put(name, sp);
+ Map map = null;
+ if (prefsFile.exists() && prefsFile.canRead()) {
+ try {
+ FileInputStream str = new FileInputStream(prefsFile);
+ map = XmlUtils.readMapXml(str);
+ str.close();
+ } catch (org.xmlpull.v1.XmlPullParserException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "getSharedPreferences", e);
+ } catch (IOException e) {
+ Log.w(TAG, "getSharedPreferences", e);
}
}
- return sp;
+ sp.replace(map);
}
+ return sp;
}
private File getPreferencesDir() {
@@ -2718,6 +2718,10 @@ class ContextImpl extends Context {
private static final class SharedPreferencesImpl implements SharedPreferences {
+ // Lock ordering rules:
+ // - acquire SharedPreferencesImpl.this before EditorImpl.this
+ // - acquire mWritingToDiskLock before EditorImpl.this
+
private final File mFile;
private final File mBackupFile;
private final int mMode;
@@ -2725,6 +2729,7 @@ class ContextImpl extends Context {
private Map<String, Object> mMap; // guarded by 'this'
private long mTimestamp; // guarded by 'this'
private int mDiskWritesInFlight = 0; // guarded by 'this'
+ private boolean mLoaded = false; // guarded by 'this'
private final Object mWritingToDiskLock = new Object();
private static final Object mContent = new Object();
@@ -2735,6 +2740,7 @@ class ContextImpl extends Context {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
+ mLoaded = initialContents != null;
mMap = initialContents != null ? initialContents : new HashMap<String, Object>();
FileStatus stat = new FileStatus();
if (FileUtils.getFileStatus(file.getPath(), stat)) {
@@ -2743,7 +2749,23 @@ class ContextImpl extends Context {
mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
}
- public boolean hasFileChanged() {
+ // Has this SharedPreferences ever had values assigned to it?
+ boolean isLoaded() {
+ synchronized (this) {
+ return mLoaded;
+ }
+ }
+
+ // Has the file changed out from under us? i.e. writes that
+ // we didn't instigate.
+ public boolean hasFileChangedUnexpectedly() {
+ synchronized (this) {
+ if (mDiskWritesInFlight > 0) {
+ // If we know we caused it, it's not unexpected.
+ Log.d(TAG, "disk write in flight, not unexpected.");
+ return false;
+ }
+ }
FileStatus stat = new FileStatus();
if (!FileUtils.getFileStatus(mFile.getPath(), stat)) {
return true;
@@ -2754,8 +2776,9 @@ class ContextImpl extends Context {
}
public void replace(Map newContents) {
- if (newContents != null) {
- synchronized (this) {
+ synchronized (this) {
+ mLoaded = true;
+ if (newContents != null) {
mMap = newContents;
}
}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 04598a3..9d44106 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -85,6 +85,12 @@ public interface FragmentTransaction {
* @return Returns the same FragmentTransaction instance.
*/
public FragmentTransaction show(Fragment fragment);
+
+ /**
+ * @return <code>true</code> if this transaction contains no operations,
+ * <code>false</code> otherwise.
+ */
+ public boolean isEmpty();
/**
* Bit mask that is set for all enter transitions.
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 4dc88b3..d7a0412 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -3,8 +3,6 @@ package android.app;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
-import dalvik.system.PathClassLoader;
-
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -168,17 +166,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
// If the application does not have (Java) code, then no ClassLoader
// has been set up for it. We will need to do our own search for
// the native code.
- path = ai.applicationInfo.dataDir + "/lib/" + System.mapLibraryName(libname);
- if (!(new File(path)).exists()) {
- path = null;
+ File libraryFile = new File(ai.applicationInfo.nativeLibraryDir,
+ System.mapLibraryName(libname));
+ if (libraryFile.exists()) {
+ path = libraryFile.getPath();
}
}
if (path == null) {
- path = ((PathClassLoader)getClassLoader()).findLibrary(libname);
- }
-
- if (path == null) {
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
diff --git a/core/java/android/app/backup/SharedPreferencesBackupHelper.java b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
index 23b1703..213bd31 100644
--- a/core/java/android/app/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/app/backup/SharedPreferencesBackupHelper.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.app.QueuedWork;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.ParcelFileDescriptor;
@@ -94,7 +95,11 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
Context context = mContext;
-
+
+ // If a SharedPreference has an outstanding write in flight,
+ // wait for it to finish flushing to disk.
+ QueuedWork.waitToFinish();
+
// make filenames for the prefGroups
String[] prefGroups = mPrefGroups;
final int N = prefGroups.length;
@@ -123,4 +128,3 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen
}
}
}
-
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 1830f6c..21d2ab0 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -30,6 +30,7 @@ public abstract class SQLiteClosable {
public void acquireReference() {
synchronized(mLock) {
+ checkRefCount();
if (mReferenceCount <= 0) {
throw new IllegalStateException(
"attempt to re-open an already-closed object: " + getObjInfo());
@@ -40,6 +41,7 @@ public abstract class SQLiteClosable {
public void releaseReference() {
synchronized(mLock) {
+ checkRefCount();
mReferenceCount--;
if (mReferenceCount == 0) {
onAllReferencesReleased();
@@ -49,6 +51,7 @@ public abstract class SQLiteClosable {
public void releaseReferenceFromContainer() {
synchronized(mLock) {
+ checkRefCount();
mReferenceCount--;
if (mReferenceCount == 0) {
onAllReferencesReleasedFromContainer();
@@ -63,8 +66,7 @@ public abstract class SQLiteClosable {
if (this instanceof SQLiteDatabase) {
buff.append("database = ");
buff.append(((SQLiteDatabase)this).getPath());
- } else if (this instanceof SQLiteProgram || this instanceof SQLiteStatement ||
- this instanceof SQLiteQuery) {
+ } else if (this instanceof SQLiteProgram) {
buff.append("mSql = ");
buff.append(((SQLiteProgram)this).mSql);
} else if (this instanceof CursorWindow) {
@@ -74,4 +76,13 @@ public abstract class SQLiteClosable {
buff.append(") ");
return buff.toString();
}
+
+ private void checkRefCount() {
+ if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
+ if (mReferenceCount > 1000) {
+ throw new IllegalStateException("refcount: " + mReferenceCount + ", " +
+ getObjInfo());
+ }
+ }
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index aa0a57d..0b5a4df 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -92,9 +92,6 @@ import android.util.Log;
// Note that native_finalize() checks to make sure that nStatement is
// non-null before destroying it.
if (nStatement != 0) {
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")");
- }
mDatabase.finalizeStatementLater(nStatement);
nStatement = 0;
}
@@ -109,16 +106,10 @@ import android.util.Log;
return false;
}
mInUse = true;
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.v(TAG, "Acquired DbObj (id#" + nStatement + ") from DB cache");
- }
return true;
}
/* package */ synchronized void release() {
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.v(TAG, "Released DbObj (id#" + nStatement + ") back to DB cache");
- }
mInUse = false;
}
@@ -144,6 +135,22 @@ import android.util.Log;
}
}
+ @Override public String toString() {
+ synchronized(this) {
+ StringBuilder buff = new StringBuilder();
+ buff.append(" nStatement=");
+ buff.append(nStatement);
+ buff.append(", db=");
+ buff.append(mDatabase.getPath());
+ buff.append(", db_connectionNum=");
+ buff.append(mDatabase.mConnectionNum);
+ buff.append(", sql=");
+ int len = mSqlStmt.length();
+ buff.append(mSqlStmt.substring(0, (len > 100) ? 100 : len));
+ return buff.toString();
+ }
+ }
+
/**
* Compiles SQL into a SQLite program.
*
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 0137ea6..6937da0 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1066,7 +1066,7 @@ public class SQLiteDatabase extends SQLiteClosable {
closePendingStatements();
releaseCustomFunctions();
// close this database instance - regardless of its reference count value
- dbclose();
+ closeDatabase();
if (mConnectionPool != null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
assert mConnectionPool != null;
@@ -1075,7 +1075,7 @@ public class SQLiteDatabase extends SQLiteClosable {
mConnectionPool.close();
}
} finally {
- unlock();
+ unlock();
}
}
@@ -1100,6 +1100,47 @@ public class SQLiteDatabase extends SQLiteClosable {
}
/**
+ * package level access for testing purposes
+ */
+ /* package */ void closeDatabase() throws SQLiteException {
+ try {
+ dbclose();
+ } catch (SQLiteUnfinalizedObjectsException e) {
+ String msg = e.getMessage();
+ String[] tokens = msg.split(",", 2);
+ int stmtId = Integer.parseInt(tokens[0]);
+ // get extra info about this statement, if it is still to be released by closeClosable()
+ Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
+ boolean found = false;
+ while (iter.hasNext()) {
+ Map.Entry<SQLiteClosable, Object> entry = iter.next();
+ SQLiteClosable program = entry.getKey();
+ if (program != null && program instanceof SQLiteProgram) {
+ SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
+ if (compiledSql.nStatement == stmtId) {
+ msg = compiledSql.toString();
+ found = true;
+ }
+ }
+ }
+ if (!found) {
+ // the statement is already released by closeClosable(). is it waiting to be
+ // finalized?
+ if (mClosedStatementIds.contains(stmtId)) {
+ Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
+ closePendingStatements();
+ // try to close the database again
+ closeDatabase();
+ }
+ } else {
+ // the statement is not yet closed. most probably programming error in the app.
+ Log.w(TAG, "dbclose failed due to un-close()d SQL statements: " + msg);
+ throw e;
+ }
+ }
+ }
+
+ /**
* Native call to close the database.
*/
private native void dbclose();
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 9b7d823..bcb0c48 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -52,7 +52,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
/**
* the SQLiteCompiledSql object for the given sql statement.
*/
- private SQLiteCompiledSql mCompiledSql;
+ /* package */ SQLiteCompiledSql mCompiledSql;
/**
* SQLiteCompiledSql statement id is populated with the corresponding object from the above
@@ -130,10 +130,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
// make sure it is acquired by me.
mCompiledSql.acquire();
mDatabase.addToCompiledQueries(mSql, mCompiledSql);
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
- ") for sql: " + mSql);
- }
} else {
// it is already in compiled-sql cache.
// try to acquire the object.
@@ -144,12 +140,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
// CompiledSql object. create a new one.
// finalize it when I am done with it in "this" object.
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
- mCompiledSql.nStatement +
- ") because the previously created DbObj (id#" + last +
- ") was not released for sql:" + mSql);
- }
// since it is not in the cache, no need to acquire() it.
}
}
diff --git a/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
new file mode 100644
index 0000000..bcf95e2
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 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.database.sqlite;
+
+/**
+ * Thrown if the database can't be closed because of some un-closed
+ * Cursor or SQLiteStatement objects. Could happen when a thread is trying to close
+ * the database while another thread still hasn't closed a Cursor on that database.
+ * @hide
+ */
+public class SQLiteUnfinalizedObjectsException extends SQLiteException {
+ public SQLiteUnfinalizedObjectsException() {}
+
+ public SQLiteUnfinalizedObjectsException(String error) {
+ super(error);
+ }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9fe6e01..a857e58 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -216,6 +216,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract Map<Integer, ? extends Sensor> getSensorStats();
/**
+ * Returns a mapping containing active process data.
+ */
+ public abstract SparseArray<? extends Pid> getPidStats();
+
+ /**
* Returns a mapping containing process statistics.
*
* @return a Map from Strings to Uid.Proc objects.
@@ -286,6 +291,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract Timer getSensorTime();
}
+ public class Pid {
+ public long mWakeSum;
+ public long mWakeStart;
+ }
+
/**
* The statistics associated with a particular process.
*/
@@ -521,6 +531,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract HistoryItem getHistory();
/**
+ * Return the base time offset for the battery history.
+ */
+ public abstract long getHistoryBaseTime();
+
+ /**
* Returns the number of times the device has been started.
*/
public abstract int getStartCount();
@@ -1673,6 +1688,7 @@ public abstract class BatteryStats implements Parcelable {
HistoryItem rec = getHistory();
if (rec != null) {
pw.println("Battery History:");
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
int oldState = 0;
int oldStatus = -1;
int oldHealth = -1;
@@ -1681,7 +1697,7 @@ public abstract class BatteryStats implements Parcelable {
int oldVolt = -1;
while (rec != null) {
pw.print(" ");
- pw.print(rec.time);
+ TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
pw.print(" ");
if (rec.cmd == HistoryItem.CMD_START) {
pw.println(" START");
@@ -1784,6 +1800,35 @@ public abstract class BatteryStats implements Parcelable {
oldState = rec.states;
rec = rec.next;
}
+ pw.println("");
+ }
+
+ SparseArray<? extends Uid> uidStats = getUidStats();
+ final int NU = uidStats.size();
+ boolean didPid = false;
+ long nowRealtime = SystemClock.elapsedRealtime();
+ StringBuilder sb = new StringBuilder(64);
+ for (int i=0; i<NU; i++) {
+ Uid uid = uidStats.valueAt(i);
+ SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+ if (pids != null) {
+ for (int j=0; j<pids.size(); j++) {
+ Uid.Pid pid = pids.valueAt(j);
+ if (!didPid) {
+ pw.println("Per-PID Stats:");
+ didPid = true;
+ }
+ long time = pid.mWakeSum + (pid.mWakeStart != 0
+ ? (nowRealtime - pid.mWakeStart) : 0);
+ pw.print(" PID "); pw.print(pids.keyAt(j));
+ pw.print(" wake time: ");
+ TimeUtils.formatDuration(time, pw);
+ pw.println("");
+ }
+ }
+ }
+ if (didPid) {
+ pw.println("");
}
pw.println("Statistics since last charge:");
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 3fe14f9..0408673 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -34,7 +34,7 @@ public class ArrowKeyMovementMethod implements MovementMethod {
* An optional controller for the cursor.
* Use {@link #setCursorController(CursorController)} to set this field.
*/
- protected CursorController mCursorController;
+ private CursorController mCursorController;
private boolean isCap(Spannable buffer) {
return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
@@ -302,7 +302,17 @@ public class ArrowKeyMovementMethod implements MovementMethod {
/**
* Defines the cursor controller.
*
- * When set, this object can be used to handle events, that can be translated in cursor updates.
+ * When set, this object can be used to handle touch events, that can be translated into cursor
+ * updates.
+ *
+ * {@link MotionEvent#ACTION_MOVE} events will call back the
+ * {@link CursorController#updatePosition(int, int)} controller's method, passing the current
+ * finger coordinates (offset by {@link CursorController#getOffsetX()} and
+ * {@link CursorController#getOffsetY()}) as parameters.
+ *
+ * When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
+ * {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
+ *
* @param cursorController A cursor controller implementation
*/
public void setCursorController(CursorController cursorController) {
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index b01a71d..60ca384 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -132,75 +132,76 @@ public class TimeUtils {
return ZoneInfoDB.getVersion();
}
+ /** @hide Field length that can hold 999 days of time */
+ public static final int HUNDRED_DAY_FIELD_LEN = 19;
+
private static final int SECONDS_PER_MINUTE = 60;
private static final int SECONDS_PER_HOUR = 60 * 60;
private static final int SECONDS_PER_DAY = 24 * 60 * 60;
- /** @hide Just for debugging; not internationalized. */
- public static void formatDuration(long duration, StringBuilder builder) {
- if (duration == 0) {
- builder.append("0");
- return;
- }
- if (duration > 0) {
- builder.append("+");
- } else {
- builder.append("-");
- duration = -duration;
- }
-
- int millis = (int)(duration%1000);
- int seconds = (int) Math.floor(duration / 1000);
- int days = 0, hours = 0, minutes = 0;
-
- if (seconds > SECONDS_PER_DAY) {
- days = seconds / SECONDS_PER_DAY;
- seconds -= days * SECONDS_PER_DAY;
- }
- if (seconds > SECONDS_PER_HOUR) {
- hours = seconds / SECONDS_PER_HOUR;
- seconds -= hours * SECONDS_PER_HOUR;
- }
- if (seconds > SECONDS_PER_MINUTE) {
- minutes = seconds / SECONDS_PER_MINUTE;
- seconds -= minutes * SECONDS_PER_MINUTE;
- }
-
- boolean doall = false;
- if (days > 0) {
- builder.append(days);
- builder.append('d');
- doall = true;
+ private static final Object sFormatSync = new Object();
+ private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
+
+ static private int accumField(int amt, int suffix, boolean always, int zeropad) {
+ if (amt > 99 || (always && zeropad >= 3)) {
+ return 3+suffix;
}
- if (doall || hours > 0) {
- builder.append(hours);
- builder.append('h');
- doall = true;
+ if (amt > 9 || (always && zeropad >= 2)) {
+ return 2+suffix;
}
- if (doall || minutes > 0) {
- builder.append(minutes);
- builder.append('m');
- doall = true;
+ if (always || amt > 0) {
+ return 1+suffix;
}
- if (doall || seconds > 0) {
- builder.append(seconds);
- builder.append('s');
- doall = true;
+ return 0;
+ }
+
+ static private int printField(char[] formatStr, int amt, char suffix, int pos,
+ boolean always, int zeropad) {
+ if (always || amt > 0) {
+ if ((always && zeropad >= 3) || amt > 99) {
+ int dig = amt/100;
+ formatStr[pos] = (char)(dig + '0');
+ pos++;
+ always = true;
+ amt -= (dig*100);
+ }
+ if ((always && zeropad >= 2) || amt > 9) {
+ int dig = amt/10;
+ formatStr[pos] = (char)(dig + '0');
+ pos++;
+ always = true;
+ amt -= (dig*10);
+ }
+ formatStr[pos] = (char)(amt + '0');
+ pos++;
+ formatStr[pos] = suffix;
+ pos++;
}
- builder.append(millis);
- builder.append("ms");
+ return pos;
}
-
- /** @hide Just for debugging; not internationalized. */
- public static void formatDuration(long duration, PrintWriter pw) {
+
+ private static int formatDurationLocked(long duration, int fieldLen) {
+ if (sFormatStr.length < fieldLen) {
+ sFormatStr = new char[fieldLen];
+ }
+
+ char[] formatStr = sFormatStr;
+
if (duration == 0) {
- pw.print("0");
- return;
+ int pos = 0;
+ fieldLen -= 1;
+ while (pos < fieldLen) {
+ formatStr[pos] = ' ';
+ }
+ formatStr[pos] = '0';
+ return pos+1;
}
+
+ char prefix;
if (duration > 0) {
- pw.print("+");
+ prefix = '+';
} else {
- pw.print("-");
+ prefix = '-';
duration = -duration;
}
@@ -221,38 +222,62 @@ public class TimeUtils {
seconds -= minutes * SECONDS_PER_MINUTE;
}
- boolean doall = false;
- if (days > 0) {
- pw.print(days);
- pw.print('d');
- doall = true;
- }
- if (doall || hours > 0) {
- pw.print(hours);
- pw.print('h');
- doall = true;
- }
- if (doall || minutes > 0) {
- pw.print(minutes);
- pw.print('m');
- doall = true;
+ int pos = 0;
+
+ if (fieldLen != 0) {
+ int myLen = accumField(days, 1, false, 0);
+ myLen += accumField(hours, 1, myLen > 0, 2);
+ myLen += accumField(minutes, 1, myLen > 0, 2);
+ myLen += accumField(seconds, 1, myLen > 0, 2);
+ myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
+ while (myLen < fieldLen) {
+ formatStr[pos] = ' ';
+ pos++;
+ myLen++;
+ }
}
- if (doall || seconds > 0) {
- pw.print(seconds);
- pw.print('s');
- doall = true;
+
+ formatStr[pos] = prefix;
+ pos++;
+
+ int start = pos;
+ boolean zeropad = fieldLen != 0;
+ pos = printField(formatStr, days, 'd', pos, false, 0);
+ pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
+ pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
+ pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
+ pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
+ formatStr[pos] = 's';
+ return pos + 1;
+ }
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, StringBuilder builder) {
+ synchronized (sFormatSync) {
+ int len = formatDurationLocked(duration, 0);
+ builder.append(sFormatStr, 0, len);
}
- pw.print(millis);
- pw.print("ms");
}
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
+ synchronized (sFormatSync) {
+ int len = formatDurationLocked(duration, fieldLen);
+ pw.print(new String(sFormatStr, 0, len));
+ }
+ }
/** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, PrintWriter pw) {
+ formatDuration(duration, pw, 0);
+ }
+
+ /** @hide Just for debugging; not internationalized. */
public static void formatDuration(long time, long now, PrintWriter pw) {
if (time == 0) {
pw.print("--");
return;
}
- formatDuration(time-now, pw);
+ formatDuration(time-now, pw, 0);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7355353..f8d79e5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7298,9 +7298,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
}
- if (Config.DEBUG && ViewDebug.profileDrawing) {
- EventLog.writeEvent(60002, hashCode());
- }
int width = mRight - mLeft;
int height = mBottom - mTop;
@@ -7738,7 +7735,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
- if (solidColor == 0) {
+ // TODO: Temporarily disable fading edges with hardware acceleration
+ if (solidColor == 0 && !canvas.isHardwareAccelerated()) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
@@ -7946,6 +7944,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @param r Right position, relative to parent
* @param b Bottom position, relative to parent
*/
+ @SuppressWarnings({"unchecked"})
public final void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
@@ -10129,11 +10128,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
final RectF mTmpTransformRect = new RectF();
/**
- * Temporary for use in computing invalidation areas with transformed views
- */
- final float[] mTmpTransformBounds = new float[8];
-
- /**
* Temporary list for use in collecting focusable descendents of a view.
*/
final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 22cc3a8..b1d5272 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -119,24 +119,21 @@ public class ViewDebug {
*
* @hide
*/
- @Debug.DebugProperty
- public static boolean profileDrawing = false;
+ public static final boolean DEBUG_PROFILE_DRAWING = false;
/**
* Profiles layout times in the events log.
*
* @hide
*/
- @Debug.DebugProperty
- public static boolean profileLayout = false;
+ public static final boolean DEBUG_PROFILE_LAYOUT = false;
/**
* Profiles real fps (times between draws) and displays the result.
*
* @hide
*/
- @Debug.DebugProperty
- public static boolean showFps = false;
+ public static final boolean DEBUG_SHOW_FPS = false;
/**
* <p>Enables or disables views consistency check. Even when this property is enabled,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index dee6e73..363ccd6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -32,8 +32,6 @@ import android.graphics.Region;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.Config;
-import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
@@ -2020,9 +2018,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
cachePaint.setAlpha(255);
mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
}
- if (Config.DEBUG && ViewDebug.profileDrawing) {
- EventLog.writeEvent(60003, hashCode());
- }
canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5d4ac37..e5fc859 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -946,8 +946,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mSurfaceHolder.mSurfaceLock.unlock();
}
}
-
- if (hwIntialized) {
+
+ if (hwIntialized || (windowShouldResize && mHwRenderer != null)) {
mHwRenderer.setup(mWidth, mHeight);
}
@@ -1008,7 +1008,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
TAG, "Laying out " + host + " to (" +
host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
long startTime = 0L;
- if (ViewDebug.profileLayout) {
+ if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
startTime = SystemClock.elapsedRealtime();
}
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
@@ -1021,7 +1021,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
}
- if (ViewDebug.profileLayout) {
+ if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
}
@@ -1321,7 +1321,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
//canvas.drawARGB(255, 255, 0, 0);
}
- if (ViewDebug.profileDrawing) {
+ if (ViewDebug.DEBUG_PROFILE_DRAWING) {
startTime = SystemClock.elapsedRealtime();
}
@@ -1364,7 +1364,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
- if (SHOW_FPS || ViewDebug.showFps) {
+ if (SHOW_FPS || ViewDebug.DEBUG_SHOW_FPS) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
@@ -1372,7 +1372,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
sDrawTime = now;
}
- if (ViewDebug.profileDrawing) {
+ if (ViewDebug.DEBUG_PROFILE_DRAWING) {
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 0ed5fc5..d81d7f2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -211,6 +211,7 @@ public class WebSettings {
private boolean mNavDump = false;
private boolean mSupportZoom = true;
private boolean mBuiltInZoomControls = false;
+ private boolean mDisplayZoomControls = true;
private boolean mAllowFileAccess = true;
private boolean mLoadWithOverviewMode = false;
private boolean mEnableSmoothTransition = false;
@@ -508,6 +509,26 @@ public class WebSettings {
}
/**
+ * Sets whether the on screen zoom buttons are used.
+ * A combination of built in zoom controls enabled
+ * and on screen zoom controls disabled allows for pinch to zoom
+ * to work without the on screen controls
+ * @hide
+ */
+ public void setDisplayZoomControls(boolean enabled) {
+ mDisplayZoomControls = enabled;
+ mWebView.updateMultiTouchSupport(mContext);
+ }
+
+ /**
+ * Returns true if the on screen zoom buttons are being used.
+ * @hide
+ */
+ public boolean getDisplayZoomControls() {
+ return mDisplayZoomControls;
+ }
+
+ /**
* Enable or disable file access within WebView. File access is enabled by
* default.
*/
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 33ebcf5..1323217 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -856,7 +856,8 @@ class ZoomManager {
private ZoomControlBase getCurrentZoomControl() {
if (mWebView.getSettings() != null && mWebView.getSettings().supportZoom()) {
if (mWebView.getSettings().getBuiltInZoomControls()) {
- if (mEmbeddedZoomControl == null) {
+ if ((mEmbeddedZoomControl == null)
+ && mWebView.getSettings().getDisplayZoomControls()) {
mEmbeddedZoomControl = new ZoomControlEmbedded(this, mWebView);
}
return mEmbeddedZoomControl;
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 0636d72..830e899 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -31,6 +31,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -128,6 +129,13 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
boolean mShouldLoop = true;
/**
+ * The width and height of some child, used as a size reference in-case our
+ * dimensions are unspecified by the parent.
+ */
+ int mReferenceChildWidth = -1;
+ int mReferenceChildHeight = -1;
+
+ /**
* TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
*/
Animation mInAnimation;
@@ -414,7 +422,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
FrameLayout fl = new FrameLayout(mContext);
fl.addView(newView);
mActiveViews[index] = fl;
- addViewInLayout(fl, -1, createOrReuseLayoutParams(fl));
+ addChild(fl);
applyTransformForChildAtIndex(fl, newRelativeIndex);
animateViewForTransition(-1, newRelativeIndex, fl);
}
@@ -451,6 +459,64 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
}
+ private void addChild(View child) {
+ addViewInLayout(child, -1, createOrReuseLayoutParams(child));
+
+ // This code is used to obtain a reference width and height of a child in case we need
+ // to decide our own size. TODO: Do we want to update the size of the child that we're
+ // using for reference size? If so, when?
+ if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) {
+ int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ child.measure(measureSpec, measureSpec);
+ mReferenceChildWidth = child.getMeasuredWidth();
+ mReferenceChildHeight = child.getMeasuredHeight();
+ }
+ }
+
+ private void measureChildren() {
+ final int count = getChildCount();
+ final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+ final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+ // We need to deal with the case where our parent hasn't told us how
+ // big we should be. In this case we try to use the desired size of the first
+ // child added.
+ if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
+ mPaddingBottom : 0;
+ } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+ heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop +
+ mPaddingBottom, heightSpecSize) : 0;
+ }
+
+ if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+ widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+ mPaddingRight : 0;
+ } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+ widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+ mPaddingRight, widthSpecSize) : 0;
+ }
+
+ setMeasuredDimension(widthSpecSize, heightSpecSize);
+ measureChildren();
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
boolean dataChanged = mDataChanged;
@@ -472,8 +538,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int childRight = mPaddingLeft + child.getMeasuredWidth();
int childBottom = mPaddingTop + child.getMeasuredHeight();
- child.layout(mPaddingLeft, mPaddingTop,
- childRight, childBottom);
+ child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
}
mDataChanged = false;
}
@@ -538,31 +603,6 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
setDisplayedChild(mWhichChild);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int count = getChildCount();
-
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
-
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- lp.width = widthSpecSize - mPaddingLeft - mPaddingRight;
- lp.height = heightSpecSize - mPaddingTop - mPaddingBottom;
-
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
- MeasureSpec.EXACTLY);
- int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
- MeasureSpec.EXACTLY);
-
- child.measure(childWidthMeasureSpec, childheightMeasureSpec);
- }
- setMeasuredDimension(widthSpecSize, heightSpecSize);
- }
-
/**
* Shows only the specified child. The other displays Views exit the screen
* with the {@link #getOutAnimation() out animation} and the specified child
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 50fbb6b..5598c65 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -247,6 +247,7 @@ public class QuickContactBadge extends ImageView implements OnClickListener {
trigger = true;
createUri = Uri.fromParts("tel", (String)cookie, null);
+ //$FALL-THROUGH$
case TOKEN_PHONE_LOOKUP: {
if (cursor != null && cursor.moveToFirst()) {
long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX);
@@ -260,12 +261,14 @@ public class QuickContactBadge extends ImageView implements OnClickListener {
trigger = true;
createUri = Uri.fromParts("mailto", (String)cookie, null);
+ //$FALL-THROUGH$
case TOKEN_EMAIL_LOOKUP: {
if (cursor != null && cursor.moveToFirst()) {
long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
lookupUri = Contacts.getLookupUri(contactId, lookupKey);
}
+ break;
}
case TOKEN_CONTACT_LOOKUP_AND_TRIGGER: {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 9816b39..9025b83 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -16,11 +16,11 @@
package android.widget;
-import java.util.WeakHashMap;
-
import android.animation.PropertyAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -28,6 +28,7 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.TableMaskFilter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -35,6 +36,8 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup.LayoutParams;
import android.view.animation.LinearInterpolator;
import android.widget.RemoteViews.RemoteView;
@@ -53,6 +56,19 @@ public class StackView extends AdapterViewAnimator {
private final int MINIMUM_ANIMATION_DURATION = 50;
/**
+ * Parameters effecting the perspective visuals
+ */
+ private static float PERSPECTIVE_SHIFT_FACTOR = 0.12f;
+ private static float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+
+ /**
+ * Represent the two possible stack modes, one where items slide up, and the other
+ * where items slide down. The perspective is also inverted between these two modes.
+ */
+ private static final int ITEMS_SLIDE_UP = 0;
+ private static final int ITEMS_SLIDE_DOWN = 1;
+
+ /**
* These specify the different gesture states
*/
private static final int GESTURE_NONE = 0;
@@ -66,8 +82,6 @@ public class StackView extends AdapterViewAnimator {
private static final float SWIPE_THRESHOLD_RATIO = 0.35f;
private static final float SLIDE_UP_RATIO = 0.7f;
- private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
-
/**
* Sentinel value for no current active pointer.
* Used by {@link #mActivePointerId}.
@@ -75,6 +89,12 @@ public class StackView extends AdapterViewAnimator {
private static final int INVALID_POINTER = -1;
/**
+ * Number of active views in the stack. One fewer view is actually visible, as one is hidden.
+ */
+ private static final int NUM_ACTIVE_VIEWS = 5;
+
+
+ /**
* These variables are all related to the current state of touch interaction
* with the stack
*/
@@ -95,6 +115,7 @@ public class StackView extends AdapterViewAnimator {
private boolean mFirstLayoutHappened = false;
private ViewGroup mAncestorContainingAllChildren = null;
private int mAncestorHeight = 0;
+ private int mStackMode;
public StackView(Context context) {
super(context);
@@ -107,7 +128,7 @@ public class StackView extends AdapterViewAnimator {
}
private void initStackView() {
- configureViewAnimator(4, 2, false);
+ configureViewAnimator(NUM_ACTIVE_VIEWS, NUM_ACTIVE_VIEWS - 2, false);
setStaticTransformationsEnabled(true);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
@@ -125,6 +146,11 @@ public class StackView extends AdapterViewAnimator {
setClipChildren(false);
setClipToPadding(false);
+ // This sets the form of the StackView, which is currently to have the perspective-shifted
+ // views above the active view, and have items slide down when sliding out. The opposite is
+ // available by using ITEMS_SLIDE_UP.
+ mStackMode = ITEMS_SLIDE_DOWN;
+
// This is a flag to indicate the the stack is loading for the first time
mWhichChild = -1;
}
@@ -140,7 +166,7 @@ public class StackView extends AdapterViewAnimator {
}
view.setVisibility(VISIBLE);
- PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ PropertyAnimator<Float> fadeIn = new PropertyAnimator<Float>(DEFAULT_ANIMATION_DURATION,
view, "alpha", view.getAlpha(), 1.0f);
fadeIn.start();
} else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
@@ -148,49 +174,32 @@ public class StackView extends AdapterViewAnimator {
view.setVisibility(VISIBLE);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
- int largestDuration =
- Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
-
- int duration = largestDuration;
- if (mYVelocity != 0) {
- duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
- }
-
- duration = Math.min(duration, largestDuration);
- duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+ int duration = Math.round(mStackSlider.getDurationForNeutralPosition(mYVelocity));
StackSlider animationSlider = new StackSlider(mStackSlider);
- PropertyAnimator slideInY = new PropertyAnimator(duration, animationSlider,
- "YProgress", mStackSlider.getYProgress(), 0);
- slideInY.setInterpolator(new LinearInterpolator());
- slideInY.start();
- PropertyAnimator slideInX = new PropertyAnimator(duration, animationSlider,
- "XProgress", mStackSlider.getXProgress(), 0);
- slideInX.setInterpolator(new LinearInterpolator());
- slideInX.start();
+ PropertyValuesHolder<Float> slideInY =
+ new PropertyValuesHolder<Float>("YProgress", 0.0f);
+ PropertyValuesHolder<Float> slideInX =
+ new PropertyValuesHolder<Float>("XProgress", 0.0f);
+ PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
+ slideInX, slideInY);
+ pa.setInterpolator(new LinearInterpolator());
+ pa.start();
} else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
// Slide item out
LayoutParams lp = (LayoutParams) view.getLayoutParams();
- int largestDuration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
- DEFAULT_ANIMATION_DURATION);
- int duration = largestDuration;
- if (mYVelocity != 0) {
- duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
- }
-
- duration = Math.min(duration, largestDuration);
- duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+ int duration = Math.round(mStackSlider.getDurationForOffscreenPosition(mYVelocity));
StackSlider animationSlider = new StackSlider(mStackSlider);
- PropertyAnimator slideOutY = new PropertyAnimator(duration, animationSlider,
- "YProgress", mStackSlider.getYProgress(), 1);
- slideOutY.setInterpolator(new LinearInterpolator());
- slideOutY.start();
- PropertyAnimator slideOutX = new PropertyAnimator(duration, animationSlider,
- "XProgress", mStackSlider.getXProgress(), 0);
- slideOutX.setInterpolator(new LinearInterpolator());
- slideOutX.start();
+ PropertyValuesHolder<Float> slideOutY =
+ new PropertyValuesHolder<Float>("YProgress", 1.0f);
+ PropertyValuesHolder<Float> slideOutX =
+ new PropertyValuesHolder<Float>("XProgress", 0.0f);
+ PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
+ slideOutX, slideOutY);
+ pa.setInterpolator(new LinearInterpolator());
+ pa.start();
} else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
// Make sure this view that is "waiting in the wings" is invisible
view.setAlpha(0.0f);
@@ -199,28 +208,41 @@ public class StackView extends AdapterViewAnimator {
lp.setVerticalOffset(-mViewHeight);
} else if (toIndex == -1) {
// Fade item out
- PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
- view, "alpha", view.getAlpha(), 0);
+ PropertyAnimator<Float> fadeOut = new PropertyAnimator<Float>
+ (DEFAULT_ANIMATION_DURATION, view, "alpha", view.getAlpha(), 0.0f);
fadeOut.start();
}
+
+ // Implement the faked perspective
+ if (toIndex != -1) {
+ float maxPerpectiveShift = mViewHeight * PERSPECTIVE_SHIFT_FACTOR;
+ int index = toIndex;
+
+ if (toIndex == mNumActiveViews -1) index--;
+
+ float r = (index * 1.0f) / (mNumActiveViews - 2);
+
+ float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
+ PropertyValuesHolder<Float> scaleX = new PropertyValuesHolder<Float>("scaleX", scale);
+ PropertyValuesHolder<Float> scaleY = new PropertyValuesHolder<Float>("scaleY", scale);
+
+ r = (float) Math.pow(r, 2);
+
+ int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+ float transY = -stackDirection * r * maxPerpectiveShift +
+ stackDirection * (1 - scale) * (mViewHeight / 2.0f);
+
+ PropertyValuesHolder<Float> translationY =
+ new PropertyValuesHolder<Float>("translationY", transY);
+ PropertyAnimator pa = new PropertyAnimator(100, view, scaleX, scaleY, translationY);
+ pa.start();
+ }
}
/**
* Apply any necessary tranforms for the child that is being added.
*/
void applyTransformForChildAtIndex(View child, int relativeIndex) {
- if (!mRotations.containsKey(child)) {
- float rotation = (float) (Math.random()*26 - 13);
- mRotations.put(child, rotation);
- child.setRotation(rotation);
- }
-
- // Child has been removed
- if (relativeIndex == -1) {
- if (mRotations.containsKey(child)) {
- mRotations.remove(child);
- }
- }
}
@Override
@@ -248,8 +270,8 @@ public class StackView extends AdapterViewAnimator {
private void onLayout() {
if (!mFirstLayoutHappened) {
- mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
- mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+ mViewHeight = Math.round(SLIDE_UP_RATIO * getMeasuredHeight());
+ mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mViewHeight);
mFirstLayoutHappened = true;
}
}
@@ -299,8 +321,14 @@ public class StackView extends AdapterViewAnimator {
cancelLongPress();
requestDisallowInterceptTouchEvent(true);
- int activeIndex = swipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
- : mNumActiveViews - 2;
+ int activeIndex;
+ if (mStackMode == ITEMS_SLIDE_UP) {
+ activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ?
+ mNumActiveViews - 1 : mNumActiveViews - 2;
+ } else {
+ activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ?
+ mNumActiveViews - 2 : mNumActiveViews - 1;
+ }
if (mAdapter == null) return;
@@ -317,6 +345,8 @@ public class StackView extends AdapterViewAnimator {
if (v == null) return;
mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
+ mHighlight.setRotation(v.getRotation());
+ mHighlight.setTranslationY(v.getTranslationY());
mHighlight.bringToFront();
v.bringToFront();
mStackSlider.setView(v);
@@ -352,14 +382,16 @@ public class StackView extends AdapterViewAnimator {
case MotionEvent.ACTION_MOVE: {
beginGestureIfNeeded(deltaY);
- float rx = deltaX/(mViewHeight*1.0f);
+ float rx = deltaX / (mViewHeight * 1.0f);
if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
- float r = (deltaY-mTouchSlop*1.0f)/mViewHeight*1.0f;
+ float r = (deltaY - mTouchSlop * 1.0f) / mViewHeight * 1.0f;
+ if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
mStackSlider.setYProgress(1 - r);
mStackSlider.setXProgress(rx);
return true;
} else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
- float r = -(deltaY + mTouchSlop*1.0f)/mViewHeight*1.0f;
+ float r = -(deltaY + mTouchSlop * 1.0f) / mViewHeight * 1.0f;
+ if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
mStackSlider.setYProgress(r);
mStackSlider.setXProgress(rx);
return true;
@@ -447,41 +479,59 @@ public class StackView extends AdapterViewAnimator {
if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN
&& mStackSlider.mMode == StackSlider.NORMAL_MODE) {
// Swipe threshold exceeded, swipe down
- showNext();
+ if (mStackMode == ITEMS_SLIDE_UP) {
+ showNext();
+ } else {
+ showPrevious();
+ }
mHighlight.bringToFront();
} else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP
&& mStackSlider.mMode == StackSlider.NORMAL_MODE) {
// Swipe threshold exceeded, swipe up
- showPrevious();
+ if (mStackMode == ITEMS_SLIDE_UP) {
+ showPrevious();
+ } else {
+ showNext();
+ }
+
mHighlight.bringToFront();
- } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP ) {
// Didn't swipe up far enough, snap back down
- int duration =
- Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);
+ int duration;
+ float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 1 : 0;
+ if (mStackMode == ITEMS_SLIDE_UP || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+ duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+ } else {
+ duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+ }
StackSlider animationSlider = new StackSlider(mStackSlider);
- PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
- "YProgress", mStackSlider.getYProgress(), 0);
- snapBackY.setInterpolator(new LinearInterpolator());
- snapBackY.start();
- PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
- "XProgress", mStackSlider.getXProgress(), 0);
- snapBackX.setInterpolator(new LinearInterpolator());
- snapBackX.start();
+ PropertyValuesHolder<Float> snapBackY =
+ new PropertyValuesHolder<Float>("YProgress", finalYProgress);
+ PropertyValuesHolder<Float> snapBackX =
+ new PropertyValuesHolder<Float>("XProgress", 0.0f);
+ PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
+ snapBackX, snapBackY);
+ pa.setInterpolator(new LinearInterpolator());
+ pa.start();
} else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
// Didn't swipe down far enough, snap back up
- int duration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
- DEFAULT_ANIMATION_DURATION);
+ float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 0 : 1;
+ int duration;
+ if (mStackMode == ITEMS_SLIDE_DOWN || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+ duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+ } else {
+ duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+ }
StackSlider animationSlider = new StackSlider(mStackSlider);
- PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
- "YProgress", mStackSlider.getYProgress(), 1);
- snapBackY.setInterpolator(new LinearInterpolator());
- snapBackY.start();
- PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
- "XProgress", mStackSlider.getXProgress(), 0);
- snapBackX.setInterpolator(new LinearInterpolator());
- snapBackX.start();
+ PropertyValuesHolder<Float> snapBackY =
+ new PropertyValuesHolder<Float>("YProgress", finalYProgress);
+ PropertyValuesHolder<Float> snapBackX =
+ new PropertyValuesHolder<Float>("XProgress", 0.0f);
+ PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
+ snapBackX, snapBackY);
+ pa.start();
}
mActivePointerId = INVALID_POINTER;
@@ -510,22 +560,22 @@ public class StackView extends AdapterViewAnimator {
}
private float cubic(float r) {
- return (float) (Math.pow(2*r-1, 3) + 1)/2.0f;
+ return (float) (Math.pow(2 * r - 1, 3) + 1) / 2.0f;
}
private float highlightAlphaInterpolator(float r) {
float pivot = 0.4f;
if (r < pivot) {
- return 0.85f*cubic(r/pivot);
+ return 0.85f * cubic(r / pivot);
} else {
- return 0.85f*cubic(1 - (r-pivot)/(1-pivot));
+ return 0.85f * cubic(1 - (r - pivot) / (1 - pivot));
}
}
private float viewAlphaInterpolator(float r) {
float pivot = 0.3f;
if (r > pivot) {
- return (r - pivot)/(1 - pivot);
+ return (r - pivot) / (1 - pivot);
} else {
return 0;
}
@@ -536,7 +586,7 @@ public class StackView extends AdapterViewAnimator {
if (r < pivot) {
return 0;
} else {
- return (r-pivot)/(1-pivot);
+ return (r - pivot) / (1 - pivot);
}
}
@@ -553,13 +603,15 @@ public class StackView extends AdapterViewAnimator {
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
+ int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+
switch (mMode) {
case NORMAL_MODE:
- viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
- highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
+ viewLp.setVerticalOffset(Math.round(-r * stackDirection * mViewHeight));
+ highlightLp.setVerticalOffset(Math.round(-r * stackDirection * mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
- float alpha = viewAlphaInterpolator(1-r);
+ float alpha = viewAlphaInterpolator(1 - r);
// We make sure that views which can't be seen (have 0 alpha) are also invisible
// so that they don't interfere with click events.
@@ -571,19 +623,19 @@ public class StackView extends AdapterViewAnimator {
}
mView.setAlpha(alpha);
- mView.setRotationX(90.0f*rotationInterpolator(r));
- mHighlight.setRotationX(90.0f*rotationInterpolator(r));
+ mView.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
+ mHighlight.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
break;
case BEGINNING_OF_STACK_MODE:
- r = r*0.2f;
- viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
- highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
+ r = r * 0.2f;
+ viewLp.setVerticalOffset(Math.round(-stackDirection * r * mViewHeight));
+ highlightLp.setVerticalOffset(Math.round(-stackDirection * r * mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
break;
case END_OF_STACK_MODE:
- r = (1-r)*0.2f;
- viewLp.setVerticalOffset(Math.round(r*mViewHeight));
- highlightLp.setVerticalOffset(Math.round(r*mViewHeight));
+ r = (1-r) * 0.2f;
+ viewLp.setVerticalOffset(Math.round(stackDirection * r * mViewHeight));
+ highlightLp.setVerticalOffset(Math.round(stackDirection * r * mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
break;
}
@@ -600,8 +652,8 @@ public class StackView extends AdapterViewAnimator {
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
r *= 0.2f;
- viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
- highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
+ viewLp.setHorizontalOffset(Math.round(r * mViewHeight));
+ highlightLp.setHorizontalOffset(Math.round(r * mViewHeight));
}
void setMode(int mode) {
@@ -609,31 +661,51 @@ public class StackView extends AdapterViewAnimator {
}
float getDurationForNeutralPosition() {
- return getDuration(false);
+ return getDuration(false, 0);
}
float getDurationForOffscreenPosition() {
- return getDuration(mMode == END_OF_STACK_MODE ? false : true);
+ return getDuration(true, 0);
+ }
+
+ float getDurationForNeutralPosition(float velocity) {
+ return getDuration(false, velocity);
+ }
+
+ float getDurationForOffscreenPosition(float velocity) {
+ return getDuration(true, velocity);
}
- private float getDuration(boolean invert) {
+ private float getDuration(boolean invert, float velocity) {
if (mView != null) {
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
- float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset,2) +
- Math.pow(viewLp.verticalOffset,2));
+ float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset, 2) +
+ Math.pow(viewLp.verticalOffset, 2));
float maxd = (float) Math.sqrt(Math.pow(mViewHeight, 2) +
- Math.pow(0.4f*mViewHeight, 2));
- return invert ? (1-d/maxd) : d/maxd;
+ Math.pow(0.4f * mViewHeight, 2));
+
+ if (velocity == 0) {
+ return (invert ? (1 - d / maxd) : d / maxd) * DEFAULT_ANIMATION_DURATION;
+ } else {
+ float duration = invert ? d / Math.abs(velocity) :
+ (maxd - d) / Math.abs(velocity);
+ if (duration < MINIMUM_ANIMATION_DURATION ||
+ duration > DEFAULT_ANIMATION_DURATION) {
+ return getDuration(invert, 0);
+ } else {
+ return duration;
+ }
+ }
}
return 0;
}
- float getYProgress() {
+ public float getYProgress() {
return mYProgress;
}
- float getXProgress() {
+ public float getXProgress() {
return mXProgress;
}
}
@@ -654,6 +726,8 @@ public class StackView extends AdapterViewAnimator {
LayoutParams lp = (LayoutParams) currentLp;
lp.setHorizontalOffset(0);
lp.setVerticalOffset(0);
+ lp.width = 0;
+ lp.width = 0;
return lp;
}
return new LayoutParams(v);
@@ -684,15 +758,59 @@ public class StackView extends AdapterViewAnimator {
child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
- //TODO: temp until fix in View
- child.setPivotX(child.getMeasuredWidth()/2);
- child.setPivotY(child.getMeasuredHeight()/2);
}
mDataChanged = false;
onLayout();
}
+ private void measureChildren() {
+ final int count = getChildCount();
+ final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+ final int childHeight = Math.round(mMeasuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR))
+ - mPaddingTop - mPaddingBottom;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+ // We need to deal with the case where our parent hasn't told us how
+ // big we should be. In this case we should
+ float factor = 1/(1 - PERSPECTIVE_SHIFT_FACTOR);
+ if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ heightSpecSize = haveChildRefSize ?
+ Math.round(mReferenceChildHeight * (1 + factor)) +
+ mPaddingTop + mPaddingBottom : 0;
+ } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+ heightSpecSize = haveChildRefSize ? Math.min(
+ Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop +
+ mPaddingBottom, heightSpecSize) : 0;
+ }
+
+ if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+ widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+ mPaddingRight : 0;
+ } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+ widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+ mPaddingRight, widthSpecSize) : 0;
+ }
+
+ setMeasuredDimension(widthSpecSize, heightSpecSize);
+ measureChildren();
+ }
+
class LayoutParams extends ViewGroup.LayoutParams {
int horizontalOffset;
int verticalOffset;
@@ -700,6 +818,8 @@ public class StackView extends AdapterViewAnimator {
LayoutParams(View view) {
super(0, 0);
+ width = 0;
+ height = 0;
horizontalOffset = 0;
verticalOffset = 0;
mView = view;
@@ -709,6 +829,8 @@ public class StackView extends AdapterViewAnimator {
super(c, attrs);
horizontalOffset = 0;
verticalOffset = 0;
+ width = 0;
+ height = 0;
}
private Rect parentRect = new Rect();
@@ -731,6 +853,10 @@ public class StackView extends AdapterViewAnimator {
gp = (View) p.getParent();
parentRect.set(p.getLeft() - gp.getScrollX(), p.getTop() - gp.getScrollY(),
p.getRight() - gp.getScrollX(), p.getBottom() - gp.getScrollY());
+
+ // TODO: we need to stop early here if we've hit the edge of the screen
+ // so as to prevent us from walking too high in the hierarchy. A lot of this
+ // code might become a lot more straightforward.
}
if (depth > mAncestorHeight) {
@@ -799,7 +925,7 @@ public class StackView extends AdapterViewAnimator {
private static class HolographicHelper {
private final Paint mHolographicPaint = new Paint();
private final Paint mErasePaint = new Paint();
- private final float STROKE_WIDTH = 3.0f;
+ private final Paint mBlurPaint = new Paint();
HolographicHelper() {
initializePaints();
@@ -808,8 +934,10 @@ public class StackView extends AdapterViewAnimator {
void initializePaints() {
mHolographicPaint.setColor(0xff6699ff);
mHolographicPaint.setFilterBitmap(true);
+ mHolographicPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mErasePaint.setFilterBitmap(true);
+ mBlurPaint.setMaskFilter(new BlurMaskFilter(2, BlurMaskFilter.Blur.NORMAL));
}
Bitmap createOutline(View v) {
@@ -822,31 +950,31 @@ public class StackView extends AdapterViewAnimator {
Canvas canvas = new Canvas(bitmap);
float rotationX = v.getRotationX();
+ float rotation = v.getRotation();
+ float translationY = v.getTranslationY();
v.setRotationX(0);
+ v.setRotation(0);
+ v.setTranslationY(0);
canvas.concat(v.getMatrix());
v.draw(canvas);
-
v.setRotationX(rotationX);
+ v.setRotation(rotation);
+ v.setTranslationY(translationY);
+ canvas.setMatrix(id);
drawOutline(canvas, bitmap);
return bitmap;
}
final Matrix id = new Matrix();
- final Matrix scaleMatrix = new Matrix();
void drawOutline(Canvas dest, Bitmap src) {
- Bitmap mask = src.extractAlpha();
-
+ int[] xy = new int[2];
+ Bitmap mask = src.extractAlpha(mBlurPaint, xy);
+ Canvas maskCanvas = new Canvas(mask);
+ maskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint);
dest.drawColor(0, PorterDuff.Mode.CLEAR);
-
- float xScale = STROKE_WIDTH*2/(dest.getWidth());
- float yScale = STROKE_WIDTH*2/(dest.getHeight());
-
- scaleMatrix.reset();
- scaleMatrix.preScale(1+xScale, 1+yScale, dest.getWidth()/2, dest.getHeight()/2);
dest.setMatrix(id);
- dest.drawBitmap(mask, scaleMatrix, mHolographicPaint);
- dest.drawBitmap(mask, id, mErasePaint);
+ dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
mask.recycle();
}
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 1048352..cf029d2 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -122,17 +122,6 @@ public class ActionBarImpl extends ActionBar {
}
@Override
- public void setStandardNavigationMode(int titleResId, int subtitleResId) {
- setStandardNavigationMode(mContext.getString(titleResId),
- mContext.getString(subtitleResId));
- }
-
- @Override
- public void setStandardNavigationMode(int titleResId) {
- setStandardNavigationMode(mContext.getString(titleResId));
- }
-
- @Override
public void setTitle(int resId) {
setTitle(mContext.getString(resId));
}
@@ -169,19 +158,6 @@ public class ActionBarImpl extends ActionBar {
mActionView.setCallback(null);
}
- public void setStandardNavigationMode(CharSequence title) {
- cleanupTabs();
- setStandardNavigationMode(title, null);
- }
-
- public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
- cleanupTabs();
- mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
- mActionView.setTitle(title);
- mActionView.setSubtitle(subtitle);
- mActionView.setCallback(null);
- }
-
public void setSelectedNavigationItem(int position) {
switch (mActionView.getNavigationMode()) {
case NAVIGATION_MODE_TABS:
@@ -211,17 +187,7 @@ public class ActionBarImpl extends ActionBar {
if (mSelectedTab != null) {
selectTab(null);
}
- if (!mTabs.isEmpty()) {
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- final FragmentTransaction trans = mActivity.openFragmentTransaction();
- final int tabCount = mTabs.size();
- for (int i = 0; i < tabCount; i++) {
- trans.remove(mTabs.get(i).getFragment());
- }
- trans.commit();
- }
- mTabs.clear();
- }
+ mTabs.clear();
}
public void setTitle(CharSequence title) {
@@ -295,31 +261,23 @@ public class ActionBarImpl extends ActionBar {
private void configureTab(Tab tab, int position) {
final TabImpl tabi = (TabImpl) tab;
final boolean isFirstTab = mTabs.isEmpty();
- final FragmentTransaction trans = mActivity.openFragmentTransaction();
- final Fragment frag = tabi.getFragment();
+ final ActionBar.TabListener callback = tabi.getCallback();
+
+ if (callback == null) {
+ throw new IllegalStateException("Action Bar Tab must have a Callback");
+ }
tabi.setPosition(position);
mTabs.add(position, tabi);
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- if (!frag.isAdded()) {
- trans.add(mTabContainerViewId, frag);
- }
- }
-
if (isFirstTab) {
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- trans.show(frag);
- } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
- trans.add(mTabContainerViewId, frag);
- }
+ final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction();
mSelectedTab = tabi;
- } else {
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- trans.hide(frag);
+ callback.onTabSelected(tab, trans);
+ if (!trans.isEmpty()) {
+ trans.commit();
}
}
- trans.commit();
}
@Override
@@ -329,8 +287,8 @@ public class ActionBarImpl extends ActionBar {
}
@Override
- public void insertTab(Tab tab, int position) {
- mActionView.insertTab(tab, position);
+ public void addTab(Tab tab, int position) {
+ mActionView.addTab(tab, position);
configureTab(tab, position);
}
@@ -367,35 +325,29 @@ public class ActionBarImpl extends ActionBar {
}
@Override
- public void setTabNavigationMode(int containerViewId) {
- mTabContainerViewId = containerViewId;
- setTabNavigationMode();
- }
-
- @Override
public void selectTab(Tab tab) {
if (mSelectedTab == tab) {
return;
}
mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
- final FragmentTransaction trans = mActivity.openFragmentTransaction();
+ final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction();
if (mSelectedTab != null) {
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- trans.hide(mSelectedTab.getFragment());
- } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
- trans.remove(mSelectedTab.getFragment());
- }
- }
- if (tab != null) {
- if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
- trans.show(tab.getFragment());
- } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
- trans.add(mTabContainerViewId, tab.getFragment());
- }
+ mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
}
mSelectedTab = (TabImpl) tab;
- trans.commit();
+ if (mSelectedTab != null) {
+ mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
+ }
+
+ if (!trans.isEmpty()) {
+ trans.commit();
+ }
+ }
+
+ @Override
+ public Tab getSelectedTab() {
+ return mSelectedTab;
}
@Override
@@ -542,14 +494,40 @@ public class ActionBarImpl extends ActionBar {
* @hide
*/
public class TabImpl extends ActionBar.Tab {
- private Fragment mFragment;
+ private ActionBar.TabListener mCallback;
+ private Object mTag;
private Drawable mIcon;
private CharSequence mText;
private int mPosition;
+ private View mCustomView;
@Override
- public Fragment getFragment() {
- return mFragment;
+ public Object getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setTag(Object tag) {
+ mTag = tag;
+ }
+
+ public ActionBar.TabListener getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void setTabListener(ActionBar.TabListener callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public View getCustomView() {
+ return mCustomView;
+ }
+
+ @Override
+ public void setCustomView(View view) {
+ mCustomView = view;
}
@Override
@@ -572,11 +550,6 @@ public class ActionBarImpl extends ActionBar {
}
@Override
- public void setFragment(Fragment fragment) {
- mFragment = fragment;
- }
-
- @Override
public void setIcon(Drawable icon) {
mIcon = icon;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0a1c8ff..a6a031a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3345,11 +3345,6 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
- public class Pid {
- long mWakeSum;
- long mWakeStart;
- }
-
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
@@ -3364,6 +3359,10 @@ public final class BatteryStatsImpl extends BatteryStats {
return ps;
}
+ public SparseArray<? extends Pid> getPidStats() {
+ return mPids;
+ }
+
public Pid getPidStatsLocked(int pid) {
Pid p = mPids.get(pid);
if (p == null) {
@@ -3588,6 +3587,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
@Override
+ public long getHistoryBaseTime() {
+ return mHistoryBaseTime;
+ }
+
+ @Override
public int getStartCount() {
return mStartCount;
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index b9e4e46..6b3d353 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -41,6 +41,7 @@ import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
+import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
@@ -87,6 +88,7 @@ public class ActionBarView extends ViewGroup {
private TextView mTitleView;
private TextView mSubtitleView;
private Spinner mSpinner;
+ private HorizontalScrollView mTabScrollView;
private LinearLayout mTabLayout;
private View mCustomNavView;
@@ -119,6 +121,8 @@ public class ActionBarView extends ViewGroup {
private OnClickListener mHomeClickListener = null;
+ private OnClickListener mTabClickListener = null;
+
public ActionBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -346,8 +350,9 @@ public class ActionBarView extends ViewGroup {
break;
case ActionBar.NAVIGATION_MODE_TABS:
if (mTabLayout != null) {
- removeView(mTabLayout);
+ removeView(mTabScrollView);
mTabLayout = null;
+ mTabScrollView = null;
}
}
@@ -365,8 +370,10 @@ public class ActionBarView extends ViewGroup {
addView(mCustomNavView);
break;
case ActionBar.NAVIGATION_MODE_TABS:
+ mTabScrollView = new HorizontalScrollView(getContext());
mTabLayout = new LinearLayout(getContext());
- addView(mTabLayout);
+ mTabScrollView.addView(mTabLayout);
+ addView(mTabScrollView);
break;
}
mNavigationMode = mode;
@@ -401,20 +408,24 @@ public class ActionBarView extends ViewGroup {
private TabView createTabView(ActionBar.Tab tab) {
final TabView tabView = new TabView(getContext(), tab);
tabView.setFocusable(true);
- tabView.setOnClickListener(new TabClickListener());
+
+ if (mTabClickListener == null) {
+ mTabClickListener = new TabClickListener();
+ }
+ tabView.setOnClickListener(mTabClickListener);
return tabView;
}
public void addTab(ActionBar.Tab tab) {
final boolean isFirst = mTabLayout.getChildCount() == 0;
- final TabView tabView = createTabView(tab);
+ View tabView = createTabView(tab);
mTabLayout.addView(tabView);
if (isFirst) {
tabView.setSelected(true);
}
}
- public void insertTab(ActionBar.Tab tab, int position) {
+ public void addTab(ActionBar.Tab tab, int position) {
final boolean isFirst = mTabLayout.getChildCount() == 0;
final TabView tabView = createTabView(tab);
mTabLayout.addView(tabView, position);
@@ -595,8 +606,8 @@ public class ActionBarView extends ViewGroup {
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
- if (mTabLayout != null) {
- mTabLayout.measure(
+ if (mTabScrollView != null) {
+ mTabScrollView.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
@@ -663,8 +674,8 @@ public class ActionBarView extends ViewGroup {
}
break;
case ActionBar.NAVIGATION_MODE_TABS:
- if (mTabLayout != null) {
- x += positionChild(mTabLayout, x, y, contentHeight) + mSpacing;
+ if (mTabScrollView != null) {
+ x += positionChild(mTabScrollView, x, y, contentHeight) + mSpacing;
}
}
@@ -703,31 +714,36 @@ public class ActionBarView extends ViewGroup {
super(context);
mTab = tab;
- // TODO Style tabs based on the theme
-
- final Drawable icon = tab.getIcon();
- final CharSequence text = tab.getText();
-
- if (icon != null) {
- ImageView iconView = new ImageView(context);
- iconView.setImageDrawable(icon);
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- iconView.setLayoutParams(lp);
- addView(iconView);
- }
+ final View custom = tab.getCustomView();
+ if (custom != null) {
+ addView(custom);
+ } else {
+ // TODO Style tabs based on the theme
+
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (icon != null) {
+ ImageView iconView = new ImageView(context);
+ iconView.setImageDrawable(icon);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ iconView.setLayoutParams(lp);
+ addView(iconView);
+ }
- if (text != null) {
- TextView textView = new TextView(context);
- textView.setText(text);
- textView.setSingleLine();
- textView.setEllipsize(TruncateAt.END);
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- textView.setLayoutParams(lp);
- addView(textView);
+ if (text != null) {
+ TextView textView = new TextView(context);
+ textView.setText(text);
+ textView.setSingleLine();
+ textView.setEllipsize(TruncateAt.END);
+ LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ textView.setLayoutParams(lp);
+ addView(textView);
+ }
}
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,